aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cirrus.yml25
-rw-r--r--Makefile.am15
-rw-r--r--build-aux/m4/bitcoin_find_bdb48.m419
-rw-r--r--build_msvc/bitcoin-qt/bitcoin-qt.vcxproj2
-rw-r--r--build_msvc/bitcoin-wallet/bitcoin-wallet.vcxproj3
-rw-r--r--build_msvc/test_bitcoin-qt/test_bitcoin-qt.vcxproj2
-rwxr-xr-xci/lint/04_install.sh3
-rwxr-xr-xci/test/00_setup_env_android.sh2
-rwxr-xr-xci/test/00_setup_env_native_valgrind.sh2
-rwxr-xr-xci/test/06_script_a.sh2
-rwxr-xr-xci/test/06_script_b.sh2
-rw-r--r--configure.ac90
-rw-r--r--contrib/devtools/pixie.py323
-rwxr-xr-xcontrib/devtools/security-check.py183
-rwxr-xr-xcontrib/devtools/symbol-check.py163
-rwxr-xr-xcontrib/devtools/test-security-check.py12
-rwxr-xr-xcontrib/devtools/test-symbol-check.py15
-rwxr-xr-xcontrib/guix/libexec/build.sh6
-rw-r--r--contrib/tracing/README.md7
-rwxr-xr-xcontrib/tracing/connectblock_benchmark.bt22
-rw-r--r--depends/packages/expat.mk2
-rw-r--r--depends/packages/fontconfig.mk2
-rw-r--r--depends/packages/freetype.mk2
-rw-r--r--depends/packages/libXau.mk2
-rw-r--r--depends/packages/qt.mk1
-rw-r--r--doc/README.md1
-rw-r--r--doc/assumeutxo.md138
-rw-r--r--doc/bips.md8
-rw-r--r--doc/descriptors.md41
-rw-r--r--doc/developer-notes.md30
-rw-r--r--doc/psbt.md3
-rw-r--r--doc/release-notes-22539.md8
-rw-r--r--doc/release-notes-23093.md11
-rw-r--r--doc/release-notes.md14
-rw-r--r--doc/release-process.md6
-rw-r--r--doc/tracing.md9
-rw-r--r--src/Makefile.am41
-rw-r--r--src/Makefile.leveldb.include5
-rw-r--r--src/Makefile.qt.include20
-rw-r--r--src/Makefile.qttest.include2
-rw-r--r--src/Makefile.test.include22
-rw-r--r--src/Makefile.univalue.include6
-rw-r--r--src/addrdb.cpp20
-rw-r--r--src/addrman.cpp207
-rw-r--r--src/addrman.h10
-rw-r--r--src/addrman_impl.h15
-rw-r--r--src/banman.h2
-rw-r--r--src/bench/bench_bitcoin.cpp4
-rw-r--r--src/bench/coin_selection.cpp11
-rw-r--r--src/bench/mempool_stress.cpp43
-rw-r--r--src/bench/rollingbloom.cpp2
-rw-r--r--src/bench/rpc_blockchain.cpp4
-rw-r--r--src/bench/wallet_balance.cpp16
-rw-r--r--src/bitcoin-cli.cpp29
-rw-r--r--src/bitcoin-tx.cpp19
-rw-r--r--src/bitcoin-wallet.cpp13
-rw-r--r--src/chain.h2
-rw-r--r--src/chainparams.cpp28
-rw-r--r--src/chainparamsbase.cpp4
-rw-r--r--src/common/bloom.cpp (renamed from src/bloom.cpp)4
-rw-r--r--src/common/bloom.h (renamed from src/bloom.h)6
-rw-r--r--src/consensus/amount.h1
-rw-r--r--src/core_io.h11
-rw-r--r--src/core_read.cpp53
-rw-r--r--src/core_write.cpp31
-rw-r--r--src/crypto/chacha_poly_aead.h4
-rw-r--r--src/dbwrapper.cpp26
-rw-r--r--src/external_signer.cpp12
-rw-r--r--src/flatfile.cpp4
-rw-r--r--src/fs.cpp9
-rw-r--r--src/fs.h132
-rw-r--r--src/hash.cpp5
-rw-r--r--src/i2p.cpp2
-rw-r--r--src/index/base.cpp2
-rw-r--r--src/init.cpp46
-rw-r--r--src/init/bitcoin-gui.cpp47
-rw-r--r--src/init/bitcoin-qt.cpp42
-rw-r--r--src/init/bitcoin-wallet.cpp12
-rw-r--r--src/init/common.cpp14
-rw-r--r--src/interfaces/chain.h2
-rw-r--r--src/ipc/process.cpp4
-rw-r--r--src/key.cpp5
-rw-r--r--src/logging.cpp1
-rw-r--r--src/logging.h1
-rw-r--r--src/merkleblock.h4
-rw-r--r--src/net.cpp50
-rw-r--r--src/net.h8
-rw-r--r--src/net_processing.cpp28
-rw-r--r--src/netaddress.h36
-rw-r--r--src/node/blockstorage.cpp19
-rw-r--r--src/node/interfaces.cpp4
-rw-r--r--src/policy/fees.cpp6
-rw-r--r--src/policy/policy.cpp7
-rw-r--r--src/policy/rbf.cpp4
-rw-r--r--src/primitives/transaction.h5
-rw-r--r--src/protocol.cpp2
-rw-r--r--src/pubkey.cpp5
-rw-r--r--src/qt/bitcoin.cpp22
-rw-r--r--src/qt/bitcoingui.cpp2
-rw-r--r--src/qt/forms/createwalletdialog.ui3
-rw-r--r--src/qt/guiutil.cpp10
-rw-r--r--src/qt/guiutil.h5
-rw-r--r--src/qt/intro.cpp2
-rw-r--r--src/qt/splashscreen.cpp4
-rw-r--r--src/qt/test/addressbooktests.cpp8
-rw-r--r--src/qt/test/test_main.cpp4
-rw-r--r--src/qt/test/wallettests.cpp19
-rw-r--r--src/rest.cpp20
-rw-r--r--src/rpc/blockchain.cpp63
-rw-r--r--src/rpc/blockchain.h3
-rw-r--r--src/rpc/request.cpp8
-rw-r--r--src/rpc/server.cpp2
-rw-r--r--src/support/allocators/secure.h17
-rw-r--r--src/support/allocators/zeroafterfree.h16
-rw-r--r--src/test/addrman_tests.cpp24
-rw-r--r--src/test/bloom_tests.cpp2
-rw-r--r--src/test/checkqueue_tests.cpp12
-rw-r--r--src/test/crypto_tests.cpp4
-rw-r--r--src/test/denialofservice_tests.cpp2
-rw-r--r--src/test/fs_tests.cpp27
-rw-r--r--src/test/fuzz/addrman.cpp33
-rw-r--r--src/test/fuzz/banman.cpp4
-rw-r--r--src/test/fuzz/bloom_filter.cpp2
-rw-r--r--src/test/fuzz/p2p_transport_serialization.cpp20
-rw-r--r--src/test/fuzz/rolling_bloom_filter.cpp2
-rw-r--r--src/test/fuzz/tx_pool.cpp4
-rw-r--r--src/test/fuzz/txrequest.cpp4
-rw-r--r--src/test/getarg_tests.cpp4
-rw-r--r--src/test/mempool_tests.cpp36
-rw-r--r--src/test/script_parse_tests.cpp2
-rw-r--r--src/test/settings_tests.cpp6
-rw-r--r--src/test/txrequest_tests.cpp6
-rw-r--r--src/test/util/chainstate.h2
-rw-r--r--src/test/util/net.h6
-rw-r--r--src/test/util/setup_common.cpp4
-rw-r--r--src/test/util/wallet.cpp12
-rw-r--r--src/test/util_tests.cpp14
-rw-r--r--src/test/validation_chainstate_tests.cpp23
-rw-r--r--src/torcontrol.cpp8
-rw-r--r--src/txmempool.cpp115
-rw-r--r--src/txmempool.h60
-rw-r--r--src/txrequest.cpp2
-rw-r--r--src/univalue/.cirrus.yml44
-rw-r--r--src/univalue/.travis.yml51
-rw-r--r--src/univalue/Makefile.am90
-rw-r--r--src/univalue/build-aux/m4/ax_cxx_compile_stdcxx.m4962
-rw-r--r--src/univalue/configure.ac9
-rw-r--r--src/univalue/gen/gen.cpp4
-rw-r--r--src/univalue/include/univalue.h4
-rw-r--r--src/univalue/lib/univalue.cpp16
-rw-r--r--src/univalue/lib/univalue_escapes.h442
-rw-r--r--src/univalue/lib/univalue_get.cpp7
-rw-r--r--src/univalue/lib/univalue_read.cpp6
-rw-r--r--src/univalue/lib/univalue_utffilter.h2
-rw-r--r--src/univalue/lib/univalue_write.cpp7
-rw-r--r--src/univalue/sources.mk95
-rw-r--r--src/univalue/test/object.cpp2
-rw-r--r--src/univalue/test/unitester.cpp4
-rw-r--r--src/util/asmap.cpp4
-rw-r--r--src/util/settings.cpp12
-rw-r--r--src/util/strencodings.h4
-rw-r--r--src/util/syscall_sandbox.cpp46
-rw-r--r--src/util/system.cpp129
-rw-r--r--src/util/system.h16
-rw-r--r--src/validation.cpp45
-rw-r--r--src/validation.h5
-rw-r--r--src/wallet/bdb.cpp40
-rw-r--r--src/wallet/bdb.h4
-rw-r--r--src/wallet/db.cpp15
-rw-r--r--src/wallet/dump.cpp10
-rw-r--r--src/wallet/init.cpp2
-rw-r--r--src/wallet/interfaces.cpp4
-rw-r--r--src/wallet/load.cpp16
-rw-r--r--src/wallet/rpcdump.cpp10
-rw-r--r--src/wallet/rpcwallet.cpp98
-rw-r--r--src/wallet/scriptpubkeyman.cpp2
-rw-r--r--src/wallet/sqlite.cpp6
-rw-r--r--src/wallet/test/coinselector_tests.cpp417
-rw-r--r--src/wallet/test/db_tests.cpp8
-rw-r--r--src/wallet/test/init_test_fixture.cpp8
-rw-r--r--src/wallet/test/init_tests.cpp8
-rw-r--r--src/wallet/test/psbt_wallet_tests.cpp44
-rw-r--r--src/wallet/test/spend_tests.cpp2
-rw-r--r--src/wallet/test/util.cpp14
-rw-r--r--src/wallet/test/wallet_test_fixture.cpp7
-rw-r--r--src/wallet/test/wallet_test_fixture.h1
-rw-r--r--src/wallet/test/wallet_tests.cpp62
-rw-r--r--src/wallet/transaction.h3
-rw-r--r--src/wallet/wallet.cpp23
-rw-r--r--src/wallet/wallet.h8
-rw-r--r--src/wallet/walletdb.cpp22
-rw-r--r--src/wallet/wallettool.cpp28
-rw-r--r--src/wallet/walletutil.cpp2
-rw-r--r--test/README.md15
-rwxr-xr-xtest/functional/combine_logs.py2
-rw-r--r--test/functional/data/__init__.py0
-rwxr-xr-xtest/functional/feature_addrman.py4
-rwxr-xr-xtest/functional/feature_filelock.py2
-rwxr-xr-xtest/functional/feature_rbf.py2
-rwxr-xr-xtest/functional/feature_segwit.py600
-rwxr-xr-xtest/functional/interface_rest.py16
-rwxr-xr-xtest/functional/mempool_accept.py6
-rwxr-xr-xtest/functional/mempool_limit.py27
-rwxr-xr-xtest/functional/mempool_package_limits.py2
-rwxr-xr-xtest/functional/mempool_packages.py3
-rwxr-xr-xtest/functional/mining_prioritisetransaction.py2
-rwxr-xr-xtest/functional/p2p_addr_relay.py1
-rwxr-xr-xtest/functional/p2p_addrv2_relay.py3
-rwxr-xr-xtest/functional/p2p_invalid_messages.py2
-rwxr-xr-xtest/functional/p2p_ping.py14
-rwxr-xr-xtest/functional/p2p_segwit.py60
-rwxr-xr-xtest/functional/rpc_blockchain.py66
-rwxr-xr-xtest/functional/rpc_invalid_address_message.py12
-rwxr-xr-xtest/functional/rpc_misc.py2
-rw-r--r--test/functional/test_framework/blocktools.py6
-rwxr-xr-xtest/functional/test_framework/script_util.py13
-rwxr-xr-xtest/functional/test_framework/test_framework.py16
-rwxr-xr-xtest/functional/test_framework/test_node.py16
-rw-r--r--test/functional/test_framework/wallet.py15
-rwxr-xr-xtest/functional/test_framework/wallet_util.py9
-rwxr-xr-xtest/functional/test_runner.py4
-rwxr-xr-xtest/functional/tool_wallet.py6
-rwxr-xr-xtest/functional/wallet_abandonconflict.py10
-rwxr-xr-xtest/functional/wallet_address_types.py3
-rwxr-xr-xtest/functional/wallet_backup.py6
-rwxr-xr-xtest/functional/wallet_basic.py2
-rwxr-xr-xtest/functional/wallet_bumpfee.py10
-rwxr-xr-xtest/functional/wallet_create_tx.py4
-rwxr-xr-xtest/functional/wallet_descriptor.py2
-rwxr-xr-xtest/functional/wallet_hd.py2
-rwxr-xr-xtest/functional/wallet_import_rescan.py2
-rwxr-xr-xtest/functional/wallet_importdescriptors.py16
-rwxr-xr-xtest/functional/wallet_keypool.py14
-rwxr-xr-xtest/functional/wallet_listdescriptors.py2
-rwxr-xr-xtest/functional/wallet_listsinceblock.py11
-rwxr-xr-xtest/functional/wallet_listtransactions.py13
-rwxr-xr-xtest/functional/wallet_multisig_descriptor_psbt.py163
-rwxr-xr-xtest/functional/wallet_multiwallet.py4
-rwxr-xr-xtest/functional/wallet_taproot.py2
-rwxr-xr-xtest/functional/wallet_transactiontime_rescan.py8
-rwxr-xr-xtest/functional/wallet_upgradewallet.py20
-rwxr-xr-xtest/lint/lint-locale-dependence.sh5
-rwxr-xr-xtest/lint/lint-python.sh2
-rw-r--r--test/sanitizer_suppressions/ubsan4
-rw-r--r--test/util/data/bitcoin-util-test.json54
245 files changed, 4424 insertions, 2734 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
index 5491f2c04b..f7962e951c 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -79,14 +79,15 @@ task:
<< : *FILTER_TEMPLATE
windows_container:
cpu: 4
- memory: 16G
+ memory: 8G
image: cirrusci/windowsservercore:visualstudio2019
timeout_in: 120m
env:
PATH: 'C:\jom;C:\Python39;C:\Python39\Scripts;C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\MSBuild\Current\Bin;%PATH%'
PYTHONUTF8: 1
- VCPKG_TAG: '75522bb1f2e7d863078bcd06322348f053a9e33f'
- VCPKG_FEATURE_FLAGS: 'manifests'
+ CI_VCPKG_TAG: '2021.05.12'
+ VCPKG_DOWNLOADS: 'C:\Users\ContainerAdministrator\AppData\Local\vcpkg\downloads'
+ VCPKG_DEFAULT_BINARY_CACHE: 'C:\Users\ContainerAdministrator\AppData\Local\vcpkg\archives'
QT_DOWNLOAD_URL: 'https://download.qt.io/official_releases/qt/5.12/5.12.11/single/qt-everywhere-src-5.12.11.zip'
QT_LOCAL_PATH: 'C:\qt-everywhere-src-5.12.11.zip'
QT_SOURCE_DIR: 'C:\qt-everywhere-src-5.12.11'
@@ -121,8 +122,20 @@ task:
- ..\configure -release -silent -opensource -confirm-license -opengl desktop -no-shared -static -static-runtime -mp -qt-zlib -qt-pcre -qt-libpng -no-libjpeg -nomake examples -nomake tests -nomake tools -no-dbus -no-libudev -no-icu -no-gtk -no-opengles3 -no-angle -no-sql-sqlite -no-sql-odbc -no-sqlite -no-libudev -no-vulkan -skip qt3d -skip qtactiveqt -skip qtandroidextras -skip qtcanvas3d -skip qtcharts -skip qtconnectivity -skip qtdatavis3d -skip qtdeclarative -skip qtdoc -skip qtgamepad -skip qtgraphicaleffects -skip qtimageformats -skip qtlocation -skip qtmacextras -skip qtmultimedia -skip qtnetworkauth -skip qtpurchasing -skip qtquickcontrols -skip qtquickcontrols2 -skip qtscript -skip qtscxml -skip qtsensors -skip qtserialbus -skip qtserialport -skip qtspeech -skip qtvirtualkeyboard -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebsockets -skip qtwebview -skip qtx11extras -skip qtxmlpatterns -no-openssl -no-feature-sql -no-feature-sqlmodel -prefix %QTBASEDIR%
- jom
- jom install
- vcpkg_cache:
- folder: 'C:\Users\ContainerAdministrator\AppData\Local\vcpkg\archives'
+ vcpkg_tools_cache:
+ folder: '%VCPKG_DOWNLOADS%\tools'
+ reupload_on_changes: false
+ fingerprint_script:
+ - echo %CI_VCPKG_TAG%
+ - msbuild -version
+ vcpkg_binary_cache:
+ folder: '%VCPKG_DEFAULT_BINARY_CACHE%'
+ reupload_on_changes: true
+ fingerprint_script:
+ - echo %CI_VCPKG_TAG%
+ - msbuild -version
+ populate_script:
+ - mkdir %VCPKG_DEFAULT_BINARY_CACHE%
install_python_script:
- choco install --yes --no-progress python3 --version=3.9.6
- pip install zmq
@@ -131,7 +144,7 @@ task:
- cd ..
- git clone --quiet https://github.com/microsoft/vcpkg.git
- cd vcpkg
- - git -c advice.detachedHead=false checkout %VCPKG_TAG%
+ - git -c advice.detachedHead=false checkout %CI_VCPKG_TAG%
- .\bootstrap-vcpkg -disableMetrics
- echo set(VCPKG_BUILD_TYPE release) >> triplets\x64-windows-static.cmake
- .\vcpkg integrate install
diff --git a/Makefile.am b/Makefile.am
index ce66331910..af63cf0cbb 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -58,8 +58,7 @@ DIST_SHARE = \
BIN_CHECKS=$(top_srcdir)/contrib/devtools/symbol-check.py \
$(top_srcdir)/contrib/devtools/security-check.py \
- $(top_srcdir)/contrib/devtools/utils.py \
- $(top_srcdir)/contrib/devtools/pixie.py
+ $(top_srcdir)/contrib/devtools/utils.py
WINDOWS_PACKAGING = $(top_srcdir)/share/pixmaps/bitcoin.ico \
$(top_srcdir)/share/pixmaps/nsis-header.bmp \
@@ -367,14 +366,14 @@ clean-local: clean-docs
test-security-check:
if TARGET_DARWIN
- $(AM_V_at) CC='$(CC)' $(PYTHON) $(top_srcdir)/contrib/devtools/test-security-check.py TestSecurityChecks.test_MACHO
- $(AM_V_at) CC='$(CC)' $(PYTHON) $(top_srcdir)/contrib/devtools/test-symbol-check.py TestSymbolChecks.test_MACHO
+ $(AM_V_at) CC='$(CC)' CFLAGS='$(CFLAGS)' CPPFLAGS='$(CPPFLAGS)' LDFLAGS='$(LDFLAGS)' $(PYTHON) $(top_srcdir)/contrib/devtools/test-security-check.py TestSecurityChecks.test_MACHO
+ $(AM_V_at) CC='$(CC)' CFLAGS='$(CFLAGS)' CPPFLAGS='$(CPPFLAGS)' LDFLAGS='$(LDFLAGS)' $(PYTHON) $(top_srcdir)/contrib/devtools/test-symbol-check.py TestSymbolChecks.test_MACHO
endif
if TARGET_WINDOWS
- $(AM_V_at) CC='$(CC)' $(PYTHON) $(top_srcdir)/contrib/devtools/test-security-check.py TestSecurityChecks.test_PE
- $(AM_V_at) CC='$(CC)' $(PYTHON) $(top_srcdir)/contrib/devtools/test-symbol-check.py TestSymbolChecks.test_PE
+ $(AM_V_at) CC='$(CC)' CFLAGS='$(CFLAGS)' CPPFLAGS='$(CPPFLAGS)' LDFLAGS='$(LDFLAGS)' $(PYTHON) $(top_srcdir)/contrib/devtools/test-security-check.py TestSecurityChecks.test_PE
+ $(AM_V_at) CC='$(CC)' CFLAGS='$(CFLAGS)' CPPFLAGS='$(CPPFLAGS)' LDFLAGS='$(LDFLAGS)' $(PYTHON) $(top_srcdir)/contrib/devtools/test-symbol-check.py TestSymbolChecks.test_PE
endif
if TARGET_LINUX
- $(AM_V_at) CC='$(CC)' $(PYTHON) $(top_srcdir)/contrib/devtools/test-security-check.py TestSecurityChecks.test_ELF
- $(AM_V_at) CC='$(CC)' CPPFILT='$(CPPFILT)' $(PYTHON) $(top_srcdir)/contrib/devtools/test-symbol-check.py TestSymbolChecks.test_ELF
+ $(AM_V_at) CC='$(CC)' CFLAGS='$(CFLAGS)' CPPFLAGS='$(CPPFLAGS)' LDFLAGS='$(LDFLAGS)' $(PYTHON) $(top_srcdir)/contrib/devtools/test-security-check.py TestSecurityChecks.test_ELF
+ $(AM_V_at) CC='$(CC)' CFLAGS='$(CFLAGS)' CPPFLAGS='$(CPPFLAGS)' LDFLAGS='$(LDFLAGS)' $(PYTHON) $(top_srcdir)/contrib/devtools/test-symbol-check.py TestSymbolChecks.test_ELF
endif
diff --git a/build-aux/m4/bitcoin_find_bdb48.m4 b/build-aux/m4/bitcoin_find_bdb48.m4
index 5fc5b493d3..3d6c8210ed 100644
--- a/build-aux/m4/bitcoin_find_bdb48.m4
+++ b/build-aux/m4/bitcoin_find_bdb48.m4
@@ -48,15 +48,22 @@ AC_DEFUN([BITCOIN_FIND_BDB48],[
if test "x$bdbpath" = "xX"; then
use_bdb=no
AC_MSG_RESULT([no])
- AC_MSG_ERROR([libdb_cxx headers missing, ]AC_PACKAGE_NAME[ requires this library for BDB wallet support (--without-bdb to disable BDB wallet support)])
+ AC_MSG_WARN([libdb_cxx headers missing])
+ AC_MSG_WARN(AC_PACKAGE_NAME[ requires this library for BDB (legacy) wallet support])
+ AC_MSG_WARN([Passing --without-bdb will suppress this warning])
elif test "x$bdb48path" = "xX"; then
BITCOIN_SUBDIR_TO_INCLUDE(BDB_CPPFLAGS,[${bdbpath}],db_cxx)
AC_ARG_WITH([incompatible-bdb],[AS_HELP_STRING([--with-incompatible-bdb], [allow using a bdb version other than 4.8])],[
- AC_MSG_WARN([Found Berkeley DB other than 4.8; BDB wallets opened by this build will not be portable!])
+ AC_MSG_WARN([Found Berkeley DB other than 4.8])
+ AC_MSG_WARN([BDB (legacy) wallets opened by this build will not be portable!])
+ use_bdb=yes
],[
- AC_MSG_ERROR([Found Berkeley DB other than 4.8, required for portable BDB wallets (--with-incompatible-bdb to ignore or --without-bdb to disable BDB wallet support)])
+ AC_MSG_WARN([Found Berkeley DB other than 4.8])
+ AC_MSG_WARN([BDB (legacy) wallets opened by this build would not be portable!])
+ AC_MSG_WARN([If this is intended, pass --with-incompatible-bdb])
+ AC_MSG_WARN([Passing --without-bdb will suppress this warning])
+ use_bdb=no
])
- use_bdb=yes
else
BITCOIN_SUBDIR_TO_INCLUDE(BDB_CPPFLAGS,[${bdb48path}],db_cxx)
bdbpath="${bdb48path}"
@@ -78,7 +85,9 @@ AC_DEFUN([BITCOIN_FIND_BDB48],[
])
done
if test "x$BDB_LIBS" = "x"; then
- AC_MSG_ERROR([libdb_cxx missing, ]AC_PACKAGE_NAME[ requires this library for BDB wallet support (--without-bdb to disable BDB wallet support)])
+ AC_MSG_WARN([libdb_cxx headers missing])
+ AC_MSG_WARN(AC_PACKAGE_NAME[ requires this library for BDB (legacy) wallet support])
+ AC_MSG_WARN([Passing --without-bdb will suppress this warning])
fi
fi
if test "x$use_bdb" != "xno"; then
diff --git a/build_msvc/bitcoin-qt/bitcoin-qt.vcxproj b/build_msvc/bitcoin-qt/bitcoin-qt.vcxproj
index 724dae1969..2800a42767 100644
--- a/build_msvc/bitcoin-qt/bitcoin-qt.vcxproj
+++ b/build_msvc/bitcoin-qt/bitcoin-qt.vcxproj
@@ -9,7 +9,7 @@
</PropertyGroup>
<ItemGroup>
<ClCompile Include="..\..\src\qt\main.cpp" />
- <ClCompile Include="..\..\src\init\bitcoind.cpp" />
+ <ClCompile Include="..\..\src\init\bitcoin-qt.cpp" />
<ResourceCompile Include="..\..\src\qt\res\bitcoin-qt-res.rc" />
</ItemGroup>
<ItemGroup>
diff --git a/build_msvc/bitcoin-wallet/bitcoin-wallet.vcxproj b/build_msvc/bitcoin-wallet/bitcoin-wallet.vcxproj
index 40c5db5522..affb60425b 100644
--- a/build_msvc/bitcoin-wallet/bitcoin-wallet.vcxproj
+++ b/build_msvc/bitcoin-wallet/bitcoin-wallet.vcxproj
@@ -10,6 +10,9 @@
</PropertyGroup>
<ItemGroup>
<ClCompile Include="..\..\src\bitcoin-wallet.cpp" />
+ <ClCompile Include="..\..\src\init\bitcoin-wallet.cpp">
+ <ObjectFileName>$(IntDir)init_bitcoin-wallet.obj</ObjectFileName>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\libbitcoinconsensus\libbitcoinconsensus.vcxproj">
diff --git a/build_msvc/test_bitcoin-qt/test_bitcoin-qt.vcxproj b/build_msvc/test_bitcoin-qt/test_bitcoin-qt.vcxproj
index 08b12bdd85..f9948b6f13 100644
--- a/build_msvc/test_bitcoin-qt/test_bitcoin-qt.vcxproj
+++ b/build_msvc/test_bitcoin-qt/test_bitcoin-qt.vcxproj
@@ -8,7 +8,7 @@
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
</PropertyGroup>
<ItemGroup>
- <ClCompile Include="..\..\src\init\bitcoind.cpp" />
+ <ClCompile Include="..\..\src\init\bitcoin-qt.cpp" />
<ClCompile Include="..\..\src\test\util\setup_common.cpp" />
<ClCompile Include="..\..\src\qt\test\addressbooktests.cpp" />
<ClCompile Include="..\..\src\qt\test\apptests.cpp" />
diff --git a/ci/lint/04_install.sh b/ci/lint/04_install.sh
index 5587618f2d..991234a436 100755
--- a/ci/lint/04_install.sh
+++ b/ci/lint/04_install.sh
@@ -13,7 +13,8 @@ update-alternatives --install /usr/bin/clang-format-diff clang-format-diff $(whi
${CI_RETRY_EXE} pip3 install codespell==2.0.0
${CI_RETRY_EXE} pip3 install flake8==3.8.3
-${CI_RETRY_EXE} pip3 install mypy==0.781
+${CI_RETRY_EXE} pip3 install mypy==0.910
+${CI_RETRY_EXE} pip3 install pyzmq==22.3.0
${CI_RETRY_EXE} pip3 install vulture==2.3
SHELLCHECK_VERSION=v0.7.2
diff --git a/ci/test/00_setup_env_android.sh b/ci/test/00_setup_env_android.sh
index 4ef3ae1ceb..2f9d1f2a9f 100755
--- a/ci/test/00_setup_env_android.sh
+++ b/ci/test/00_setup_env_android.sh
@@ -22,4 +22,4 @@ export ANDROID_HOME="${DEPENDS_DIR}/SDKs/android"
export ANDROID_NDK_HOME="${ANDROID_HOME}/ndk/${ANDROID_NDK_VERSION}"
export DEP_OPTS="ANDROID_SDK=${ANDROID_HOME} ANDROID_NDK=${ANDROID_NDK_HOME} ANDROID_API_LEVEL=${ANDROID_API_LEVEL} ANDROID_TOOLCHAIN_BIN=${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/"
-export BITCOIN_CONFIG="--disable-ccache"
+export BITCOIN_CONFIG="--disable-ccache --disable-tests --enable-gui-tests --disable-bench --disable-fuzz-binary --without-utils --without-libs --without-daemon"
diff --git a/ci/test/00_setup_env_native_valgrind.sh b/ci/test/00_setup_env_native_valgrind.sh
index 78af869e70..0058a042f5 100755
--- a/ci/test/00_setup_env_native_valgrind.sh
+++ b/ci/test/00_setup_env_native_valgrind.sh
@@ -11,6 +11,6 @@ export CONTAINER_NAME=ci_native_valgrind
export PACKAGES="valgrind clang llvm python3-zmq libevent-dev bsdmainutils libboost-dev libboost-system-dev libboost-filesystem-dev libboost-test-dev libdb5.3++-dev libminiupnpc-dev libnatpmp-dev libzmq3-dev libsqlite3-dev"
export USE_VALGRIND=1
export NO_DEPENDS=1
-export TEST_RUNNER_EXTRA="--exclude rpc_bind,feature_bind_extra" # Excluded for now, see https://github.com/bitcoin/bitcoin/issues/17765#issuecomment-602068547
+export TEST_RUNNER_EXTRA="--nosandbox --exclude rpc_bind,feature_bind_extra" # Excluded for now, see https://github.com/bitcoin/bitcoin/issues/17765#issuecomment-602068547
export GOAL="install"
export BITCOIN_CONFIG="--enable-zmq --with-incompatible-bdb --with-gui=no CC=clang CXX=clang++" # TODO enable GUI
diff --git a/ci/test/06_script_a.sh b/ci/test/06_script_a.sh
index a42cd6cee1..b1d83883d1 100755
--- a/ci/test/06_script_a.sh
+++ b/ci/test/06_script_a.sh
@@ -10,7 +10,7 @@ if [ -n "$ANDROID_TOOLS_URL" ]; then
DOCKER_EXEC make distclean || true
DOCKER_EXEC ./autogen.sh
DOCKER_EXEC ./configure $BITCOIN_CONFIG --prefix=$DEPENDS_DIR/aarch64-linux-android || ( (DOCKER_EXEC cat config.log) && false)
- DOCKER_EXEC "cd src/qt && make $MAKEJOBS && ANDROID_HOME=${ANDROID_HOME} ANDROID_NDK_HOME=${ANDROID_NDK_HOME} make apk"
+ DOCKER_EXEC "make $MAKEJOBS && cd src/qt && ANDROID_HOME=${ANDROID_HOME} ANDROID_NDK_HOME=${ANDROID_NDK_HOME} make apk"
exit 0
fi
diff --git a/ci/test/06_script_b.sh b/ci/test/06_script_b.sh
index 194b14beab..311a43755a 100755
--- a/ci/test/06_script_b.sh
+++ b/ci/test/06_script_b.sh
@@ -9,14 +9,12 @@ export LC_ALL=C.UTF-8
if [[ $HOST = *-mingw32 ]]; then
# Generate all binaries, so that they can be wrapped
DOCKER_EXEC make $MAKEJOBS -C src/secp256k1 VERBOSE=1
- DOCKER_EXEC make $MAKEJOBS -C src/univalue VERBOSE=1
DOCKER_EXEC "${BASE_ROOT_DIR}/ci/test/wrap-wine.sh"
fi
if [ -n "$QEMU_USER_CMD" ]; then
# Generate all binaries, so that they can be wrapped
DOCKER_EXEC make $MAKEJOBS -C src/secp256k1 VERBOSE=1
- DOCKER_EXEC make $MAKEJOBS -C src/univalue VERBOSE=1
DOCKER_EXEC "${BASE_ROOT_DIR}/ci/test/wrap-qemu.sh"
fi
diff --git a/configure.ac b/configure.ac
index 7dbefd9042..dc3b07e4bb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -107,13 +107,12 @@ AC_PATH_TOOL(GCOV, gcov)
AC_PATH_TOOL(LLVM_COV, llvm-cov)
AC_PATH_PROG(LCOV, lcov)
dnl Python 3.6 is specified in .python-version and should be used if available, see doc/dependencies.md
-AC_PATH_PROGS([PYTHON], [python3.6 python3.7 python3.8 python3.9, python3.10, python3 python])
+AC_PATH_PROGS([PYTHON], [python3.6 python3.7 python3.8 python3.9 python3.10 python3.11 python3 python])
AC_PATH_PROG(GENHTML, genhtml)
AC_PATH_PROG([GIT], [git])
AC_PATH_PROG(CCACHE,ccache)
AC_PATH_PROG(XGETTEXT,xgettext)
AC_PATH_PROG(HEXDUMP,hexdump)
-AC_PATH_TOOL(CPPFILT, c++filt)
AC_PATH_TOOL(OBJCOPY, objcopy)
AC_PATH_PROG(DOXYGEN, doxygen)
AM_CONDITIONAL([HAVE_DOXYGEN], [test -n "$DOXYGEN"])
@@ -262,12 +261,6 @@ if test "x$use_asm" = xyes; then
AC_DEFINE(USE_ASM, 1, [Define this symbol to build in assembly routines])
fi
-AC_ARG_WITH([system-univalue],
- [AS_HELP_STRING([--with-system-univalue],
- [Build with system UniValue (default is no)])],
- [system_univalue=$withval],
- [system_univalue=no]
-)
AC_ARG_ENABLE([zmq],
[AS_HELP_STRING([--disable-zmq],
[disable ZMQ notifications])],
@@ -321,7 +314,7 @@ AC_ARG_ENABLE([gprof],
dnl Turn warnings into errors
AC_ARG_ENABLE([werror],
[AS_HELP_STRING([--enable-werror],
- [Treat certain compiler warnings as errors (default is no)])],
+ [Treat compiler warnings as errors (default is no)])],
[enable_werror=$enableval],
[enable_werror=no])
@@ -409,34 +402,13 @@ if test "x$enable_werror" = "xyes"; then
if test "x$CXXFLAG_WERROR" = "x"; then
AC_MSG_ERROR("enable-werror set but -Werror is not usable")
fi
- AX_CHECK_COMPILE_FLAG([-Werror=gnu],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=gnu"],,[[$CXXFLAG_WERROR]])
- AX_CHECK_COMPILE_FLAG([-Werror=vla],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=vla"],,[[$CXXFLAG_WERROR]])
- AX_CHECK_COMPILE_FLAG([-Werror=shadow-field],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=shadow-field"],,[[$CXXFLAG_WERROR]])
- AX_CHECK_COMPILE_FLAG([-Werror=switch],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=switch"],,[[$CXXFLAG_WERROR]])
- AX_CHECK_COMPILE_FLAG([-Werror=thread-safety],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=thread-safety"],,[[$CXXFLAG_WERROR]])
- AX_CHECK_COMPILE_FLAG([-Werror=range-loop-analysis],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=range-loop-analysis"],,[[$CXXFLAG_WERROR]])
- AX_CHECK_COMPILE_FLAG([-Werror=unused-variable],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=unused-variable"],,[[$CXXFLAG_WERROR]])
- AX_CHECK_COMPILE_FLAG([-Werror=date-time],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=date-time"],,[[$CXXFLAG_WERROR]])
+ ERROR_CXXFLAGS=$CXXFLAG_WERROR
dnl -Wreturn-type is broken in GCC for MinGW-w64.
dnl https://sourceforge.net/p/mingw-w64/bugs/306/
- AX_CHECK_COMPILE_FLAG([-Werror=return-type], [ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=return-type"], [], [$CXXFLAG_WERROR],
+ AX_CHECK_COMPILE_FLAG([-Werror=return-type], [], [ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Wno-error=return-type"], [$CXXFLAG_WERROR],
[AC_LANG_SOURCE([[#include <cassert>
int f(){ assert(false); }]])])
-
- AX_CHECK_COMPILE_FLAG([-Werror=conditional-uninitialized],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=conditional-uninitialized"],,[[$CXXFLAG_WERROR]])
- AX_CHECK_COMPILE_FLAG([-Werror=sign-compare],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=sign-compare"],,[[$CXXFLAG_WERROR]])
- dnl -Wsuggest-override is broken with GCC before 9.2
- dnl https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78010
- AX_CHECK_COMPILE_FLAG([-Werror=suggest-override],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=suggest-override"],,[[$CXXFLAG_WERROR]],
- [AC_LANG_SOURCE([[struct A { virtual void f(); }; struct B : A { void f() final; };]])])
- AX_CHECK_COMPILE_FLAG([-Werror=unreachable-code-loop-increment],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=unreachable-code-loop-increment"],,[[$CXXFLAG_WERROR]])
- AX_CHECK_COMPILE_FLAG([-Werror=mismatched-tags], [ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=mismatched-tags"], [], [$CXXFLAG_WERROR])
- AX_CHECK_COMPILE_FLAG([-Werror=implicit-fallthrough], [ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=implicit-fallthrough"], [], [$CXXFLAG_WERROR])
-
- if test x$suppress_external_warnings != xno ; then
- AX_CHECK_COMPILE_FLAG([-Werror=documentation],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=documentation"],,[[$CXXFLAG_WERROR]])
- fi
fi
if test "x$CXXFLAGS_overridden" = "xno"; then
@@ -447,19 +419,18 @@ if test "x$CXXFLAGS_overridden" = "xno"; then
AX_CHECK_COMPILE_FLAG([-Wformat -Wformat-security],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wformat -Wformat-security"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Wvla],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wvla"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Wshadow-field],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wshadow-field"],,[[$CXXFLAG_WERROR]])
- AX_CHECK_COMPILE_FLAG([-Wswitch],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wswitch"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Wthread-safety],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wthread-safety"],,[[$CXXFLAG_WERROR]])
- AX_CHECK_COMPILE_FLAG([-Wrange-loop-analysis],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wrange-loop-analysis"],,[[$CXXFLAG_WERROR]])
+ AX_CHECK_COMPILE_FLAG([-Wloop-analysis],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wloop-analysis"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Wredundant-decls],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wredundant-decls"],,[[$CXXFLAG_WERROR]])
- AX_CHECK_COMPILE_FLAG([-Wunused-variable],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wunused-variable"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Wunused-member-function],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wunused-member-function"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Wdate-time],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wdate-time"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Wconditional-uninitialized],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wconditional-uninitialized"],,[[$CXXFLAG_WERROR]])
- AX_CHECK_COMPILE_FLAG([-Wsign-compare],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wsign-compare"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Wduplicated-branches],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wduplicated-branches"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Wduplicated-cond],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wduplicated-cond"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Wlogical-op],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wlogical-op"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Woverloaded-virtual],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Woverloaded-virtual"],,[[$CXXFLAG_WERROR]])
+ dnl -Wsuggest-override is broken with GCC before 9.2
+ dnl https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78010
AX_CHECK_COMPILE_FLAG([-Wsuggest-override],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wsuggest-override"],,[[$CXXFLAG_WERROR]],
[AC_LANG_SOURCE([[struct A { virtual void f(); }; struct B : A { void f() final; };]])])
AX_CHECK_COMPILE_FLAG([-Wunreachable-code-loop-increment],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wunreachable-code-loop-increment"],,[[$CXXFLAG_WERROR]])
@@ -474,7 +445,6 @@ if test "x$CXXFLAGS_overridden" = "xno"; then
dnl set the -Wno-foo case if it works.
AX_CHECK_COMPILE_FLAG([-Wunused-parameter],[NOWARN_CXXFLAGS="$NOWARN_CXXFLAGS -Wno-unused-parameter"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Wself-assign],[NOWARN_CXXFLAGS="$NOWARN_CXXFLAGS -Wno-self-assign"],,[[$CXXFLAG_WERROR]])
- AX_CHECK_COMPILE_FLAG([-Wunused-local-typedef],[NOWARN_CXXFLAGS="$NOWARN_CXXFLAGS -Wno-unused-local-typedef"],,[[$CXXFLAG_WERROR]])
if test x$suppress_external_warnings != xyes ; then
AX_CHECK_COMPILE_FLAG([-Wdeprecated-copy],[NOWARN_CXXFLAGS="$NOWARN_CXXFLAGS -Wno-deprecated-copy"],,[[$CXXFLAG_WERROR]])
fi
@@ -1249,14 +1219,6 @@ if test "x$have_any_system" != "xno"; then
AC_DEFINE(HAVE_SYSTEM, 1, Define to 1 if std::system or ::wsystem is available.)
fi
-LEVELDB_CPPFLAGS=
-LIBLEVELDB=
-LIBMEMENV=
-AM_CONDITIONAL([EMBEDDED_LEVELDB],[true])
-AC_SUBST(LEVELDB_CPPFLAGS)
-AC_SUBST(LIBLEVELDB)
-AC_SUBST(LIBMEMENV)
-
dnl SUPPRESSED_CPPFLAGS=SUPPRESS_WARNINGS([$SOME_CPPFLAGS])
dnl Replace -I with -isystem in $SOME_CPPFLAGS to suppress warnings from
dnl headers from its include directories and return the result.
@@ -1546,34 +1508,6 @@ if test "x$use_zmq" = xyes; then
esac
fi
-dnl univalue check
-
-need_bundled_univalue=yes
-if test x$build_bitcoin_wallet$build_bitcoin_cli$build_bitcoin_tx$build_bitcoin_util$build_bitcoind$bitcoin_enable_qt$use_tests$use_bench = xnononononononono; then
- need_bundled_univalue=no
-else
- if test x$system_univalue != xno; then
- PKG_CHECK_MODULES([UNIVALUE], [libunivalue >= 1.0.4], [found_univalue=yes], [found_univalue=no])
- if test x$found_univalue = xyes; then
- system_univalue=yes
- need_bundled_univalue=no
- elif test x$system_univalue = xyes; then
- AC_MSG_ERROR([univalue not found])
- else
- system_univalue=no
- fi
- fi
-
- if test x$need_bundled_univalue = xyes; then
- UNIVALUE_CFLAGS='-I$(srcdir)/univalue/include'
- UNIVALUE_LIBS='univalue/libunivalue.la'
- fi
-fi
-
-AM_CONDITIONAL([EMBEDDED_UNIVALUE],[test x$need_bundled_univalue = xyes])
-AC_SUBST(UNIVALUE_CFLAGS)
-AC_SUBST(UNIVALUE_LIBS)
-
dnl libmultiprocess library check
libmultiprocess_found=no
@@ -1661,8 +1595,8 @@ if test "x$use_ccache" != "xno"; then
fi
AC_MSG_RESULT($use_ccache)
if test "x$use_ccache" = "xyes"; then
- AX_CHECK_COMPILE_FLAG([-fdebug-prefix-map=A=B],[DEBUG_CXXFLAGS="$DEBUG_CXXFLAGS -fdebug-prefix-map=\$(abs_srcdir)=."],,[[$CXXFLAG_WERROR]])
- AX_CHECK_PREPROC_FLAG([-fmacro-prefix-map=A=B],[DEBUG_CPPFLAGS="$DEBUG_CPPFLAGS -fmacro-prefix-map=\$(abs_srcdir)=."],,[[$CXXFLAG_WERROR]])
+ AX_CHECK_COMPILE_FLAG([-fdebug-prefix-map=A=B],[DEBUG_CXXFLAGS="$DEBUG_CXXFLAGS -fdebug-prefix-map=\$(abs_top_srcdir)=."],,[[$CXXFLAG_WERROR]])
+ AX_CHECK_PREPROC_FLAG([-fmacro-prefix-map=A=B],[DEBUG_CPPFLAGS="$DEBUG_CPPFLAGS -fmacro-prefix-map=\$(abs_top_srcdir)=."],,[[$CXXFLAG_WERROR]])
fi
fi
@@ -1936,11 +1870,7 @@ PKGCONFIG_LIBDIR_TEMP="$PKG_CONFIG_LIBDIR"
unset PKG_CONFIG_LIBDIR
PKG_CONFIG_LIBDIR="$PKGCONFIG_LIBDIR_TEMP"
-if test x$need_bundled_univalue = xyes; then
- AC_CONFIG_SUBDIRS([src/univalue])
-fi
-
-ac_configure_args="${ac_configure_args} --disable-shared --with-pic --enable-benchmark=no --enable-module-recovery --enable-module-schnorrsig --enable-experimental"
+ac_configure_args="${ac_configure_args} --disable-shared --with-pic --enable-benchmark=no --enable-module-recovery --enable-module-schnorrsig --enable-experimental --disable-openssl-tests"
AC_CONFIG_SUBDIRS([src/secp256k1])
AC_OUTPUT
diff --git a/contrib/devtools/pixie.py b/contrib/devtools/pixie.py
deleted file mode 100644
index 64660968ad..0000000000
--- a/contrib/devtools/pixie.py
+++ /dev/null
@@ -1,323 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (c) 2020 Wladimir J. van der Laan
-# Distributed under the MIT software license, see the accompanying
-# file COPYING or http://www.opensource.org/licenses/mit-license.php.
-'''
-Compact, self-contained ELF implementation for bitcoin-core security checks.
-'''
-import struct
-import types
-from typing import Dict, List, Optional, Union, Tuple
-
-# you can find all these values in elf.h
-EI_NIDENT = 16
-
-# Byte indices in e_ident
-EI_CLASS = 4 # ELFCLASSxx
-EI_DATA = 5 # ELFDATAxxxx
-
-ELFCLASS32 = 1 # 32-bit
-ELFCLASS64 = 2 # 64-bit
-
-ELFDATA2LSB = 1 # little endian
-ELFDATA2MSB = 2 # big endian
-
-# relevant values for e_machine
-EM_386 = 3
-EM_PPC64 = 21
-EM_ARM = 40
-EM_AARCH64 = 183
-EM_X86_64 = 62
-EM_RISCV = 243
-
-# relevant values for e_type
-ET_DYN = 3
-
-# relevant values for sh_type
-SHT_PROGBITS = 1
-SHT_STRTAB = 3
-SHT_DYNAMIC = 6
-SHT_DYNSYM = 11
-SHT_GNU_verneed = 0x6ffffffe
-SHT_GNU_versym = 0x6fffffff
-
-# relevant values for p_type
-PT_LOAD = 1
-PT_GNU_STACK = 0x6474e551
-PT_GNU_RELRO = 0x6474e552
-
-# relevant values for p_flags
-PF_X = (1 << 0)
-PF_W = (1 << 1)
-PF_R = (1 << 2)
-
-# relevant values for d_tag
-DT_NEEDED = 1
-DT_FLAGS = 30
-
-# relevant values of `d_un.d_val' in the DT_FLAGS entry
-DF_BIND_NOW = 0x00000008
-
-# relevant d_tags with string payload
-STRING_TAGS = {DT_NEEDED}
-
-# rrlevant values for ST_BIND subfield of st_info (symbol binding)
-STB_LOCAL = 0
-
-class ELFRecord(types.SimpleNamespace):
- '''Unified parsing for ELF records.'''
- def __init__(self, data: bytes, offset: int, eh: 'ELFHeader', total_size: Optional[int]) -> None:
- hdr_struct = self.STRUCT[eh.ei_class][0][eh.ei_data]
- if total_size is not None and hdr_struct.size > total_size:
- raise ValueError(f'{self.__class__.__name__} header size too small ({total_size} < {hdr_struct.size})')
- for field, value in zip(self.STRUCT[eh.ei_class][1], hdr_struct.unpack(data[offset:offset + hdr_struct.size])):
- setattr(self, field, value)
-
-def BiStruct(chars: str) -> Dict[int, struct.Struct]:
- '''Compile a struct parser for both endians.'''
- return {
- ELFDATA2LSB: struct.Struct('<' + chars),
- ELFDATA2MSB: struct.Struct('>' + chars),
- }
-
-class ELFHeader(ELFRecord):
- FIELDS = ['e_type', 'e_machine', 'e_version', 'e_entry', 'e_phoff', 'e_shoff', 'e_flags', 'e_ehsize', 'e_phentsize', 'e_phnum', 'e_shentsize', 'e_shnum', 'e_shstrndx']
- STRUCT = {
- ELFCLASS32: (BiStruct('HHIIIIIHHHHHH'), FIELDS),
- ELFCLASS64: (BiStruct('HHIQQQIHHHHHH'), FIELDS),
- }
-
- def __init__(self, data: bytes, offset: int) -> None:
- self.e_ident = data[offset:offset + EI_NIDENT]
- if self.e_ident[0:4] != b'\x7fELF':
- raise ValueError('invalid ELF magic')
- self.ei_class = self.e_ident[EI_CLASS]
- self.ei_data = self.e_ident[EI_DATA]
-
- super().__init__(data, offset + EI_NIDENT, self, None)
-
- def __repr__(self) -> str:
- return f'Header(e_ident={self.e_ident!r}, e_type={self.e_type}, e_machine={self.e_machine}, e_version={self.e_version}, e_entry={self.e_entry}, e_phoff={self.e_phoff}, e_shoff={self.e_shoff}, e_flags={self.e_flags}, e_ehsize={self.e_ehsize}, e_phentsize={self.e_phentsize}, e_phnum={self.e_phnum}, e_shentsize={self.e_shentsize}, e_shnum={self.e_shnum}, e_shstrndx={self.e_shstrndx})'
-
-class Section(ELFRecord):
- name: Optional[bytes] = None
- FIELDS = ['sh_name', 'sh_type', 'sh_flags', 'sh_addr', 'sh_offset', 'sh_size', 'sh_link', 'sh_info', 'sh_addralign', 'sh_entsize']
- STRUCT = {
- ELFCLASS32: (BiStruct('IIIIIIIIII'), FIELDS),
- ELFCLASS64: (BiStruct('IIQQQQIIQQ'), FIELDS),
- }
-
- def __init__(self, data: bytes, offset: int, eh: ELFHeader) -> None:
- super().__init__(data, offset, eh, eh.e_shentsize)
- self._data = data
-
- def __repr__(self) -> str:
- return f'Section(sh_name={self.sh_name}({self.name!r}), sh_type=0x{self.sh_type:x}, sh_flags={self.sh_flags}, sh_addr=0x{self.sh_addr:x}, sh_offset=0x{self.sh_offset:x}, sh_size={self.sh_size}, sh_link={self.sh_link}, sh_info={self.sh_info}, sh_addralign={self.sh_addralign}, sh_entsize={self.sh_entsize})'
-
- def contents(self) -> bytes:
- '''Return section contents.'''
- return self._data[self.sh_offset:self.sh_offset + self.sh_size]
-
-class ProgramHeader(ELFRecord):
- STRUCT = {
- # different ELF classes have the same fields, but in a different order to optimize space versus alignment
- ELFCLASS32: (BiStruct('IIIIIIII'), ['p_type', 'p_offset', 'p_vaddr', 'p_paddr', 'p_filesz', 'p_memsz', 'p_flags', 'p_align']),
- ELFCLASS64: (BiStruct('IIQQQQQQ'), ['p_type', 'p_flags', 'p_offset', 'p_vaddr', 'p_paddr', 'p_filesz', 'p_memsz', 'p_align']),
- }
-
- def __init__(self, data: bytes, offset: int, eh: ELFHeader) -> None:
- super().__init__(data, offset, eh, eh.e_phentsize)
-
- def __repr__(self) -> str:
- return f'ProgramHeader(p_type={self.p_type}, p_offset={self.p_offset}, p_vaddr={self.p_vaddr}, p_paddr={self.p_paddr}, p_filesz={self.p_filesz}, p_memsz={self.p_memsz}, p_flags={self.p_flags}, p_align={self.p_align})'
-
-class Symbol(ELFRecord):
- STRUCT = {
- # different ELF classes have the same fields, but in a different order to optimize space versus alignment
- ELFCLASS32: (BiStruct('IIIBBH'), ['st_name', 'st_value', 'st_size', 'st_info', 'st_other', 'st_shndx']),
- ELFCLASS64: (BiStruct('IBBHQQ'), ['st_name', 'st_info', 'st_other', 'st_shndx', 'st_value', 'st_size']),
- }
-
- def __init__(self, data: bytes, offset: int, eh: ELFHeader, symtab: Section, strings: bytes, version: Optional[bytes]) -> None:
- super().__init__(data, offset, eh, symtab.sh_entsize)
- self.name = _lookup_string(strings, self.st_name)
- self.version = version
-
- def __repr__(self) -> str:
- return f'Symbol(st_name={self.st_name}({self.name!r}), st_value={self.st_value}, st_size={self.st_size}, st_info={self.st_info}, st_other={self.st_other}, st_shndx={self.st_shndx}, version={self.version!r})'
-
- @property
- def is_import(self) -> bool:
- '''Returns whether the symbol is an imported symbol.'''
- return self.st_bind != STB_LOCAL and self.st_shndx == 0
-
- @property
- def is_export(self) -> bool:
- '''Returns whether the symbol is an exported symbol.'''
- return self.st_bind != STB_LOCAL and self.st_shndx != 0
-
- @property
- def st_bind(self) -> int:
- '''Returns STB_*.'''
- return self.st_info >> 4
-
-class Verneed(ELFRecord):
- DEF = (BiStruct('HHIII'), ['vn_version', 'vn_cnt', 'vn_file', 'vn_aux', 'vn_next'])
- STRUCT = { ELFCLASS32: DEF, ELFCLASS64: DEF }
-
- def __init__(self, data: bytes, offset: int, eh: ELFHeader) -> None:
- super().__init__(data, offset, eh, None)
-
- def __repr__(self) -> str:
- return f'Verneed(vn_version={self.vn_version}, vn_cnt={self.vn_cnt}, vn_file={self.vn_file}, vn_aux={self.vn_aux}, vn_next={self.vn_next})'
-
-class Vernaux(ELFRecord):
- DEF = (BiStruct('IHHII'), ['vna_hash', 'vna_flags', 'vna_other', 'vna_name', 'vna_next'])
- STRUCT = { ELFCLASS32: DEF, ELFCLASS64: DEF }
-
- def __init__(self, data: bytes, offset: int, eh: ELFHeader, strings: bytes) -> None:
- super().__init__(data, offset, eh, None)
- self.name = _lookup_string(strings, self.vna_name)
-
- def __repr__(self) -> str:
- return f'Veraux(vna_hash={self.vna_hash}, vna_flags={self.vna_flags}, vna_other={self.vna_other}, vna_name={self.vna_name}({self.name!r}), vna_next={self.vna_next})'
-
-class DynTag(ELFRecord):
- STRUCT = {
- ELFCLASS32: (BiStruct('II'), ['d_tag', 'd_val']),
- ELFCLASS64: (BiStruct('QQ'), ['d_tag', 'd_val']),
- }
-
- def __init__(self, data: bytes, offset: int, eh: ELFHeader, section: Section) -> None:
- super().__init__(data, offset, eh, section.sh_entsize)
-
- def __repr__(self) -> str:
- return f'DynTag(d_tag={self.d_tag}, d_val={self.d_val})'
-
-def _lookup_string(data: bytes, index: int) -> bytes:
- '''Look up string by offset in ELF string table.'''
- endx = data.find(b'\x00', index)
- assert endx != -1
- return data[index:endx]
-
-VERSYM_S = BiStruct('H') # .gnu_version section has a single 16-bit integer per symbol in the linked section
-def _parse_symbol_table(section: Section, strings: bytes, eh: ELFHeader, versym: bytes, verneed: Dict[int, bytes]) -> List[Symbol]:
- '''Parse symbol table, return a list of symbols.'''
- data = section.contents()
- symbols = []
- versym_iter = (verneed.get(v[0]) for v in VERSYM_S[eh.ei_data].iter_unpack(versym))
- for ofs, version in zip(range(0, len(data), section.sh_entsize), versym_iter):
- symbols.append(Symbol(data, ofs, eh, section, strings, version))
- return symbols
-
-def _parse_verneed(section: Section, strings: bytes, eh: ELFHeader) -> Dict[int, bytes]:
- '''Parse .gnu.version_r section, return a dictionary of {versym: 'GLIBC_...'}.'''
- data = section.contents()
- ofs = 0
- result = {}
- while True:
- verneed = Verneed(data, ofs, eh)
- aofs = ofs + verneed.vn_aux
- while True:
- vernaux = Vernaux(data, aofs, eh, strings)
- result[vernaux.vna_other] = vernaux.name
- if not vernaux.vna_next:
- break
- aofs += vernaux.vna_next
-
- if not verneed.vn_next:
- break
- ofs += verneed.vn_next
-
- return result
-
-def _parse_dyn_tags(section: Section, strings: bytes, eh: ELFHeader) -> List[Tuple[int, Union[bytes, int]]]:
- '''Parse dynamic tags. Return array of tuples.'''
- data = section.contents()
- ofs = 0
- result = []
- for ofs in range(0, len(data), section.sh_entsize):
- tag = DynTag(data, ofs, eh, section)
- val = _lookup_string(strings, tag.d_val) if tag.d_tag in STRING_TAGS else tag.d_val
- result.append((tag.d_tag, val))
-
- return result
-
-class ELFFile:
- sections: List[Section]
- program_headers: List[ProgramHeader]
- dyn_symbols: List[Symbol]
- dyn_tags: List[Tuple[int, Union[bytes, int]]]
-
- def __init__(self, data: bytes) -> None:
- self.data = data
- self.hdr = ELFHeader(self.data, 0)
- self._load_sections()
- self._load_program_headers()
- self._load_dyn_symbols()
- self._load_dyn_tags()
- self._section_to_segment_mapping()
-
- def _load_sections(self) -> None:
- self.sections = []
- for idx in range(self.hdr.e_shnum):
- offset = self.hdr.e_shoff + idx * self.hdr.e_shentsize
- self.sections.append(Section(self.data, offset, self.hdr))
-
- shstr = self.sections[self.hdr.e_shstrndx].contents()
- for section in self.sections:
- section.name = _lookup_string(shstr, section.sh_name)
-
- def _load_program_headers(self) -> None:
- self.program_headers = []
- for idx in range(self.hdr.e_phnum):
- offset = self.hdr.e_phoff + idx * self.hdr.e_phentsize
- self.program_headers.append(ProgramHeader(self.data, offset, self.hdr))
-
- def _load_dyn_symbols(self) -> None:
- # first, load 'verneed' section
- verneed = None
- for section in self.sections:
- if section.sh_type == SHT_GNU_verneed:
- strtab = self.sections[section.sh_link].contents() # associated string table
- assert verneed is None # only one section of this kind please
- verneed = _parse_verneed(section, strtab, self.hdr)
- assert verneed is not None
-
- # then, correlate GNU versym sections with dynamic symbol sections
- versym = {}
- for section in self.sections:
- if section.sh_type == SHT_GNU_versym:
- versym[section.sh_link] = section
-
- # finally, load dynsym sections
- self.dyn_symbols = []
- for idx, section in enumerate(self.sections):
- if section.sh_type == SHT_DYNSYM: # find dynamic symbol tables
- strtab_data = self.sections[section.sh_link].contents() # associated string table
- versym_data = versym[idx].contents() # associated symbol version table
- self.dyn_symbols += _parse_symbol_table(section, strtab_data, self.hdr, versym_data, verneed)
-
- def _load_dyn_tags(self) -> None:
- self.dyn_tags = []
- for idx, section in enumerate(self.sections):
- if section.sh_type == SHT_DYNAMIC: # find dynamic tag tables
- strtab = self.sections[section.sh_link].contents() # associated string table
- self.dyn_tags += _parse_dyn_tags(section, strtab, self.hdr)
-
- def _section_to_segment_mapping(self) -> None:
- for ph in self.program_headers:
- ph.sections = []
- for section in self.sections:
- if ph.p_vaddr <= section.sh_addr < (ph.p_vaddr + ph.p_memsz):
- ph.sections.append(section)
-
- def query_dyn_tags(self, tag_in: int) -> List[Union[int, bytes]]:
- '''Return the values of all dyn tags with the specified tag.'''
- return [val for (tag, val) in self.dyn_tags if tag == tag_in]
-
-
-def load(filename: str) -> ELFFile:
- with open(filename, 'rb') as f:
- data = f.read()
- return ELFFile(data)
diff --git a/contrib/devtools/security-check.py b/contrib/devtools/security-check.py
index 0b59d8eada..ef421aebb1 100755
--- a/contrib/devtools/security-check.py
+++ b/contrib/devtools/security-check.py
@@ -8,192 +8,155 @@ Exit status will be 0 if successful, and the program will be silent.
Otherwise the exit status will be 1 and it will log which executables failed which checks.
'''
import sys
-from typing import List, Optional
+from typing import List
-import lief
-import pixie
+import lief #type:ignore
-def check_ELF_PIE(executable) -> bool:
- '''
- Check for position independent executable (PIE), allowing for address space randomization.
- '''
- elf = pixie.load(executable)
- return elf.hdr.e_type == pixie.ET_DYN
-
-def check_ELF_NX(executable) -> bool:
- '''
- Check that no sections are writable and executable (including the stack)
- '''
- elf = pixie.load(executable)
- have_wx = False
- have_gnu_stack = False
- for ph in elf.program_headers:
- if ph.p_type == pixie.PT_GNU_STACK:
- have_gnu_stack = True
- if (ph.p_flags & pixie.PF_W) != 0 and (ph.p_flags & pixie.PF_X) != 0: # section is both writable and executable
- have_wx = True
- return have_gnu_stack and not have_wx
-
-def check_ELF_RELRO(executable) -> bool:
+def check_ELF_RELRO(binary) -> bool:
'''
Check for read-only relocations.
GNU_RELRO program header must exist
Dynamic section must have BIND_NOW flag
'''
- elf = pixie.load(executable)
have_gnu_relro = False
- for ph in elf.program_headers:
+ for segment in binary.segments:
# Note: not checking p_flags == PF_R: here as linkers set the permission differently
# This does not affect security: the permission flags of the GNU_RELRO program
# header are ignored, the PT_LOAD header determines the effective permissions.
# However, the dynamic linker need to write to this area so these are RW.
# Glibc itself takes care of mprotecting this area R after relocations are finished.
# See also https://marc.info/?l=binutils&m=1498883354122353
- if ph.p_type == pixie.PT_GNU_RELRO:
+ if segment.type == lief.ELF.SEGMENT_TYPES.GNU_RELRO:
have_gnu_relro = True
have_bindnow = False
- for flags in elf.query_dyn_tags(pixie.DT_FLAGS):
- assert isinstance(flags, int)
- if flags & pixie.DF_BIND_NOW:
+ try:
+ flags = binary.get(lief.ELF.DYNAMIC_TAGS.FLAGS)
+ if flags.value & lief.ELF.DYNAMIC_FLAGS.BIND_NOW:
have_bindnow = True
+ except:
+ have_bindnow = False
return have_gnu_relro and have_bindnow
-def check_ELF_Canary(executable) -> bool:
+def check_ELF_Canary(binary) -> bool:
'''
Check for use of stack canary
'''
- elf = pixie.load(executable)
- ok = False
- for symbol in elf.dyn_symbols:
- if symbol.name == b'__stack_chk_fail':
- ok = True
- return ok
+ return binary.has_symbol('__stack_chk_fail')
-def check_ELF_separate_code(executable):
+def check_ELF_separate_code(binary):
'''
Check that sections are appropriately separated in virtual memory,
based on their permissions. This checks for missing -Wl,-z,separate-code
and potentially other problems.
'''
- elf = pixie.load(executable)
- R = pixie.PF_R
- W = pixie.PF_W
- E = pixie.PF_X
+ R = lief.ELF.SEGMENT_FLAGS.R
+ W = lief.ELF.SEGMENT_FLAGS.W
+ E = lief.ELF.SEGMENT_FLAGS.X
EXPECTED_FLAGS = {
# Read + execute
- b'.init': R | E,
- b'.plt': R | E,
- b'.plt.got': R | E,
- b'.plt.sec': R | E,
- b'.text': R | E,
- b'.fini': R | E,
+ '.init': R | E,
+ '.plt': R | E,
+ '.plt.got': R | E,
+ '.plt.sec': R | E,
+ '.text': R | E,
+ '.fini': R | E,
# Read-only data
- b'.interp': R,
- b'.note.gnu.property': R,
- b'.note.gnu.build-id': R,
- b'.note.ABI-tag': R,
- b'.gnu.hash': R,
- b'.dynsym': R,
- b'.dynstr': R,
- b'.gnu.version': R,
- b'.gnu.version_r': R,
- b'.rela.dyn': R,
- b'.rela.plt': R,
- b'.rodata': R,
- b'.eh_frame_hdr': R,
- b'.eh_frame': R,
- b'.qtmetadata': R,
- b'.gcc_except_table': R,
- b'.stapsdt.base': R,
+ '.interp': R,
+ '.note.gnu.property': R,
+ '.note.gnu.build-id': R,
+ '.note.ABI-tag': R,
+ '.gnu.hash': R,
+ '.dynsym': R,
+ '.dynstr': R,
+ '.gnu.version': R,
+ '.gnu.version_r': R,
+ '.rela.dyn': R,
+ '.rela.plt': R,
+ '.rodata': R,
+ '.eh_frame_hdr': R,
+ '.eh_frame': R,
+ '.qtmetadata': R,
+ '.gcc_except_table': R,
+ '.stapsdt.base': R,
# Writable data
- b'.init_array': R | W,
- b'.fini_array': R | W,
- b'.dynamic': R | W,
- b'.got': R | W,
- b'.data': R | W,
- b'.bss': R | W,
+ '.init_array': R | W,
+ '.fini_array': R | W,
+ '.dynamic': R | W,
+ '.got': R | W,
+ '.data': R | W,
+ '.bss': R | W,
}
- if elf.hdr.e_machine == pixie.EM_PPC64:
+ if binary.header.machine_type == lief.ELF.ARCH.PPC64:
# .plt is RW on ppc64 even with separate-code
- EXPECTED_FLAGS[b'.plt'] = R | W
+ EXPECTED_FLAGS['.plt'] = R | W
# For all LOAD program headers get mapping to the list of sections,
# and for each section, remember the flags of the associated program header.
flags_per_section = {}
- for ph in elf.program_headers:
- if ph.p_type == pixie.PT_LOAD:
- for section in ph.sections:
+ for segment in binary.segments:
+ if segment.type == lief.ELF.SEGMENT_TYPES.LOAD:
+ for section in segment.sections:
assert(section.name not in flags_per_section)
- flags_per_section[section.name] = ph.p_flags
+ flags_per_section[section.name] = segment.flags
# Spot-check ELF LOAD program header flags per section
# If these sections exist, check them against the expected R/W/E flags
for (section, flags) in flags_per_section.items():
if section in EXPECTED_FLAGS:
- if EXPECTED_FLAGS[section] != flags:
+ if int(EXPECTED_FLAGS[section]) != int(flags):
return False
return True
-def check_PE_DYNAMIC_BASE(executable) -> bool:
+def check_PE_DYNAMIC_BASE(binary) -> bool:
'''PIE: DllCharacteristics bit 0x40 signifies dynamicbase (ASLR)'''
- binary = lief.parse(executable)
return lief.PE.DLL_CHARACTERISTICS.DYNAMIC_BASE in binary.optional_header.dll_characteristics_lists
# Must support high-entropy 64-bit address space layout randomization
# in addition to DYNAMIC_BASE to have secure ASLR.
-def check_PE_HIGH_ENTROPY_VA(executable) -> bool:
+def check_PE_HIGH_ENTROPY_VA(binary) -> bool:
'''PIE: DllCharacteristics bit 0x20 signifies high-entropy ASLR'''
- binary = lief.parse(executable)
return lief.PE.DLL_CHARACTERISTICS.HIGH_ENTROPY_VA in binary.optional_header.dll_characteristics_lists
-def check_PE_RELOC_SECTION(executable) -> bool:
+def check_PE_RELOC_SECTION(binary) -> bool:
'''Check for a reloc section. This is required for functional ASLR.'''
- binary = lief.parse(executable)
return binary.has_relocations
-def check_MACHO_NOUNDEFS(executable) -> bool:
+def check_MACHO_NOUNDEFS(binary) -> bool:
'''
Check for no undefined references.
'''
- binary = lief.parse(executable)
return binary.header.has(lief.MachO.HEADER_FLAGS.NOUNDEFS)
-def check_MACHO_LAZY_BINDINGS(executable) -> bool:
+def check_MACHO_LAZY_BINDINGS(binary) -> bool:
'''
Check for no lazy bindings.
We don't use or check for MH_BINDATLOAD. See #18295.
'''
- binary = lief.parse(executable)
return binary.dyld_info.lazy_bind == (0,0)
-def check_MACHO_Canary(executable) -> bool:
+def check_MACHO_Canary(binary) -> bool:
'''
Check for use of stack canary
'''
- binary = lief.parse(executable)
return binary.has_symbol('___stack_chk_fail')
-def check_PIE(executable) -> bool:
+def check_PIE(binary) -> bool:
'''
Check for position independent executable (PIE),
allowing for address space randomization.
'''
- binary = lief.parse(executable)
return binary.is_pie
-def check_NX(executable) -> bool:
+def check_NX(binary) -> bool:
'''
Check for no stack execution
'''
- binary = lief.parse(executable)
return binary.has_nx
-def check_control_flow(executable) -> bool:
+def check_control_flow(binary) -> bool:
'''
Check for control flow instrumentation
'''
- binary = lief.parse(executable)
-
content = binary.get_content_from_virtual_address(binary.entrypoint, 4, lief.Binary.VA_TYPES.AUTO)
if content == [243, 15, 30, 250]: # endbr64
@@ -203,8 +166,8 @@ def check_control_flow(executable) -> bool:
CHECKS = {
'ELF': [
- ('PIE', check_ELF_PIE),
- ('NX', check_ELF_NX),
+ ('PIE', check_PIE),
+ ('NX', check_NX),
('RELRO', check_ELF_RELRO),
('Canary', check_ELF_Canary),
('separate_code', check_ELF_separate_code),
@@ -226,30 +189,20 @@ CHECKS = {
]
}
-def identify_executable(executable) -> Optional[str]:
- with open(filename, 'rb') as f:
- magic = f.read(4)
- if magic.startswith(b'MZ'):
- return 'PE'
- elif magic.startswith(b'\x7fELF'):
- return 'ELF'
- elif magic.startswith(b'\xcf\xfa'):
- return 'MACHO'
- return None
-
if __name__ == '__main__':
retval: int = 0
for filename in sys.argv[1:]:
try:
- etype = identify_executable(filename)
- if etype is None:
- print(f'{filename}: unknown format')
+ binary = lief.parse(filename)
+ etype = binary.format.name
+ if etype == lief.EXE_FORMATS.UNKNOWN:
+ print(f'{filename}: unknown executable format')
retval = 1
continue
failed: List[str] = []
for (name, func) in CHECKS[etype]:
- if not func(filename):
+ if not func(binary):
failed.append(name)
if failed:
print(f'{filename}: failed {" ".join(failed)}')
diff --git a/contrib/devtools/symbol-check.py b/contrib/devtools/symbol-check.py
index 98cab1b7fc..136a9b70c1 100755
--- a/contrib/devtools/symbol-check.py
+++ b/contrib/devtools/symbol-check.py
@@ -10,14 +10,14 @@ Example usage:
find ../path/to/binaries -type f -executable | xargs python3 contrib/devtools/symbol-check.py
'''
-import subprocess
import sys
-from typing import List, Optional
+from typing import List, Dict
-import lief
-import pixie
+import lief #type:ignore
-from utils import determine_wellknown_cmd
+# temporary constant, to be replaced with lief.ELF.ARCH.RISCV
+# https://github.com/lief-project/LIEF/pull/562
+LIEF_ELF_ARCH_RISCV = lief.ELF.ARCH(243)
# Debian 8 (Jessie) EOL: 2020. https://wiki.debian.org/DebianReleases#Production_Releases
#
@@ -43,12 +43,12 @@ from utils import determine_wellknown_cmd
MAX_VERSIONS = {
'GCC': (4,8,0),
'GLIBC': {
- pixie.EM_386: (2,17),
- pixie.EM_X86_64: (2,17),
- pixie.EM_ARM: (2,17),
- pixie.EM_AARCH64:(2,17),
- pixie.EM_PPC64: (2,17),
- pixie.EM_RISCV: (2,27),
+ lief.ELF.ARCH.i386: (2,17),
+ lief.ELF.ARCH.x86_64: (2,17),
+ lief.ELF.ARCH.ARM: (2,17),
+ lief.ELF.ARCH.AARCH64:(2,17),
+ lief.ELF.ARCH.PPC64: (2,17),
+ LIEF_ELF_ARCH_RISCV: (2,27),
},
'LIBATOMIC': (1,0),
'V': (0,5,0), # xkb (bitcoin-qt only)
@@ -58,10 +58,35 @@ MAX_VERSIONS = {
# Ignore symbols that are exported as part of every executable
IGNORE_EXPORTS = {
-'_edata', '_end', '__end__', '_init', '__bss_start', '__bss_start__', '_bss_end__', '__bss_end__', '_fini', '_IO_stdin_used', 'stdin', 'stdout', 'stderr',
+'_edata', '_end', '__end__', '_init', '__bss_start', '__bss_start__', '_bss_end__',
+'__bss_end__', '_fini', '_IO_stdin_used', 'stdin', 'stdout', 'stderr',
'environ', '_environ', '__environ',
}
+# Expected linker-loader names can be found here:
+# https://sourceware.org/glibc/wiki/ABIList?action=recall&rev=16
+ELF_INTERPRETER_NAMES: Dict[lief.ELF.ARCH, Dict[lief.ENDIANNESS, str]] = {
+ lief.ELF.ARCH.i386: {
+ lief.ENDIANNESS.LITTLE: "/lib/ld-linux.so.2",
+ },
+ lief.ELF.ARCH.x86_64: {
+ lief.ENDIANNESS.LITTLE: "/lib64/ld-linux-x86-64.so.2",
+ },
+ lief.ELF.ARCH.ARM: {
+ lief.ENDIANNESS.LITTLE: "/lib/ld-linux-armhf.so.3",
+ },
+ lief.ELF.ARCH.AARCH64: {
+ lief.ENDIANNESS.LITTLE: "/lib/ld-linux-aarch64.so.1",
+ },
+ lief.ELF.ARCH.PPC64: {
+ lief.ENDIANNESS.BIG: "/lib64/ld64.so.1",
+ lief.ENDIANNESS.LITTLE: "/lib64/ld64.so.2",
+ },
+ LIEF_ELF_ARCH_RISCV: {
+ lief.ENDIANNESS.LITTLE: "/lib/ld-linux-riscv64-lp64d.so.1",
+ },
+}
+
# Allowed NEEDED libraries
ELF_ALLOWED_LIBRARIES = {
# bitcoind and bitcoin-qt
@@ -133,31 +158,8 @@ PE_ALLOWED_LIBRARIES = {
'WTSAPI32.dll',
}
-class CPPFilt(object):
- '''
- Demangle C++ symbol names.
-
- Use a pipe to the 'c++filt' command.
- '''
- def __init__(self):
- self.proc = subprocess.Popen(determine_wellknown_cmd('CPPFILT', 'c++filt'), stdin=subprocess.PIPE, stdout=subprocess.PIPE, universal_newlines=True)
-
- def __call__(self, mangled):
- self.proc.stdin.write(mangled + '\n')
- self.proc.stdin.flush()
- return self.proc.stdout.readline().rstrip()
-
- def close(self):
- self.proc.stdin.close()
- self.proc.stdout.close()
- self.proc.wait()
-
def check_version(max_versions, version, arch) -> bool:
- if '_' in version:
- (lib, _, ver) = version.rpartition('_')
- else:
- lib = version
- ver = '0'
+ (lib, _, ver) = version.rpartition('_')
ver = tuple([int(x) for x in ver.split('.')])
if not lib in max_versions:
return False
@@ -166,48 +168,45 @@ def check_version(max_versions, version, arch) -> bool:
else:
return ver <= max_versions[lib][arch]
-def check_imported_symbols(filename) -> bool:
- elf = pixie.load(filename)
- cppfilt = CPPFilt()
+def check_imported_symbols(binary) -> bool:
ok: bool = True
- for symbol in elf.dyn_symbols:
- if not symbol.is_import:
+ for symbol in binary.imported_symbols:
+ if not symbol.imported:
continue
- sym = symbol.name.decode()
- version = symbol.version.decode() if symbol.version is not None else None
- if version and not check_version(MAX_VERSIONS, version, elf.hdr.e_machine):
- print('{}: symbol {} from unsupported version {}'.format(filename, cppfilt(sym), version))
- ok = False
+
+ version = symbol.symbol_version if symbol.has_version else None
+
+ if version:
+ aux_version = version.symbol_version_auxiliary.name if version.has_auxiliary_version else None
+ if aux_version and not check_version(MAX_VERSIONS, aux_version, binary.header.machine_type):
+ print(f'{filename}: symbol {symbol.name} from unsupported version {version}')
+ ok = False
return ok
-def check_exported_symbols(filename) -> bool:
- elf = pixie.load(filename)
- cppfilt = CPPFilt()
+def check_exported_symbols(binary) -> bool:
ok: bool = True
- for symbol in elf.dyn_symbols:
- if not symbol.is_export:
+
+ for symbol in binary.dynamic_symbols:
+ if not symbol.exported:
continue
- sym = symbol.name.decode()
- if elf.hdr.e_machine == pixie.EM_RISCV or sym in IGNORE_EXPORTS:
+ name = symbol.name
+ if binary.header.machine_type == LIEF_ELF_ARCH_RISCV or name in IGNORE_EXPORTS:
continue
- print('{}: export of symbol {} not allowed'.format(filename, cppfilt(sym)))
+ print(f'{binary.name}: export of symbol {name} not allowed!')
ok = False
return ok
-def check_ELF_libraries(filename) -> bool:
+def check_ELF_libraries(binary) -> bool:
ok: bool = True
- elf = pixie.load(filename)
- for library_name in elf.query_dyn_tags(pixie.DT_NEEDED):
- assert(isinstance(library_name, bytes))
- if library_name.decode() not in ELF_ALLOWED_LIBRARIES:
- print('{}: NEEDED library {} is not allowed'.format(filename, library_name.decode()))
+ for library in binary.libraries:
+ if library not in ELF_ALLOWED_LIBRARIES:
+ print(f'{filename}: {library} is not in ALLOWED_LIBRARIES!')
ok = False
return ok
-def check_MACHO_libraries(filename) -> bool:
+def check_MACHO_libraries(binary) -> bool:
ok: bool = True
- binary = lief.parse(filename)
for dylib in binary.libraries:
split = dylib.name.split('/')
if split[-1] not in MACHO_ALLOWED_LIBRARIES:
@@ -215,40 +214,42 @@ def check_MACHO_libraries(filename) -> bool:
ok = False
return ok
-def check_MACHO_min_os(filename) -> bool:
- binary = lief.parse(filename)
+def check_MACHO_min_os(binary) -> bool:
if binary.build_version.minos == [10,15,0]:
return True
return False
-def check_MACHO_sdk(filename) -> bool:
- binary = lief.parse(filename)
+def check_MACHO_sdk(binary) -> bool:
if binary.build_version.sdk == [10, 15, 6]:
return True
return False
-def check_PE_libraries(filename) -> bool:
+def check_PE_libraries(binary) -> bool:
ok: bool = True
- binary = lief.parse(filename)
for dylib in binary.libraries:
if dylib not in PE_ALLOWED_LIBRARIES:
print(f'{dylib} is not in ALLOWED_LIBRARIES!')
ok = False
return ok
-def check_PE_subsystem_version(filename) -> bool:
- binary = lief.parse(filename)
+def check_PE_subsystem_version(binary) -> bool:
major: int = binary.optional_header.major_subsystem_version
minor: int = binary.optional_header.minor_subsystem_version
if major == 6 and minor == 1:
return True
return False
+def check_ELF_interpreter(binary) -> bool:
+ expected_interpreter = ELF_INTERPRETER_NAMES[binary.header.machine_type][binary.abstract.header.endianness]
+
+ return binary.concrete.interpreter == expected_interpreter
+
CHECKS = {
'ELF': [
('IMPORTED_SYMBOLS', check_imported_symbols),
('EXPORTED_SYMBOLS', check_exported_symbols),
- ('LIBRARY_DEPENDENCIES', check_ELF_libraries)
+ ('LIBRARY_DEPENDENCIES', check_ELF_libraries),
+ ('INTERPRETER_NAME', check_ELF_interpreter),
],
'MACHO': [
('DYNAMIC_LIBRARIES', check_MACHO_libraries),
@@ -261,30 +262,20 @@ CHECKS = {
]
}
-def identify_executable(executable) -> Optional[str]:
- with open(filename, 'rb') as f:
- magic = f.read(4)
- if magic.startswith(b'MZ'):
- return 'PE'
- elif magic.startswith(b'\x7fELF'):
- return 'ELF'
- elif magic.startswith(b'\xcf\xfa'):
- return 'MACHO'
- return None
-
if __name__ == '__main__':
retval: int = 0
for filename in sys.argv[1:]:
try:
- etype = identify_executable(filename)
- if etype is None:
- print(f'{filename}: unknown format')
+ binary = lief.parse(filename)
+ etype = binary.format.name
+ if etype == lief.EXE_FORMATS.UNKNOWN:
+ print(f'{filename}: unknown executable format')
retval = 1
continue
failed: List[str] = []
for (name, func) in CHECKS[etype]:
- if not func(filename):
+ if not func(binary):
failed.append(name)
if failed:
print(f'{filename}: failed {" ".join(failed)}')
diff --git a/contrib/devtools/test-security-check.py b/contrib/devtools/test-security-check.py
index 14058e2cc8..0af7cdf5e6 100755
--- a/contrib/devtools/test-security-check.py
+++ b/contrib/devtools/test-security-check.py
@@ -7,6 +7,7 @@ Test script for security-check.py
'''
import os
import subprocess
+from typing import List
import unittest
from utils import determine_wellknown_cmd
@@ -27,7 +28,16 @@ def clean_files(source, executable):
os.remove(executable)
def call_security_check(cc, source, executable, options):
- subprocess.run([*cc,source,'-o',executable] + options, check=True)
+ # This should behave the same as AC_TRY_LINK, so arrange well-known flags
+ # in the same order as autoconf would.
+ #
+ # See the definitions for ac_link in autoconf's lib/autoconf/c.m4 file for
+ # reference.
+ env_flags: List[str] = []
+ for var in ['CFLAGS', 'CPPFLAGS', 'LDFLAGS']:
+ env_flags += filter(None, os.environ.get(var, '').split(' '))
+
+ subprocess.run([*cc,source,'-o',executable] + env_flags + options, check=True)
p = subprocess.run(['./contrib/devtools/security-check.py',executable], stdout=subprocess.PIPE, universal_newlines=True)
return (p.returncode, p.stdout.rstrip())
diff --git a/contrib/devtools/test-symbol-check.py b/contrib/devtools/test-symbol-check.py
index 651589c11b..5246375fe3 100755
--- a/contrib/devtools/test-symbol-check.py
+++ b/contrib/devtools/test-symbol-check.py
@@ -13,7 +13,16 @@ import unittest
from utils import determine_wellknown_cmd
def call_symbol_check(cc: List[str], source, executable, options):
- subprocess.run([*cc,source,'-o',executable] + options, check=True)
+ # This should behave the same as AC_TRY_LINK, so arrange well-known flags
+ # in the same order as autoconf would.
+ #
+ # See the definitions for ac_link in autoconf's lib/autoconf/c.m4 file for
+ # reference.
+ env_flags: List[str] = []
+ for var in ['CFLAGS', 'CPPFLAGS', 'LDFLAGS']:
+ env_flags += filter(None, os.environ.get(var, '').split(' '))
+
+ subprocess.run([*cc,source,'-o',executable] + env_flags + options, check=True)
p = subprocess.run(['./contrib/devtools/symbol-check.py',executable], stdout=subprocess.PIPE, universal_newlines=True)
os.remove(source)
os.remove(executable)
@@ -51,7 +60,7 @@ class TestSymbolChecks(unittest.TestCase):
''')
self.assertEqual(call_symbol_check(cc, source, executable, ['-lm']),
- (1, executable + ': symbol nextup from unsupported version GLIBC_2.24\n' +
+ (1, executable + ': symbol nextup from unsupported version GLIBC_2.24(3)\n' +
executable + ': failed IMPORTED_SYMBOLS'))
# -lutil is part of the libc6 package so a safe bet that it's installed
@@ -70,7 +79,7 @@ class TestSymbolChecks(unittest.TestCase):
''')
self.assertEqual(call_symbol_check(cc, source, executable, ['-lutil']),
- (1, executable + ': NEEDED library libutil.so.1 is not allowed\n' +
+ (1, executable + ': libutil.so.1 is not in ALLOWED_LIBRARIES!\n' +
executable + ': failed LIBRARY_DEPENDENCIES'))
# finally, check a simple conforming binary
diff --git a/contrib/guix/libexec/build.sh b/contrib/guix/libexec/build.sh
index 93526f8c45..e009f97c60 100755
--- a/contrib/guix/libexec/build.sh
+++ b/contrib/guix/libexec/build.sh
@@ -169,8 +169,8 @@ case "$HOST" in
arm-linux-gnueabihf) echo /lib/ld-linux-armhf.so.3 ;;
aarch64-linux-gnu) echo /lib/ld-linux-aarch64.so.1 ;;
riscv64-linux-gnu) echo /lib/ld-linux-riscv64-lp64d.so.1 ;;
- powerpc64-linux-gnu) echo /lib/ld64.so.1;;
- powerpc64le-linux-gnu) echo /lib/ld64.so.2;;
+ powerpc64-linux-gnu) echo /lib64/ld64.so.1;;
+ powerpc64le-linux-gnu) echo /lib64/ld64.so.2;;
*) exit 1 ;;
esac
)
@@ -297,7 +297,7 @@ mkdir -p "$DISTSRC"
${HOST_CXXFLAGS:+CXXFLAGS="${HOST_CXXFLAGS}"} \
${HOST_LDFLAGS:+LDFLAGS="${HOST_LDFLAGS}"}
- sed -i.old 's/-lstdc++ //g' config.status libtool src/univalue/config.status src/univalue/libtool
+ sed -i.old 's/-lstdc++ //g' config.status libtool
# Build Bitcoin Core
make --jobs="$JOBS" ${V:+V=1}
diff --git a/contrib/tracing/README.md b/contrib/tracing/README.md
index 047354cda1..1f93474fa0 100644
--- a/contrib/tracing/README.md
+++ b/contrib/tracing/README.md
@@ -176,17 +176,12 @@ third acts as a duration threshold in milliseconds. When the `ConnectBlock()`
function takes longer than the threshold, information about the block, is
printed. For more details, see the header comment in the script.
-By default, `bpftrace` limits strings to 64 bytes due to the limited stack size
-in the kernel VM. Block hashes as zero-terminated hex strings are 65 bytes which
-exceed the string limit. The string size limit can be set to 65 bytes with the
-environment variable `BPFTRACE_STRLEN`.
-
The following command can be used to benchmark, for example, `ConnectBlock()`
between height 20000 and 38000 on SigNet while logging all blocks that take
longer than 25ms to connect.
```
-$ BPFTRACE_STRLEN=65 bpftrace contrib/tracing/connectblock_benchmark.bt 20000 38000 25
+$ bpftrace contrib/tracing/connectblock_benchmark.bt 20000 38000 25
```
In a different terminal, starting Bitcoin Core in SigNet mode and with
diff --git a/contrib/tracing/connectblock_benchmark.bt b/contrib/tracing/connectblock_benchmark.bt
index d268eff7f8..6e7a98ef07 100755
--- a/contrib/tracing/connectblock_benchmark.bt
+++ b/contrib/tracing/connectblock_benchmark.bt
@@ -4,11 +4,8 @@
USAGE:
- BPFTRACE_STRLEN=65 bpftrace contrib/tracing/connectblock_benchmark.bt <start height> <end height> <logging threshold in ms>
+ bpftrace contrib/tracing/connectblock_benchmark.bt <start height> <end height> <logging threshold in ms>
- - The environment variable BPFTRACE_STRLEN needs to be set to 65 chars as
- strings are limited to 64 chars by default. Hex strings with Bitcoin block
- hashes are 64 hex chars + 1 null-termination char.
- <start height> sets the height at which the benchmark should start. Setting
the start height to 0 starts the benchmark immediately, even before the
first block is connected.
@@ -23,7 +20,7 @@
EXAMPLES:
- BPFTRACE_STRLEN=65 bpftrace contrib/tracing/connectblock_benchmark.bt 300000 680000 1000
+ bpftrace contrib/tracing/connectblock_benchmark.bt 300000 680000 1000
When run together 'bitcoind -reindex', this benchmarks the time it takes to
connect the blocks between height 300.000 and 680.000 (inclusive) and prints
@@ -31,7 +28,7 @@
histogram with block connection times when the benchmark is finished.
- BPFTRACE_STRLEN=65 bpftrace contrib/tracing/connectblock_benchmark.bt 0 0 500
+ bpftrace contrib/tracing/connectblock_benchmark.bt 0 0 500
When running together 'bitcoind', all newly connected blocks that
take longer than 500ms to connect are logged. A histogram with block
@@ -107,14 +104,23 @@ usdt:./src/bitcoind:validation:block_connected /arg1 >= $1 && (arg1 <= $2 || $2
*/
usdt:./src/bitcoind:validation:block_connected / (uint64) arg5 / 1000> $3 /
{
- $hash_str = str(arg0);
+ $hash = arg0;
$height = (int32) arg1;
$transactions = (uint64) arg2;
$inputs = (int32) arg3;
$sigops = (int64) arg4;
$duration = (int64) arg5;
- printf("Block %d (%s) %4d tx %5d ins %5d sigops took %4d ms\n", $height, $hash_str, $transactions, $inputs, $sigops, (uint64) $duration / 1000);
+
+ printf("Block %d (", $height);
+ /* Prints each byte of the block hash as hex in big-endian (the block-explorer format) */
+ $p = $hash + 31;
+ unroll(32) {
+ $b = *(uint8*)$p;
+ printf("%02x", $b);
+ $p -= 1;
+ }
+ printf(") %4d tx %5d ins %5d sigops took %4d ms\n", $transactions, $inputs, $sigops, (uint64) $duration / 1000);
}
diff --git a/depends/packages/expat.mk b/depends/packages/expat.mk
index 902fe43be2..41c1114be0 100644
--- a/depends/packages/expat.mk
+++ b/depends/packages/expat.mk
@@ -23,5 +23,5 @@ define $(package)_stage_cmds
endef
define $(package)_postprocess_cmds
- rm lib/*.la
+ rm -rf share lib/*.la
endef
diff --git a/depends/packages/fontconfig.mk b/depends/packages/fontconfig.mk
index 0d5f94f380..22b5022f06 100644
--- a/depends/packages/fontconfig.mk
+++ b/depends/packages/fontconfig.mk
@@ -29,5 +29,5 @@ define $(package)_stage_cmds
endef
define $(package)_postprocess_cmds
- rm lib/*.la
+ rm -rf var lib/*.la
endef
diff --git a/depends/packages/freetype.mk b/depends/packages/freetype.mk
index a1584608e1..aebc8a5f3b 100644
--- a/depends/packages/freetype.mk
+++ b/depends/packages/freetype.mk
@@ -23,5 +23,5 @@ define $(package)_stage_cmds
endef
define $(package)_postprocess_cmds
- rm lib/*.la
+ rm -rf share/man lib/*.la
endef
diff --git a/depends/packages/libXau.mk b/depends/packages/libXau.mk
index 4c55c2df04..24e0e9d325 100644
--- a/depends/packages/libXau.mk
+++ b/depends/packages/libXau.mk
@@ -30,5 +30,5 @@ define $(package)_stage_cmds
endef
define $(package)_postprocess_cmds
- rm lib/*.la
+ rm -rf share lib/*.la
endef
diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk
index 9004b064d6..12e0494ad4 100644
--- a/depends/packages/qt.mk
+++ b/depends/packages/qt.mk
@@ -248,7 +248,6 @@ endef
define $(package)_config_cmds
export PKG_CONFIG_SYSROOT_DIR=/ && \
export PKG_CONFIG_LIBDIR=$(host_prefix)/lib/pkgconfig && \
- export PKG_CONFIG_PATH=$(host_prefix)/share/pkgconfig && \
cd qtbase && \
./configure -top-level $($(package)_config_opts)
endef
diff --git a/doc/README.md b/doc/README.md
index aabfe220bc..4845f00ade 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -71,6 +71,7 @@ The Bitcoin repo's [root README](/README.md) contains relevant information on th
### Miscellaneous
- [Assets Attribution](assets-attribution.md)
+- [Assumeutxo design](assumeutxo.md)
- [bitcoin.conf Configuration File](bitcoin-conf.md)
- [Files](files.md)
- [Fuzz-testing](fuzzing.md)
diff --git a/doc/assumeutxo.md b/doc/assumeutxo.md
new file mode 100644
index 0000000000..2726cf779b
--- /dev/null
+++ b/doc/assumeutxo.md
@@ -0,0 +1,138 @@
+# assumeutxo
+
+Assumeutxo is a feature that allows fast bootstrapping of a validating bitcoind
+instance with a very similar security model to assumevalid.
+
+The RPC commands `dumptxoutset` and `loadtxoutset` are used to respectively generate
+and load UTXO snapshots. The utility script `./contrib/devtools/utxo_snapshot.sh` may
+be of use.
+
+## General background
+
+- [assumeutxo proposal](https://github.com/jamesob/assumeutxo-docs/tree/2019-04-proposal/proposal)
+- [Github issue](https://github.com/bitcoin/bitcoin/issues/15605)
+- [draft PR](https://github.com/bitcoin/bitcoin/pull/15606)
+
+## Design notes
+
+- A new block index `nStatus` flag is introduced, `BLOCK_ASSUMED_VALID`, to mark block
+ index entries that are required to be assumed-valid by a chainstate created
+ from a UTXO snapshot. This flag is mostly used as a way to modify certain
+ CheckBlockIndex() logic to account for index entries that are pending validation by a
+ chainstate running asynchronously in the background. We also use this flag to control
+ which index entries are added to setBlockIndexCandidates during LoadBlockIndex().
+
+- Indexing implementations via BaseIndex can no longer assume that indexation happens
+ sequentially, since background validation chainstates can submit BlockConnected
+ events out of order with the active chain.
+
+- The concept of UTXO snapshots is treated as an implementation detail that lives
+ behind the ChainstateManager interface. The external presentation of the changes
+ required to facilitate the use of UTXO snapshots is the understanding that there are
+ now certain regions of the chain that can be temporarily assumed to be valid (using
+ the nStatus flag mentioned above). In certain cases, e.g. wallet rescanning, this is
+ very similar to dealing with a pruned chain.
+
+ Logic outside ChainstateManager should try not to know about snapshots, instead
+ preferring to work in terms of more general states like assumed-valid.
+
+
+## Chainstate phases
+
+Chainstate within the system goes through a number of phases when UTXO snapshots are
+used, as managed by `ChainstateManager`. At various points there can be multiple
+`CChainState` objects in existence to facilitate both maintaining the network tip and
+performing historical validation of the assumed-valid chain.
+
+It is worth noting that though there are multiple separate chainstates, those
+chainstates share use of a common block index (i.e. they hold the same `BlockManager`
+reference).
+
+The subheadings below outline the phases and the corresponding changes to chainstate
+data.
+
+### "Normal" operation via initial block download
+
+`ChainstateManager` manages a single CChainState object, for which
+`m_snapshot_blockhash` is null. This chainstate is (maybe obviously)
+considered active. This is the "traditional" mode of operation for bitcoind.
+
+| | |
+| ---------- | ----------- |
+| number of chainstates | 1 |
+| active chainstate | ibd |
+
+### User loads a UTXO snapshot via `loadtxoutset` RPC
+
+`ChainstateManager` initializes a new chainstate (see `ActivateSnapshot()`) to load the
+snapshot contents into. During snapshot load and validation (see
+`PopulateAndValidateSnapshot()`), the new chainstate is not considered active and the
+original chainstate remains in use as active.
+
+| | |
+| ---------- | ----------- |
+| number of chainstates | 2 |
+| active chainstate | ibd |
+
+Once the snapshot chainstate is loaded and validated, it is promoted to active
+chainstate and a sync to tip begins. A new chainstate directory is created in the
+datadir for the snapshot chainstate called
+`chainstate_[SHA256 blockhash of snapshot base block]`.
+
+| | |
+| ---------- | ----------- |
+| number of chainstates | 2 |
+| active chainstate | snapshot |
+
+The snapshot begins to sync to tip from its base block, technically in parallel with
+the original chainstate, but it is given priority during block download and is
+allocated most of the cache (see `MaybeRebalanceCaches()` and usages) as our chief
+consideration is getting to network tip.
+
+**Failure consideration:** if shutdown happens at any point during this phase, both
+chainstates will be detected during the next init and the process will resume.
+
+### Snapshot chainstate hits network tip
+
+Once the snapshot chainstate leaves IBD, caches are rebalanced
+(via `MaybeRebalanceCaches()` in `ActivateBestChain()`) and more cache is given
+to the background chainstate, which is responsible for doing full validation of the
+assumed-valid parts of the chain.
+
+**Note:** at this point, ValidationInterface callbacks will be coming in from both
+chainstates. Considerations here must be made for indexing, which may no longer be happening
+sequentially.
+
+### Background chainstate hits snapshot base block
+
+Once the tip of the background chainstate hits the base block of the snapshot
+chainstate, we stop use of the background chainstate by setting `m_stop_use` (not yet
+committed - see #15606), in `CompleteSnapshotValidation()`, which is checked in
+`ActivateBestChain()`). We hash the background chainstate's UTXO set contents and
+ensure it matches the compiled value in `CMainParams::m_assumeutxo_data`.
+
+The background chainstate data lingers on disk until shutdown, when in
+`ChainstateManager::Reset()`, the background chainstate is cleaned up with
+`ValidatedSnapshotShutdownCleanup()`, which renames the `chainstate_[hash]` datadir as
+`chainstate`.
+
+| | |
+| ---------- | ----------- |
+| number of chainstates | 2 (ibd has `m_stop_use=true`) |
+| active chainstate | snapshot |
+
+**Failure consideration:** if bitcoind unexpectedly halts after `m_stop_use` is set on
+the background chainstate but before `CompleteSnapshotValidation()` can finish, the
+need to complete snapshot validation will be detected on subsequent init by
+`ChainstateManager::CheckForUncleanShutdown()`.
+
+### Bitcoind restarts sometime after snapshot validation has completed
+
+When bitcoind initializes again, what began as the snapshot chainstate is now
+indistinguishable from a chainstate that has been built from the traditional IBD
+process, and will be initialized as such.
+
+| | |
+| ---------- | ----------- |
+| number of chainstates | 1 |
+| active chainstate | ibd |
diff --git a/doc/bips.md b/doc/bips.md
index 45954bcfd8..b5fa9315d3 100644
--- a/doc/bips.md
+++ b/doc/bips.md
@@ -57,3 +57,11 @@ BIPs that are implemented by Bitcoin Core (up-to-date up to **v22.0**):
with mainnet activation as of **v0.21.1** ([PR 21377](https://github.com/bitcoin/bitcoin/pull/21377),
[PR 21686](https://github.com/bitcoin/bitcoin/pull/21686)).
* [`BIP 350`](https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki): Addresses for native v1+ segregated Witness outputs use Bech32m instead of Bech32 as of **v22.0** ([PR 20861](https://github.com/bitcoin/bitcoin/pull/20861)).
+* [`BIP 380`](https://github.com/bitcoin/bips/blob/master/bip-0380.mediawiki)
+ [`381`](https://github.com/bitcoin/bips/blob/master/bip-0381.mediawiki)
+ [`382`](https://github.com/bitcoin/bips/blob/master/bip-0382.mediawiki)
+ [`383`](https://github.com/bitcoin/bips/blob/master/bip-0383.mediawiki)
+ [`384`](https://github.com/bitcoin/bips/blob/master/bip-0384.mediawiki)
+ [`385`](https://github.com/bitcoin/bips/blob/master/bip-0385.mediawiki):
+ Output Script Descriptors, and most of Script Expressions are implemented as of **v0.17.0** ([PR 13697](https://github.com/bitcoin/bitcoin/pull/13697)).
+* [`BIP 386`](https://github.com/bitcoin/bips/blob/master/bip-0386.mediawiki): tr() Output Script Descriptors are implemented as of **v22.0** ([PR 22051](https://github.com/bitcoin/bitcoin/pull/22051)).
diff --git a/doc/descriptors.md b/doc/descriptors.md
index 3bbb626a42..57a0f99d70 100644
--- a/doc/descriptors.md
+++ b/doc/descriptors.md
@@ -139,6 +139,47 @@ Key order does not matter for `sortedmulti()`. `sortedmulti()` behaves in the sa
as `multi()` does but the keys are reordered in the resulting script such that they
are lexicographically ordered as described in BIP67.
+#### Basic multisig example
+
+For a good example of a basic M-of-N multisig between multiple participants using descriptor
+wallets and PSBTs, as well as a signing flow, see [this functional test](/test/functional/wallet_multisig_descriptor_psbt.py).
+
+Disclaimers: It is important to note that this example serves as a quick-start and is kept basic for readability. A downside of the approach
+outlined here is that each participant must maintain (and backup) two separate wallets: a signer and the corresponding multisig.
+It should also be noted that privacy best-practices are not "by default" here - participants should take care to only use the signer to sign
+transactions related to the multisig. Lastly, it is not recommended to use anything other than a Bitcoin Core descriptor wallet to serve as your
+signer(s). Other wallets, whether hardware or software, likely impose additional checks and safeguards to prevent users from signing transactions that
+could lead to loss of funds, or are deemed security hazards. Conforming to various 3rd-party checks and verifications is not in the scope of this example.
+
+The basic steps are:
+
+ 1. Every participant generates an xpub. The most straightforward way is to create a new descriptor wallet which we will refer to as
+ the participant's signer wallet. Avoid reusing this wallet for any purpose other than signing transactions from the
+ corresponding multisig we are about to create. Hint: extract the wallet's xpubs using `listdescriptors` and pick the one from the
+ `pkh` descriptor since it's least likely to be accidentally reused (legacy addresses)
+ 2. Create a watch-only descriptor wallet (blank, private keys disabled). Now the multisig is created by importing the two descriptors:
+ `wsh(sortedmulti(<M>,XPUB1/0/*,XPUB2/0/*,…,XPUBN/0/*))` and `wsh(sortedmulti(<M>,XPUB1/1/*,XPUB2/1/*,…,XPUBN/1/*))`
+ (one descriptor w/ `0` for receiving addresses and another w/ `1` for change). Every participant does this
+ 3. A receiving address is generated for the multisig. As a check to ensure step 2 was done correctly, every participant
+ should verify they get the same addresses
+ 4. Funds are sent to the resulting address
+ 5. A sending transaction from the multisig is created using `walletcreatefundedpsbt` (anyone can initiate this). It is simple to do
+ this in the GUI by going to the `Send` tab in the multisig wallet and creating an unsigned transaction (PSBT)
+ 6. At least `M` participants check the PSBT with their multisig using `decodepsbt` to verify the transaction is OK before signing it.
+ 7. (If OK) the participant signs the PSBT with their signer wallet using `walletprocesspsbt`. It is simple to do this in the GUI by
+ loading the PSBT from file and signing it
+ 8. The signed PSBTs are collected with `combinepsbt`, finalized w/ `finalizepsbt`, and then the resulting transaction is broadcasted
+ to the network. Note that any wallet (eg one of the signers or multisig) is capable of doing this.
+ 9. Checks that balances are correct after the transaction has been included in a block
+
+You may prefer a daisy chained signing flow where each participant signs the PSBT one after another until
+the PSBT has been signed `M` times and is "complete." For the most part, the steps above remain the same, except (6, 7)
+change slightly from signing the original PSBT in parallel to signing it in series. `combinepsbt` is not necessary with
+this signing flow and the last (`m`th) signer can just broadcast the PSBT after signing. Note that a parallel signing flow may be
+preferable in cases where there are more signers. This signing flow is also included in the test / Python example.
+[The test](/test/functional/wallet_multisig_descriptor_psbt.py) is meant to be documentation as much as it is a functional test, so
+it is kept as simple and readable as possible.
+
### BIP32 derived keys and chains
Most modern wallet software and hardware uses keys that are derived using
diff --git a/doc/developer-notes.md b/doc/developer-notes.md
index 0a5a7066ab..a05ea93a46 100644
--- a/doc/developer-notes.md
+++ b/doc/developer-notes.md
@@ -12,6 +12,7 @@ Developer Notes
- [Generating Documentation](#generating-documentation)
- [Development tips and tricks](#development-tips-and-tricks)
- [Compiling for debugging](#compiling-for-debugging)
+ - [Show sources in debugging](#show-sources-in-debugging)
- [Compiling for gprof profiling](#compiling-for-gprof-profiling)
- [`debug.log`](#debuglog)
- [Signet, testnet, and regtest modes](#signet-testnet-and-regtest-modes)
@@ -253,6 +254,35 @@ Development tips and tricks
Run configure with `--enable-debug` to add additional compiler flags that
produce better debugging builds.
+### Show sources in debugging
+
+If you have ccache enabled, absolute paths are stripped from debug information
+with the -fdebug-prefix-map and -fmacro-prefix-map options (if supported by the
+compiler). This might break source file detection in case you move binaries
+after compilation, debug from the directory other than the project root or use
+an IDE that only supports absolute paths for debugging.
+
+There are a few possible fixes:
+
+1. Configure source file mapping.
+
+For `gdb` create or append to `.gdbinit` file:
+```
+set substitute-path ./src /path/to/project/root/src
+```
+
+For `lldb` create or append to `.lldbinit` file:
+```
+settings set target.source-map ./src /path/to/project/root/src
+```
+
+2. Add a symlink to the `./src` directory:
+```
+ln -s /path/to/project/root/src src
+```
+
+3. Use `debugedit` to modify debug information in the binary.
+
### Compiling for gprof profiling
Run configure with the `--enable-gprof` option, then make.
diff --git a/doc/psbt.md b/doc/psbt.md
index c411b31d5d..0f31cb8eba 100644
--- a/doc/psbt.md
+++ b/doc/psbt.md
@@ -92,6 +92,9 @@ hardware implementations will typically implement multiple roles simultaneously.
#### Multisig with multiple Bitcoin Core instances
+For a quick start see [Basic M-of-N multisig example using descriptor wallets and PSBTs](./descriptors.md#basic-multisig-example).
+If you are using legacy wallets feel free to continue with the example provided here.
+
Alice, Bob, and Carol want to create a 2-of-3 multisig address. They're all using
Bitcoin Core. We assume their wallets only contain the multisig funds. In case
they also have a personal wallet, this can be accomplished through the
diff --git a/doc/release-notes-22539.md b/doc/release-notes-22539.md
deleted file mode 100644
index 9f56071451..0000000000
--- a/doc/release-notes-22539.md
+++ /dev/null
@@ -1,8 +0,0 @@
-Notable changes
-===============
-
-P2P and network changes
------------------------
-
-- Fee estimation now takes the feerate of replacement (RBF) transactions into
- account.
diff --git a/doc/release-notes-23093.md b/doc/release-notes-23093.md
new file mode 100644
index 0000000000..68fbaec53c
--- /dev/null
+++ b/doc/release-notes-23093.md
@@ -0,0 +1,11 @@
+Notable changes
+===============
+
+Updated RPCs
+------------
+
+- `upgradewallet` will now automatically flush the keypool if upgrading
+from a non-HD wallet to an HD wallet, to immediately start using the
+newly-generated HD keys.
+- a new RPC `newkeypool` has been added, which will flush (entirely
+clear and refill) the keypool.
diff --git a/doc/release-notes.md b/doc/release-notes.md
index 81e79dd3a9..b460cd3eb2 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -61,6 +61,12 @@ P2P and network changes
They will become eligible for address gossip after sending an ADDR, ADDRV2,
or GETADDR message. (#21528)
+Fee estimation changes
+----------------------
+
+- Fee estimation now takes the feerate of replacement (RBF) transactions into
+ account. (#22539)
+
Rescan startup parameter removed
--------------------------------
@@ -76,6 +82,14 @@ Updated RPCs
`gettransaction verbose=true` and REST endpoints `/rest/tx`, `/rest/getutxos`,
`/rest/block` no longer return the `addresses` and `reqSigs` fields, which
were previously deprecated in 22.0. (#22650)
+- The `getblock` RPC command now supports verbose level 3 containing transaction inputs
+ `prevout` information. The existing `/rest/block/` REST endpoint is modified to contain
+ this information too. Every `vin` field will contain an additional `prevout` subfield
+ describing the spent output. `prevout` contains the following keys:
+ - `generated` - true if the spent coins was a coinbase.
+ - `height`
+ - `value`
+ - `scriptPubKey`
- `listunspent` now includes `ancestorcount`, `ancestorsize`, and
`ancestorfees` for each transaction output that is still in the mempool.
diff --git a/doc/release-process.md b/doc/release-process.md
index 6a5202d0f9..14567d4f15 100644
--- a/doc/release-process.md
+++ b/doc/release-process.md
@@ -60,7 +60,7 @@ Release Process
To tag the version (or release candidate) in git, use the `make-tag.py` script from [bitcoin-maintainer-tools](https://github.com/bitcoin-core/bitcoin-maintainer-tools). From the root of the repository run:
- ../bitcoin-maintainer-tools/make-tag.py v(new version, e.g. 0.20.0)
+ ../bitcoin-maintainer-tools/make-tag.py v(new version, e.g. 23.0)
This will perform a few last-minute consistency checks in the build system files, and if they pass, create a signed tag.
@@ -253,6 +253,10 @@ cat "$VERSION"/*/all.SHA256SUMS.asc > SHA256SUMS.asc
- bitcoincore.org maintained versions update:
[table](https://github.com/bitcoin-core/bitcoincore.org/commits/master/_includes/posts/maintenance-table.md)
+ - Delete post-EOL [release branches](https://github.com/bitcoin/bitcoin/branches/all) and create a tag `v${branch_name}-final`.
+
+ - Delete ["Needs backport" labels](https://github.com/bitcoin/bitcoin/labels?q=backport) for non-existing branches.
+
- bitcoincore.org RPC documentation update
- Install [golang](https://golang.org/doc/install)
diff --git a/doc/tracing.md b/doc/tracing.md
index 87fc9603fe..57104c43a0 100644
--- a/doc/tracing.md
+++ b/doc/tracing.md
@@ -101,19 +101,12 @@ Is called *after* a block is connected to the chain. Can, for example, be used
to benchmark block connections together with `-reindex`.
Arguments passed:
-1. Block Header Hash as `pointer to C-style String` (64 characters)
+1. Block Header Hash as `pointer to unsigned chars` (i.e. 32 bytes in little-endian)
2. Block Height as `int32`
3. Transactions in the Block as `uint64`
4. Inputs spend in the Block as `int32`
5. SigOps in the Block (excluding coinbase SigOps) `uint64`
6. Time it took to connect the Block in microseconds (µs) as `uint64`
-7. Block Header Hash as `pointer to unsigned chars` (i.e. 32 bytes in little-endian)
-
-Note: The 7th argument can't be accessed by bpftrace and is purposefully chosen
-to be the block header hash as bytes. See [bpftrace argument limit] for more
-details.
-
-[bpftrace argument limit]: #bpftrace-argument-limit
## Adding tracepoints to Bitcoin Core
diff --git a/src/Makefile.am b/src/Makefile.am
index 12fdc9ad75..3f5c3a2f4a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -6,7 +6,7 @@
print-%: FORCE
@echo '$*'='$($*)'
-DIST_SUBDIRS = secp256k1 univalue
+DIST_SUBDIRS = secp256k1
AM_LDFLAGS = $(LIBTOOL_LDFLAGS) $(HARDENED_LDFLAGS) $(GPROF_LDFLAGS) $(SANITIZER_LDFLAGS)
AM_CXXFLAGS = $(DEBUG_CXXFLAGS) $(HARDENED_CXXFLAGS) $(WARN_CXXFLAGS) $(NOWARN_CXXFLAGS) $(ERROR_CXXFLAGS) $(GPROF_CXXFLAGS) $(SANITIZER_CXXFLAGS)
@@ -15,18 +15,7 @@ AM_LIBTOOLFLAGS = --preserve-dup-deps
PTHREAD_FLAGS = $(PTHREAD_CFLAGS) $(PTHREAD_LIBS)
EXTRA_LIBRARIES =
-if EMBEDDED_UNIVALUE
-LIBUNIVALUE = univalue/libunivalue.la
-
-$(LIBUNIVALUE): $(wildcard univalue/lib/*) $(wildcard univalue/include/*)
- $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F)
-else
-LIBUNIVALUE = $(UNIVALUE_LIBS)
-endif
-
-BITCOIN_INCLUDES=-I$(builddir) -I$(srcdir)/secp256k1/include $(BDB_CPPFLAGS) $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS)
-
-BITCOIN_INCLUDES += $(UNIVALUE_CFLAGS)
+BITCOIN_INCLUDES=-I$(builddir) -I$(srcdir)/secp256k1/include -I$(srcdir)/$(UNIVALUE_INCLUDE_DIR_INT) $(BDB_CPPFLAGS) $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS)
LIBBITCOIN_SERVER=libbitcoin_server.a
LIBBITCOIN_COMMON=libbitcoin_common.a
@@ -80,6 +69,7 @@ EXTRA_LIBRARIES += \
$(LIBBITCOIN_ZMQ)
lib_LTLIBRARIES = $(LIBBITCOINCONSENSUS)
+noinst_LTLIBRARIES =
bin_PROGRAMS =
noinst_PROGRAMS =
@@ -124,7 +114,6 @@ BITCOIN_CORE_H = \
bech32.h \
blockencodings.h \
blockfilter.h \
- bloom.h \
chain.h \
chainparams.h \
chainparamsbase.h \
@@ -132,6 +121,7 @@ BITCOIN_CORE_H = \
checkqueue.h \
clientversion.h \
coins.h \
+ common/bloom.h \
compat.h \
compat/assumptions.h \
compat/byteswap.h \
@@ -539,9 +529,9 @@ libbitcoin_common_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_common_a_SOURCES = \
base58.cpp \
bech32.cpp \
- bloom.cpp \
chainparams.cpp \
coins.cpp \
+ common/bloom.cpp \
compressor.cpp \
core_read.cpp \
core_write.cpp \
@@ -713,6 +703,7 @@ bitcoin_tx_LDADD += $(BOOST_LIBS)
# bitcoin-wallet binary #
bitcoin_wallet_SOURCES = bitcoin-wallet.cpp
+bitcoin_wallet_SOURCES += init/bitcoin-wallet.cpp
bitcoin_wallet_CPPFLAGS = $(bitcoin_bin_cppflags)
bitcoin_wallet_CXXFLAGS = $(bitcoin_bin_cxxflags)
bitcoin_wallet_LDFLAGS = $(bitcoin_bin_ldflags)
@@ -797,7 +788,6 @@ $(top_srcdir)/$(subdir)/config/bitcoin-config.h.in: $(am__configure_deps)
clean-local:
-$(MAKE) -C secp256k1 clean
- -$(MAKE) -C univalue clean
-rm -f leveldb/*/*.gcda leveldb/*/*.gcno leveldb/helpers/memenv/*.gcda leveldb/helpers/memenv/*.gcno
-rm -f config.h
-rm -rf test/__pycache__
@@ -808,20 +798,8 @@ clean-local:
$(AM_V_GEN) $(WINDRES) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(CPPFLAGS) -DWINDRES_PREPROC -i $< -o $@
check-symbols: $(bin_PROGRAMS)
-if TARGET_DARWIN
- @echo "Checking macOS dynamic libraries..."
+ @echo "Running symbol and dynamic library checks..."
$(AM_V_at) $(PYTHON) $(top_srcdir)/contrib/devtools/symbol-check.py $(bin_PROGRAMS)
-endif
-
-if TARGET_WINDOWS
- @echo "Checking Windows dynamic libraries..."
- $(AM_V_at) $(PYTHON) $(top_srcdir)/contrib/devtools/symbol-check.py $(bin_PROGRAMS)
-endif
-
-if TARGET_LINUX
- @echo "Checking glibc back compat..."
- $(AM_V_at) CPPFILT='$(CPPFILT)' $(PYTHON) $(top_srcdir)/contrib/devtools/symbol-check.py $(bin_PROGRAMS)
-endif
check-security: $(bin_PROGRAMS)
if HARDEN
@@ -864,11 +842,8 @@ nodist_libbitcoin_ipc_a_SOURCES = $(libbitcoin_ipc_mpgen_output)
CLEANFILES += $(libbitcoin_ipc_mpgen_output)
endif
-if EMBEDDED_LEVELDB
include Makefile.crc32c.include
include Makefile.leveldb.include
-endif
-
include Makefile.test_util.include
include Makefile.test_fuzz.include
@@ -887,3 +862,5 @@ endif
if ENABLE_QT_TESTS
include Makefile.qttest.include
endif
+
+include Makefile.univalue.include
diff --git a/src/Makefile.leveldb.include b/src/Makefile.leveldb.include
index ce1f93f11f..3bec92482a 100644
--- a/src/Makefile.leveldb.include
+++ b/src/Makefile.leveldb.include
@@ -8,9 +8,10 @@ LIBMEMENV_INT = leveldb/libmemenv.a
EXTRA_LIBRARIES += $(LIBLEVELDB_INT)
EXTRA_LIBRARIES += $(LIBMEMENV_INT)
-LIBLEVELDB += $(LIBLEVELDB_INT) $(LIBCRC32C)
-LIBMEMENV += $(LIBMEMENV_INT)
+LIBLEVELDB = $(LIBLEVELDB_INT) $(LIBCRC32C)
+LIBMEMENV = $(LIBMEMENV_INT)
+LEVELDB_CPPFLAGS =
LEVELDB_CPPFLAGS += -I$(srcdir)/leveldb/include
LEVELDB_CPPFLAGS += -I$(srcdir)/leveldb/helpers/memenv
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index f4b0b3adbe..e6ea8bdf61 100644
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -168,10 +168,10 @@ BITCOIN_QT_H = \
qt/walletview.h \
qt/winshutdownmonitor.h
-RES_FONTS = \
+QT_RES_FONTS = \
qt/res/fonts/RobotoMono-Bold.ttf
-RES_ICONS = \
+QT_RES_ICONS = \
qt/res/icons/add.png \
qt/res/icons/address-book.png \
qt/res/icons/bitcoin.ico \
@@ -287,9 +287,9 @@ if ENABLE_WALLET
BITCOIN_QT_CPP += $(BITCOIN_QT_WALLET_CPP)
endif # ENABLE_WALLET
-RES_ANIMATION = $(wildcard $(srcdir)/qt/res/animation/spinner-*.png)
+QT_RES_ANIMATION = $(wildcard $(srcdir)/qt/res/animation/spinner-*.png)
-BITCOIN_RC = qt/res/bitcoin-qt-res.rc
+BITCOIN_QT_RC = qt/res/bitcoin-qt-res.rc
BITCOIN_QT_INCLUDES = -DQT_NO_KEYWORDS -DQT_USE_QSTRINGBUILDER
@@ -299,7 +299,7 @@ qt_libbitcoinqt_a_CXXFLAGS = $(AM_CXXFLAGS) $(QT_PIE_FLAGS)
qt_libbitcoinqt_a_OBJCXXFLAGS = $(AM_OBJCXXFLAGS) $(QT_PIE_FLAGS)
qt_libbitcoinqt_a_SOURCES = $(BITCOIN_QT_CPP) $(BITCOIN_QT_H) $(QT_FORMS_UI) \
- $(QT_QRC) $(QT_QRC_LOCALE) $(QT_TS) $(RES_FONTS) $(RES_ICONS) $(RES_ANIMATION)
+ $(QT_QRC) $(QT_QRC_LOCALE) $(QT_TS) $(QT_RES_FONTS) $(QT_RES_ICONS) $(QT_RES_ANIMATION)
if TARGET_DARWIN
qt_libbitcoinqt_a_SOURCES += $(BITCOIN_MM)
endif
@@ -321,7 +321,7 @@ bitcoin_qt_cxxflags = $(AM_CXXFLAGS) $(QT_PIE_FLAGS)
bitcoin_qt_sources = qt/main.cpp
if TARGET_WINDOWS
- bitcoin_qt_sources += $(BITCOIN_RC)
+ bitcoin_qt_sources += $(BITCOIN_QT_RC)
endif
bitcoin_qt_ldadd = qt/libbitcoinqt.a $(LIBBITCOIN_SERVER)
if ENABLE_WALLET
@@ -338,15 +338,15 @@ bitcoin_qt_libtoolflags = $(AM_LIBTOOLFLAGS) --tag CXX
qt_bitcoin_qt_CPPFLAGS = $(bitcoin_qt_cppflags)
qt_bitcoin_qt_CXXFLAGS = $(bitcoin_qt_cxxflags)
-qt_bitcoin_qt_SOURCES = $(bitcoin_qt_sources) init/bitcoind.cpp
+qt_bitcoin_qt_SOURCES = $(bitcoin_qt_sources) init/bitcoin-qt.cpp
qt_bitcoin_qt_LDADD = $(bitcoin_qt_ldadd)
qt_bitcoin_qt_LDFLAGS = $(bitcoin_qt_ldflags)
qt_bitcoin_qt_LIBTOOLFLAGS = $(bitcoin_qt_libtoolflags)
bitcoin_gui_CPPFLAGS = $(bitcoin_qt_cppflags)
bitcoin_gui_CXXFLAGS = $(bitcoin_qt_cxxflags)
-bitcoin_gui_SOURCES = $(bitcoin_qt_sources) init/bitcoind.cpp
-bitcoin_gui_LDADD = $(bitcoin_qt_ldadd)
+bitcoin_gui_SOURCES = $(bitcoin_qt_sources) init/bitcoin-gui.cpp
+bitcoin_gui_LDADD = $(bitcoin_qt_ldadd) $(LIBBITCOIN_IPC) $(LIBBITCOIN_UTIL) $(LIBMULTIPROCESS_LIBS)
bitcoin_gui_LDFLAGS = $(bitcoin_qt_ldflags)
bitcoin_gui_LIBTOOLFLAGS = $(bitcoin_qt_libtoolflags)
@@ -371,7 +371,7 @@ $(QT_QRC_LOCALE_CPP): $(QT_QRC_LOCALE) $(QT_QM)
$(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(RCC) -name bitcoin_locale --format-version 1 $(@D)/temp_$(<F) > $@
@rm $(@D)/temp_$(<F)
-$(QT_QRC_CPP): $(QT_QRC) $(QT_FORMS_H) $(RES_FONTS) $(RES_ICONS) $(RES_ANIMATION)
+$(QT_QRC_CPP): $(QT_QRC) $(QT_FORMS_H) $(QT_RES_FONTS) $(QT_RES_ICONS) $(QT_RES_ANIMATION)
@test -f $(RCC)
$(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(RCC) -name bitcoin --format-version 1 $< > $@
diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include
index 8a5521eeb5..797e1f9a97 100644
--- a/src/Makefile.qttest.include
+++ b/src/Makefile.qttest.include
@@ -28,7 +28,7 @@ qt_test_test_bitcoin_qt_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BITCOIN_
$(QT_INCLUDES) $(QT_TEST_INCLUDES)
qt_test_test_bitcoin_qt_SOURCES = \
- init/bitcoind.cpp \
+ init/bitcoin-qt.cpp \
qt/test/apptests.cpp \
qt/test/rpcnestedtests.cpp \
qt/test/test_main.cpp \
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index d70793ffa9..27f9382631 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -350,8 +350,26 @@ if ENABLE_BENCH
endif
endif
$(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C secp256k1 check
-if EMBEDDED_UNIVALUE
- $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C univalue check
+
+if !ENABLE_FUZZ
+UNIVALUE_TESTS = univalue/test/object univalue/test/unitester univalue/test/no_nul
+noinst_PROGRAMS += $(UNIVALUE_TESTS)
+TESTS += $(UNIVALUE_TESTS)
+
+univalue_test_unitester_SOURCES = $(UNIVALUE_TEST_UNITESTER_INT)
+univalue_test_unitester_LDADD = $(LIBUNIVALUE)
+univalue_test_unitester_CPPFLAGS = -I$(srcdir)/$(UNIVALUE_INCLUDE_DIR_INT) -DJSON_TEST_SRC=\"$(srcdir)/$(UNIVALUE_TEST_DATA_DIR_INT)\"
+univalue_test_unitester_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS)
+
+univalue_test_no_nul_SOURCES = $(UNIVALUE_TEST_NO_NUL_INT)
+univalue_test_no_nul_LDADD = $(LIBUNIVALUE)
+univalue_test_no_nul_CPPFLAGS = -I$(srcdir)/$(UNIVALUE_INCLUDE_DIR_INT)
+univalue_test_no_nul_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS)
+
+univalue_test_object_SOURCES = $(UNIVALUE_TEST_OBJECT_INT)
+univalue_test_object_LDADD = $(LIBUNIVALUE)
+univalue_test_object_CPPFLAGS = -I$(srcdir)/$(UNIVALUE_INCLUDE_DIR_INT)
+univalue_test_object_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS)
endif
%.cpp.test: %.cpp
diff --git a/src/Makefile.univalue.include b/src/Makefile.univalue.include
new file mode 100644
index 0000000000..3644e36368
--- /dev/null
+++ b/src/Makefile.univalue.include
@@ -0,0 +1,6 @@
+include univalue/sources.mk
+
+LIBUNIVALUE = libunivalue.la
+noinst_LTLIBRARIES += $(LIBUNIVALUE)
+libunivalue_la_SOURCES = $(UNIVALUE_LIB_SOURCES_INT) $(UNIVALUE_DIST_HEADERS_INT) $(UNIVALUE_LIB_HEADERS_INT) $(UNIVALUE_TEST_FILES_INT)
+libunivalue_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/$(UNIVALUE_INCLUDE_DIR_INT)
diff --git a/src/addrdb.cpp b/src/addrdb.cpp
index 50fd09101e..bdb1fc6b2b 100644
--- a/src/addrdb.cpp
+++ b/src/addrdb.cpp
@@ -58,7 +58,7 @@ bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data
if (fileout.IsNull()) {
fileout.fclose();
remove(pathTmp);
- return error("%s: Failed to open file %s", __func__, pathTmp.string());
+ return error("%s: Failed to open file %s", __func__, fs::PathToString(pathTmp));
}
// Serialize
@@ -70,7 +70,7 @@ bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data
if (!FileCommit(fileout.Get())) {
fileout.fclose();
remove(pathTmp);
- return error("%s: Failed to flush file %s", __func__, pathTmp.string());
+ return error("%s: Failed to flush file %s", __func__, fs::PathToString(pathTmp));
}
fileout.fclose();
@@ -122,8 +122,8 @@ void DeserializeFileDB(const fs::path& path, Data& data, int version)
} // namespace
CBanDB::CBanDB(fs::path ban_list_path)
- : m_banlist_dat(ban_list_path.string() + ".dat"),
- m_banlist_json(ban_list_path.string() + ".json")
+ : m_banlist_dat(ban_list_path + ".dat"),
+ m_banlist_json(ban_list_path + ".json")
{
}
@@ -143,7 +143,7 @@ bool CBanDB::Write(const banmap_t& banSet)
bool CBanDB::Read(banmap_t& banSet)
{
if (fs::exists(m_banlist_dat)) {
- LogPrintf("banlist.dat ignored because it can only be read by " PACKAGE_NAME " version 22.x. Remove %s to silence this warning.\n", m_banlist_dat);
+ LogPrintf("banlist.dat ignored because it can only be read by " PACKAGE_NAME " version 22.x. Remove %s to silence this warning.\n", fs::quoted(fs::PathToString(m_banlist_dat)));
}
// If the JSON banlist does not exist, then recreate it
if (!fs::exists(m_banlist_json)) {
@@ -155,7 +155,7 @@ bool CBanDB::Read(banmap_t& banSet)
if (!util::ReadSettings(m_banlist_json, settings, errors)) {
for (const auto& err : errors) {
- LogPrintf("Cannot load banlist %s: %s\n", m_banlist_json.string(), err);
+ LogPrintf("Cannot load banlist %s: %s\n", fs::PathToString(m_banlist_json), err);
}
return false;
}
@@ -163,7 +163,7 @@ bool CBanDB::Read(banmap_t& banSet)
try {
BanMapFromJson(settings[JSON_KEY], banSet);
} catch (const std::runtime_error& e) {
- LogPrintf("Cannot parse banlist %s: %s\n", m_banlist_json.string(), e.what());
+ LogPrintf("Cannot parse banlist %s: %s\n", fs::PathToString(m_banlist_json), e.what());
return false;
}
@@ -194,12 +194,12 @@ std::optional<bilingual_str> LoadAddrman(const std::vector<bool>& asmap, const A
} catch (const DbNotFoundError&) {
// Addrman can be in an inconsistent state after failure, reset it
addrman = std::make_unique<AddrMan>(asmap, /* deterministic */ false, /* consistency_check_ratio */ check_addrman);
- LogPrintf("Creating peers.dat because the file was not found (%s)\n", path_addr);
+ LogPrintf("Creating peers.dat because the file was not found (%s)\n", fs::quoted(fs::PathToString(path_addr)));
DumpPeerAddresses(args, *addrman);
} catch (const std::exception& e) {
addrman = nullptr;
return strprintf(_("Invalid or corrupt peers.dat (%s). If you believe this is a bug, please report it to %s. As a workaround, you can move the file (%s) out of the way (rename, move, or delete) to have a new one created on the next start."),
- e.what(), PACKAGE_BUGREPORT, path_addr);
+ e.what(), PACKAGE_BUGREPORT, fs::quoted(fs::PathToString(path_addr)));
}
return std::nullopt;
}
@@ -215,7 +215,7 @@ std::vector<CAddress> ReadAnchors(const fs::path& anchors_db_path)
std::vector<CAddress> anchors;
try {
DeserializeFileDB(anchors_db_path, anchors, CLIENT_VERSION | ADDRV2_FORMAT);
- LogPrintf("Loaded %i addresses from %s\n", anchors.size(), anchors_db_path.filename());
+ LogPrintf("Loaded %i addresses from %s\n", anchors.size(), fs::quoted(fs::PathToString(anchors_db_path.filename())));
} catch (const std::exception&) {
anchors.clear();
}
diff --git a/src/addrman.cpp b/src/addrman.cpp
index c364a7710b..825b5239c8 100644
--- a/src/addrman.cpp
+++ b/src/addrman.cpp
@@ -45,10 +45,7 @@ int AddrInfo::GetTriedBucket(const uint256& nKey, const std::vector<bool>& asmap
{
uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << GetKey()).GetCheapHash();
uint64_t hash2 = (CHashWriter(SER_GETHASH, 0) << nKey << GetGroup(asmap) << (hash1 % ADDRMAN_TRIED_BUCKETS_PER_GROUP)).GetCheapHash();
- int tried_bucket = hash2 % ADDRMAN_TRIED_BUCKET_COUNT;
- uint32_t mapped_as = GetMappedAS(asmap);
- LogPrint(BCLog::NET, "IP %s mapped to AS%i belongs to tried bucket %i\n", ToStringIP(), mapped_as, tried_bucket);
- return tried_bucket;
+ return hash2 % ADDRMAN_TRIED_BUCKET_COUNT;
}
int AddrInfo::GetNewBucket(const uint256& nKey, const CNetAddr& src, const std::vector<bool>& asmap) const
@@ -56,10 +53,7 @@ int AddrInfo::GetNewBucket(const uint256& nKey, const CNetAddr& src, const std::
std::vector<unsigned char> vchSourceGroupKey = src.GetGroup(asmap);
uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << GetGroup(asmap) << vchSourceGroupKey).GetCheapHash();
uint64_t hash2 = (CHashWriter(SER_GETHASH, 0) << nKey << vchSourceGroupKey << (hash1 % ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP)).GetCheapHash();
- int new_bucket = hash2 % ADDRMAN_NEW_BUCKET_COUNT;
- uint32_t mapped_as = GetMappedAS(asmap);
- LogPrint(BCLog::NET, "IP %s mapped to AS%i belongs to new bucket %i\n", ToStringIP(), mapped_as, new_bucket);
- return new_bucket;
+ return hash2 % ADDRMAN_NEW_BUCKET_COUNT;
}
int AddrInfo::GetBucketPosition(const uint256& nKey, bool fNew, int nBucket) const
@@ -177,7 +171,7 @@ void AddrManImpl::Serialize(Stream& s_) const
// Increment `lowest_compatible` iff a newly introduced format is incompatible with
// the previous one.
- static constexpr uint8_t lowest_compatible = Format::V3_BIP155;
+ static constexpr uint8_t lowest_compatible = Format::V4_MULTIPORT;
s << static_cast<uint8_t>(INCOMPATIBILITY_BASE + lowest_compatible);
s << nKey;
@@ -401,7 +395,7 @@ void AddrManImpl::Unserialize(Stream& s_)
}
}
-AddrInfo* AddrManImpl::Find(const CNetAddr& addr, int* pnId)
+AddrInfo* AddrManImpl::Find(const CService& addr, int* pnId)
{
AssertLockHeld(cs);
@@ -481,6 +475,7 @@ void AddrManImpl::ClearNew(int nUBucket, int nUBucketPos)
assert(infoDelete.nRefCount > 0);
infoDelete.nRefCount--;
vvNew[nUBucket][nUBucketPos] = -1;
+ LogPrint(BCLog::ADDRMAN, "Removed %s from new[%i][%i]\n", infoDelete.ToString(), nUBucket, nUBucketPos);
if (infoDelete.nRefCount == 0) {
Delete(nIdDelete);
}
@@ -532,6 +527,8 @@ void AddrManImpl::MakeTried(AddrInfo& info, int nId)
infoOld.nRefCount = 1;
vvNew[nUBucket][nUBucketPos] = nIdEvict;
nNew++;
+ LogPrint(BCLog::ADDRMAN, "Moved %s from tried[%i][%i] to new[%i][%i] to make space\n",
+ infoOld.ToString(), nKBucket, nKBucketPos, nUBucket, nUBucketPos);
}
assert(vvTried[nKBucket][nKBucketPos] == -1);
@@ -540,70 +537,13 @@ void AddrManImpl::MakeTried(AddrInfo& info, int nId)
info.fInTried = true;
}
-void AddrManImpl::Good_(const CService& addr, bool test_before_evict, int64_t nTime)
-{
- AssertLockHeld(cs);
-
- int nId;
-
- nLastGood = nTime;
-
- AddrInfo* pinfo = Find(addr, &nId);
-
- // if not found, bail out
- if (!pinfo)
- return;
-
- AddrInfo& info = *pinfo;
-
- // check whether we are talking about the exact same CService (including same port)
- if (info != addr)
- return;
-
- // update info
- info.nLastSuccess = nTime;
- info.nLastTry = nTime;
- info.nAttempts = 0;
- // nTime is not updated here, to avoid leaking information about
- // currently-connected peers.
-
- // if it is already in the tried set, don't do anything else
- if (info.fInTried)
- return;
-
- // if it is not in new, something bad happened
- if (!Assume(info.nRefCount > 0)) {
- return;
- }
-
- // which tried bucket to move the entry to
- int tried_bucket = info.GetTriedBucket(nKey, m_asmap);
- int tried_bucket_pos = info.GetBucketPosition(nKey, false, tried_bucket);
-
- // Will moving this address into tried evict another entry?
- if (test_before_evict && (vvTried[tried_bucket][tried_bucket_pos] != -1)) {
- // Output the entry we'd be colliding with, for debugging purposes
- auto colliding_entry = mapInfo.find(vvTried[tried_bucket][tried_bucket_pos]);
- LogPrint(BCLog::ADDRMAN, "Collision inserting element into tried table (%s), moving %s to m_tried_collisions=%d\n", colliding_entry != mapInfo.end() ? colliding_entry->second.ToString() : "", addr.ToString(), m_tried_collisions.size());
- if (m_tried_collisions.size() < ADDRMAN_SET_TRIED_COLLISION_SIZE) {
- m_tried_collisions.insert(nId);
- }
- } else {
- LogPrint(BCLog::ADDRMAN, "Moving %s to tried\n", addr.ToString());
-
- // move nId to the tried tables
- MakeTried(info, nId);
- }
-}
-
-bool AddrManImpl::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimePenalty)
+bool AddrManImpl::AddSingle(const CAddress& addr, const CNetAddr& source, int64_t nTimePenalty)
{
AssertLockHeld(cs);
if (!addr.IsRoutable())
return false;
- bool fNew = false;
int nId;
AddrInfo* pinfo = Find(addr, &nId);
@@ -644,13 +584,12 @@ bool AddrManImpl::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTi
pinfo = Create(addr, source, &nId);
pinfo->nTime = std::max((int64_t)0, (int64_t)pinfo->nTime - nTimePenalty);
nNew++;
- fNew = true;
}
int nUBucket = pinfo->GetNewBucket(nKey, source, m_asmap);
int nUBucketPos = pinfo->GetBucketPosition(nKey, true, nUBucket);
+ bool fInsert = vvNew[nUBucket][nUBucketPos] == -1;
if (vvNew[nUBucket][nUBucketPos] != nId) {
- bool fInsert = vvNew[nUBucket][nUBucketPos] == -1;
if (!fInsert) {
AddrInfo& infoExisting = mapInfo[vvNew[nUBucket][nUBucketPos]];
if (infoExisting.IsTerrible() || (infoExisting.nRefCount > 1 && pinfo->nRefCount == 0)) {
@@ -662,20 +601,26 @@ bool AddrManImpl::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTi
ClearNew(nUBucket, nUBucketPos);
pinfo->nRefCount++;
vvNew[nUBucket][nUBucketPos] = nId;
+ LogPrint(BCLog::ADDRMAN, "Added %s mapped to AS%i to new[%i][%i]\n",
+ addr.ToString(), addr.GetMappedAS(m_asmap), nUBucket, nUBucketPos);
} else {
if (pinfo->nRefCount == 0) {
Delete(nId);
}
}
}
- return fNew;
+ return fInsert;
}
-void AddrManImpl::Attempt_(const CService& addr, bool fCountFailure, int64_t nTime)
+void AddrManImpl::Good_(const CService& addr, bool test_before_evict, int64_t nTime)
{
AssertLockHeld(cs);
- AddrInfo* pinfo = Find(addr);
+ int nId;
+
+ nLastGood = nTime;
+
+ AddrInfo* pinfo = Find(addr, &nId);
// if not found, bail out
if (!pinfo)
@@ -683,10 +628,69 @@ void AddrManImpl::Attempt_(const CService& addr, bool fCountFailure, int64_t nTi
AddrInfo& info = *pinfo;
- // check whether we are talking about the exact same CService (including same port)
- if (info != addr)
+ // update info
+ info.nLastSuccess = nTime;
+ info.nLastTry = nTime;
+ info.nAttempts = 0;
+ // nTime is not updated here, to avoid leaking information about
+ // currently-connected peers.
+
+ // if it is already in the tried set, don't do anything else
+ if (info.fInTried)
return;
+ // if it is not in new, something bad happened
+ if (!Assume(info.nRefCount > 0)) {
+ return;
+ }
+
+ // which tried bucket to move the entry to
+ int tried_bucket = info.GetTriedBucket(nKey, m_asmap);
+ int tried_bucket_pos = info.GetBucketPosition(nKey, false, tried_bucket);
+
+ // Will moving this address into tried evict another entry?
+ if (test_before_evict && (vvTried[tried_bucket][tried_bucket_pos] != -1)) {
+ if (m_tried_collisions.size() < ADDRMAN_SET_TRIED_COLLISION_SIZE) {
+ m_tried_collisions.insert(nId);
+ }
+ // Output the entry we'd be colliding with, for debugging purposes
+ auto colliding_entry = mapInfo.find(vvTried[tried_bucket][tried_bucket_pos]);
+ LogPrint(BCLog::ADDRMAN, "Collision with %s while attempting to move %s to tried table. Collisions=%d\n",
+ colliding_entry != mapInfo.end() ? colliding_entry->second.ToString() : "",
+ addr.ToString(),
+ m_tried_collisions.size());
+ } else {
+ // move nId to the tried tables
+ MakeTried(info, nId);
+ LogPrint(BCLog::ADDRMAN, "Moved %s mapped to AS%i to tried[%i][%i]\n",
+ addr.ToString(), addr.GetMappedAS(m_asmap), tried_bucket, tried_bucket_pos);
+ }
+}
+
+bool AddrManImpl::Add_(const std::vector<CAddress> &vAddr, const CNetAddr& source, int64_t nTimePenalty)
+{
+ int added{0};
+ for (std::vector<CAddress>::const_iterator it = vAddr.begin(); it != vAddr.end(); it++) {
+ added += AddSingle(*it, source, nTimePenalty) ? 1 : 0;
+ }
+ if (added > 0) {
+ LogPrint(BCLog::ADDRMAN, "Added %i addresses (of %i) from %s: %i tried, %i new\n", added, vAddr.size(), source.ToString(), nTried, nNew);
+ }
+ return added > 0;
+}
+
+void AddrManImpl::Attempt_(const CService& addr, bool fCountFailure, int64_t nTime)
+{
+ AssertLockHeld(cs);
+
+ AddrInfo* pinfo = Find(addr);
+
+ // if not found, bail out
+ if (!pinfo)
+ return;
+
+ AddrInfo& info = *pinfo;
+
// update info
info.nLastTry = nTime;
if (fCountFailure && info.nLastCountAttempt < nLastGood) {
@@ -709,38 +713,56 @@ std::pair<CAddress, int64_t> AddrManImpl::Select_(bool newOnly) const
// use a tried node
double fChanceFactor = 1.0;
while (1) {
+ // Pick a tried bucket, and an initial position in that bucket.
int nKBucket = insecure_rand.randrange(ADDRMAN_TRIED_BUCKET_COUNT);
int nKBucketPos = insecure_rand.randrange(ADDRMAN_BUCKET_SIZE);
- while (vvTried[nKBucket][nKBucketPos] == -1) {
- nKBucket = (nKBucket + insecure_rand.randbits(ADDRMAN_TRIED_BUCKET_COUNT_LOG2)) % ADDRMAN_TRIED_BUCKET_COUNT;
- nKBucketPos = (nKBucketPos + insecure_rand.randbits(ADDRMAN_BUCKET_SIZE_LOG2)) % ADDRMAN_BUCKET_SIZE;
+ // Iterate over the positions of that bucket, starting at the initial one,
+ // and looping around.
+ int i;
+ for (i = 0; i < ADDRMAN_BUCKET_SIZE; ++i) {
+ if (vvTried[nKBucket][(nKBucketPos + i) % ADDRMAN_BUCKET_SIZE] != -1) break;
}
- int nId = vvTried[nKBucket][nKBucketPos];
+ // If the bucket is entirely empty, start over with a (likely) different one.
+ if (i == ADDRMAN_BUCKET_SIZE) continue;
+ // Find the entry to return.
+ int nId = vvTried[nKBucket][(nKBucketPos + i) % ADDRMAN_BUCKET_SIZE];
const auto it_found{mapInfo.find(nId)};
assert(it_found != mapInfo.end());
const AddrInfo& info{it_found->second};
+ // With probability GetChance() * fChanceFactor, return the entry.
if (insecure_rand.randbits(30) < fChanceFactor * info.GetChance() * (1 << 30)) {
+ LogPrint(BCLog::ADDRMAN, "Selected %s from tried\n", info.ToString());
return {info, info.nLastTry};
}
+ // Otherwise start over with a (likely) different bucket, and increased chance factor.
fChanceFactor *= 1.2;
}
} else {
// use a new node
double fChanceFactor = 1.0;
while (1) {
+ // Pick a new bucket, and an initial position in that bucket.
int nUBucket = insecure_rand.randrange(ADDRMAN_NEW_BUCKET_COUNT);
int nUBucketPos = insecure_rand.randrange(ADDRMAN_BUCKET_SIZE);
- while (vvNew[nUBucket][nUBucketPos] == -1) {
- nUBucket = (nUBucket + insecure_rand.randbits(ADDRMAN_NEW_BUCKET_COUNT_LOG2)) % ADDRMAN_NEW_BUCKET_COUNT;
- nUBucketPos = (nUBucketPos + insecure_rand.randbits(ADDRMAN_BUCKET_SIZE_LOG2)) % ADDRMAN_BUCKET_SIZE;
+ // Iterate over the positions of that bucket, starting at the initial one,
+ // and looping around.
+ int i;
+ for (i = 0; i < ADDRMAN_BUCKET_SIZE; ++i) {
+ if (vvNew[nUBucket][(nUBucketPos + i) % ADDRMAN_BUCKET_SIZE] != -1) break;
}
- int nId = vvNew[nUBucket][nUBucketPos];
+ // If the bucket is entirely empty, start over with a (likely) different one.
+ if (i == ADDRMAN_BUCKET_SIZE) continue;
+ // Find the entry to return.
+ int nId = vvNew[nUBucket][(nUBucketPos + i) % ADDRMAN_BUCKET_SIZE];
const auto it_found{mapInfo.find(nId)};
assert(it_found != mapInfo.end());
const AddrInfo& info{it_found->second};
+ // With probability GetChance() * fChanceFactor, return the entry.
if (insecure_rand.randbits(30) < fChanceFactor * info.GetChance() * (1 << 30)) {
+ LogPrint(BCLog::ADDRMAN, "Selected %s from new\n", info.ToString());
return {info, info.nLastTry};
}
+ // Otherwise start over with a (likely) different bucket, and increased chance factor.
fChanceFactor *= 1.2;
}
}
@@ -780,7 +802,7 @@ std::vector<CAddress> AddrManImpl::GetAddr_(size_t max_addresses, size_t max_pct
addresses.push_back(ai);
}
-
+ LogPrint(BCLog::ADDRMAN, "GetAddr returned %d random addresses\n", addresses.size());
return addresses;
}
@@ -796,10 +818,6 @@ void AddrManImpl::Connected_(const CService& addr, int64_t nTime)
AddrInfo& info = *pinfo;
- // check whether we are talking about the exact same CService (including same port)
- if (info != addr)
- return;
-
// update info
int64_t nUpdateInterval = 20 * 60;
if (nTime - info.nTime > nUpdateInterval)
@@ -818,10 +836,6 @@ void AddrManImpl::SetServices_(const CService& addr, ServiceFlags nServices)
AddrInfo& info = *pinfo;
- // check whether we are talking about the exact same CService (including same port)
- if (info != addr)
- return;
-
// update info
info.nServices = nServices;
}
@@ -1027,15 +1041,10 @@ size_t AddrManImpl::size() const
bool AddrManImpl::Add(const std::vector<CAddress>& vAddr, const CNetAddr& source, int64_t nTimePenalty)
{
LOCK(cs);
- int nAdd = 0;
Check();
- for (std::vector<CAddress>::const_iterator it = vAddr.begin(); it != vAddr.end(); it++)
- nAdd += Add_(*it, source, nTimePenalty) ? 1 : 0;
+ auto ret = Add_(vAddr, source, nTimePenalty);
Check();
- if (nAdd) {
- LogPrint(BCLog::ADDRMAN, "Added %i addresses from %s: %i tried, %i new\n", nAdd, source.ToString(), nTried, nNew);
- }
- return nAdd > 0;
+ return ret;
}
void AddrManImpl::Good(const CService& addr, int64_t nTime)
diff --git a/src/addrman.h b/src/addrman.h
index 174ab4f811..a9f697f66f 100644
--- a/src/addrman.h
+++ b/src/addrman.h
@@ -69,7 +69,15 @@ public:
//! Return the number of (unique) addresses in all tables.
size_t size() const;
- //! Add addresses to addrman's new table.
+ /**
+ * Attempt to add one or more addresses to addrman's new table.
+ *
+ * @param[in] vAddr Address records to attempt to add.
+ * @param[in] source The address of the node that sent us these addr records.
+ * @param[in] nTimePenalty A "time penalty" to apply to the address record's nTime. If a peer
+ * sends us an address record with nTime=n, then we'll add it to our
+ * addrman with nTime=(n - nTimePenalty).
+ * @return true if at least one address is successfully added. */
bool Add(const std::vector<CAddress>& vAddr, const CNetAddr& source, int64_t nTimePenalty = 0);
//! Mark an entry as accessible, possibly moving it from "new" to "tried".
diff --git a/src/addrman_impl.h b/src/addrman_impl.h
index 1dc7f25f9c..e097932ee8 100644
--- a/src/addrman_impl.h
+++ b/src/addrman_impl.h
@@ -157,6 +157,7 @@ private:
V1_DETERMINISTIC = 1, //!< for pre-asmap files
V2_ASMAP = 2, //!< for files including asmap version
V3_BIP155 = 3, //!< same as V2_ASMAP plus addresses are in BIP155 format
+ V4_MULTIPORT = 4, //!< adds support for multiple ports per IP
};
//! The maximum format this software knows it can unserialize. Also, we always serialize
@@ -164,7 +165,7 @@ private:
//! The format (first byte in the serialized stream) can be higher than this and
//! still this software may be able to unserialize the file - if the second byte
//! (see `lowest_compatible` in `Unserialize()`) is less or equal to this.
- static constexpr Format FILE_FORMAT = Format::V3_BIP155;
+ static constexpr Format FILE_FORMAT = Format::V4_MULTIPORT;
//! The initial value of a field that is incremented every time an incompatible format
//! change is made (such that old software versions would not be able to parse and
@@ -179,8 +180,8 @@ private:
//! table with information about all nIds
std::unordered_map<int, AddrInfo> mapInfo GUARDED_BY(cs);
- //! find an nId based on its network address
- std::unordered_map<CNetAddr, int, CNetAddrHash> mapAddr GUARDED_BY(cs);
+ //! find an nId based on its network address and port.
+ std::unordered_map<CService, int, CServiceHash> mapAddr GUARDED_BY(cs);
//! randomly-ordered vector of all nIds
//! This is mutable because it is unobservable outside the class, so any
@@ -225,7 +226,7 @@ private:
const std::vector<bool> m_asmap;
//! Find an entry.
- AddrInfo* Find(const CNetAddr& addr, int* pnId = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs);
+ AddrInfo* Find(const CService& addr, int* pnId = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs);
//! Create a new entry and add it to the internal data structures mapInfo, mapAddr and vRandom.
AddrInfo* Create(const CAddress& addr, const CNetAddr& addrSource, int* pnId = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs);
@@ -242,9 +243,13 @@ private:
//! Move an entry from the "new" table(s) to the "tried" table
void MakeTried(AddrInfo& info, int nId) EXCLUSIVE_LOCKS_REQUIRED(cs);
+ /** Attempt to add a single address to addrman's new table.
+ * @see AddrMan::Add() for parameters. */
+ bool AddSingle(const CAddress& addr, const CNetAddr& source, int64_t nTimePenalty) EXCLUSIVE_LOCKS_REQUIRED(cs);
+
void Good_(const CService& addr, bool test_before_evict, int64_t time) EXCLUSIVE_LOCKS_REQUIRED(cs);
- bool Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimePenalty) EXCLUSIVE_LOCKS_REQUIRED(cs);
+ bool Add_(const std::vector<CAddress> &vAddr, const CNetAddr& source, int64_t nTimePenalty) EXCLUSIVE_LOCKS_REQUIRED(cs);
void Attempt_(const CService& addr, bool fCountFailure, int64_t nTime) EXCLUSIVE_LOCKS_REQUIRED(cs);
diff --git a/src/banman.h b/src/banman.h
index 8a03a9e3fc..f495dab49d 100644
--- a/src/banman.h
+++ b/src/banman.h
@@ -6,7 +6,7 @@
#define BITCOIN_BANMAN_H
#include <addrdb.h>
-#include <bloom.h>
+#include <common/bloom.h>
#include <fs.h>
#include <net_types.h> // For banmap_t
#include <sync.h>
diff --git a/src/bench/bench_bitcoin.cpp b/src/bench/bench_bitcoin.cpp
index 0b43ea1fd5..83609d2787 100644
--- a/src/bench/bench_bitcoin.cpp
+++ b/src/bench/bench_bitcoin.cpp
@@ -24,8 +24,8 @@ static void SetupBenchArgs(ArgsManager& argsman)
argsman.AddArg("-asymptote=<n1,n2,n3,...>", "Test asymptotic growth of the runtime of an algorithm, if supported by the benchmark", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-filter=<regex>", strprintf("Regular expression filter to select benchmark by name (default: %s)", DEFAULT_BENCH_FILTER), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
- argsman.AddArg("-list", "List benchmarks without executing them", ArgsManager::ALLOW_BOOL, OptionsCategory::OPTIONS);
- argsman.AddArg("-min_time=<milliseconds>", strprintf("Minimum runtime per benchmark, in milliseconds (default: %d)", DEFAULT_MIN_TIME_MS), ArgsManager::ALLOW_INT, OptionsCategory::OPTIONS);
+ argsman.AddArg("-list", "List benchmarks without executing them", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ argsman.AddArg("-min_time=<milliseconds>", strprintf("Minimum runtime per benchmark, in milliseconds (default: %d)", DEFAULT_MIN_TIME_MS), ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::OPTIONS);
argsman.AddArg("-output_csv=<output.csv>", "Generate CSV file with the most important benchmark results", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-output_json=<output.json>", "Generate JSON file with all benchmark results", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
}
diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp
index 934b574f8b..fd5145950b 100644
--- a/src/bench/coin_selection.cpp
+++ b/src/bench/coin_selection.cpp
@@ -33,7 +33,6 @@ static void CoinSelection(benchmark::Bench& bench)
NodeContext node;
auto chain = interfaces::MakeChain(node);
CWallet wallet(chain.get(), "", CreateDummyWalletDatabase());
- wallet.SetupLegacyScriptPubKeyMan();
std::vector<std::unique_ptr<CWalletTx>> wtxs;
LOCK(wallet.cs_wallet);
@@ -65,10 +64,6 @@ static void CoinSelection(benchmark::Bench& bench)
}
typedef std::set<CInputCoin> CoinSet;
-static NodeContext testNode;
-static auto testChain = interfaces::MakeChain(testNode);
-static CWallet testWallet(testChain.get(), "", CreateDummyWalletDatabase());
-std::vector<std::unique_ptr<CWalletTx>> wtxn;
// Copied from src/wallet/test/coinselector_tests.cpp
static void add_coin(const CAmount& nValue, int nInput, std::vector<OutputGroup>& set)
@@ -76,10 +71,9 @@ static void add_coin(const CAmount& nValue, int nInput, std::vector<OutputGroup>
CMutableTransaction tx;
tx.vout.resize(nInput + 1);
tx.vout[nInput].nValue = nValue;
- std::unique_ptr<CWalletTx> wtx = std::make_unique<CWalletTx>(MakeTransactionRef(std::move(tx)));
+ CInputCoin coin(MakeTransactionRef(tx), nInput);
set.emplace_back();
- set.back().Insert(COutput(testWallet, *wtx, nInput, 0, true, true, true).GetInputCoin(), 0, true, 0, 0, false);
- wtxn.emplace_back(std::move(wtx));
+ set.back().Insert(coin, 0, true, 0, 0, false);
}
// Copied from src/wallet/test/coinselector_tests.cpp
static CAmount make_hard_case(int utxos, std::vector<OutputGroup>& utxo_pool)
@@ -97,7 +91,6 @@ static CAmount make_hard_case(int utxos, std::vector<OutputGroup>& utxo_pool)
static void BnBExhaustion(benchmark::Bench& bench)
{
// Setup
- testWallet.SetupLegacyScriptPubKeyMan();
std::vector<OutputGroup> utxo_pool;
CoinSet selection;
CAmount value_ret = 0;
diff --git a/src/bench/mempool_stress.cpp b/src/bench/mempool_stress.cpp
index f28768efc8..a0a82ea359 100644
--- a/src/bench/mempool_stress.cpp
+++ b/src/bench/mempool_stress.cpp
@@ -6,6 +6,7 @@
#include <policy/policy.h>
#include <test/util/setup_common.h>
#include <txmempool.h>
+#include <validation.h>
#include <vector>
@@ -26,14 +27,8 @@ struct Available {
Available(CTransactionRef& ref, size_t tx_count) : ref(ref), tx_count(tx_count){}
};
-static void ComplexMemPool(benchmark::Bench& bench)
+static std::vector<CTransactionRef> CreateOrderedCoins(FastRandomContext& det_rand, int childTxs, int min_ancestors)
{
- int childTxs = 800;
- if (bench.complexityN() > 1) {
- childTxs = static_cast<int>(bench.complexityN());
- }
-
- FastRandomContext det_rand{true};
std::vector<Available> available_coins;
std::vector<CTransactionRef> ordered_coins;
// Create some base transactions
@@ -58,8 +53,10 @@ static void ComplexMemPool(benchmark::Bench& bench)
size_t idx = det_rand.randrange(available_coins.size());
Available coin = available_coins[idx];
uint256 hash = coin.ref->GetHash();
- // biased towards taking just one ancestor, but maybe more
- size_t n_to_take = det_rand.randrange(2) == 0 ? 1 : 1+det_rand.randrange(coin.ref->vout.size() - coin.vin_left);
+ // biased towards taking min_ancestors parents, but maybe more
+ size_t n_to_take = det_rand.randrange(2) == 0 ?
+ min_ancestors :
+ min_ancestors + det_rand.randrange(coin.ref->vout.size() - coin.vin_left);
for (size_t i = 0; i < n_to_take; ++i) {
tx.vin.emplace_back();
tx.vin.back().prevout = COutPoint(hash, coin.vin_left++);
@@ -79,6 +76,17 @@ static void ComplexMemPool(benchmark::Bench& bench)
ordered_coins.emplace_back(MakeTransactionRef(tx));
available_coins.emplace_back(ordered_coins.back(), tx_counter++);
}
+ return ordered_coins;
+}
+
+static void ComplexMemPool(benchmark::Bench& bench)
+{
+ FastRandomContext det_rand{true};
+ int childTxs = 800;
+ if (bench.complexityN() > 1) {
+ childTxs = static_cast<int>(bench.complexityN());
+ }
+ std::vector<CTransactionRef> ordered_coins = CreateOrderedCoins(det_rand, childTxs, /* min_ancestors */ 1);
const auto testing_setup = MakeNoLogFileContext<const TestingSetup>(CBaseChainParams::MAIN);
CTxMemPool pool;
LOCK2(cs_main, pool.cs);
@@ -91,4 +99,21 @@ static void ComplexMemPool(benchmark::Bench& bench)
});
}
+static void MempoolCheck(benchmark::Bench& bench)
+{
+ FastRandomContext det_rand{true};
+ const int childTxs = bench.complexityN() > 1 ? static_cast<int>(bench.complexityN()) : 2000;
+ const std::vector<CTransactionRef> ordered_coins = CreateOrderedCoins(det_rand, childTxs, /* min_ancestors */ 5);
+ const auto testing_setup = MakeNoLogFileContext<const TestingSetup>(CBaseChainParams::MAIN, {"-checkmempool=1"});
+ CTxMemPool pool;
+ LOCK2(cs_main, pool.cs);
+ const CCoinsViewCache& coins_tip = testing_setup.get()->m_node.chainman->ActiveChainstate().CoinsTip();
+ for (auto& tx : ordered_coins) AddTx(tx, pool);
+
+ bench.run([&]() NO_THREAD_SAFETY_ANALYSIS {
+ pool.check(coins_tip, /* spendheight */ 2);
+ });
+}
+
BENCHMARK(ComplexMemPool);
+BENCHMARK(MempoolCheck);
diff --git a/src/bench/rollingbloom.cpp b/src/bench/rollingbloom.cpp
index 28167767db..30bc1d5fdf 100644
--- a/src/bench/rollingbloom.cpp
+++ b/src/bench/rollingbloom.cpp
@@ -4,7 +4,7 @@
#include <bench/bench.h>
-#include <bloom.h>
+#include <common/bloom.h>
static void RollingBloom(benchmark::Bench& bench)
{
diff --git a/src/bench/rpc_blockchain.cpp b/src/bench/rpc_blockchain.cpp
index c8886a4c23..3bef64f720 100644
--- a/src/bench/rpc_blockchain.cpp
+++ b/src/bench/rpc_blockchain.cpp
@@ -40,7 +40,7 @@ static void BlockToJsonVerbose(benchmark::Bench& bench)
{
TestBlockAndIndex data;
bench.run([&] {
- auto univalue = blockToJSON(data.block, &data.blockindex, &data.blockindex, /*verbose*/ true);
+ auto univalue = blockToJSON(data.block, &data.blockindex, &data.blockindex, TxVerbosity::SHOW_DETAILS_AND_PREVOUT);
ankerl::nanobench::doNotOptimizeAway(univalue);
});
}
@@ -50,7 +50,7 @@ BENCHMARK(BlockToJsonVerbose);
static void BlockToJsonVerboseWrite(benchmark::Bench& bench)
{
TestBlockAndIndex data;
- auto univalue = blockToJSON(data.block, &data.blockindex, &data.blockindex, /*verbose*/ true);
+ auto univalue = blockToJSON(data.block, &data.blockindex, &data.blockindex, TxVerbosity::SHOW_DETAILS_AND_PREVOUT);
bench.run([&] {
auto str = univalue.write();
ankerl::nanobench::doNotOptimizeAway(str);
diff --git a/src/bench/wallet_balance.cpp b/src/bench/wallet_balance.cpp
index a205d8b6e7..166ed16042 100644
--- a/src/bench/wallet_balance.cpp
+++ b/src/bench/wallet_balance.cpp
@@ -14,7 +14,7 @@
#include <optional>
-static void WalletBalance(benchmark::Bench& bench, const bool set_dirty, const bool add_watchonly, const bool add_mine)
+static void WalletBalance(benchmark::Bench& bench, const bool set_dirty, const bool add_mine)
{
const auto test_setup = MakeNoLogFileContext<const TestingSetup>();
@@ -22,13 +22,14 @@ static void WalletBalance(benchmark::Bench& bench, const bool set_dirty, const b
CWallet wallet{test_setup->m_node.chain.get(), "", CreateMockWalletDatabase()};
{
- wallet.SetupLegacyScriptPubKeyMan();
+ LOCK(wallet.cs_wallet);
+ wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
+ wallet.SetupDescriptorScriptPubKeyMans();
if (wallet.LoadWallet() != DBErrors::LOAD_OK) assert(false);
}
auto handler = test_setup->m_node.chain->handleNotifications({&wallet, [](CWallet*) {}});
const std::optional<std::string> address_mine{add_mine ? std::optional<std::string>{getnewaddress(wallet)} : std::nullopt};
- if (add_watchonly) importaddress(wallet, ADDRESS_WATCHONLY);
for (int i = 0; i < 100; ++i) {
generatetoaddress(test_setup->m_node, address_mine.value_or(ADDRESS_WATCHONLY));
@@ -42,14 +43,13 @@ static void WalletBalance(benchmark::Bench& bench, const bool set_dirty, const b
if (set_dirty) wallet.MarkDirty();
bal = GetBalance(wallet);
if (add_mine) assert(bal.m_mine_trusted > 0);
- if (add_watchonly) assert(bal.m_watchonly_trusted > 0);
});
}
-static void WalletBalanceDirty(benchmark::Bench& bench) { WalletBalance(bench, /* set_dirty */ true, /* add_watchonly */ true, /* add_mine */ true); }
-static void WalletBalanceClean(benchmark::Bench& bench) { WalletBalance(bench, /* set_dirty */ false, /* add_watchonly */ true, /* add_mine */ true); }
-static void WalletBalanceMine(benchmark::Bench& bench) { WalletBalance(bench, /* set_dirty */ false, /* add_watchonly */ false, /* add_mine */ true); }
-static void WalletBalanceWatch(benchmark::Bench& bench) { WalletBalance(bench, /* set_dirty */ false, /* add_watchonly */ true, /* add_mine */ false); }
+static void WalletBalanceDirty(benchmark::Bench& bench) { WalletBalance(bench, /* set_dirty */ true, /* add_mine */ true); }
+static void WalletBalanceClean(benchmark::Bench& bench) { WalletBalance(bench, /* set_dirty */ false, /* add_mine */ true); }
+static void WalletBalanceMine(benchmark::Bench& bench) { WalletBalance(bench, /* set_dirty */ false, /* add_mine */ true); }
+static void WalletBalanceWatch(benchmark::Bench& bench) { WalletBalance(bench, /* set_dirty */ false, /* add_mine */ false); }
BENCHMARK(WalletBalanceDirty);
BENCHMARK(WalletBalanceClean);
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index 3c22ee0f67..338d32ef6b 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -74,7 +74,7 @@ static void SetupCliArgs(ArgsManager& argsman)
argsman.AddArg("-netinfo", "Get network peer connection information from the remote server. An optional integer argument from 0 to 4 can be passed for different peers listings (default: 0). Pass \"help\" for detailed help documentation.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
SetupChainParamsBaseOptions(argsman);
- argsman.AddArg("-color=<when>", strprintf("Color setting for CLI output (default: %s). Valid values: always, auto (add color codes when standard output is connected to a terminal and OS is not WIN32), never.", DEFAULT_COLOR_SETTING), ArgsManager::ALLOW_STRING, OptionsCategory::OPTIONS);
+ argsman.AddArg("-color=<when>", strprintf("Color setting for CLI output (default: %s). Valid values: always, auto (add color codes when standard output is connected to a terminal and OS is not WIN32), never.", DEFAULT_COLOR_SETTING), ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::OPTIONS);
argsman.AddArg("-named", strprintf("Pass named instead of positional arguments (default: %s)", DEFAULT_NAMED), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-rpcclienttimeout=<n>", strprintf("Timeout in seconds during HTTP requests, or 0 for no timeout. (default: %d)", DEFAULT_HTTP_CLIENT_TIMEOUT), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-rpcconnect=<ip>", strprintf("Send commands to node running on <ip> (default: %s)", DEFAULT_RPCCONNECT), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
@@ -83,7 +83,7 @@ static void SetupCliArgs(ArgsManager& argsman)
argsman.AddArg("-rpcport=<port>", strprintf("Connect to JSON-RPC on <port> (default: %u, testnet: %u, signet: %u, regtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), signetBaseParams->RPCPort(), regtestBaseParams->RPCPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::OPTIONS);
argsman.AddArg("-rpcuser=<user>", "Username for JSON-RPC connections", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-rpcwait", "Wait for RPC server to start", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
- argsman.AddArg("-rpcwaittimeout=<n>", strprintf("Timeout in seconds to wait for the RPC server to start, or 0 for no timeout. (default: %d)", DEFAULT_WAIT_CLIENT_TIMEOUT), ArgsManager::ALLOW_INT, OptionsCategory::OPTIONS);
+ argsman.AddArg("-rpcwaittimeout=<n>", strprintf("Timeout in seconds to wait for the RPC server to start, or 0 for no timeout. (default: %d)", DEFAULT_WAIT_CLIENT_TIMEOUT), ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::OPTIONS);
argsman.AddArg("-rpcwallet=<walletname>", "Send RPC for non-default wallet on RPC server (needs to exactly match corresponding -wallet option passed to bitcoind). This changes the RPC endpoint used, e.g. http://127.0.0.1:8332/wallet/<walletname>", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-stdin", "Read extra arguments from standard input, one per line until EOF/Ctrl-D (recommended for sensitive information such as passphrases). When combined with -stdinrpcpass, the first line from standard input is used for the RPC password.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-stdinrpcpass", "Read RPC password from standard input as a single line. When combined with -stdin, the first line from standard input is used for the RPC password. When combined with -stdinwalletpassphrase, -stdinrpcpass consumes the first line, and -stdinwalletpassphrase consumes the second.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
@@ -551,15 +551,26 @@ public:
}
// Report peer connection totals by type.
- result += " ipv4 ipv6 onion";
- const bool any_i2p_peers = m_counts.at(2).at(3); // false if total i2p peers count is 0, otherwise true
- if (any_i2p_peers) result += " i2p";
+ result += " ";
+ std::vector<int8_t> reachable_networks;
+ for (const UniValue& network : networkinfo["networks"].getValues()) {
+ if (network["reachable"].get_bool()) {
+ const std::string& network_name{network["name"].get_str()};
+ const int8_t network_id{NetworkStringToId(network_name)};
+ if (network_id == UNKNOWN_NETWORK) continue;
+ result += strprintf("%8s", network_name); // column header
+ reachable_networks.push_back(network_id);
+ }
+ };
result += " total block";
if (m_manual_peers_count) result += " manual";
+
const std::array rows{"in", "out", "total"};
- for (uint8_t i = 0; i < 3; ++i) {
- result += strprintf("\n%-5s %5i %5i %5i", rows.at(i), m_counts.at(i).at(0), m_counts.at(i).at(1), m_counts.at(i).at(2)); // ipv4/ipv6/onion peers counts
- if (any_i2p_peers) result += strprintf(" %5i", m_counts.at(i).at(3)); // i2p peers count
+ for (size_t i = 0; i < rows.size(); ++i) {
+ result += strprintf("\n%-5s", rows[i]); // row header
+ for (int8_t n : reachable_networks) {
+ result += strprintf("%8i", m_counts.at(i).at(n)); // network peers count
+ }
result += strprintf(" %5i", m_counts.at(i).at(m_networks.size())); // total peers count
if (i == 1) { // the outbound row has two extra columns for block relay and manual peer counts
result += strprintf(" %5i", m_block_relay_peers_count);
@@ -786,7 +797,7 @@ static UniValue CallRPC(BaseRequestHandler* rh, const std::string& strMethod, co
if (failedToGetAuthCookie) {
throw std::runtime_error(strprintf(
"Could not locate RPC credentials. No authentication cookie could be found, and RPC password is not set. See -rpcpassword and -stdinrpcpass. Configuration file: (%s)",
- GetConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)).string()));
+ fs::PathToString(GetConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)))));
} else {
throw std::runtime_error("Authorization failed: Incorrect rpcuser or rpcpassword");
}
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index fc3bc6aa71..eb97cfc6f6 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -235,6 +235,16 @@ static void MutateTxRBFOptIn(CMutableTransaction& tx, const std::string& strInId
}
}
+template <typename T>
+static T TrimAndParse(const std::string& int_str, const std::string& err)
+{
+ const auto parsed{ToIntegral<T>(TrimString(int_str))};
+ if (!parsed.has_value()) {
+ throw std::runtime_error(err + " '" + int_str + "'");
+ }
+ return parsed.value();
+}
+
static void MutateTxAddInput(CMutableTransaction& tx, const std::string& strInput)
{
std::vector<std::string> vStrInputParts;
@@ -261,8 +271,9 @@ static void MutateTxAddInput(CMutableTransaction& tx, const std::string& strInpu
// extract the optional sequence number
uint32_t nSequenceIn = CTxIn::SEQUENCE_FINAL;
- if (vStrInputParts.size() > 2)
- nSequenceIn = std::stoul(vStrInputParts[2]);
+ if (vStrInputParts.size() > 2) {
+ nSequenceIn = TrimAndParse<uint32_t>(vStrInputParts.at(2), "invalid TX sequence id");
+ }
// append to transaction input list
CTxIn txin(txid, vout, CScript(), nSequenceIn);
@@ -352,10 +363,10 @@ static void MutateTxAddOutMultiSig(CMutableTransaction& tx, const std::string& s
CAmount value = ExtractAndValidateValue(vStrInputParts[0]);
// Extract REQUIRED
- uint32_t required = stoul(vStrInputParts[1]);
+ const uint32_t required{TrimAndParse<uint32_t>(vStrInputParts.at(1), "invalid multisig required number")};
// Extract NUMKEYS
- uint32_t numkeys = stoul(vStrInputParts[2]);
+ const uint32_t numkeys{TrimAndParse<uint32_t>(vStrInputParts.at(2), "invalid multisig total number")};
// Validate there are the correct number of pubkeys
if (vStrInputParts.size() < numkeys + 3)
diff --git a/src/bitcoin-wallet.cpp b/src/bitcoin-wallet.cpp
index 765954c92e..afa5b99549 100644
--- a/src/bitcoin-wallet.cpp
+++ b/src/bitcoin-wallet.cpp
@@ -8,6 +8,7 @@
#include <chainparams.h>
#include <chainparamsbase.h>
+#include <interfaces/init.h>
#include <logging.h>
#include <util/system.h>
#include <util/translation.h>
@@ -27,9 +28,10 @@ static void SetupWalletToolArgs(ArgsManager& argsman)
argsman.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-wallet=<wallet-name>", "Specify wallet name", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::OPTIONS);
- argsman.AddArg("-dumpfile=<file name>", "When used with 'dump', writes out the records to this file. When used with 'createfromdump', loads the records into a new wallet.", ArgsManager::ALLOW_STRING, OptionsCategory::OPTIONS);
+ argsman.AddArg("-dumpfile=<file name>", "When used with 'dump', writes out the records to this file. When used with 'createfromdump', loads the records into a new wallet.", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::OPTIONS);
argsman.AddArg("-debug=<category>", "Output debugging information (default: 0).", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
- argsman.AddArg("-descriptors", "Create descriptors wallet. Only for 'create'", ArgsManager::ALLOW_BOOL, OptionsCategory::OPTIONS);
+ argsman.AddArg("-descriptors", "Create descriptors wallet. Only for 'create'", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ argsman.AddArg("-legacy", "Create legacy wallet. Only for 'create'", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-format=<format>", "The format of the wallet file to create. Either \"bdb\" or \"sqlite\". Only used with 'createfromdump'", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-printtoconsole", "Send trace/debug info to console (default: 1 when no -debug is true, 0 otherwise).", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
@@ -83,6 +85,13 @@ int main(int argc, char* argv[])
util::WinCmdLineArgs winArgs;
std::tie(argc, argv) = winArgs.get();
#endif
+
+ int exit_status;
+ std::unique_ptr<interfaces::Init> init = interfaces::MakeWalletInit(argc, argv, exit_status);
+ if (!init) {
+ return exit_status;
+ }
+
SetupEnvironment();
RandomInit();
try {
diff --git a/src/chain.h b/src/chain.h
index 365a7f79b6..15ca8f8750 100644
--- a/src/chain.h
+++ b/src/chain.h
@@ -178,7 +178,7 @@ public:
//! (memory only) Number of transactions in the chain up to and including this block.
//! This value will be non-zero only if and only if transactions for this block and all its parents are available.
- //! Change to 64-bit type when necessary; won't happen before 2030
+ //! Change to 64-bit type before 2024 (assuming worst case of 60 byte transactions).
//!
//! Note: this value is faked during use of a UTXO snapshot because we don't
//! have the underlying block data available during snapshot load.
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index b155745794..2e823c1211 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -118,15 +118,15 @@ public:
// This is fine at runtime as we'll fall back to using them as an addrfetch if they don't support the
// service bits we want, but we should get them updated to support all service bits wanted by any
// release ASAP to avoid it where possible.
- vSeeds.emplace_back("seed.bitcoin.sipa.be"); // Pieter Wuille, only supports x1, x5, x9, and xd
- vSeeds.emplace_back("dnsseed.bluematt.me"); // Matt Corallo, only supports x9
- vSeeds.emplace_back("dnsseed.bitcoin.dashjr.org"); // Luke Dashjr
- vSeeds.emplace_back("seed.bitcoinstats.com"); // Christian Decker, supports x1 - xf
- vSeeds.emplace_back("seed.bitcoin.jonasschnelli.ch"); // Jonas Schnelli, only supports x1, x5, x9, and xd
- vSeeds.emplace_back("seed.btc.petertodd.org"); // Peter Todd, only supports x1, x5, x9, and xd
- vSeeds.emplace_back("seed.bitcoin.sprovoost.nl"); // Sjors Provoost
- vSeeds.emplace_back("dnsseed.emzy.de"); // Stephan Oeste
- vSeeds.emplace_back("seed.bitcoin.wiz.biz"); // Jason Maurice
+ vSeeds.emplace_back("seed.bitcoin.sipa.be."); // Pieter Wuille, only supports x1, x5, x9, and xd
+ vSeeds.emplace_back("dnsseed.bluematt.me."); // Matt Corallo, only supports x9
+ vSeeds.emplace_back("dnsseed.bitcoin.dashjr.org."); // Luke Dashjr
+ vSeeds.emplace_back("seed.bitcoinstats.com."); // Christian Decker, supports x1 - xf
+ vSeeds.emplace_back("seed.bitcoin.jonasschnelli.ch."); // Jonas Schnelli, only supports x1, x5, x9, and xd
+ vSeeds.emplace_back("seed.btc.petertodd.org."); // Peter Todd, only supports x1, x5, x9, and xd
+ vSeeds.emplace_back("seed.bitcoin.sprovoost.nl."); // Sjors Provoost
+ vSeeds.emplace_back("dnsseed.emzy.de."); // Stephan Oeste
+ vSeeds.emplace_back("seed.bitcoin.wiz.biz."); // Jason Maurice
base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,0);
base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,5);
@@ -230,10 +230,10 @@ public:
vFixedSeeds.clear();
vSeeds.clear();
// nodes with support for servicebits filtering should be at the top
- vSeeds.emplace_back("testnet-seed.bitcoin.jonasschnelli.ch");
- vSeeds.emplace_back("seed.tbtc.petertodd.org");
- vSeeds.emplace_back("seed.testnet.bitcoin.sprovoost.nl");
- vSeeds.emplace_back("testnet-seed.bluematt.me"); // Just a static list of stable node(s), only supports x9
+ vSeeds.emplace_back("testnet-seed.bitcoin.jonasschnelli.ch.");
+ vSeeds.emplace_back("seed.tbtc.petertodd.org.");
+ vSeeds.emplace_back("seed.testnet.bitcoin.sprovoost.nl.");
+ vSeeds.emplace_back("testnet-seed.bluematt.me."); // Just a static list of stable node(s), only supports x9
base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,111);
base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,196);
@@ -280,7 +280,7 @@ public:
if (!args.IsArgSet("-signetchallenge")) {
bin = ParseHex("512103ad5e0edad18cb1f0fc0d28a3d4f1f3e445640337489abb10404f2d1e086be430210359ef5021964fe22d6f8e05b2463c9540ce96883fe3b278760f048f5189f2e6c452ae");
- vSeeds.emplace_back("seed.signet.bitcoin.sprovoost.nl");
+ vSeeds.emplace_back("seed.signet.bitcoin.sprovoost.nl.");
// Hardcoded nodes can be removed once there are more DNS seeds
vSeeds.emplace_back("178.128.221.177");
diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp
index dc484f5c03..f24863f419 100644
--- a/src/chainparamsbase.cpp
+++ b/src/chainparamsbase.cpp
@@ -24,8 +24,8 @@ void SetupChainParamsBaseOptions(ArgsManager& argsman)
argsman.AddArg("-testnet", "Use the test chain. Equivalent to -chain=test.", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-vbparams=deployment:start:end[:min_activation_height]", "Use given start/end times and min_activation_height for specified version bits deployment (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-signet", "Use the signet chain. Equivalent to -chain=signet. Note that the network is defined by the -signetchallenge parameter", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
- argsman.AddArg("-signetchallenge", "Blocks must satisfy the given script to be considered valid (only for signet networks; defaults to the global default signet test network challenge)", ArgsManager::ALLOW_STRING, OptionsCategory::CHAINPARAMS);
- argsman.AddArg("-signetseednode", "Specify a seed node for the signet network, in the hostname[:port] format, e.g. sig.net:1234 (may be used multiple times to specify multiple seed nodes; defaults to the global default signet test network seed node(s))", ArgsManager::ALLOW_STRING, OptionsCategory::CHAINPARAMS);
+ argsman.AddArg("-signetchallenge", "Blocks must satisfy the given script to be considered valid (only for signet networks; defaults to the global default signet test network challenge)", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::CHAINPARAMS);
+ argsman.AddArg("-signetseednode", "Specify a seed node for the signet network, in the hostname[:port] format, e.g. sig.net:1234 (may be used multiple times to specify multiple seed nodes; defaults to the global default signet test network seed node(s))", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::CHAINPARAMS);
}
static std::unique_ptr<CBaseChainParams> globalChainBaseParams;
diff --git a/src/bloom.cpp b/src/common/bloom.cpp
index 15e06389de..26b70b4d14 100644
--- a/src/bloom.cpp
+++ b/src/common/bloom.cpp
@@ -2,7 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <bloom.h>
+#include <common/bloom.h>
#include <hash.h>
#include <primitives/transaction.h>
@@ -82,7 +82,7 @@ bool CBloomFilter::contains(const COutPoint& outpoint) const
{
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
stream << outpoint;
- return contains(MakeUCharSpan(stream));
+ return contains(stream);
}
bool CBloomFilter::IsWithinSizeConstraints() const
diff --git a/src/bloom.h b/src/common/bloom.h
index 422646d8b9..25c16fbfe2 100644
--- a/src/bloom.h
+++ b/src/common/bloom.h
@@ -2,8 +2,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#ifndef BITCOIN_BLOOM_H
-#define BITCOIN_BLOOM_H
+#ifndef BITCOIN_COMMON_BLOOM_H
+#define BITCOIN_COMMON_BLOOM_H
#include <serialize.h>
#include <span.h>
@@ -124,4 +124,4 @@ private:
int nHashFuncs;
};
-#endif // BITCOIN_BLOOM_H
+#endif // BITCOIN_COMMON_BLOOM_H
diff --git a/src/consensus/amount.h b/src/consensus/amount.h
index 8b41a2277d..96566ea13f 100644
--- a/src/consensus/amount.h
+++ b/src/consensus/amount.h
@@ -11,6 +11,7 @@
/** Amount in satoshis (Can be negative) */
typedef int64_t CAmount;
+/** The amount of satoshis in one BTC. */
static constexpr CAmount COIN = 100000000;
/** No amount larger than this (in satoshi) is valid.
diff --git a/src/core_io.h b/src/core_io.h
index be93a17efe..4d7199ab12 100644
--- a/src/core_io.h
+++ b/src/core_io.h
@@ -20,6 +20,15 @@ class uint256;
class UniValue;
class CTxUndo;
+/**
+ * Verbose level for block's transaction
+ */
+enum class TxVerbosity {
+ SHOW_TXID, //!< Only TXID for each block's transaction
+ SHOW_DETAILS, //!< Include TXID, inputs, outputs, and other common block's transaction information
+ SHOW_DETAILS_AND_PREVOUT //!< The same as previous option with information about prevouts if available
+};
+
// core_read.cpp
CScript ParseScript(const std::string& s);
std::string ScriptToAsmStr(const CScript& script, const bool fAttemptSighashDecode = false);
@@ -46,6 +55,6 @@ std::string EncodeHexTx(const CTransaction& tx, const int serializeFlags = 0);
std::string SighashToStr(unsigned char sighash_type);
void ScriptPubKeyToUniv(const CScript& scriptPubKey, UniValue& out, bool include_hex, bool include_address = true);
void ScriptToUniv(const CScript& script, UniValue& out);
-void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, bool include_hex = true, int serialize_flags = 0, const CTxUndo* txundo = nullptr);
+void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, bool include_hex = true, int serialize_flags = 0, const CTxUndo* txundo = nullptr, TxVerbosity verbosity = TxVerbosity::SHOW_DETAILS);
#endif // BITCOIN_CORE_IO_H
diff --git a/src/core_read.cpp b/src/core_read.cpp
index 320811b9e9..2149b428d2 100644
--- a/src/core_read.cpp
+++ b/src/core_read.cpp
@@ -26,20 +26,20 @@ opcodetype ParseOpCode(const std::string& s)
{
static std::map<std::string, opcodetype> mapOpNames;
- if (mapOpNames.empty())
- {
- for (unsigned int op = 0; op <= MAX_OPCODE; op++)
- {
+ if (mapOpNames.empty()) {
+ for (unsigned int op = 0; op <= MAX_OPCODE; op++) {
// Allow OP_RESERVED to get into mapOpNames
- if (op < OP_NOP && op != OP_RESERVED)
+ if (op < OP_NOP && op != OP_RESERVED) {
continue;
+ }
std::string strName = GetOpName(static_cast<opcodetype>(op));
- if (strName == "OP_UNKNOWN")
+ if (strName == "OP_UNKNOWN") {
continue;
+ }
mapOpNames[strName] = static_cast<opcodetype>(op);
// Convenience: OP_ADD and just ADD are both recognized:
- if (strName.compare(0, 3, "OP_") == 0) { // strName starts with "OP_"
+ if (strName.compare(0, 3, "OP_") == 0) { // strName starts with "OP_"
mapOpNames[strName.substr(3)] = static_cast<opcodetype>(op);
}
}
@@ -59,44 +59,35 @@ CScript ParseScript(const std::string& s)
std::vector<std::string> words;
boost::algorithm::split(words, s, boost::algorithm::is_any_of(" \t\n"), boost::algorithm::token_compress_on);
- for (std::vector<std::string>::const_iterator w = words.begin(); w != words.end(); ++w)
- {
- if (w->empty())
- {
+ for (const std::string& w : words) {
+ if (w.empty()) {
// Empty string, ignore. (boost::split given '' will return one word)
- }
- else if (std::all_of(w->begin(), w->end(), ::IsDigit) ||
- (w->front() == '-' && w->size() > 1 && std::all_of(w->begin()+1, w->end(), ::IsDigit)))
+ } else if (std::all_of(w.begin(), w.end(), ::IsDigit) ||
+ (w.front() == '-' && w.size() > 1 && std::all_of(w.begin() + 1, w.end(), ::IsDigit)))
{
// Number
- int64_t n = LocaleIndependentAtoi<int64_t>(*w);
+ const auto num{ToIntegral<int64_t>(w)};
- //limit the range of numbers ParseScript accepts in decimal
- //since numbers outside -0xFFFFFFFF...0xFFFFFFFF are illegal in scripts
- if (n > int64_t{0xffffffff} || n < -1 * int64_t{0xffffffff}) {
+ // limit the range of numbers ParseScript accepts in decimal
+ // since numbers outside -0xFFFFFFFF...0xFFFFFFFF are illegal in scripts
+ if (!num.has_value() || num > int64_t{0xffffffff} || num < -1 * int64_t{0xffffffff}) {
throw std::runtime_error("script parse error: decimal numeric value only allowed in the "
"range -0xFFFFFFFF...0xFFFFFFFF");
}
- result << n;
- }
- else if (w->substr(0,2) == "0x" && w->size() > 2 && IsHex(std::string(w->begin()+2, w->end())))
- {
+ result << num.value();
+ } else if (w.substr(0, 2) == "0x" && w.size() > 2 && IsHex(std::string(w.begin() + 2, w.end()))) {
// Raw hex data, inserted NOT pushed onto stack:
- std::vector<unsigned char> raw = ParseHex(std::string(w->begin()+2, w->end()));
+ std::vector<unsigned char> raw = ParseHex(std::string(w.begin() + 2, w.end()));
result.insert(result.end(), raw.begin(), raw.end());
- }
- else if (w->size() >= 2 && w->front() == '\'' && w->back() == '\'')
- {
+ } else if (w.size() >= 2 && w.front() == '\'' && w.back() == '\'') {
// Single-quoted string, pushed as data. NOTE: this is poor-man's
// parsing, spaces/tabs/newlines in single-quoted strings won't work.
- std::vector<unsigned char> value(w->begin()+1, w->end()-1);
+ std::vector<unsigned char> value(w.begin() + 1, w.end() - 1);
result << value;
- }
- else
- {
+ } else {
// opcode, e.g. OP_ADD or ADD:
- result << ParseOpCode(*w);
+ result << ParseOpCode(w);
}
}
diff --git a/src/core_write.cpp b/src/core_write.cpp
index 6b13e4c586..468694b011 100644
--- a/src/core_write.cpp
+++ b/src/core_write.cpp
@@ -163,7 +163,7 @@ void ScriptPubKeyToUniv(const CScript& scriptPubKey, UniValue& out, bool include
out.pushKV("type", GetTxnOutputType(type));
}
-void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, bool include_hex, int serialize_flags, const CTxUndo* txundo)
+void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, bool include_hex, int serialize_flags, const CTxUndo* txundo, TxVerbosity verbosity)
{
entry.pushKV("txid", tx.GetHash().GetHex());
entry.pushKV("hash", tx.GetWitnessHash().GetHex());
@@ -179,7 +179,7 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry,
// If available, use Undo data to calculate the fee. Note that txundo == nullptr
// for coinbase transactions and for transactions where undo data is unavailable.
- const bool calculate_fee = txundo != nullptr;
+ const bool have_undo = txundo != nullptr;
CAmount amt_total_in = 0;
CAmount amt_total_out = 0;
@@ -203,9 +203,28 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry,
}
in.pushKV("txinwitness", txinwitness);
}
- if (calculate_fee) {
- const CTxOut& prev_txout = txundo->vprevout[i].out;
+ if (have_undo) {
+ const Coin& prev_coin = txundo->vprevout[i];
+ const CTxOut& prev_txout = prev_coin.out;
+
amt_total_in += prev_txout.nValue;
+ switch (verbosity) {
+ case TxVerbosity::SHOW_TXID:
+ case TxVerbosity::SHOW_DETAILS:
+ break;
+
+ case TxVerbosity::SHOW_DETAILS_AND_PREVOUT:
+ UniValue o_script_pub_key(UniValue::VOBJ);
+ ScriptPubKeyToUniv(prev_txout.scriptPubKey, o_script_pub_key, /* includeHex */ true);
+
+ UniValue p(UniValue::VOBJ);
+ p.pushKV("generated", bool(prev_coin.fCoinBase));
+ p.pushKV("height", uint64_t(prev_coin.nHeight));
+ p.pushKV("value", ValueFromAmount(prev_txout.nValue));
+ p.pushKV("scriptPubKey", o_script_pub_key);
+ in.pushKV("prevout", p);
+ break;
+ }
}
in.pushKV("sequence", (int64_t)txin.nSequence);
vin.push_back(in);
@@ -226,13 +245,13 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry,
out.pushKV("scriptPubKey", o);
vout.push_back(out);
- if (calculate_fee) {
+ if (have_undo) {
amt_total_out += txout.nValue;
}
}
entry.pushKV("vout", vout);
- if (calculate_fee) {
+ if (have_undo) {
const CAmount fee = amt_total_in - amt_total_out;
CHECK_NONFATAL(MoneyRange(fee));
entry.pushKV("fee", ValueFromAmount(fee));
diff --git a/src/crypto/chacha_poly_aead.h b/src/crypto/chacha_poly_aead.h
index 0afe8fcc14..6a7998335d 100644
--- a/src/crypto/chacha_poly_aead.h
+++ b/src/crypto/chacha_poly_aead.h
@@ -117,8 +117,8 @@ static constexpr int AAD_PACKAGES_PER_ROUND = 21; /* 64 / 3 round down*/
class ChaCha20Poly1305AEAD
{
private:
- ChaCha20 m_chacha_main; // payload and poly1305 key-derivation cipher instance
- ChaCha20 m_chacha_header; // AAD cipher instance (encrypted length)
+ ChaCha20 m_chacha_header; // AAD cipher instance (encrypted length) and poly1305 key-derivation cipher instance
+ ChaCha20 m_chacha_main; // payload
unsigned char m_aad_keystream_buffer[CHACHA20_ROUND_OUTPUT]; // aad keystream cache
uint64_t m_cached_aad_seqnr; // aad keystream cache hint
diff --git a/src/dbwrapper.cpp b/src/dbwrapper.cpp
index 3a1086bf4c..2fdc54464a 100644
--- a/src/dbwrapper.cpp
+++ b/src/dbwrapper.cpp
@@ -115,7 +115,7 @@ static leveldb::Options GetOptions(size_t nCacheSize)
}
CDBWrapper::CDBWrapper(const fs::path& path, size_t nCacheSize, bool fMemory, bool fWipe, bool obfuscate)
- : m_name{path.stem().string()}
+ : m_name{fs::PathToString(path.stem())}
{
penv = nullptr;
readoptions.verify_checksums = true;
@@ -129,21 +129,21 @@ CDBWrapper::CDBWrapper(const fs::path& path, size_t nCacheSize, bool fMemory, bo
options.env = penv;
} else {
if (fWipe) {
- LogPrintf("Wiping LevelDB in %s\n", path.string());
- leveldb::Status result = leveldb::DestroyDB(path.string(), options);
+ LogPrintf("Wiping LevelDB in %s\n", fs::PathToString(path));
+ leveldb::Status result = leveldb::DestroyDB(fs::PathToString(path), options);
dbwrapper_private::HandleError(result);
}
TryCreateDirectories(path);
- LogPrintf("Opening LevelDB in %s\n", path.string());
+ LogPrintf("Opening LevelDB in %s\n", fs::PathToString(path));
}
- leveldb::Status status = leveldb::DB::Open(options, path.string(), &pdb);
+ leveldb::Status status = leveldb::DB::Open(options, fs::PathToString(path), &pdb);
dbwrapper_private::HandleError(status);
LogPrintf("Opened LevelDB successfully\n");
if (gArgs.GetBoolArg("-forcecompactdb", false)) {
- LogPrintf("Starting database compaction of %s\n", path.string());
+ LogPrintf("Starting database compaction of %s\n", fs::PathToString(path));
pdb->CompactRange(nullptr, nullptr);
- LogPrintf("Finished database compaction of %s\n", path.string());
+ LogPrintf("Finished database compaction of %s\n", fs::PathToString(path));
}
// The base-case obfuscation key, which is a noop.
@@ -160,10 +160,10 @@ CDBWrapper::CDBWrapper(const fs::path& path, size_t nCacheSize, bool fMemory, bo
Write(OBFUSCATE_KEY_KEY, new_key);
obfuscate_key = new_key;
- LogPrintf("Wrote new obfuscate key for %s: %s\n", path.string(), HexStr(obfuscate_key));
+ LogPrintf("Wrote new obfuscate key for %s: %s\n", fs::PathToString(path), HexStr(obfuscate_key));
}
- LogPrintf("Using obfuscation key for %s: %s\n", path.string(), HexStr(obfuscate_key));
+ LogPrintf("Using obfuscation key for %s: %s\n", fs::PathToString(path), HexStr(obfuscate_key));
}
CDBWrapper::~CDBWrapper()
@@ -197,13 +197,15 @@ bool CDBWrapper::WriteBatch(CDBBatch& batch, bool fSync)
return true;
}
-size_t CDBWrapper::DynamicMemoryUsage() const {
+size_t CDBWrapper::DynamicMemoryUsage() const
+{
std::string memory;
- if (!pdb->GetProperty("leveldb.approximate-memory-usage", &memory)) {
+ std::optional<size_t> parsed;
+ if (!pdb->GetProperty("leveldb.approximate-memory-usage", &memory) || !(parsed = ToIntegral<size_t>(memory))) {
LogPrint(BCLog::LEVELDB, "Failed to get approximate-memory-usage property\n");
return 0;
}
- return stoul(memory);
+ return parsed.value();
}
// Prefixed with null character to avoid collisions with other keys
diff --git a/src/external_signer.cpp b/src/external_signer.cpp
index d6388b759a..75070899c6 100644
--- a/src/external_signer.cpp
+++ b/src/external_signer.cpp
@@ -9,6 +9,7 @@
#include <util/system.h>
#include <external_signer.h>
+#include <algorithm>
#include <stdexcept>
#include <string>
#include <vector>
@@ -75,15 +76,14 @@ bool ExternalSigner::SignTransaction(PartiallySignedTransaction& psbtx, std::str
ssTx << psbtx;
// Check if signer fingerprint matches any input master key fingerprint
- bool match = false;
- for (unsigned int i = 0; i < psbtx.inputs.size(); ++i) {
- const PSBTInput& input = psbtx.inputs[i];
+ auto matches_signer_fingerprint = [&](const PSBTInput& input) {
for (const auto& entry : input.hd_keypaths) {
- if (m_fingerprint == strprintf("%08x", ReadBE32(entry.second.fingerprint))) match = true;
+ if (m_fingerprint == strprintf("%08x", ReadBE32(entry.second.fingerprint))) return true;
}
- }
+ return false;
+ };
- if (!match) {
+ if (!std::any_of(psbtx.inputs.begin(), psbtx.inputs.end(), matches_signer_fingerprint)) {
error = "Signer fingerprint " + m_fingerprint + " does not match any of the inputs:\n" + EncodeBase64(ssTx.str());
return false;
}
diff --git a/src/flatfile.cpp b/src/flatfile.cpp
index 151f1a38f1..929808c7fa 100644
--- a/src/flatfile.cpp
+++ b/src/flatfile.cpp
@@ -41,11 +41,11 @@ FILE* FlatFileSeq::Open(const FlatFilePos& pos, bool read_only)
if (!file && !read_only)
file = fsbridge::fopen(path, "wb+");
if (!file) {
- LogPrintf("Unable to open file %s\n", path.string());
+ LogPrintf("Unable to open file %s\n", fs::PathToString(path));
return nullptr;
}
if (pos.nPos && fseek(file, pos.nPos, SEEK_SET)) {
- LogPrintf("Unable to seek to position %u of %s\n", pos.nPos, path.string());
+ LogPrintf("Unable to seek to position %u of %s\n", pos.nPos, fs::PathToString(path));
fclose(file);
return nullptr;
}
diff --git a/src/fs.cpp b/src/fs.cpp
index b9b3c46d8d..7a99444eef 100644
--- a/src/fs.cpp
+++ b/src/fs.cpp
@@ -16,6 +16,7 @@
#define NOMINMAX
#endif
#include <codecvt>
+#include <limits>
#include <windows.h>
#endif
@@ -24,7 +25,7 @@ namespace fsbridge {
FILE *fopen(const fs::path& p, const char *mode)
{
#ifndef WIN32
- return ::fopen(p.string().c_str(), mode);
+ return ::fopen(p.c_str(), mode);
#else
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>,wchar_t> utf8_cvt;
return ::_wfopen(p.wstring().c_str(), utf8_cvt.from_bytes(mode).c_str());
@@ -46,7 +47,7 @@ static std::string GetErrorReason()
FileLock::FileLock(const fs::path& file)
{
- fd = open(file.string().c_str(), O_RDWR);
+ fd = open(file.c_str(), O_RDWR);
if (fd == -1) {
reason = GetErrorReason();
}
@@ -249,9 +250,9 @@ void ofstream::close()
#else // __GLIBCXX__
#if BOOST_VERSION >= 107700
-static_assert(sizeof(*BOOST_FILESYSTEM_C_STR(fs::path())) == sizeof(wchar_t),
+static_assert(sizeof(*BOOST_FILESYSTEM_C_STR(boost::filesystem::path())) == sizeof(wchar_t),
#else
-static_assert(sizeof(*fs::path().BOOST_FILESYSTEM_C_STR) == sizeof(wchar_t),
+static_assert(sizeof(*boost::filesystem::path().BOOST_FILESYSTEM_C_STR) == sizeof(wchar_t),
#endif // BOOST_VERSION >= 107700
"Warning: This build is using boost::filesystem ofstream and ifstream "
"implementations which will fail to open paths containing multibyte "
diff --git a/src/fs.h b/src/fs.h
index d77b90be66..4a0bf39e95 100644
--- a/src/fs.h
+++ b/src/fs.h
@@ -13,9 +13,132 @@
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
+#include <tinyformat.h>
/** Filesystem operations and types */
-namespace fs = boost::filesystem;
+namespace fs {
+
+using namespace boost::filesystem;
+
+/**
+ * Path class wrapper to prepare application code for transition from
+ * boost::filesystem library to std::filesystem implementation. The main
+ * purpose of the class is to define fs::path::u8string() and fs::u8path()
+ * functions not present in boost. It also blocks calls to the
+ * fs::path(std::string) implicit constructor and the fs::path::string()
+ * method, which worked well in the boost::filesystem implementation, but have
+ * unsafe and unpredictable behavior on Windows in the std::filesystem
+ * implementation (see implementation note in \ref PathToString for details).
+ */
+class path : public boost::filesystem::path
+{
+public:
+ using boost::filesystem::path::path;
+
+ // Allow path objects arguments for compatibility.
+ path(boost::filesystem::path path) : boost::filesystem::path::path(std::move(path)) {}
+ path& operator=(boost::filesystem::path path) { boost::filesystem::path::operator=(std::move(path)); return *this; }
+ path& operator/=(boost::filesystem::path path) { boost::filesystem::path::operator/=(std::move(path)); return *this; }
+
+ // Allow literal string arguments, which are safe as long as the literals are ASCII.
+ path(const char* c) : boost::filesystem::path(c) {}
+ path& operator=(const char* c) { boost::filesystem::path::operator=(c); return *this; }
+ path& operator/=(const char* c) { boost::filesystem::path::operator/=(c); return *this; }
+ path& append(const char* c) { boost::filesystem::path::append(c); return *this; }
+
+ // Disallow std::string arguments to avoid locale-dependent decoding on windows.
+ path(std::string) = delete;
+ path& operator=(std::string) = delete;
+ path& operator/=(std::string) = delete;
+ path& append(std::string) = delete;
+
+ // Disallow std::string conversion method to avoid locale-dependent encoding on windows.
+ std::string string() const = delete;
+
+ // Define UTF-8 string conversion method not present in boost::filesystem but present in std::filesystem.
+ std::string u8string() const { return boost::filesystem::path::string(); }
+};
+
+// Define UTF-8 string conversion function not present in boost::filesystem but present in std::filesystem.
+static inline path u8path(const std::string& string)
+{
+ return boost::filesystem::path(string);
+}
+
+// Disallow implicit std::string conversion for system_complete to avoid
+// locale-dependent encoding on windows.
+static inline path system_complete(const path& p)
+{
+ return boost::filesystem::system_complete(p);
+}
+
+// Disallow implicit std::string conversion for exists to avoid
+// locale-dependent encoding on windows.
+static inline bool exists(const path& p)
+{
+ return boost::filesystem::exists(p);
+}
+
+// Allow explicit quoted stream I/O.
+static inline auto quoted(const std::string& s)
+{
+ return boost::io::quoted(s, '&');
+}
+
+// Allow safe path append operations.
+static inline path operator+(path p1, path p2)
+{
+ p1 += std::move(p2);
+ return p1;
+}
+
+/**
+ * Convert path object to byte string. On POSIX, paths natively are byte
+ * strings so this is trivial. On Windows, paths natively are Unicode, so an
+ * encoding step is necessary.
+ *
+ * The inverse of \ref PathToString is \ref PathFromString. The strings
+ * returned and parsed by these functions can be used to call POSIX APIs, and
+ * for roundtrip conversion, logging, and debugging. But they are not
+ * guaranteed to be valid UTF-8, and are generally meant to be used internally,
+ * not externally. When communicating with external programs and libraries that
+ * require UTF-8, fs::path::u8string() and fs::u8path() methods can be used.
+ * For other applications, if support for non UTF-8 paths is required, or if
+ * higher-level JSON or XML or URI or C-style escapes are preferred, it may be
+ * also be appropriate to use different path encoding functions.
+ *
+ * Implementation note: On Windows, the std::filesystem::path(string)
+ * constructor and std::filesystem::path::string() method are not safe to use
+ * here, because these methods encode the path using C++'s narrow multibyte
+ * encoding, which on Windows corresponds to the current "code page", which is
+ * unpredictable and typically not able to represent all valid paths. So
+ * std::filesystem::path::u8string() and std::filesystem::u8path() functions
+ * are used instead on Windows. On POSIX, u8string/u8path functions are not
+ * safe to use because paths are not always valid UTF-8, so plain string
+ * methods which do not transform the path there are used.
+ */
+static inline std::string PathToString(const path& path)
+{
+#ifdef WIN32
+ return path.u8string();
+#else
+ static_assert(std::is_same<path::string_type, std::string>::value, "PathToString not implemented on this platform");
+ return path.boost::filesystem::path::string();
+#endif
+}
+
+/**
+ * Convert byte string to path object. Inverse of \ref PathToString.
+ */
+static inline path PathFromString(const std::string& string)
+{
+#ifdef WIN32
+ return u8path(string);
+#else
+ return boost::filesystem::path(string);
+#endif
+}
+} // namespace fs
/** Bridge operations to C stdio */
namespace fsbridge {
@@ -103,4 +226,11 @@ namespace fsbridge {
#endif // WIN32 && __GLIBCXX__
};
+// Disallow path operator<< formatting in tinyformat to avoid locale-dependent
+// encoding on windows.
+namespace tinyformat {
+template<> inline void formatValue(std::ostream&, const char*, const char*, int, const boost::filesystem::path&) = delete;
+template<> inline void formatValue(std::ostream&, const char*, const char*, int, const fs::path&) = delete;
+} // namespace tinyformat
+
#endif // BITCOIN_FS_H
diff --git a/src/hash.cpp b/src/hash.cpp
index 92c923fbd2..0e5bd975e4 100644
--- a/src/hash.cpp
+++ b/src/hash.cpp
@@ -75,10 +75,7 @@ unsigned int MurmurHash3(unsigned int nHashSeed, Span<const unsigned char> vData
void BIP32Hash(const ChainCode &chainCode, unsigned int nChild, unsigned char header, const unsigned char data[32], unsigned char output[64])
{
unsigned char num[4];
- num[0] = (nChild >> 24) & 0xFF;
- num[1] = (nChild >> 16) & 0xFF;
- num[2] = (nChild >> 8) & 0xFF;
- num[3] = (nChild >> 0) & 0xFF;
+ WriteBE32(num, nChild);
CHMAC_SHA512(chainCode.begin(), chainCode.size()).Write(&header, 1).Write(data, 32).Write(num, 4).Finalize(output);
}
diff --git a/src/i2p.cpp b/src/i2p.cpp
index 5e7e42fb77..35ac8731f2 100644
--- a/src/i2p.cpp
+++ b/src/i2p.cpp
@@ -328,7 +328,7 @@ void Session::GenerateAndSavePrivateKey(const Sock& sock)
if (!WriteBinaryFile(m_private_key_file,
std::string(m_private_key.begin(), m_private_key.end()))) {
throw std::runtime_error(
- strprintf("Cannot save I2P private key to %s", m_private_key_file));
+ strprintf("Cannot save I2P private key to %s", fs::quoted(fs::PathToString(m_private_key_file))));
}
}
diff --git a/src/index/base.cpp b/src/index/base.cpp
index 3ca86a310e..fc6dd77a72 100644
--- a/src/index/base.cpp
+++ b/src/index/base.cpp
@@ -321,7 +321,7 @@ bool BaseIndex::BlockUntilSyncedToCurrentChain() const
{
// Skip the queue-draining stuff if we know we're caught up with
- // ::ChainActive().Tip().
+ // m_chain.Tip().
LOCK(cs_main);
const CBlockIndex* chain_tip = m_chainstate->m_chain.Tip();
const CBlockIndex* best_block_index = m_best_block_index.load();
diff --git a/src/init.cpp b/src/init.cpp
index 4495ded64d..e554a18fbf 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -113,7 +113,7 @@ static const char* BITCOIN_PID_FILENAME = "bitcoind.pid";
static fs::path GetPidFile(const ArgsManager& args)
{
- return AbsPathForConfigVal(fs::path(args.GetArg("-pid", BITCOIN_PID_FILENAME)));
+ return AbsPathForConfigVal(fs::PathFromString(args.GetArg("-pid", BITCOIN_PID_FILENAME)));
}
[[nodiscard]] static bool CreatePidFile(const ArgsManager& args)
@@ -127,7 +127,7 @@ static fs::path GetPidFile(const ArgsManager& args)
#endif
return true;
} else {
- return InitError(strprintf(_("Unable to create the PID file '%s': %s"), GetPidFile(args).string(), std::strerror(errno)));
+ return InitError(strprintf(_("Unable to create the PID file '%s': %s"), fs::PathToString(GetPidFile(args)), std::strerror(errno)));
}
}
@@ -425,10 +425,10 @@ void SetupServerArgs(ArgsManager& argsman)
argsman.AddArg("-connect=<ip>", "Connect only to the specified node; -noconnect disables automatic connections (the rules for this peer are the same as for -addnode). This option can be specified multiple times to connect to multiple nodes.", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
argsman.AddArg("-discover", "Discover own IP addresses (default: 1 when listening and no -externalip or -proxy)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-dns", strprintf("Allow DNS lookups for -addnode, -seednode and -connect (default: %u)", DEFAULT_NAME_LOOKUP), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
- argsman.AddArg("-dnsseed", strprintf("Query for peer addresses via DNS lookup, if low on addresses (default: %u unless -connect used)", DEFAULT_DNSSEED), ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION);
+ argsman.AddArg("-dnsseed", strprintf("Query for peer addresses via DNS lookup, if low on addresses (default: %u unless -connect used)", DEFAULT_DNSSEED), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-externalip=<ip>", "Specify your own public address", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
- argsman.AddArg("-fixedseeds", strprintf("Allow fixed seeds if DNS seeds don't provide peers (default: %u)", DEFAULT_FIXEDSEEDS), ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION);
- argsman.AddArg("-forcednsseed", strprintf("Always query for peer addresses via DNS lookup (default: %u)", DEFAULT_FORCEDNSSEED), ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION);
+ argsman.AddArg("-fixedseeds", strprintf("Allow fixed seeds if DNS seeds don't provide peers (default: %u)", DEFAULT_FIXEDSEEDS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ argsman.AddArg("-forcednsseed", strprintf("Always query for peer addresses via DNS lookup (default: %u)", DEFAULT_FORCEDNSSEED), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-listen", "Accept connections from outside (default: 1 if no -proxy or -connect)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-listenonion", strprintf("Automatically create Tor onion service (default: %d)", DEFAULT_LISTEN_ONION), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-maxconnections=<n>", strprintf("Maintain at most <n> connections to peers (default: %u). This limit does not apply to connections manually added via -addnode or the addnode RPC, which have a separate limit of %u.", DEFAULT_MAX_PEER_CONNECTIONS, MAX_ADDNODE_CONNECTIONS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
@@ -438,7 +438,7 @@ void SetupServerArgs(ArgsManager& argsman)
argsman.AddArg("-maxuploadtarget=<n>", strprintf("Tries to keep outbound traffic under the given target (in MiB per 24h). Limit does not apply to peers with 'download' permission. 0 = no limit (default: %d)", DEFAULT_MAX_UPLOAD_TARGET), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-onion=<ip:port>", "Use separate SOCKS5 proxy to reach peers via Tor onion services, set -noonion to disable (default: -proxy)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-i2psam=<ip:port>", "I2P SAM proxy to reach I2P peers and accept I2P connections (default: none)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
- argsman.AddArg("-i2pacceptincoming", "If set and -i2psam is also set then incoming I2P connections are accepted via the SAM proxy. If this is not set but -i2psam is set then only outgoing connections will be made to the I2P network. Ignored if -i2psam is not set. Listening for incoming I2P connections is done through the SAM proxy, not by binding to a local address and port (default: 1)", ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION);
+ argsman.AddArg("-i2pacceptincoming", "If set and -i2psam is also set then incoming I2P connections are accepted via the SAM proxy. If this is not set but -i2psam is set then only outgoing connections will be made to the I2P network. Ignored if -i2psam is not set. Listening for incoming I2P connections is done through the SAM proxy, not by binding to a local address and port (default: 1)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-onlynet=<net>", "Make outgoing connections only through network <net> (" + Join(GetNetworkNames(), ", ") + "). Incoming connections are not affected by this option. This option can be specified multiple times to allow multiple networks. Warning: if it is used with non-onion networks and the -onion or -proxy option is set, then outbound onion connections will still be made; use -noonion or -onion=0 to disable outbound onion connections in this case.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-peerbloomfilters", strprintf("Support filtering of blocks and transaction with bloom filters (default: %u)", DEFAULT_PEERBLOOMFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-peerblockfilters", strprintf("Serve compact block filters to peers per BIP 157 (default: %u)", DEFAULT_PEERBLOCKFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
@@ -447,7 +447,7 @@ void SetupServerArgs(ArgsManager& argsman)
argsman.AddArg("-proxy=<ip:port>", "Connect through SOCKS5 proxy, set -noproxy to disable (default: disabled)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-proxyrandomize", strprintf("Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)", DEFAULT_PROXYRANDOMIZE), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-seednode=<ip>", "Connect to a node to retrieve peer addresses, and disconnect. This option can be specified multiple times to connect to multiple nodes.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
- argsman.AddArg("-networkactive", "Enable all P2P network activity (default: 1). Can be changed by the setnetworkactive RPC command", ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION);
+ argsman.AddArg("-networkactive", "Enable all P2P network activity (default: 1). Can be changed by the setnetworkactive RPC command", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-timeout=<n>", strprintf("Specify socket connection timeout in milliseconds. If an initial attempt to connect is unsuccessful after this amount of time, drop it (minimum: 1, default: %d)", DEFAULT_CONNECT_TIMEOUT), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-peertimeout=<n>", strprintf("Specify a p2p connection timeout delay in seconds. After connecting to a peer, wait this amount of time before considering disconnection based on inactivity (minimum: 1, default: %d)", DEFAULT_PEER_CONNECT_TIMEOUT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CONNECTION);
argsman.AddArg("-torcontrol=<ip>:<port>", strprintf("Tor control port to use if onion listening enabled (default: %s)", DEFAULT_TOR_CONTROL), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
@@ -462,7 +462,7 @@ void SetupServerArgs(ArgsManager& argsman)
hidden_args.emplace_back("-upnp");
#endif
#ifdef USE_NATPMP
- argsman.AddArg("-natpmp", strprintf("Use NAT-PMP to map the listening port (default: %s)", DEFAULT_NATPMP ? "1 when listening and no -proxy" : "0"), ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION);
+ argsman.AddArg("-natpmp", strprintf("Use NAT-PMP to map the listening port (default: %s)", DEFAULT_NATPMP ? "1 when listening and no -proxy" : "0"), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
#else
hidden_args.emplace_back("-natpmp");
#endif // USE_NATPMP
@@ -514,7 +514,7 @@ void SetupServerArgs(ArgsManager& argsman)
argsman.AddArg("-limitdescendantcount=<n>", strprintf("Do not accept transactions if any ancestor would have <n> or more in-mempool descendants (default: %u)", DEFAULT_DESCENDANT_LIMIT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-limitdescendantsize=<n>", strprintf("Do not accept transactions if any ancestor would have more than <n> kilobytes of in-mempool descendants (default: %u).", DEFAULT_DESCENDANT_SIZE_LIMIT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-addrmantest", "Allows to test address relay on localhost", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
- argsman.AddArg("-capturemessages", "Capture all P2P messages to disk", ArgsManager::ALLOW_BOOL | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
+ argsman.AddArg("-capturemessages", "Capture all P2P messages to disk", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-mocktime=<n>", "Replace actual time with " + UNIX_EPOCH_TIME + " (default: 0)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-maxsigcachesize=<n>", strprintf("Limit sum of signature cache and script execution cache sizes to <n> MiB (default: %u)", DEFAULT_MAX_SIG_CACHE_SIZE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-maxtipage=<n>", strprintf("Maximum tip age in seconds to consider node in initial block download (default: %u)", DEFAULT_MAX_TIP_AGE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
@@ -551,13 +551,13 @@ void SetupServerArgs(ArgsManager& argsman)
argsman.AddArg("-rpcthreads=<n>", strprintf("Set the number of threads to service RPC calls (default: %d)", DEFAULT_HTTP_THREADS), ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
argsman.AddArg("-rpcuser=<user>", "Username for JSON-RPC connections", ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE, OptionsCategory::RPC);
argsman.AddArg("-rpcwhitelist=<whitelist>", "Set a whitelist to filter incoming RPC calls for a specific user. The field <whitelist> comes in the format: <USERNAME>:<rpc 1>,<rpc 2>,...,<rpc n>. If multiple whitelists are set for a given user, they are set-intersected. See -rpcwhitelistdefault documentation for information on default whitelist behavior.", ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
- argsman.AddArg("-rpcwhitelistdefault", "Sets default behavior for rpc whitelisting. Unless rpcwhitelistdefault is set to 0, if any -rpcwhitelist is set, the rpc server acts as if all rpc users are subject to empty-unless-otherwise-specified whitelists. If rpcwhitelistdefault is set to 1 and no -rpcwhitelist is set, rpc server acts as if all rpc users are subject to empty whitelists.", ArgsManager::ALLOW_BOOL, OptionsCategory::RPC);
+ argsman.AddArg("-rpcwhitelistdefault", "Sets default behavior for rpc whitelisting. Unless rpcwhitelistdefault is set to 0, if any -rpcwhitelist is set, the rpc server acts as if all rpc users are subject to empty-unless-otherwise-specified whitelists. If rpcwhitelistdefault is set to 1 and no -rpcwhitelist is set, rpc server acts as if all rpc users are subject to empty whitelists.", ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
argsman.AddArg("-rpcworkqueue=<n>", strprintf("Set the depth of the work queue to service RPC calls (default: %d)", DEFAULT_HTTP_WORKQUEUE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::RPC);
argsman.AddArg("-server", "Accept command line and JSON-RPC commands", ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
#if HAVE_DECL_FORK
- argsman.AddArg("-daemon", strprintf("Run in the background as a daemon and accept commands (default: %d)", DEFAULT_DAEMON), ArgsManager::ALLOW_BOOL, OptionsCategory::OPTIONS);
- argsman.AddArg("-daemonwait", strprintf("Wait for initialization to be finished before exiting. This implies -daemon (default: %d)", DEFAULT_DAEMONWAIT), ArgsManager::ALLOW_BOOL, OptionsCategory::OPTIONS);
+ argsman.AddArg("-daemon", strprintf("Run in the background as a daemon and accept commands (default: %d)", DEFAULT_DAEMON), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ argsman.AddArg("-daemonwait", strprintf("Wait for initialization to be finished before exiting. This implies -daemon (default: %d)", DEFAULT_DAEMONWAIT), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#else
hidden_args.emplace_back("-daemon");
hidden_args.emplace_back("-daemonwait");
@@ -1062,10 +1062,10 @@ static bool LockDataDirectory(bool probeOnly)
// Make sure only a single Bitcoin process is using the data directory.
fs::path datadir = gArgs.GetDataDirNet();
if (!DirIsWritable(datadir)) {
- return InitError(strprintf(_("Cannot write to data directory '%s'; check permissions."), datadir.string()));
+ return InitError(strprintf(_("Cannot write to data directory '%s'; check permissions."), fs::PathToString(datadir)));
}
if (!LockDirectory(datadir, ".lock", probeOnly)) {
- return InitError(strprintf(_("Cannot obtain a lock on data directory %s. %s is probably already running."), datadir.string(), PACKAGE_NAME));
+ return InitError(strprintf(_("Cannot obtain a lock on data directory %s. %s is probably already running."), fs::PathToString(datadir), PACKAGE_NAME));
}
return true;
}
@@ -1126,12 +1126,12 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
LogPrintf("Using at most %i automatic connections (%i file descriptors available)\n", nMaxConnections, nFD);
// Warn about relative -datadir path.
- if (args.IsArgSet("-datadir") && !fs::path(args.GetArg("-datadir", "")).is_absolute()) {
+ if (args.IsArgSet("-datadir") && !fs::PathFromString(args.GetArg("-datadir", "")).is_absolute()) {
LogPrintf("Warning: relative datadir option '%s' specified, which will be interpreted relative to the " /* Continued */
"current working directory '%s'. This is fragile, because if bitcoin is started in the future "
"from a different location, it will be unable to locate the current data files. There could "
"also be data loss if bitcoin is started while in a temporary directory.\n",
- args.GetArg("-datadir", ""), fs::current_path().string());
+ args.GetArg("-datadir", ""), fs::PathToString(fs::current_path()));
}
InitSignatureCache();
@@ -1215,20 +1215,20 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
// Read asmap file if configured
std::vector<bool> asmap;
if (args.IsArgSet("-asmap")) {
- fs::path asmap_path = fs::path(args.GetArg("-asmap", ""));
+ fs::path asmap_path = fs::PathFromString(args.GetArg("-asmap", ""));
if (asmap_path.empty()) {
- asmap_path = DEFAULT_ASMAP_FILENAME;
+ asmap_path = fs::PathFromString(DEFAULT_ASMAP_FILENAME);
}
if (!asmap_path.is_absolute()) {
asmap_path = gArgs.GetDataDirNet() / asmap_path;
}
if (!fs::exists(asmap_path)) {
- InitError(strprintf(_("Could not find asmap file %s"), asmap_path));
+ InitError(strprintf(_("Could not find asmap file %s"), fs::quoted(fs::PathToString(asmap_path))));
return false;
}
asmap = DecodeAsmap(asmap_path);
if (asmap.size() == 0) {
- InitError(strprintf(_("Could not parse asmap file %s"), asmap_path));
+ InitError(strprintf(_("Could not parse asmap file %s"), fs::quoted(fs::PathToString(asmap_path))));
return false;
}
const uint256 asmap_version = SerializeHash(asmap);
@@ -1653,11 +1653,11 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
// ********************************************************* Step 11: import blocks
if (!CheckDiskSpace(gArgs.GetDataDirNet())) {
- InitError(strprintf(_("Error: Disk space is low for %s"), gArgs.GetDataDirNet()));
+ InitError(strprintf(_("Error: Disk space is low for %s"), fs::quoted(fs::PathToString(gArgs.GetDataDirNet()))));
return false;
}
if (!CheckDiskSpace(gArgs.GetBlocksDirPath())) {
- InitError(strprintf(_("Error: Disk space is low for %s"), gArgs.GetBlocksDirPath()));
+ InitError(strprintf(_("Error: Disk space is low for %s"), fs::quoted(fs::PathToString(gArgs.GetBlocksDirPath()))));
return false;
}
@@ -1685,7 +1685,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
std::vector<fs::path> vImportFiles;
for (const std::string& strFile : args.GetArgs("-loadblock")) {
- vImportFiles.push_back(strFile);
+ vImportFiles.push_back(fs::PathFromString(strFile));
}
chainman.m_load_block = std::thread(&util::TraceThread, "loadblk", [=, &chainman, &args] {
diff --git a/src/init/bitcoin-gui.cpp b/src/init/bitcoin-gui.cpp
new file mode 100644
index 0000000000..c549ed3cc0
--- /dev/null
+++ b/src/init/bitcoin-gui.cpp
@@ -0,0 +1,47 @@
+// Copyright (c) 2021 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 <interfaces/chain.h>
+#include <interfaces/echo.h>
+#include <interfaces/init.h>
+#include <interfaces/ipc.h>
+#include <interfaces/node.h>
+#include <interfaces/wallet.h>
+#include <node/context.h>
+#include <util/system.h>
+
+#include <memory>
+
+namespace init {
+namespace {
+const char* EXE_NAME = "bitcoin-gui";
+
+class BitcoinGuiInit : public interfaces::Init
+{
+public:
+ BitcoinGuiInit(const char* arg0) : m_ipc(interfaces::MakeIpc(EXE_NAME, arg0, *this))
+ {
+ m_node.args = &gArgs;
+ m_node.init = this;
+ }
+ std::unique_ptr<interfaces::Node> makeNode() override { return interfaces::MakeNode(m_node); }
+ std::unique_ptr<interfaces::Chain> makeChain() override { return interfaces::MakeChain(m_node); }
+ std::unique_ptr<interfaces::WalletClient> makeWalletClient(interfaces::Chain& chain) override
+ {
+ return MakeWalletClient(chain, *Assert(m_node.args));
+ }
+ std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); }
+ interfaces::Ipc* ipc() override { return m_ipc.get(); }
+ NodeContext m_node;
+ std::unique_ptr<interfaces::Ipc> m_ipc;
+};
+} // namespace
+} // namespace init
+
+namespace interfaces {
+std::unique_ptr<Init> MakeGuiInit(int argc, char* argv[])
+{
+ return std::make_unique<init::BitcoinGuiInit>(argc > 0 ? argv[0] : "");
+}
+} // namespace interfaces
diff --git a/src/init/bitcoin-qt.cpp b/src/init/bitcoin-qt.cpp
new file mode 100644
index 0000000000..d71177e885
--- /dev/null
+++ b/src/init/bitcoin-qt.cpp
@@ -0,0 +1,42 @@
+// Copyright (c) 2021 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 <interfaces/chain.h>
+#include <interfaces/echo.h>
+#include <interfaces/init.h>
+#include <interfaces/node.h>
+#include <interfaces/wallet.h>
+#include <node/context.h>
+#include <util/system.h>
+
+#include <memory>
+
+namespace init {
+namespace {
+class BitcoinQtInit : public interfaces::Init
+{
+public:
+ BitcoinQtInit()
+ {
+ m_node.args = &gArgs;
+ m_node.init = this;
+ }
+ std::unique_ptr<interfaces::Node> makeNode() override { return interfaces::MakeNode(m_node); }
+ std::unique_ptr<interfaces::Chain> makeChain() override { return interfaces::MakeChain(m_node); }
+ std::unique_ptr<interfaces::WalletClient> makeWalletClient(interfaces::Chain& chain) override
+ {
+ return MakeWalletClient(chain, *Assert(m_node.args));
+ }
+ std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); }
+ NodeContext m_node;
+};
+} // namespace
+} // namespace init
+
+namespace interfaces {
+std::unique_ptr<Init> MakeGuiInit(int argc, char* argv[])
+{
+ return std::make_unique<init::BitcoinQtInit>();
+}
+} // namespace interfaces
diff --git a/src/init/bitcoin-wallet.cpp b/src/init/bitcoin-wallet.cpp
new file mode 100644
index 0000000000..e9dcde72fe
--- /dev/null
+++ b/src/init/bitcoin-wallet.cpp
@@ -0,0 +1,12 @@
+// Copyright (c) 2021 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 <interfaces/init.h>
+
+namespace interfaces {
+std::unique_ptr<Init> MakeWalletInit(int argc, char* argv[], int& exit_status)
+{
+ return std::make_unique<Init>();
+}
+} // namespace interfaces
diff --git a/src/init/common.cpp b/src/init/common.cpp
index 5c1f469081..8f9e0ebc87 100644
--- a/src/init/common.cpp
+++ b/src/init/common.cpp
@@ -81,7 +81,7 @@ void AddLoggingArgs(ArgsManager& argsman)
void SetLoggingOptions(const ArgsManager& args)
{
LogInstance().m_print_to_file = !args.IsArgNegated("-debuglogfile");
- LogInstance().m_file_path = AbsPathForConfigVal(args.GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE));
+ LogInstance().m_file_path = AbsPathForConfigVal(fs::PathFromString(args.GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE)));
LogInstance().m_print_to_console = args.GetBoolArg("-printtoconsole", !args.GetBoolArg("-daemon", false));
LogInstance().m_log_timestamps = args.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS);
LogInstance().m_log_time_micros = args.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS);
@@ -128,24 +128,24 @@ bool StartLogging(const ArgsManager& args)
}
if (!LogInstance().StartLogging()) {
return InitError(strprintf(Untranslated("Could not open debug log file %s"),
- LogInstance().m_file_path.string()));
+ fs::PathToString(LogInstance().m_file_path)));
}
if (!LogInstance().m_log_timestamps)
LogPrintf("Startup time: %s\n", FormatISO8601DateTime(GetTime()));
- LogPrintf("Default data directory %s\n", GetDefaultDataDir().string());
- LogPrintf("Using data directory %s\n", gArgs.GetDataDirNet().string());
+ LogPrintf("Default data directory %s\n", fs::PathToString(GetDefaultDataDir()));
+ LogPrintf("Using data directory %s\n", fs::PathToString(gArgs.GetDataDirNet()));
// Only log conf file usage message if conf file actually exists.
fs::path config_file_path = GetConfigFile(args.GetArg("-conf", BITCOIN_CONF_FILENAME));
if (fs::exists(config_file_path)) {
- LogPrintf("Config file: %s\n", config_file_path.string());
+ LogPrintf("Config file: %s\n", fs::PathToString(config_file_path));
} else if (args.IsArgSet("-conf")) {
// Warn if no conf file exists at path provided by user
- InitWarning(strprintf(_("The specified config file %s does not exist"), config_file_path.string()));
+ InitWarning(strprintf(_("The specified config file %s does not exist"), fs::PathToString(config_file_path)));
} else {
// Not categorizing as "Warning" because it's the default behavior
- LogPrintf("Config file: %s (not found, skipping)\n", config_file_path.string());
+ LogPrintf("Config file: %s (not found, skipping)\n", fs::PathToString(config_file_path));
}
// Log the config arguments to debug.log
diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h
index 9a97cad1f8..d4ceb517dd 100644
--- a/src/interfaces/chain.h
+++ b/src/interfaces/chain.h
@@ -289,7 +289,7 @@ public:
virtual void requestMempoolTransactions(Notifications& notifications) = 0;
//! Check if Taproot has activated
- virtual bool isTaprootActive() const = 0;
+ virtual bool isTaprootActive() = 0;
};
//! Interface to let node manage chain clients (wallets, or maybe tools for
diff --git a/src/ipc/process.cpp b/src/ipc/process.cpp
index 43ed1f1bae..9036b80c45 100644
--- a/src/ipc/process.cpp
+++ b/src/ipc/process.cpp
@@ -30,8 +30,8 @@ public:
return mp::SpawnProcess(pid, [&](int fd) {
fs::path path = argv0_path;
path.remove_filename();
- path.append(new_exe_name);
- return std::vector<std::string>{path.string(), "-ipcfd", strprintf("%i", fd)};
+ path /= fs::PathFromString(new_exe_name);
+ return std::vector<std::string>{fs::PathToString(path), "-ipcfd", strprintf("%i", fd)};
});
}
int waitSpawned(int pid) override { return mp::WaitProcess(pid); }
diff --git a/src/key.cpp b/src/key.cpp
index 40df248e02..39155e4311 100644
--- a/src/key.cpp
+++ b/src/key.cpp
@@ -343,8 +343,7 @@ CExtPubKey CExtKey::Neuter() const {
void CExtKey::Encode(unsigned char code[BIP32_EXTKEY_SIZE]) const {
code[0] = nDepth;
memcpy(code+1, vchFingerprint, 4);
- code[5] = (nChild >> 24) & 0xFF; code[6] = (nChild >> 16) & 0xFF;
- code[7] = (nChild >> 8) & 0xFF; code[8] = (nChild >> 0) & 0xFF;
+ WriteBE32(code+5, nChild);
memcpy(code+9, chaincode.begin(), 32);
code[41] = 0;
assert(key.size() == 32);
@@ -354,7 +353,7 @@ void CExtKey::Encode(unsigned char code[BIP32_EXTKEY_SIZE]) const {
void CExtKey::Decode(const unsigned char code[BIP32_EXTKEY_SIZE]) {
nDepth = code[0];
memcpy(vchFingerprint, code+1, 4);
- nChild = (code[5] << 24) | (code[6] << 16) | (code[7] << 8) | code[8];
+ nChild = ReadBE32(code+5);
memcpy(chaincode.begin(), code+9, 32);
key.Set(code+42, code+BIP32_EXTKEY_SIZE, true);
if ((nDepth == 0 && (nChild != 0 || ReadLE32(vchFingerprint) != 0)) || code[41] != 0) key = CKey();
diff --git a/src/logging.cpp b/src/logging.cpp
index a352e106e5..1efce21bdb 100644
--- a/src/logging.cpp
+++ b/src/logging.cpp
@@ -161,6 +161,7 @@ const CLogCategoryDesc LogCategories[] =
{BCLog::IPC, "ipc"},
{BCLog::LOCK, "lock"},
{BCLog::UTIL, "util"},
+ {BCLog::BLOCKSTORE, "blockstorage"},
{BCLog::ALL, "1"},
{BCLog::ALL, "all"},
};
diff --git a/src/logging.h b/src/logging.h
index 02e64a7c48..f46104364c 100644
--- a/src/logging.h
+++ b/src/logging.h
@@ -61,6 +61,7 @@ namespace BCLog {
IPC = (1 << 23),
LOCK = (1 << 24),
UTIL = (1 << 25),
+ BLOCKSTORE = (1 << 26),
ALL = ~(uint32_t)0,
};
diff --git a/src/merkleblock.h b/src/merkleblock.h
index 0e4ed72130..70749b6378 100644
--- a/src/merkleblock.h
+++ b/src/merkleblock.h
@@ -6,10 +6,10 @@
#ifndef BITCOIN_MERKLEBLOCK_H
#define BITCOIN_MERKLEBLOCK_H
+#include <common/bloom.h>
+#include <primitives/block.h>
#include <serialize.h>
#include <uint256.h>
-#include <primitives/block.h>
-#include <bloom.h>
#include <vector>
diff --git a/src/net.cpp b/src/net.cpp
index 7271ff22b2..8cf59f0b0d 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -626,25 +626,26 @@ bool CNode::ReceiveMsgBytes(Span<const uint8_t> msg_bytes, bool& complete)
if (m_deserializer->Complete()) {
// decompose a transport agnostic CNetMessage from the deserializer
- uint32_t out_err_raw_size{0};
- std::optional<CNetMessage> result{m_deserializer->GetMessage(time, out_err_raw_size)};
- if (!result) {
+ bool reject_message{false};
+ CNetMessage msg = m_deserializer->GetMessage(time, reject_message);
+ if (reject_message) {
// Message deserialization failed. Drop the message but don't disconnect the peer.
// store the size of the corrupt message
- mapRecvBytesPerMsgCmd.find(NET_MESSAGE_COMMAND_OTHER)->second += out_err_raw_size;
+ mapRecvBytesPerMsgCmd.at(NET_MESSAGE_COMMAND_OTHER) += msg.m_raw_message_size;
continue;
}
- //store received bytes per message command
- //to prevent a memory DOS, only allow valid commands
- mapMsgCmdSize::iterator i = mapRecvBytesPerMsgCmd.find(result->m_command);
- if (i == mapRecvBytesPerMsgCmd.end())
+ // Store received bytes per message command
+ // to prevent a memory DOS, only allow valid commands
+ auto i = mapRecvBytesPerMsgCmd.find(msg.m_command);
+ if (i == mapRecvBytesPerMsgCmd.end()) {
i = mapRecvBytesPerMsgCmd.find(NET_MESSAGE_COMMAND_OTHER);
+ }
assert(i != mapRecvBytesPerMsgCmd.end());
- i->second += result->m_raw_message_size;
+ i->second += msg.m_raw_message_size;
// push the message to the process queue,
- vRecvMsg.push_back(std::move(*result));
+ vRecvMsg.push_back(std::move(msg));
complete = true;
}
@@ -718,16 +719,18 @@ const uint256& V1TransportDeserializer::GetMessageHash() const
return data_hash;
}
-std::optional<CNetMessage> V1TransportDeserializer::GetMessage(const std::chrono::microseconds time, uint32_t& out_err_raw_size)
+CNetMessage V1TransportDeserializer::GetMessage(const std::chrono::microseconds time, bool& reject_message)
{
+ // Initialize out parameter
+ reject_message = false;
// decompose a single CNetMessage from the TransportDeserializer
- std::optional<CNetMessage> msg(std::move(vRecv));
+ CNetMessage msg(std::move(vRecv));
// store command string, time, and sizes
- msg->m_command = hdr.GetCommand();
- msg->m_time = time;
- msg->m_message_size = hdr.nMessageSize;
- msg->m_raw_message_size = hdr.nMessageSize + CMessageHeader::HEADER_SIZE;
+ msg.m_command = hdr.GetCommand();
+ msg.m_time = time;
+ msg.m_message_size = hdr.nMessageSize;
+ msg.m_raw_message_size = hdr.nMessageSize + CMessageHeader::HEADER_SIZE;
uint256 hash = GetMessageHash();
@@ -737,17 +740,15 @@ std::optional<CNetMessage> V1TransportDeserializer::GetMessage(const std::chrono
// Check checksum and header command string
if (memcmp(hash.begin(), hdr.pchChecksum, CMessageHeader::CHECKSUM_SIZE) != 0) {
LogPrint(BCLog::NET, "Header error: Wrong checksum (%s, %u bytes), expected %s was %s, peer=%d\n",
- SanitizeString(msg->m_command), msg->m_message_size,
+ SanitizeString(msg.m_command), msg.m_message_size,
HexStr(Span<uint8_t>(hash.begin(), hash.begin() + CMessageHeader::CHECKSUM_SIZE)),
HexStr(hdr.pchChecksum),
m_node_id);
- out_err_raw_size = msg->m_raw_message_size;
- msg = std::nullopt;
+ reject_message = true;
} else if (!hdr.IsCommandValid()) {
LogPrint(BCLog::NET, "Header error: Invalid message type (%s, %u bytes), peer=%d\n",
- SanitizeString(hdr.GetCommand()), msg->m_message_size, m_node_id);
- out_err_raw_size = msg->m_raw_message_size;
- msg.reset();
+ SanitizeString(hdr.GetCommand()), msg.m_message_size, m_node_id);
+ reject_message = true;
}
// Always reset the network deserializer (prepare for the next message)
@@ -1295,9 +1296,8 @@ void CConnman::NotifyNumConnectionsChanged()
}
}
-bool CConnman::ShouldRunInactivityChecks(const CNode& node, std::optional<int64_t> now_in) const
+bool CConnman::ShouldRunInactivityChecks(const CNode& node, int64_t now) const
{
- const int64_t now = now_in ? now_in.value() : GetTimeSeconds();
return node.nTimeConnected + m_peer_connect_timeout < now;
}
@@ -2981,7 +2981,7 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, SOCKET hSocketIn, const
LogPrint(BCLog::NET, "Added connection peer=%d\n", id);
}
- m_deserializer = std::make_unique<V1TransportDeserializer>(V1TransportDeserializer(Params(), GetId(), SER_NETWORK, INIT_PROTO_VERSION));
+ m_deserializer = std::make_unique<V1TransportDeserializer>(V1TransportDeserializer(Params(), id, SER_NETWORK, INIT_PROTO_VERSION));
m_serializer = std::make_unique<V1TransportSerializer>(V1TransportSerializer());
}
diff --git a/src/net.h b/src/net.h
index e2071414b4..878f10cd42 100644
--- a/src/net.h
+++ b/src/net.h
@@ -7,8 +7,8 @@
#define BITCOIN_NET_H
#include <addrman.h>
-#include <bloom.h>
#include <chainparams.h>
+#include <common/bloom.h>
#include <compat.h>
#include <consensus/amount.h>
#include <crypto/siphash.h>
@@ -308,7 +308,7 @@ public:
/** read and deserialize data, advances msg_bytes data pointer */
virtual int Read(Span<const uint8_t>& msg_bytes) = 0;
// decomposes a message from the context
- virtual std::optional<CNetMessage> GetMessage(std::chrono::microseconds time, uint32_t& out_err) = 0;
+ virtual CNetMessage GetMessage(std::chrono::microseconds time, bool& reject_message) = 0;
virtual ~TransportDeserializer() {}
};
@@ -372,7 +372,7 @@ public:
}
return ret;
}
- std::optional<CNetMessage> GetMessage(std::chrono::microseconds time, uint32_t& out_err_raw_size) override;
+ CNetMessage GetMessage(std::chrono::microseconds time, bool& reject_message) override;
};
/** The TransportSerializer prepares messages for the network transport
@@ -942,7 +942,7 @@ public:
std::chrono::microseconds PoissonNextSendInbound(std::chrono::microseconds now, std::chrono::seconds average_interval);
/** Return true if we should disconnect the peer for failing an inactivity check. */
- bool ShouldRunInactivityChecks(const CNode& node, std::optional<int64_t> now=std::nullopt) const;
+ bool ShouldRunInactivityChecks(const CNode& node, int64_t secs_now) const;
private:
struct ListenSocket {
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 66b99aa2bb..9f3aa5b4a3 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -2299,7 +2299,8 @@ void PeerManagerImpl::ProcessOrphanTx(std::set<uint256>& orphan_work_set)
break;
}
}
- m_mempool.check(m_chainman.ActiveChainstate());
+ CChainState& active_chainstate = m_chainman.ActiveChainstate();
+ m_mempool.check(active_chainstate.CoinsTip(), active_chainstate.m_chain.Height() + 1);
}
bool PeerManagerImpl::PrepareBlockFilterRequest(CNode& peer,
@@ -2960,16 +2961,17 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
best_block = &inv.hash;
}
} else if (inv.IsGenTxMsg()) {
+ if (reject_tx_invs) {
+ LogPrint(BCLog::NET, "transaction (%s) inv sent in violation of protocol, disconnecting peer=%d\n", inv.hash.ToString(), pfrom.GetId());
+ pfrom.fDisconnect = true;
+ return;
+ }
const GenTxid gtxid = ToGenTxid(inv);
const bool fAlreadyHave = AlreadyHaveTx(gtxid);
LogPrint(BCLog::NET, "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom.GetId());
pfrom.AddKnownTx(inv.hash);
- if (reject_tx_invs) {
- LogPrint(BCLog::NET, "transaction (%s) inv sent in violation of protocol, disconnecting peer=%d\n", inv.hash.ToString(), pfrom.GetId());
- pfrom.fDisconnect = true;
- return;
- } else if (!fAlreadyHave && !m_chainman.ActiveChainstate().IsInitialBlockDownload()) {
+ if (!fAlreadyHave && !m_chainman.ActiveChainstate().IsInitialBlockDownload()) {
AddTxAnnouncement(pfrom, gtxid, current_time);
}
} else {
@@ -3242,12 +3244,12 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// already; and an adversary can already relay us old transactions
// (older than our recency filter) if trying to DoS us, without any need
// for witness malleation.
- if (AlreadyHaveTx(GenTxid(/* is_wtxid=*/true, wtxid))) {
+ if (AlreadyHaveTx(GenTxid::Wtxid(wtxid))) {
if (pfrom.HasPermission(NetPermissionFlags::ForceRelay)) {
// Always relay transactions received from peers with forcerelay
// permission, even if they were already in the mempool, allowing
// the node to function as a gateway for nodes hidden behind it.
- if (!m_mempool.exists(tx.GetHash())) {
+ if (!m_mempool.exists(GenTxid::Txid(tx.GetHash()))) {
LogPrintf("Not relaying non-mempool transaction %s from forcerelay peer=%d\n", tx.GetHash().ToString(), pfrom.GetId());
} else {
LogPrintf("Force relaying tx %s from peer=%d\n", tx.GetHash().ToString(), pfrom.GetId());
@@ -3261,7 +3263,8 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
const TxValidationState& state = result.m_state;
if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
- m_mempool.check(m_chainman.ActiveChainstate());
+ CChainState& active_chainstate = m_chainman.ActiveChainstate();
+ m_mempool.check(active_chainstate.CoinsTip(), active_chainstate.m_chain.Height() + 1);
// As this version of the transaction was acceptable, we can forget about any
// requests for it.
m_txrequest.ForgetTxHash(tx.GetHash());
@@ -3312,7 +3315,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// wtxidrelay peers.
// Eventually we should replace this with an improved
// protocol for getting all unconfirmed parents.
- const GenTxid gtxid{/* is_wtxid=*/false, parent_txid};
+ const auto gtxid{GenTxid::Txid(parent_txid)};
pfrom.AddKnownTx(parent_txid);
if (!AlreadyHaveTx(gtxid)) AddTxAnnouncement(pfrom, gtxid, current_time);
}
@@ -4312,8 +4315,11 @@ void PeerManagerImpl::CheckForStaleTipAndEvictPeers()
void PeerManagerImpl::MaybeSendPing(CNode& node_to, Peer& peer, std::chrono::microseconds now)
{
- if (m_connman.ShouldRunInactivityChecks(node_to) && peer.m_ping_nonce_sent &&
+ if (m_connman.ShouldRunInactivityChecks(node_to, std::chrono::duration_cast<std::chrono::seconds>(now).count()) &&
+ peer.m_ping_nonce_sent &&
now > peer.m_ping_start.load() + std::chrono::seconds{TIMEOUT_INTERVAL}) {
+ // The ping timeout is using mocktime. To disable the check during
+ // testing, increase -peertimeout.
LogPrint(BCLog::NET, "ping timeout: %fs peer=%d\n", 0.000001 * count_microseconds(now - peer.m_ping_start.load()), peer.m_id);
node_to.fDisconnect = true;
return;
diff --git a/src/netaddress.h b/src/netaddress.h
index 66c8c48f08..57eb8bc72f 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -253,7 +253,6 @@ public:
}
}
- friend class CNetAddrHash;
friend class CSubNet;
private:
@@ -467,22 +466,6 @@ private:
}
};
-class CNetAddrHash
-{
-public:
- size_t operator()(const CNetAddr& a) const noexcept
- {
- CSipHasher hasher(m_salt_k0, m_salt_k1);
- hasher.Write(a.m_net);
- hasher.Write(a.m_addr.data(), a.m_addr.size());
- return static_cast<size_t>(hasher.Finalize());
- }
-
-private:
- const uint64_t m_salt_k0 = GetRand(std::numeric_limits<uint64_t>::max());
- const uint64_t m_salt_k1 = GetRand(std::numeric_limits<uint64_t>::max());
-};
-
class CSubNet
{
protected:
@@ -565,6 +548,25 @@ public:
READWRITEAS(CNetAddr, obj);
READWRITE(Using<BigEndianFormatter<2>>(obj.port));
}
+
+ friend class CServiceHash;
+};
+
+class CServiceHash
+{
+public:
+ size_t operator()(const CService& a) const noexcept
+ {
+ CSipHasher hasher(m_salt_k0, m_salt_k1);
+ hasher.Write(a.m_net);
+ hasher.Write(a.port);
+ hasher.Write(a.m_addr.data(), a.m_addr.size());
+ return static_cast<size_t>(hasher.Finalize());
+ }
+
+private:
+ const uint64_t m_salt_k0 = GetRand(std::numeric_limits<uint64_t>::max());
+ const uint64_t m_salt_k1 = GetRand(std::numeric_limits<uint64_t>::max());
};
#endif // BITCOIN_NETADDRESS_H
diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp
index 0b7df9bd9a..53bc2b5069 100644
--- a/src/node/blockstorage.cpp
+++ b/src/node/blockstorage.cpp
@@ -68,13 +68,14 @@ void CleanupBlockRevFiles()
LogPrintf("Removing unusable blk?????.dat and rev?????.dat files for -reindex with -prune\n");
fs::path blocksdir = gArgs.GetBlocksDirPath();
for (fs::directory_iterator it(blocksdir); it != fs::directory_iterator(); it++) {
+ const std::string path = fs::PathToString(it->path().filename());
if (fs::is_regular_file(*it) &&
- it->path().filename().string().length() == 12 &&
- it->path().filename().string().substr(8,4) == ".dat")
+ path.length() == 12 &&
+ path.substr(8,4) == ".dat")
{
- if (it->path().filename().string().substr(0, 3) == "blk") {
- mapBlockFiles[it->path().filename().string().substr(3, 5)] = it->path();
- } else if (it->path().filename().string().substr(0, 3) == "rev") {
+ if (path.substr(0, 3) == "blk") {
+ mapBlockFiles[path.substr(3, 5)] = it->path();
+ } else if (path.substr(0, 3) == "rev") {
remove(it->path());
}
}
@@ -204,7 +205,7 @@ void UnlinkPrunedFiles(const std::set<int>& setFilesToPrune)
FlatFilePos pos(*it, 0);
fs::remove(BlockFileSeq().FileName(pos));
fs::remove(UndoFileSeq().FileName(pos));
- LogPrintf("Prune: %s deleted blk/rev (%05u)\n", __func__, *it);
+ LogPrint(BCLog::BLOCKSTORE, "Prune: %s deleted blk/rev (%05u)\n", __func__, *it);
}
}
@@ -261,7 +262,7 @@ bool FindBlockPos(FlatFilePos& pos, unsigned int nAddSize, unsigned int nHeight,
if ((int)nFile != nLastBlockFile) {
if (!fKnown) {
- LogPrint(BCLog::VALIDATION, "Leaving block file %i: %s\n", nLastBlockFile, vinfoBlockFile[nLastBlockFile].ToString());
+ LogPrint(BCLog::BLOCKSTORE, "Leaving block file %i: %s\n", nLastBlockFile, vinfoBlockFile[nLastBlockFile].ToString());
}
FlushBlockFile(!fKnown, finalize_undo);
nLastBlockFile = nFile;
@@ -527,14 +528,14 @@ void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFile
for (const fs::path& path : vImportFiles) {
FILE* file = fsbridge::fopen(path, "rb");
if (file) {
- LogPrintf("Importing blocks file %s...\n", path.string());
+ LogPrintf("Importing blocks file %s...\n", fs::PathToString(path));
chainman.ActiveChainstate().LoadExternalBlockFile(file);
if (ShutdownRequested()) {
LogPrintf("Shutdown requested. Exit %s\n", __func__);
return;
}
} else {
- LogPrintf("Warning: Could not open blocks file %s\n", path.string());
+ LogPrintf("Warning: Could not open blocks file %s\n", fs::PathToString(path));
}
}
diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp
index 5b6d8416a7..192caf7994 100644
--- a/src/node/interfaces.cpp
+++ b/src/node/interfaces.cpp
@@ -555,7 +555,7 @@ public:
{
if (!m_node.mempool) return false;
LOCK(m_node.mempool->cs);
- return m_node.mempool->exists(txid);
+ return m_node.mempool->exists(GenTxid::Txid(txid));
}
bool hasDescendantsInMempool(const uint256& txid) override
{
@@ -698,7 +698,7 @@ public:
notifications.transactionAddedToMempool(entry.GetSharedTx(), 0 /* mempool_sequence */);
}
}
- bool isTaprootActive() const override
+ bool isTaprootActive() override
{
LOCK(::cs_main);
const CBlockIndex* tip = Assert(m_node.chainman)->ActiveChain().Tip();
diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp
index 2ae5798ebe..d8c21bd833 100644
--- a/src/policy/fees.cpp
+++ b/src/policy/fees.cpp
@@ -527,7 +527,7 @@ CBlockPolicyEstimator::CBlockPolicyEstimator()
fs::path est_filepath = gArgs.GetDataDirNet() / FEE_ESTIMATES_FILENAME;
CAutoFile est_file(fsbridge::fopen(est_filepath, "rb"), SER_DISK, CLIENT_VERSION);
if (est_file.IsNull() || !Read(est_file)) {
- LogPrintf("Failed to read fee estimates from %s. Continue anyway.\n", est_filepath.string());
+ LogPrintf("Failed to read fee estimates from %s. Continue anyway.\n", fs::PathToString(est_filepath));
}
}
@@ -549,7 +549,7 @@ void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, boo
if (txHeight != nBestSeenHeight) {
// Ignore side chains and re-orgs; assuming they are random they don't
// affect the estimate. We'll potentially double count transactions in 1-block reorgs.
- // Ignore txs if BlockPolicyEstimator is not in sync with ::ChainActive().Tip().
+ // Ignore txs if BlockPolicyEstimator is not in sync with ActiveChain().Tip().
// It will be synced next time a block is processed.
return;
}
@@ -887,7 +887,7 @@ void CBlockPolicyEstimator::Flush() {
fs::path est_filepath = gArgs.GetDataDirNet() / FEE_ESTIMATES_FILENAME;
CAutoFile est_file(fsbridge::fopen(est_filepath, "wb"), SER_DISK, CLIENT_VERSION);
if (est_file.IsNull() || !Write(est_file)) {
- LogPrintf("Failed to write fee estimates to %s. Continue anyway.\n", est_filepath.string());
+ LogPrintf("Failed to write fee estimates to %s. Continue anyway.\n", fs::PathToString(est_filepath));
}
}
diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp
index 9e433584e7..fced397e51 100644
--- a/src/policy/policy.cpp
+++ b/src/policy/policy.cpp
@@ -22,7 +22,7 @@ CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFeeIn)
// so dust is a spendable txout less than
// 182*dustRelayFee/1000 (in satoshis).
// 546 satoshis at the default rate of 3000 sat/kvB.
- // A typical spendable segwit txout is 31 bytes big, and will
+ // A typical spendable segwit P2WPKH txout is 31 bytes big, and will
// need a CTxIn of at least 67 bytes to spend:
// so dust is a spendable txout less than
// 98*dustRelayFee/1000 (in satoshis).
@@ -34,6 +34,11 @@ CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFeeIn)
int witnessversion = 0;
std::vector<unsigned char> witnessprogram;
+ // Note this computation is for spending a Segwit v0 P2WPKH output (a 33 bytes
+ // public key + an ECDSA signature). For Segwit v1 Taproot outputs the minimum
+ // satisfaction is lower (a single BIP340 signature) but this computation was
+ // kept to not further reduce the dust level.
+ // See discussion in https://github.com/bitcoin/bitcoin/pull/22779 for details.
if (txout.scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) {
// sum the sizes of the parts of a transaction input
// with 75% segwit discount applied to the script size.
diff --git a/src/policy/rbf.cpp b/src/policy/rbf.cpp
index 7ac2e22006..7e6b0cf245 100644
--- a/src/policy/rbf.cpp
+++ b/src/policy/rbf.cpp
@@ -22,7 +22,7 @@ RBFTransactionState IsRBFOptIn(const CTransaction& tx, const CTxMemPool& pool)
// If this transaction is not in our mempool, then we can't be sure
// we will know about all its inputs.
- if (!pool.exists(tx.GetHash())) {
+ if (!pool.exists(GenTxid::Txid(tx.GetHash()))) {
return RBFTransactionState::UNKNOWN;
}
@@ -98,7 +98,7 @@ std::optional<std::string> HasNoNewUnconfirmed(const CTransaction& tx,
if (!parents_of_conflicts.count(tx.vin[j].prevout.hash)) {
// Rather than check the UTXO set - potentially expensive - it's cheaper to just check
// if the new input refers to a tx that's in the mempool.
- if (pool.exists(tx.vin[j].prevout.hash)) {
+ if (pool.exists(GenTxid::Txid(tx.vin[j].prevout.hash))) {
return strprintf("replacement %s adds unconfirmed input, idx %d",
tx.GetHash().ToString(), j);
}
diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h
index 46db39f8db..947c1c60bb 100644
--- a/src/primitives/transaction.h
+++ b/src/primitives/transaction.h
@@ -391,8 +391,11 @@ class GenTxid
{
bool m_is_wtxid;
uint256 m_hash;
-public:
GenTxid(bool is_wtxid, const uint256& hash) : m_is_wtxid(is_wtxid), m_hash(hash) {}
+
+public:
+ static GenTxid Txid(const uint256& hash) { return GenTxid{false, hash}; }
+ static GenTxid Wtxid(const uint256& hash) { return GenTxid{true, hash}; }
bool IsWtxid() const { return m_is_wtxid; }
const uint256& GetHash() const { return m_hash; }
friend bool operator==(const GenTxid& a, const GenTxid& b) { return a.m_is_wtxid == b.m_is_wtxid && a.m_hash == b.m_hash; }
diff --git a/src/protocol.cpp b/src/protocol.cpp
index 2e70b41e4c..7506c81815 100644
--- a/src/protocol.cpp
+++ b/src/protocol.cpp
@@ -223,5 +223,5 @@ std::vector<std::string> serviceFlagsToStr(uint64_t flags)
GenTxid ToGenTxid(const CInv& inv)
{
assert(inv.IsGenTxMsg());
- return {inv.IsMsgWtx(), inv.hash};
+ return inv.IsMsgWtx() ? GenTxid::Wtxid(inv.hash) : GenTxid::Txid(inv.hash);
}
diff --git a/src/pubkey.cpp b/src/pubkey.cpp
index d14a20b870..956ff2b34a 100644
--- a/src/pubkey.cpp
+++ b/src/pubkey.cpp
@@ -337,8 +337,7 @@ bool CPubKey::Derive(CPubKey& pubkeyChild, ChainCode &ccChild, unsigned int nChi
void CExtPubKey::Encode(unsigned char code[BIP32_EXTKEY_SIZE]) const {
code[0] = nDepth;
memcpy(code+1, vchFingerprint, 4);
- code[5] = (nChild >> 24) & 0xFF; code[6] = (nChild >> 16) & 0xFF;
- code[7] = (nChild >> 8) & 0xFF; code[8] = (nChild >> 0) & 0xFF;
+ WriteBE32(code+5, nChild);
memcpy(code+9, chaincode.begin(), 32);
assert(pubkey.size() == CPubKey::COMPRESSED_SIZE);
memcpy(code+41, pubkey.begin(), CPubKey::COMPRESSED_SIZE);
@@ -347,7 +346,7 @@ void CExtPubKey::Encode(unsigned char code[BIP32_EXTKEY_SIZE]) const {
void CExtPubKey::Decode(const unsigned char code[BIP32_EXTKEY_SIZE]) {
nDepth = code[0];
memcpy(vchFingerprint, code+1, 4);
- nChild = (code[5] << 24) | (code[6] << 16) | (code[7] << 8) | code[8];
+ nChild = ReadBE32(code+5);
memcpy(chaincode.begin(), code+9, 32);
pubkey.Set(code+41, code+BIP32_EXTKEY_SIZE);
if ((nDepth == 0 && (nChild != 0 || ReadLE32(vchFingerprint) != 0)) || !pubkey.IsFullyValid()) pubkey = CPubKey();
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 00c9fd3059..3f412d8f19 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -13,7 +13,6 @@
#include <interfaces/handler.h>
#include <interfaces/init.h>
#include <interfaces/node.h>
-#include <node/context.h>
#include <node/ui_interface.h>
#include <noui.h>
#include <qt/bitcoingui.h>
@@ -46,7 +45,6 @@
#include <QApplication>
#include <QDebug>
-#include <QFontDatabase>
#include <QLatin1String>
#include <QLibraryInfo>
#include <QLocale>
@@ -155,10 +153,11 @@ static bool InitSettings()
std::vector<std::string> errors;
if (!gArgs.ReadSettingsFile(&errors)) {
- bilingual_str error = _("Settings file could not be read");
- InitError(Untranslated(strprintf("%s:\n%s\n", error.original, MakeUnorderedList(errors))));
+ std::string error = QT_TRANSLATE_NOOP("bitcoin-core", "Settings file could not be read");
+ std::string error_translated = QCoreApplication::translate("bitcoin-core", error.c_str()).toStdString();
+ InitError(Untranslated(strprintf("%s:\n%s\n", error, MakeUnorderedList(errors))));
- QMessageBox messagebox(QMessageBox::Critical, PACKAGE_NAME, QString::fromStdString(strprintf("%s.", error.translated)), QMessageBox::Reset | QMessageBox::Abort);
+ QMessageBox messagebox(QMessageBox::Critical, PACKAGE_NAME, QString::fromStdString(strprintf("%s.", error_translated)), QMessageBox::Reset | QMessageBox::Abort);
/*: Explanatory text shown on startup when the settings file cannot be read.
Prompts user to make a choice between resetting or aborting. */
messagebox.setInformativeText(QObject::tr("Do you want to reset settings to default values, or to abort without making changes?"));
@@ -177,10 +176,11 @@ static bool InitSettings()
errors.clear();
if (!gArgs.WriteSettingsFile(&errors)) {
- bilingual_str error = _("Settings file could not be written");
- InitError(Untranslated(strprintf("%s:\n%s\n", error.original, MakeUnorderedList(errors))));
+ std::string error = QT_TRANSLATE_NOOP("bitcoin-core", "Settings file could not be written");
+ std::string error_translated = QCoreApplication::translate("bitcoin-core", error.c_str()).toStdString();
+ InitError(Untranslated(strprintf("%s:\n%s\n", error, MakeUnorderedList(errors))));
- QMessageBox messagebox(QMessageBox::Critical, PACKAGE_NAME, QString::fromStdString(strprintf("%s.", error.translated)), QMessageBox::Ok);
+ QMessageBox messagebox(QMessageBox::Critical, PACKAGE_NAME, QString::fromStdString(strprintf("%s.", error_translated)), QMessageBox::Ok);
/*: Explanatory text shown on startup when the settings file could not be written.
Prompts user to check that we have the ability to write to the file.
Explains that the user has the option of running without a settings file.*/
@@ -463,9 +463,7 @@ int GuiMain(int argc, char* argv[])
std::tie(argc, argv) = winArgs.get();
#endif
- NodeContext node_context;
- int unused_exit_status;
- std::unique_ptr<interfaces::Init> init = interfaces::MakeNodeInit(node_context, argc, argv, unused_exit_status);
+ std::unique_ptr<interfaces::Init> init = interfaces::MakeGuiInit(argc, argv);
SetupEnvironment();
util::ThreadSetInternalName("main");
@@ -492,7 +490,7 @@ int GuiMain(int argc, char* argv[])
#endif
BitcoinApplication app;
- QFontDatabase::addApplicationFont(":/fonts/monospace");
+ GUIUtil::LoadFont(QStringLiteral(":/fonts/monospace"));
/// 2. Parse command-line options. We do this after qt in order to show an error if there are problems parsing these
// Command-line options take precedence:
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 610637360b..b68ce39b53 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -1304,8 +1304,6 @@ void BitcoinGUI::setHDStatus(bool privkeyDisabled, int hdEnabled)
labelWalletHDStatusIcon->setThemedPixmap(privkeyDisabled ? QStringLiteral(":/icons/eye") : hdEnabled ? QStringLiteral(":/icons/hd_enabled") : QStringLiteral(":/icons/hd_disabled"), STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE);
labelWalletHDStatusIcon->setToolTip(privkeyDisabled ? tr("Private key <b>disabled</b>") : hdEnabled ? tr("HD key generation is <b>enabled</b>") : tr("HD key generation is <b>disabled</b>"));
labelWalletHDStatusIcon->show();
- // eventually disable the QLabel to set its opacity to 50%
- labelWalletHDStatusIcon->setEnabled(hdEnabled);
}
void BitcoinGUI::setEncryptionStatus(int status)
diff --git a/src/qt/forms/createwalletdialog.ui b/src/qt/forms/createwalletdialog.ui
index b11fb026b0..56adbe17a5 100644
--- a/src/qt/forms/createwalletdialog.ui
+++ b/src/qt/forms/createwalletdialog.ui
@@ -107,6 +107,9 @@
<property name="text">
<string>Descriptor Wallet</string>
</property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
</widget>
</item>
<item>
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index 7b1384b485..4262866f32 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -272,6 +272,12 @@ bool hasEntryData(const QAbstractItemView *view, int column, int role)
return !selection.at(0).data(role).toString().isEmpty();
}
+void LoadFont(const QString& file_name)
+{
+ const int id = QFontDatabase::addApplicationFont(file_name);
+ assert(id != -1);
+}
+
QString getDefaultDataDirectory()
{
return boostPathToQString(GetDefaultDataDir());
@@ -647,12 +653,12 @@ void setClipboard(const QString& str)
fs::path qstringToBoostPath(const QString &path)
{
- return fs::path(path.toStdString());
+ return fs::u8path(path.toStdString());
}
QString boostPathToQString(const fs::path &path)
{
- return QString::fromStdString(path.string());
+ return QString::fromStdString(path.u8string());
}
QString NetworkToQString(Network net)
diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h
index 6287f95192..211f3f506d 100644
--- a/src/qt/guiutil.h
+++ b/src/qt/guiutil.h
@@ -114,6 +114,11 @@ namespace GUIUtil
void setClipboard(const QString& str);
/**
+ * Loads the font from the file specified by file_name, aborts if it fails.
+ */
+ void LoadFont(const QString& file_name);
+
+ /**
* Determine default data directory for operating system.
*/
QString getDefaultDataDirectory();
diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp
index 4c78fba752..2ca4b6a21e 100644
--- a/src/qt/intro.cpp
+++ b/src/qt/intro.cpp
@@ -263,7 +263,7 @@ bool Intro::showIfNeeded(bool& did_show_intro, int64_t& prune_MiB)
* (to be consistent with bitcoind behavior)
*/
if(dataDir != GUIUtil::getDefaultDataDirectory()) {
- gArgs.SoftSetArg("-datadir", GUIUtil::qstringToBoostPath(dataDir).string()); // use OS locale for path setting
+ gArgs.SoftSetArg("-datadir", fs::PathToString(GUIUtil::qstringToBoostPath(dataDir))); // use OS locale for path setting
}
return true;
}
diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp
index 2292c01d6a..61b52fd08a 100644
--- a/src/qt/splashscreen.cpp
+++ b/src/qt/splashscreen.cpp
@@ -184,8 +184,8 @@ static void InitMessage(SplashScreen *splash, const std::string &message)
static void ShowProgress(SplashScreen *splash, const std::string &title, int nProgress, bool resume_possible)
{
InitMessage(splash, title + std::string("\n") +
- (resume_possible ? _("(press q to shutdown and continue later)").translated
- : _("press q to shutdown").translated) +
+ (resume_possible ? SplashScreen::tr("(press q to shutdown and continue later)").toStdString()
+ : SplashScreen::tr("press q to shutdown").toStdString()) +
strprintf("\n%d", nProgress) + "%");
}
diff --git a/src/qt/test/addressbooktests.cpp b/src/qt/test/addressbooktests.cpp
index 0de781661a..ede0e4cf9e 100644
--- a/src/qt/test/addressbooktests.cpp
+++ b/src/qt/test/addressbooktests.cpp
@@ -63,9 +63,13 @@ void TestAddAddressesToSendBook(interfaces::Node& node)
auto wallet_client = interfaces::MakeWalletClient(*test.m_node.chain, *Assert(test.m_node.args));
test.m_node.wallet_client = wallet_client.get();
node.setContext(&test.m_node);
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", CreateMockWalletDatabase());
- wallet->SetupLegacyScriptPubKeyMan();
+ const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", CreateMockWalletDatabase());
wallet->LoadWallet();
+ wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
+ {
+ LOCK(wallet->cs_wallet);
+ wallet->SetupDescriptorScriptPubKeyMans();
+ }
auto build_address = [&wallet]() {
CKey key;
diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp
index 55d00bb37e..b26cddf4ae 100644
--- a/src/qt/test/test_main.cpp
+++ b/src/qt/test/test_main.cpp
@@ -52,9 +52,7 @@ int main(int argc, char* argv[])
BasicTestingSetup dummy{CBaseChainParams::REGTEST};
}
- NodeContext node_context;
- int unused_exit_status;
- std::unique_ptr<interfaces::Init> init = interfaces::MakeNodeInit(node_context, argc, argv, unused_exit_status);
+ std::unique_ptr<interfaces::Init> init = interfaces::MakeGuiInit(argc, argv);
gArgs.ForceSetArg("-listen", "0");
gArgs.ForceSetArg("-listenonion", "0");
gArgs.ForceSetArg("-discover", "0");
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index 62b135d3f1..badaf019b0 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -141,13 +141,22 @@ void TestGUI(interfaces::Node& node)
auto wallet_client = interfaces::MakeWalletClient(*test.m_node.chain, *Assert(test.m_node.args));
test.m_node.wallet_client = wallet_client.get();
node.setContext(&test.m_node);
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", CreateMockWalletDatabase());
+ const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", CreateMockWalletDatabase());
wallet->LoadWallet();
+ wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
{
- auto spk_man = wallet->GetOrCreateLegacyScriptPubKeyMan();
- LOCK2(wallet->cs_wallet, spk_man->cs_KeyStore);
- wallet->SetAddressBook(GetDestinationForKey(test.coinbaseKey.GetPubKey(), wallet->m_default_address_type), "", "receive");
- spk_man->AddKeyPubKey(test.coinbaseKey, test.coinbaseKey.GetPubKey());
+ LOCK(wallet->cs_wallet);
+ wallet->SetupDescriptorScriptPubKeyMans();
+
+ // Add the coinbase key
+ FlatSigningProvider provider;
+ std::string error;
+ std::unique_ptr<Descriptor> desc = Parse("combo(" + EncodeSecret(test.coinbaseKey) + ")", provider, error, /* require_checksum=*/ false);
+ assert(desc);
+ WalletDescriptor w_desc(std::move(desc), 0, 0, 1, 1);
+ if (!wallet->AddWalletDescriptor(w_desc, provider, "", false)) assert(false);
+ CTxDestination dest = GetDestinationForKey(test.coinbaseKey.GetPubKey(), wallet->m_default_address_type);
+ wallet->SetAddressBook(dest, "", "receive");
wallet->SetLastBlockProcessed(105, node.context()->chainman->ActiveChain().Tip()->GetBlockHash());
}
{
diff --git a/src/rest.cpp b/src/rest.cpp
index e50ab33e54..3746fd752a 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -189,9 +189,10 @@ static bool rest_headers(const std::any& context,
if (path.size() != 2)
return RESTERR(req, HTTP_BAD_REQUEST, "No header count specified. Use /rest/headers/<count>/<hash>.<ext>.");
- long count = strtol(path[0].c_str(), nullptr, 10);
- if (count < 1 || count > 2000)
+ const auto parsed_count{ToIntegral<size_t>(path[0])};
+ if (!parsed_count.has_value() || *parsed_count < 1 || *parsed_count > 2000) {
return RESTERR(req, HTTP_BAD_REQUEST, "Header count out of range: " + path[0]);
+ }
std::string hashStr = path[1];
uint256 hash;
@@ -199,8 +200,8 @@ static bool rest_headers(const std::any& context,
return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
const CBlockIndex* tip = nullptr;
- std::vector<const CBlockIndex *> headers;
- headers.reserve(count);
+ std::vector<const CBlockIndex*> headers;
+ headers.reserve(*parsed_count);
{
ChainstateManager* maybe_chainman = GetChainman(context, req);
if (!maybe_chainman) return false;
@@ -211,8 +212,9 @@ static bool rest_headers(const std::any& context,
const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(hash);
while (pindex != nullptr && active_chain.Contains(pindex)) {
headers.push_back(pindex);
- if (headers.size() == (unsigned long)count)
+ if (headers.size() == *parsed_count) {
break;
+ }
pindex = active_chain.Next(pindex);
}
}
@@ -260,7 +262,7 @@ static bool rest_headers(const std::any& context,
static bool rest_block(const std::any& context,
HTTPRequest* req,
const std::string& strURIPart,
- bool showTxDetails)
+ TxVerbosity tx_verbosity)
{
if (!CheckWarmup(req))
return false;
@@ -312,7 +314,7 @@ static bool rest_block(const std::any& context,
}
case RetFormat::JSON: {
- UniValue objBlock = blockToJSON(block, tip, pblockindex, showTxDetails);
+ UniValue objBlock = blockToJSON(block, tip, pblockindex, tx_verbosity);
std::string strJSON = objBlock.write() + "\n";
req->WriteHeader("Content-Type", "application/json");
req->WriteReply(HTTP_OK, strJSON);
@@ -327,12 +329,12 @@ static bool rest_block(const std::any& context,
static bool rest_block_extended(const std::any& context, HTTPRequest* req, const std::string& strURIPart)
{
- return rest_block(context, req, strURIPart, true);
+ return rest_block(context, req, strURIPart, TxVerbosity::SHOW_DETAILS_AND_PREVOUT);
}
static bool rest_block_notxdetails(const std::any& context, HTTPRequest* req, const std::string& strURIPart)
{
- return rest_block(context, req, strURIPart, false);
+ return rest_block(context, req, strURIPart, TxVerbosity::SHOW_TXID);
}
// A bit of a hack - dependency on a function defined in rpc/blockchain.cpp
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index ac746de32f..aa7a55e7a9 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -200,7 +200,7 @@ UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex
return result;
}
-UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, bool txDetails)
+UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, TxVerbosity verbosity)
{
UniValue result = blockheaderToJSON(tip, blockindex);
@@ -208,22 +208,29 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIn
result.pushKV("size", (int)::GetSerializeSize(block, PROTOCOL_VERSION));
result.pushKV("weight", (int)::GetBlockWeight(block));
UniValue txs(UniValue::VARR);
- if (txDetails) {
- CBlockUndo blockUndo;
- const bool have_undo = !IsBlockPruned(blockindex) && UndoReadFromDisk(blockUndo, blockindex);
- for (size_t i = 0; i < block.vtx.size(); ++i) {
- const CTransactionRef& tx = block.vtx.at(i);
- // coinbase transaction (i == 0) doesn't have undo data
- const CTxUndo* txundo = (have_undo && i) ? &blockUndo.vtxundo.at(i - 1) : nullptr;
- UniValue objTx(UniValue::VOBJ);
- TxToUniv(*tx, uint256(), objTx, true, RPCSerializationFlags(), txundo);
- txs.push_back(objTx);
- }
- } else {
- for (const CTransactionRef& tx : block.vtx) {
- txs.push_back(tx->GetHash().GetHex());
- }
+
+ switch (verbosity) {
+ case TxVerbosity::SHOW_TXID:
+ for (const CTransactionRef& tx : block.vtx) {
+ txs.push_back(tx->GetHash().GetHex());
+ }
+ break;
+
+ case TxVerbosity::SHOW_DETAILS:
+ case TxVerbosity::SHOW_DETAILS_AND_PREVOUT:
+ CBlockUndo blockUndo;
+ const bool have_undo = !IsBlockPruned(blockindex) && UndoReadFromDisk(blockUndo, blockindex);
+
+ for (size_t i = 0; i < block.vtx.size(); ++i) {
+ const CTransactionRef& tx = block.vtx.at(i);
+ // coinbase transaction (i.e. i == 0) doesn't have undo data
+ const CTxUndo* txundo = (have_undo && i > 0) ? &blockUndo.vtxundo.at(i - 1) : nullptr;
+ UniValue objTx(UniValue::VOBJ);
+ TxToUniv(*tx, uint256(), objTx, true, RPCSerializationFlags(), txundo, verbosity);
+ txs.push_back(objTx);
+ }
}
+
result.pushKV("tx", txs);
return result;
@@ -509,7 +516,7 @@ static void entryToJSON(const CTxMemPool& pool, UniValue& info, const CTxMemPool
std::set<std::string> setDepends;
for (const CTxIn& txin : tx.vin)
{
- if (pool.exists(txin.prevout.hash))
+ if (pool.exists(GenTxid::Txid(txin.prevout.hash)))
setDepends.insert(txin.prevout.hash.ToString());
}
@@ -931,7 +938,8 @@ static RPCHelpMan getblock()
return RPCHelpMan{"getblock",
"\nIf verbosity is 0, returns a string that is serialized, hex-encoded data for block 'hash'.\n"
"If verbosity is 1, returns an Object with information about block <hash>.\n"
- "If verbosity is 2, returns an Object with information about block <hash> and information about each transaction. \n",
+ "If verbosity is 2, returns an Object with information about block <hash> and information about each transaction.\n"
+ "If verbosity is 3, returns an Object with information about block <hash> and information about each transaction, including prevout information for inputs (only for unpruned blocks in the current best chain).\n",
{
{"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash"},
{"verbosity|verbose", RPCArg::Type::NUM, RPCArg::Default{1}, "0 for hex-encoded data, 1 for a json object, and 2 for json object with transaction data"},
@@ -1018,7 +1026,16 @@ static RPCHelpMan getblock()
return strHex;
}
- return blockToJSON(block, tip, pblockindex, verbosity >= 2);
+ TxVerbosity tx_verbosity;
+ if (verbosity == 1) {
+ tx_verbosity = TxVerbosity::SHOW_TXID;
+ } else if (verbosity == 2) {
+ tx_verbosity = TxVerbosity::SHOW_DETAILS;
+ } else {
+ tx_verbosity = TxVerbosity::SHOW_DETAILS_AND_PREVOUT;
+ }
+
+ return blockToJSON(block, tip, pblockindex, tx_verbosity);
},
};
}
@@ -2537,15 +2554,15 @@ static RPCHelpMan dumptxoutset()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- const fs::path path = fsbridge::AbsPathJoin(gArgs.GetDataDirNet(), request.params[0].get_str());
+ const fs::path path = fsbridge::AbsPathJoin(gArgs.GetDataDirNet(), fs::u8path(request.params[0].get_str()));
// Write to a temporary path and then move into `path` on completion
// to avoid confusion due to an interruption.
- const fs::path temppath = fsbridge::AbsPathJoin(gArgs.GetDataDirNet(), request.params[0].get_str() + ".incomplete");
+ const fs::path temppath = fsbridge::AbsPathJoin(gArgs.GetDataDirNet(), fs::u8path(request.params[0].get_str() + ".incomplete"));
if (fs::exists(path)) {
throw JSONRPCError(
RPC_INVALID_PARAMETER,
- path.string() + " already exists. If you are sure this is what you want, "
+ path.u8string() + " already exists. If you are sure this is what you want, "
"move it out of the way first");
}
@@ -2555,7 +2572,7 @@ static RPCHelpMan dumptxoutset()
UniValue result = CreateUTXOSnapshot(node, node.chainman->ActiveChainstate(), afile);
fs::rename(temppath, path);
- result.pushKV("path", path.string());
+ result.pushKV("path", path.u8string());
return result;
},
};
diff --git a/src/rpc/blockchain.h b/src/rpc/blockchain.h
index 09e471afdd..d9c6761f47 100644
--- a/src/rpc/blockchain.h
+++ b/src/rpc/blockchain.h
@@ -6,6 +6,7 @@
#define BITCOIN_RPC_BLOCKCHAIN_H
#include <consensus/amount.h>
+#include <core_io.h>
#include <streams.h>
#include <sync.h>
@@ -38,7 +39,7 @@ double GetDifficulty(const CBlockIndex* blockindex);
void RPCNotifyBlockChange(const CBlockIndex*);
/** Block description to JSON */
-UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, bool txDetails = false) LOCKS_EXCLUDED(cs_main);
+UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, TxVerbosity verbosity) LOCKS_EXCLUDED(cs_main);
/** Mempool information to JSON */
UniValue MempoolInfoToJSON(const CTxMemPool& pool);
diff --git a/src/rpc/request.cpp b/src/rpc/request.cpp
index a7866474e1..3245e04cdf 100644
--- a/src/rpc/request.cpp
+++ b/src/rpc/request.cpp
@@ -70,7 +70,7 @@ static fs::path GetAuthCookieFile(bool temp=false)
if (temp) {
arg += ".tmp";
}
- return AbsPathForConfigVal(fs::path(arg));
+ return AbsPathForConfigVal(fs::PathFromString(arg));
}
bool GenerateAuthCookie(std::string *cookie_out)
@@ -87,7 +87,7 @@ bool GenerateAuthCookie(std::string *cookie_out)
fs::path filepath_tmp = GetAuthCookieFile(true);
file.open(filepath_tmp);
if (!file.is_open()) {
- LogPrintf("Unable to open cookie authentication file %s for writing\n", filepath_tmp.string());
+ LogPrintf("Unable to open cookie authentication file %s for writing\n", fs::PathToString(filepath_tmp));
return false;
}
file << cookie;
@@ -95,10 +95,10 @@ bool GenerateAuthCookie(std::string *cookie_out)
fs::path filepath = GetAuthCookieFile(false);
if (!RenameOver(filepath_tmp, filepath)) {
- LogPrintf("Unable to rename cookie authentication file %s to %s\n", filepath_tmp.string(), filepath.string());
+ LogPrintf("Unable to rename cookie authentication file %s to %s\n", fs::PathToString(filepath_tmp), fs::PathToString(filepath));
return false;
}
- LogPrintf("Generated RPC authentication cookie %s\n", filepath.string());
+ LogPrintf("Generated RPC authentication cookie %s\n", fs::PathToString(filepath));
if (cookie_out)
*cookie_out = cookie;
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
index 0d02ec5c47..9bcfba3507 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -239,7 +239,7 @@ static RPCHelpMan getrpcinfo()
UniValue result(UniValue::VOBJ);
result.pushKV("active_commands", active_commands);
- const std::string path = LogInstance().m_file_path.string();
+ const std::string path = LogInstance().m_file_path.u8string();
UniValue log_path(UniValue::VSTR, path);
result.pushKV("logpath", log_path);
diff --git a/src/support/allocators/secure.h b/src/support/allocators/secure.h
index 0e31ad3ce3..c4923dc56f 100644
--- a/src/support/allocators/secure.h
+++ b/src/support/allocators/secure.h
@@ -9,6 +9,7 @@
#include <support/lockedpool.h>
#include <support/cleanse.h>
+#include <memory>
#include <string>
//
@@ -17,15 +18,13 @@
//
template <typename T>
struct secure_allocator : public std::allocator<T> {
- // MSVC8 default copy constructor is broken
- typedef std::allocator<T> base;
- typedef typename base::size_type size_type;
- typedef typename base::difference_type difference_type;
- typedef typename base::pointer pointer;
- typedef typename base::const_pointer const_pointer;
- typedef typename base::reference reference;
- typedef typename base::const_reference const_reference;
- typedef typename base::value_type value_type;
+ using base = std::allocator<T>;
+ using traits = std::allocator_traits<base>;
+ using size_type = typename traits::size_type;
+ using difference_type = typename traits::difference_type;
+ using pointer = typename traits::pointer;
+ using const_pointer = typename traits::const_pointer;
+ using value_type = typename traits::value_type;
secure_allocator() noexcept {}
secure_allocator(const secure_allocator& a) noexcept : base(a) {}
template <typename U>
diff --git a/src/support/allocators/zeroafterfree.h b/src/support/allocators/zeroafterfree.h
index 418f0ee656..77de4b1e69 100644
--- a/src/support/allocators/zeroafterfree.h
+++ b/src/support/allocators/zeroafterfree.h
@@ -13,15 +13,13 @@
template <typename T>
struct zero_after_free_allocator : public std::allocator<T> {
- // MSVC8 default copy constructor is broken
- typedef std::allocator<T> base;
- typedef typename base::size_type size_type;
- typedef typename base::difference_type difference_type;
- typedef typename base::pointer pointer;
- typedef typename base::const_pointer const_pointer;
- typedef typename base::reference reference;
- typedef typename base::const_reference const_reference;
- typedef typename base::value_type value_type;
+ using base = std::allocator<T>;
+ using traits = std::allocator_traits<base>;
+ using size_type = typename traits::size_type;
+ using difference_type = typename traits::difference_type;
+ using pointer = typename traits::pointer;
+ using const_pointer = typename traits::const_pointer;
+ using value_type = typename traits::value_type;
zero_after_free_allocator() noexcept {}
zero_after_free_allocator(const zero_after_free_allocator& a) noexcept : base(a) {}
template <typename U>
diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp
index bd6f470219..eabc11c467 100644
--- a/src/test/addrman_tests.cpp
+++ b/src/test/addrman_tests.cpp
@@ -89,7 +89,7 @@ public:
deterministic = makeDeterministic;
}
- AddrInfo* Find(const CNetAddr& addr, int* pnId = nullptr)
+ AddrInfo* Find(const CService& addr, int* pnId = nullptr)
{
LOCK(m_impl->cs);
return m_impl->Find(addr, pnId);
@@ -222,15 +222,15 @@ BOOST_AUTO_TEST_CASE(addrman_ports)
BOOST_CHECK_EQUAL(addrman.size(), 1U);
CService addr1_port = ResolveService("250.1.1.1", 8334);
- BOOST_CHECK(!addrman.Add({CAddress(addr1_port, NODE_NONE)}, source));
- BOOST_CHECK_EQUAL(addrman.size(), 1U);
+ BOOST_CHECK(addrman.Add({CAddress(addr1_port, NODE_NONE)}, source));
+ BOOST_CHECK_EQUAL(addrman.size(), 2U);
auto addr_ret2 = addrman.Select().first;
- BOOST_CHECK_EQUAL(addr_ret2.ToString(), "250.1.1.1:8333");
+ BOOST_CHECK(addr_ret2.ToString() == "250.1.1.1:8333" || addr_ret2.ToString() == "250.1.1.1:8334");
- // Test: Add same IP but diff port to tried table, it doesn't get added.
- // Perhaps this is not ideal behavior but it is the current behavior.
+ // Test: Add same IP but diff port to tried table; this converts the entry with
+ // the specified port to tried, but not the other.
addrman.Good(CAddress(addr1_port, NODE_NONE));
- BOOST_CHECK_EQUAL(addrman.size(), 1U);
+ BOOST_CHECK_EQUAL(addrman.size(), 2U);
bool newOnly = true;
auto addr_ret3 = addrman.Select(newOnly).first;
BOOST_CHECK_EQUAL(addr_ret3.ToString(), "250.1.1.1:8333");
@@ -347,7 +347,7 @@ BOOST_AUTO_TEST_CASE(addrman_tried_collisions)
//Test: tried table collision!
CService addr1 = ResolveService("250.1.1." + ToString(++num_addrs));
uint32_t collisions{1};
- BOOST_CHECK(addrman.Add({CAddress(addr1, NODE_NONE)}, source));
+ BOOST_CHECK(!addrman.Add({CAddress(addr1, NODE_NONE)}, source));
BOOST_CHECK_EQUAL(addrman.size(), num_addrs - collisions);
CService addr2 = ResolveService("250.1.1." + ToString(++num_addrs));
@@ -369,18 +369,18 @@ BOOST_AUTO_TEST_CASE(addrman_find)
CNetAddr source2 = ResolveIP("250.1.2.2");
BOOST_CHECK(addrman.Add({addr1}, source1));
- BOOST_CHECK(!addrman.Add({addr2}, source2));
+ BOOST_CHECK(addrman.Add({addr2}, source2));
BOOST_CHECK(addrman.Add({addr3}, source1));
- // Test: ensure Find returns an IP matching what we searched on.
+ // Test: ensure Find returns an IP/port matching what we searched on.
AddrInfo* info1 = addrman.Find(addr1);
BOOST_REQUIRE(info1);
BOOST_CHECK_EQUAL(info1->ToString(), "250.1.2.1:8333");
- // Test 18; Find does not discriminate by port number.
+ // Test; Find discriminates by port number.
AddrInfo* info2 = addrman.Find(addr2);
BOOST_REQUIRE(info2);
- BOOST_CHECK_EQUAL(info2->ToString(), info1->ToString());
+ BOOST_CHECK_EQUAL(info2->ToString(), "250.1.2.1:9999");
// Test: Find returns another IP matching what we searched on.
AddrInfo* info3 = addrman.Find(addr3);
diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp
index 23ef2062ef..fe5ed0a3c8 100644
--- a/src/test/bloom_tests.cpp
+++ b/src/test/bloom_tests.cpp
@@ -2,7 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <bloom.h>
+#include <common/bloom.h>
#include <clientversion.h>
#include <key.h>
diff --git a/src/test/checkqueue_tests.cpp b/src/test/checkqueue_tests.cpp
index 64c6d7f634..4d34407ca8 100644
--- a/src/test/checkqueue_tests.cpp
+++ b/src/test/checkqueue_tests.cpp
@@ -18,7 +18,17 @@
#include <utility>
#include <vector>
-BOOST_FIXTURE_TEST_SUITE(checkqueue_tests, TestingSetup)
+/**
+ * Identical to TestingSetup but excludes lock contention logging, as some of
+ * these tests are designed to be heavily contested to trigger race conditions
+ * or other issues.
+ */
+struct NoLockLoggingTestingSetup : public TestingSetup {
+ NoLockLoggingTestingSetup()
+ : TestingSetup{CBaseChainParams::MAIN, /*extra_args=*/{"-debugexclude=lock"}} {}
+};
+
+BOOST_FIXTURE_TEST_SUITE(checkqueue_tests, NoLockLoggingTestingSetup)
static const unsigned int QUEUE_BATCH_SIZE = 128;
static const int SCRIPT_CHECK_THREADS = 3;
diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp
index 5b3b39fdb8..1483bd3cb3 100644
--- a/src/test/crypto_tests.cpp
+++ b/src/test/crypto_tests.cpp
@@ -694,8 +694,8 @@ BOOST_AUTO_TEST_CASE(chacha20_poly1305_aead_testvector)
TestChaCha20Poly1305AEAD(true, 0,
/* m */ "0000000000000000000000000000000000000000000000000000000000000000",
- /* k1 (payload) */ "0000000000000000000000000000000000000000000000000000000000000000",
- /* k2 (AAD) */ "0000000000000000000000000000000000000000000000000000000000000000",
+ /* k1 (AAD) */ "0000000000000000000000000000000000000000000000000000000000000000",
+ /* k2 (payload) */ "0000000000000000000000000000000000000000000000000000000000000000",
/* AAD keystream */ "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586",
/* encrypted message & MAC */ "76b8e09f07e7be5551387a98ba977c732d080dcb0f29a048e3656912c6533e32d2fc11829c1b6c1df1f551cd6131ff08",
/* encrypted message & MAC at sequence 999 */ "b0a03d5bd2855d60699e7d3a3133fa47be740fe4e4c1f967555e2d9271f31c3aaa7aa16ec62c5e24f040c08bb20c3598");
diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp
index 0bfe6eecd9..668ff150ee 100644
--- a/src/test/denialofservice_tests.cpp
+++ b/src/test/denialofservice_tests.cpp
@@ -52,6 +52,8 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
{
const CChainParams& chainparams = Params();
auto connman = std::make_unique<CConnman>(0x1337, 0x1337, *m_node.addrman);
+ // Disable inactivity checks for this test to avoid interference
+ static_cast<ConnmanTestMsg*>(connman.get())->SetPeerConnectTimeout(99999);
auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, nullptr,
*m_node.chainman, *m_node.mempool, false);
diff --git a/src/test/fs_tests.cpp b/src/test/fs_tests.cpp
index 526a3c27be..ecb838a7dd 100644
--- a/src/test/fs_tests.cpp
+++ b/src/test/fs_tests.cpp
@@ -11,6 +11,33 @@
BOOST_FIXTURE_TEST_SUITE(fs_tests, BasicTestingSetup)
+BOOST_AUTO_TEST_CASE(fsbridge_pathtostring)
+{
+ std::string u8_str = "fs_tests_₿_🏃";
+ BOOST_CHECK_EQUAL(fs::PathToString(fs::PathFromString(u8_str)), u8_str);
+ BOOST_CHECK_EQUAL(fs::u8path(u8_str).u8string(), u8_str);
+ BOOST_CHECK_EQUAL(fs::PathFromString(u8_str).u8string(), u8_str);
+ BOOST_CHECK_EQUAL(fs::PathToString(fs::u8path(u8_str)), u8_str);
+#ifndef WIN32
+ // On non-windows systems, verify that arbitrary byte strings containing
+ // invalid UTF-8 can be round tripped successfully with PathToString and
+ // PathFromString. On non-windows systems, paths are just byte strings so
+ // these functions do not do any encoding. On windows, paths are Unicode,
+ // and these functions do encoding and decoding, so the behavior of this
+ // test would be undefined.
+ std::string invalid_u8_str = "\xf0";
+ BOOST_CHECK_EQUAL(invalid_u8_str.size(), 1);
+ BOOST_CHECK_EQUAL(fs::PathToString(fs::PathFromString(invalid_u8_str)), invalid_u8_str);
+#endif
+}
+
+BOOST_AUTO_TEST_CASE(fsbridge_stem)
+{
+ std::string test_filename = "fs_tests_₿_🏃.dat";
+ std::string expected_stem = "fs_tests_₿_🏃";
+ BOOST_CHECK_EQUAL(fs::PathToString(fs::PathFromString(test_filename).stem()), expected_stem);
+}
+
BOOST_AUTO_TEST_CASE(fsbridge_fstream)
{
fs::path tmpfolder = m_args.GetDataDirBase();
diff --git a/src/test/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp
index 8df3707fc9..c6df6a0e61 100644
--- a/src/test/fuzz/addrman.cpp
+++ b/src/test/fuzz/addrman.cpp
@@ -137,24 +137,29 @@ public:
// Check that all values in `mapInfo` are equal to all values in `other.mapInfo`.
// Keys may be different.
- using AddrInfoHasher = std::function<size_t(const AddrInfo&)>;
- using AddrInfoEq = std::function<bool(const AddrInfo&, const AddrInfo&)>;
-
- CNetAddrHash netaddr_hasher;
-
- AddrInfoHasher addrinfo_hasher = [&netaddr_hasher](const AddrInfo& a) {
- return netaddr_hasher(static_cast<CNetAddr>(a)) ^ netaddr_hasher(a.source) ^
- a.nLastSuccess ^ a.nAttempts ^ a.nRefCount ^ a.fInTried;
+ auto addrinfo_hasher = [](const AddrInfo& a) {
+ CSipHasher hasher(0, 0);
+ auto addr_key = a.GetKey();
+ auto source_key = a.source.GetAddrBytes();
+ hasher.Write(a.nLastSuccess);
+ hasher.Write(a.nAttempts);
+ hasher.Write(a.nRefCount);
+ hasher.Write(a.fInTried);
+ hasher.Write(a.GetNetwork());
+ hasher.Write(a.source.GetNetwork());
+ hasher.Write(addr_key.size());
+ hasher.Write(source_key.size());
+ hasher.Write(addr_key.data(), addr_key.size());
+ hasher.Write(source_key.data(), source_key.size());
+ return (size_t)hasher.Finalize();
};
- AddrInfoEq addrinfo_eq = [](const AddrInfo& lhs, const AddrInfo& rhs) {
- return static_cast<CNetAddr>(lhs) == static_cast<CNetAddr>(rhs) &&
- lhs.source == rhs.source && lhs.nLastSuccess == rhs.nLastSuccess &&
- lhs.nAttempts == rhs.nAttempts && lhs.nRefCount == rhs.nRefCount &&
- lhs.fInTried == rhs.fInTried;
+ auto addrinfo_eq = [](const AddrInfo& lhs, const AddrInfo& rhs) {
+ return std::tie(static_cast<const CService&>(lhs), lhs.source, lhs.nLastSuccess, lhs.nAttempts, lhs.nRefCount, lhs.fInTried) ==
+ std::tie(static_cast<const CService&>(rhs), rhs.source, rhs.nLastSuccess, rhs.nAttempts, rhs.nRefCount, rhs.fInTried);
};
- using Addresses = std::unordered_set<AddrInfo, AddrInfoHasher, AddrInfoEq>;
+ using Addresses = std::unordered_set<AddrInfo, decltype(addrinfo_hasher), decltype(addrinfo_eq)>;
const size_t num_addresses{m_impl->mapInfo.size()};
diff --git a/src/test/fuzz/banman.cpp b/src/test/fuzz/banman.cpp
index 561cc83c72..fbba25c404 100644
--- a/src/test/fuzz/banman.cpp
+++ b/src/test/fuzz/banman.cpp
@@ -48,7 +48,7 @@ FUZZ_TARGET_INIT(banman, initialize_banman)
const bool start_with_corrupted_banlist{fuzzed_data_provider.ConsumeBool()};
bool force_read_and_write_to_err{false};
if (start_with_corrupted_banlist) {
- assert(WriteBinaryFile(banlist_file.string() + ".json",
+ assert(WriteBinaryFile(banlist_file + ".json",
fuzzed_data_provider.ConsumeRandomLengthString()));
} else {
force_read_and_write_to_err = fuzzed_data_provider.ConsumeBool();
@@ -111,5 +111,5 @@ FUZZ_TARGET_INIT(banman, initialize_banman)
assert(banmap == banmap_read);
}
}
- fs::remove(banlist_file.string() + ".json");
+ fs::remove(fs::PathToString(banlist_file + ".json"));
}
diff --git a/src/test/fuzz/bloom_filter.cpp b/src/test/fuzz/bloom_filter.cpp
index c5bb8744a4..746591a176 100644
--- a/src/test/fuzz/bloom_filter.cpp
+++ b/src/test/fuzz/bloom_filter.cpp
@@ -2,7 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <bloom.h>
+#include <common/bloom.h>
#include <primitives/transaction.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
diff --git a/src/test/fuzz/p2p_transport_serialization.cpp b/src/test/fuzz/p2p_transport_serialization.cpp
index edee5aeef7..29b7223c90 100644
--- a/src/test/fuzz/p2p_transport_serialization.cpp
+++ b/src/test/fuzz/p2p_transport_serialization.cpp
@@ -68,18 +68,16 @@ FUZZ_TARGET_INIT(p2p_transport_serialization, initialize_p2p_transport_serializa
}
if (deserializer.Complete()) {
const std::chrono::microseconds m_time{std::numeric_limits<int64_t>::max()};
- uint32_t out_err_raw_size{0};
- std::optional<CNetMessage> result{deserializer.GetMessage(m_time, out_err_raw_size)};
- if (result) {
- assert(result->m_command.size() <= CMessageHeader::COMMAND_SIZE);
- assert(result->m_raw_message_size <= mutable_msg_bytes.size());
- assert(result->m_raw_message_size == CMessageHeader::HEADER_SIZE + result->m_message_size);
- assert(result->m_time == m_time);
+ bool reject_message{false};
+ CNetMessage msg = deserializer.GetMessage(m_time, reject_message);
+ assert(msg.m_command.size() <= CMessageHeader::COMMAND_SIZE);
+ assert(msg.m_raw_message_size <= mutable_msg_bytes.size());
+ assert(msg.m_raw_message_size == CMessageHeader::HEADER_SIZE + msg.m_message_size);
+ assert(msg.m_time == m_time);
- std::vector<unsigned char> header;
- auto msg = CNetMsgMaker{result->m_recv.GetVersion()}.Make(result->m_command, MakeUCharSpan(result->m_recv));
- serializer.prepareForTransport(msg, header);
- }
+ std::vector<unsigned char> header;
+ auto msg2 = CNetMsgMaker{msg.m_recv.GetVersion()}.Make(msg.m_command, MakeUCharSpan(msg.m_recv));
+ serializer.prepareForTransport(msg2, header);
}
}
}
diff --git a/src/test/fuzz/rolling_bloom_filter.cpp b/src/test/fuzz/rolling_bloom_filter.cpp
index b9ed497e68..9c18ad49cb 100644
--- a/src/test/fuzz/rolling_bloom_filter.cpp
+++ b/src/test/fuzz/rolling_bloom_filter.cpp
@@ -2,7 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <bloom.h>
+#include <common/bloom.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
diff --git a/src/test/fuzz/tx_pool.cpp b/src/test/fuzz/tx_pool.cpp
index 6201cc813c..17b5ef88b9 100644
--- a/src/test/fuzz/tx_pool.cpp
+++ b/src/test/fuzz/tx_pool.cpp
@@ -81,7 +81,7 @@ void SetMempoolConstraints(ArgsManager& args, FuzzedDataProvider& fuzzed_data_pr
void Finish(FuzzedDataProvider& fuzzed_data_provider, MockedTxPool& tx_pool, CChainState& chainstate)
{
- WITH_LOCK(::cs_main, tx_pool.check(chainstate));
+ WITH_LOCK(::cs_main, tx_pool.check(chainstate.CoinsTip(), chainstate.m_chain.Height() + 1));
{
BlockAssembler::Options options;
options.nBlockMaxWeight = fuzzed_data_provider.ConsumeIntegralInRange(0U, MAX_BLOCK_WEIGHT);
@@ -97,7 +97,7 @@ void Finish(FuzzedDataProvider& fuzzed_data_provider, MockedTxPool& tx_pool, CCh
std::vector<uint256> all_txids;
tx_pool.queryHashes(all_txids);
assert(all_txids.size() < info_all.size());
- WITH_LOCK(::cs_main, tx_pool.check(chainstate));
+ WITH_LOCK(::cs_main, tx_pool.check(chainstate.CoinsTip(), chainstate.m_chain.Height() + 1));
}
SyncWithValidationInterfaceQueue();
}
diff --git a/src/test/fuzz/txrequest.cpp b/src/test/fuzz/txrequest.cpp
index 72438ff2d7..a73bbcfc25 100644
--- a/src/test/fuzz/txrequest.cpp
+++ b/src/test/fuzz/txrequest.cpp
@@ -204,7 +204,7 @@ public:
}
// Call TxRequestTracker's implementation.
- m_tracker.ReceivedInv(peer, GenTxid{is_wtxid, TXHASHES[txhash]}, preferred, reqtime);
+ m_tracker.ReceivedInv(peer, is_wtxid ? GenTxid::Wtxid(TXHASHES[txhash]) : GenTxid::Txid(TXHASHES[txhash]), preferred, reqtime);
}
void RequestedTx(int peer, int txhash, std::chrono::microseconds exptime)
@@ -252,7 +252,7 @@ public:
for (int peer2 = 0; peer2 < MAX_PEERS; ++peer2) {
Announcement& ann2 = m_announcements[txhash][peer2];
if (ann2.m_state == State::REQUESTED && ann2.m_time <= m_now) {
- expected_expired.emplace_back(peer2, GenTxid{ann2.m_is_wtxid, TXHASHES[txhash]});
+ expected_expired.emplace_back(peer2, ann2.m_is_wtxid ? GenTxid::Wtxid(TXHASHES[txhash]) : GenTxid::Txid(TXHASHES[txhash]));
ann2.m_state = State::COMPLETED;
break;
}
diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp
index 17e904fcff..b0c8068ab9 100644
--- a/src/test/getarg_tests.cpp
+++ b/src/test/getarg_tests.cpp
@@ -194,8 +194,8 @@ BOOST_AUTO_TEST_CASE(boolargno)
BOOST_AUTO_TEST_CASE(logargs)
{
- const auto okaylog_bool = std::make_pair("-okaylog-bool", ArgsManager::ALLOW_BOOL);
- const auto okaylog_negbool = std::make_pair("-okaylog-negbool", ArgsManager::ALLOW_BOOL);
+ const auto okaylog_bool = std::make_pair("-okaylog-bool", ArgsManager::ALLOW_ANY);
+ const auto okaylog_negbool = std::make_pair("-okaylog-negbool", ArgsManager::ALLOW_ANY);
const auto okaylog = std::make_pair("-okaylog", ArgsManager::ALLOW_ANY);
const auto dontlog = std::make_pair("-dontlog", ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE);
SetupArgs({okaylog_bool, okaylog_negbool, okaylog, dontlog});
diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp
index bf36f8a6c9..b3497b8ef8 100644
--- a/src/test/mempool_tests.cpp
+++ b/src/test/mempool_tests.cpp
@@ -444,12 +444,12 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
pool.addUnchecked(entry.Fee(5000LL).FromTx(tx2));
pool.TrimToSize(pool.DynamicMemoryUsage()); // should do nothing
- BOOST_CHECK(pool.exists(tx1.GetHash()));
- BOOST_CHECK(pool.exists(tx2.GetHash()));
+ BOOST_CHECK(pool.exists(GenTxid::Txid(tx1.GetHash())));
+ BOOST_CHECK(pool.exists(GenTxid::Txid(tx2.GetHash())));
pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4); // should remove the lower-feerate transaction
- BOOST_CHECK(pool.exists(tx1.GetHash()));
- BOOST_CHECK(!pool.exists(tx2.GetHash()));
+ BOOST_CHECK(pool.exists(GenTxid::Txid(tx1.GetHash())));
+ BOOST_CHECK(!pool.exists(GenTxid::Txid(tx2.GetHash())));
pool.addUnchecked(entry.FromTx(tx2));
CMutableTransaction tx3 = CMutableTransaction();
@@ -462,14 +462,14 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
pool.addUnchecked(entry.Fee(20000LL).FromTx(tx3));
pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4); // tx3 should pay for tx2 (CPFP)
- BOOST_CHECK(!pool.exists(tx1.GetHash()));
- BOOST_CHECK(pool.exists(tx2.GetHash()));
- BOOST_CHECK(pool.exists(tx3.GetHash()));
+ BOOST_CHECK(!pool.exists(GenTxid::Txid(tx1.GetHash())));
+ BOOST_CHECK(pool.exists(GenTxid::Txid(tx2.GetHash())));
+ BOOST_CHECK(pool.exists(GenTxid::Txid(tx3.GetHash())));
pool.TrimToSize(GetVirtualTransactionSize(CTransaction(tx1))); // mempool is limited to tx1's size in memory usage, so nothing fits
- BOOST_CHECK(!pool.exists(tx1.GetHash()));
- BOOST_CHECK(!pool.exists(tx2.GetHash()));
- BOOST_CHECK(!pool.exists(tx3.GetHash()));
+ BOOST_CHECK(!pool.exists(GenTxid::Txid(tx1.GetHash())));
+ BOOST_CHECK(!pool.exists(GenTxid::Txid(tx2.GetHash())));
+ BOOST_CHECK(!pool.exists(GenTxid::Txid(tx3.GetHash())));
CFeeRate maxFeeRateRemoved(25000, GetVirtualTransactionSize(CTransaction(tx3)) + GetVirtualTransactionSize(CTransaction(tx2)));
BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + 1000);
@@ -529,19 +529,19 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
// we only require this to remove, at max, 2 txn, because it's not clear what we're really optimizing for aside from that
pool.TrimToSize(pool.DynamicMemoryUsage() - 1);
- BOOST_CHECK(pool.exists(tx4.GetHash()));
- BOOST_CHECK(pool.exists(tx6.GetHash()));
- BOOST_CHECK(!pool.exists(tx7.GetHash()));
+ BOOST_CHECK(pool.exists(GenTxid::Txid(tx4.GetHash())));
+ BOOST_CHECK(pool.exists(GenTxid::Txid(tx6.GetHash())));
+ BOOST_CHECK(!pool.exists(GenTxid::Txid(tx7.GetHash())));
- if (!pool.exists(tx5.GetHash()))
+ if (!pool.exists(GenTxid::Txid(tx5.GetHash())))
pool.addUnchecked(entry.Fee(1000LL).FromTx(tx5));
pool.addUnchecked(entry.Fee(9000LL).FromTx(tx7));
pool.TrimToSize(pool.DynamicMemoryUsage() / 2); // should maximize mempool size by only removing 5/7
- BOOST_CHECK(pool.exists(tx4.GetHash()));
- BOOST_CHECK(!pool.exists(tx5.GetHash()));
- BOOST_CHECK(pool.exists(tx6.GetHash()));
- BOOST_CHECK(!pool.exists(tx7.GetHash()));
+ BOOST_CHECK(pool.exists(GenTxid::Txid(tx4.GetHash())));
+ BOOST_CHECK(!pool.exists(GenTxid::Txid(tx5.GetHash())));
+ BOOST_CHECK(pool.exists(GenTxid::Txid(tx6.GetHash())));
+ BOOST_CHECK(!pool.exists(GenTxid::Txid(tx7.GetHash())));
pool.addUnchecked(entry.Fee(1000LL).FromTx(tx5));
pool.addUnchecked(entry.Fee(9000LL).FromTx(tx7));
diff --git a/src/test/script_parse_tests.cpp b/src/test/script_parse_tests.cpp
index 5b8b6a725f..004c1a9a84 100644
--- a/src/test/script_parse_tests.cpp
+++ b/src/test/script_parse_tests.cpp
@@ -38,7 +38,6 @@ BOOST_AUTO_TEST_CASE(parse_script)
{"'17'", "023137"},
{"ELSE", "67"},
{"NOP10", "b9"},
- {"11111111111111111111", "00"},
};
std::string all_in;
std::string all_out;
@@ -49,6 +48,7 @@ BOOST_AUTO_TEST_CASE(parse_script)
}
BOOST_CHECK_EQUAL(HexStr(ParseScript(all_in)), all_out);
+ BOOST_CHECK_EXCEPTION(ParseScript("11111111111111111111"), std::runtime_error, HasReason("script parse error: decimal numeric value only allowed in the range -0xFFFFFFFF...0xFFFFFFFF"));
BOOST_CHECK_EXCEPTION(ParseScript("11111111111"), std::runtime_error, HasReason("script parse error: decimal numeric value only allowed in the range -0xFFFFFFFF...0xFFFFFFFF"));
BOOST_CHECK_EXCEPTION(ParseScript("OP_CHECKSIGADD"), std::runtime_error, HasReason("script parse error: unknown opcode"));
}
diff --git a/src/test/settings_tests.cpp b/src/test/settings_tests.cpp
index 340ce33d91..15cba9e3e5 100644
--- a/src/test/settings_tests.cpp
+++ b/src/test/settings_tests.cpp
@@ -80,19 +80,19 @@ BOOST_AUTO_TEST_CASE(ReadWrite)
"dupe": "dupe"
})");
BOOST_CHECK(!util::ReadSettings(path, values, errors));
- std::vector<std::string> dup_keys = {strprintf("Found duplicate key dupe in settings file %s", path.string())};
+ std::vector<std::string> dup_keys = {strprintf("Found duplicate key dupe in settings file %s", fs::PathToString(path))};
BOOST_CHECK_EQUAL_COLLECTIONS(errors.begin(), errors.end(), dup_keys.begin(), dup_keys.end());
// Check non-kv json files not allowed
WriteText(path, R"("non-kv")");
BOOST_CHECK(!util::ReadSettings(path, values, errors));
- std::vector<std::string> non_kv = {strprintf("Found non-object value \"non-kv\" in settings file %s", path.string())};
+ std::vector<std::string> non_kv = {strprintf("Found non-object value \"non-kv\" in settings file %s", fs::PathToString(path))};
BOOST_CHECK_EQUAL_COLLECTIONS(errors.begin(), errors.end(), non_kv.begin(), non_kv.end());
// Check invalid json not allowed
WriteText(path, R"(invalid json)");
BOOST_CHECK(!util::ReadSettings(path, values, errors));
- std::vector<std::string> fail_parse = {strprintf("Unable to parse settings file %s", path.string())};
+ std::vector<std::string> fail_parse = {strprintf("Unable to parse settings file %s", fs::PathToString(path))};
BOOST_CHECK_EQUAL_COLLECTIONS(errors.begin(), errors.end(), fail_parse.begin(), fail_parse.end());
}
diff --git a/src/test/txrequest_tests.cpp b/src/test/txrequest_tests.cpp
index 1d137b03b1..99d41882c9 100644
--- a/src/test/txrequest_tests.cpp
+++ b/src/test/txrequest_tests.cpp
@@ -221,7 +221,7 @@ public:
/** Generate a random GenTxid; the txhash follows NewTxHash; the is_wtxid flag is random. */
GenTxid NewGTxid(const std::vector<std::vector<NodeId>>& orders = {})
{
- return {InsecureRandBool(), NewTxHash(orders)};
+ return InsecureRandBool() ? GenTxid::Wtxid(NewTxHash(orders)) : GenTxid::Txid(NewTxHash(orders));
}
/** Generate a new random NodeId to use as peer. The same NodeId is never returned twice
@@ -494,8 +494,8 @@ void BuildWtxidTest(Scenario& scenario, int config)
auto peerT = scenario.NewPeer();
auto peerW = scenario.NewPeer();
auto txhash = scenario.NewTxHash();
- GenTxid txid{false, txhash};
- GenTxid wtxid{true, txhash};
+ auto txid{GenTxid::Txid(txhash)};
+ auto wtxid{GenTxid::Wtxid(txhash)};
auto reqtimeT = InsecureRandBool() ? MIN_TIME : scenario.Now() + RandomTime8s();
auto reqtimeW = InsecureRandBool() ? MIN_TIME : scenario.Now() + RandomTime8s();
diff --git a/src/test/util/chainstate.h b/src/test/util/chainstate.h
index 81ea4c38f5..e95573022c 100644
--- a/src/test/util/chainstate.h
+++ b/src/test/util/chainstate.h
@@ -36,7 +36,7 @@ CreateAndActivateUTXOSnapshot(NodeContext& node, const fs::path root, F malleati
UniValue result = CreateUTXOSnapshot(node, node.chainman->ActiveChainstate(), auto_outfile);
BOOST_TEST_MESSAGE(
- "Wrote UTXO snapshot to " << snapshot_path.make_preferred().string() << ": " << result.write());
+ "Wrote UTXO snapshot to " << fs::PathToString(snapshot_path.make_preferred()) << ": " << result.write());
// Read the written snapshot in and then activate it.
//
diff --git a/src/test/util/net.h b/src/test/util/net.h
index 939ec322ed..d89fc34b75 100644
--- a/src/test/util/net.h
+++ b/src/test/util/net.h
@@ -17,6 +17,12 @@
struct ConnmanTestMsg : public CConnman {
using CConnman::CConnman;
+
+ void SetPeerConnectTimeout(int64_t timeout)
+ {
+ m_peer_connect_timeout = timeout;
+ }
+
void AddTestNode(CNode& node)
{
LOCK(cs_vNodes);
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index ebefa9974e..a3c7564d76 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -91,8 +91,8 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName, const std::ve
extra_args);
util::ThreadRename("test");
fs::create_directories(m_path_root);
- m_args.ForceSetArg("-datadir", m_path_root.string());
- gArgs.ForceSetArg("-datadir", m_path_root.string());
+ m_args.ForceSetArg("-datadir", fs::PathToString(m_path_root));
+ gArgs.ForceSetArg("-datadir", fs::PathToString(m_path_root));
gArgs.ClearPathCache();
{
SetupServerArgs(*m_node.args);
diff --git a/src/test/util/wallet.cpp b/src/test/util/wallet.cpp
index 061659818f..76c1bf93a5 100644
--- a/src/test/util/wallet.cpp
+++ b/src/test/util/wallet.cpp
@@ -25,16 +25,4 @@ std::string getnewaddress(CWallet& w)
return EncodeDestination(dest);
}
-void importaddress(CWallet& wallet, const std::string& address)
-{
- auto spk_man = wallet.GetLegacyScriptPubKeyMan();
- LOCK2(wallet.cs_wallet, spk_man->cs_KeyStore);
- const auto dest = DecodeDestination(address);
- assert(IsValidDestination(dest));
- const auto script = GetScriptForDestination(dest);
- wallet.MarkDirty();
- assert(!spk_man->HaveWatchOnly(script));
- if (!spk_man->AddWatchOnly(script, 0 /* nCreateTime */)) assert(false);
- wallet.SetAddressBook(dest, /* label */ "", "receive");
-}
#endif // ENABLE_WALLET
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index b5088d3c33..b1300d06ba 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -51,23 +51,23 @@ BOOST_AUTO_TEST_CASE(util_datadir)
{
// Use local args variable instead of m_args to avoid making assumptions about test setup
ArgsManager args;
- args.ForceSetArg("-datadir", m_path_root.string());
+ args.ForceSetArg("-datadir", fs::PathToString(m_path_root));
const fs::path dd_norm = args.GetDataDirBase();
- args.ForceSetArg("-datadir", dd_norm.string() + "/");
+ args.ForceSetArg("-datadir", fs::PathToString(dd_norm) + "/");
args.ClearPathCache();
BOOST_CHECK_EQUAL(dd_norm, args.GetDataDirBase());
- args.ForceSetArg("-datadir", dd_norm.string() + "/.");
+ args.ForceSetArg("-datadir", fs::PathToString(dd_norm) + "/.");
args.ClearPathCache();
BOOST_CHECK_EQUAL(dd_norm, args.GetDataDirBase());
- args.ForceSetArg("-datadir", dd_norm.string() + "/./");
+ args.ForceSetArg("-datadir", fs::PathToString(dd_norm) + "/./");
args.ClearPathCache();
BOOST_CHECK_EQUAL(dd_norm, args.GetDataDirBase());
- args.ForceSetArg("-datadir", dd_norm.string() + "/.//");
+ args.ForceSetArg("-datadir", fs::PathToString(dd_norm) + "/.//");
args.ClearPathCache();
BOOST_CHECK_EQUAL(dd_norm, args.GetDataDirBase());
}
@@ -1181,13 +1181,13 @@ BOOST_AUTO_TEST_CASE(util_ReadWriteSettings)
{
// Test writing setting.
TestArgsManager args1;
- args1.ForceSetArg("-datadir", m_path_root.string());
+ args1.ForceSetArg("-datadir", fs::PathToString(m_path_root));
args1.LockSettings([&](util::Settings& settings) { settings.rw_settings["name"] = "value"; });
args1.WriteSettingsFile();
// Test reading setting.
TestArgsManager args2;
- args2.ForceSetArg("-datadir", m_path_root.string());
+ args2.ForceSetArg("-datadir", fs::PathToString(m_path_root));
args2.ReadSettingsFile();
args2.LockSettings([&](util::Settings& settings) { BOOST_CHECK_EQUAL(settings.rw_settings["name"].get_str(), "value"); });
diff --git a/src/test/validation_chainstate_tests.cpp b/src/test/validation_chainstate_tests.cpp
index 726c9ebbb8..9bb08f774f 100644
--- a/src/test/validation_chainstate_tests.cpp
+++ b/src/test/validation_chainstate_tests.cpp
@@ -107,20 +107,21 @@ BOOST_FIXTURE_TEST_CASE(chainstate_update_tip, TestChain100Setup)
curr_tip = ::g_best_block;
- CChainState* background_cs;
-
BOOST_CHECK_EQUAL(chainman.GetAll().size(), 2);
- for (CChainState* cs : chainman.GetAll()) {
- if (cs != &chainman.ActiveChainstate()) {
- background_cs = cs;
+
+ CChainState& background_cs{*[&] {
+ for (CChainState* cs : chainman.GetAll()) {
+ if (cs != &chainman.ActiveChainstate()) {
+ return cs;
+ }
}
- }
- BOOST_CHECK(background_cs);
+ assert(false);
+ }()};
// Create a block to append to the validation chain.
std::vector<CMutableTransaction> noTxns;
CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
- CBlock validation_block = this->CreateBlock(noTxns, scriptPubKey, *background_cs);
+ CBlock validation_block = this->CreateBlock(noTxns, scriptPubKey, background_cs);
auto pblock = std::make_shared<const CBlock>(validation_block);
BlockValidationState state;
CBlockIndex* pindex = nullptr;
@@ -133,15 +134,15 @@ BOOST_FIXTURE_TEST_CASE(chainstate_update_tip, TestChain100Setup)
LOCK(::cs_main);
bool checked = CheckBlock(*pblock, state, chainparams.GetConsensus());
BOOST_CHECK(checked);
- bool accepted = background_cs->AcceptBlock(
+ bool accepted = background_cs.AcceptBlock(
pblock, state, &pindex, true, nullptr, &newblock);
BOOST_CHECK(accepted);
}
// UpdateTip is called here
- bool block_added = background_cs->ActivateBestChain(state, pblock);
+ bool block_added = background_cs.ActivateBestChain(state, pblock);
// Ensure tip is as expected
- BOOST_CHECK_EQUAL(background_cs->m_chain.Tip()->GetBlockHash(), validation_block.GetHash());
+ BOOST_CHECK_EQUAL(background_cs.m_chain.Tip()->GetBlockHash(), validation_block.GetHash());
// g_best_block should be unchanged after adding a block to the background
// validation chain.
diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp
index f9caf48df8..55618a5c57 100644
--- a/src/torcontrol.cpp
+++ b/src/torcontrol.cpp
@@ -318,7 +318,7 @@ TorController::TorController(struct event_base* _base, const std::string& tor_co
// Read service private key if cached
std::pair<bool,std::string> pkf = ReadBinaryFile(GetPrivateKeyFile());
if (pkf.first) {
- LogPrint(BCLog::TOR, "tor: Reading cached private key from %s\n", GetPrivateKeyFile().string());
+ LogPrint(BCLog::TOR, "tor: Reading cached private key from %s\n", fs::PathToString(GetPrivateKeyFile()));
private_key = pkf.second;
}
}
@@ -356,9 +356,9 @@ void TorController::add_onion_cb(TorControlConnection& _conn, const TorControlRe
service = LookupNumeric(std::string(service_id+".onion"), Params().GetDefaultPort());
LogPrintf("tor: Got service ID %s, advertising service %s\n", service_id, service.ToString());
if (WriteBinaryFile(GetPrivateKeyFile(), private_key)) {
- LogPrint(BCLog::TOR, "tor: Cached service private key to %s\n", GetPrivateKeyFile().string());
+ LogPrint(BCLog::TOR, "tor: Cached service private key to %s\n", fs::PathToString(GetPrivateKeyFile()));
} else {
- LogPrintf("tor: Error writing service private key to %s\n", GetPrivateKeyFile().string());
+ LogPrintf("tor: Error writing service private key to %s\n", fs::PathToString(GetPrivateKeyFile()));
}
AddLocal(service, LOCAL_MANUAL);
// ... onion requested - keep connection open
@@ -508,7 +508,7 @@ void TorController::protocolinfo_cb(TorControlConnection& _conn, const TorContro
} else if (methods.count("SAFECOOKIE")) {
// Cookie: hexdump -e '32/1 "%02x""\n"' ~/.tor/control_auth_cookie
LogPrint(BCLog::TOR, "tor: Using SAFECOOKIE authentication, reading cookie authentication from %s\n", cookiefile);
- std::pair<bool,std::string> status_cookie = ReadBinaryFile(cookiefile, TOR_COOKIE_SIZE);
+ std::pair<bool,std::string> status_cookie = ReadBinaryFile(fs::PathFromString(cookiefile), TOR_COOKIE_SIZE);
if (status_cookie.first && status_cookie.second.size() == TOR_COOKIE_SIZE) {
// _conn.Command("AUTHENTICATE " + HexStr(status_cookie.second), std::bind(&TorController::auth_cb, this, std::placeholders::_1, std::placeholders::_2));
cookie = std::vector<uint8_t>(status_cookie.second.begin(), status_cookie.second.end());
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 5a93f30c8a..502a27dc6b 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -5,6 +5,7 @@
#include <txmempool.h>
+#include <coins.h>
#include <consensus/consensus.h>
#include <consensus/tx_verify.h>
#include <consensus/validation.h>
@@ -21,6 +22,58 @@
#include <cmath>
#include <optional>
+// Helpers for modifying CTxMemPool::mapTx, which is a boost multi_index.
+struct update_descendant_state
+{
+ update_descendant_state(int64_t _modifySize, CAmount _modifyFee, int64_t _modifyCount) :
+ modifySize(_modifySize), modifyFee(_modifyFee), modifyCount(_modifyCount)
+ {}
+
+ void operator() (CTxMemPoolEntry &e)
+ { e.UpdateDescendantState(modifySize, modifyFee, modifyCount); }
+
+ private:
+ int64_t modifySize;
+ CAmount modifyFee;
+ int64_t modifyCount;
+};
+
+struct update_ancestor_state
+{
+ update_ancestor_state(int64_t _modifySize, CAmount _modifyFee, int64_t _modifyCount, int64_t _modifySigOpsCost) :
+ modifySize(_modifySize), modifyFee(_modifyFee), modifyCount(_modifyCount), modifySigOpsCost(_modifySigOpsCost)
+ {}
+
+ void operator() (CTxMemPoolEntry &e)
+ { e.UpdateAncestorState(modifySize, modifyFee, modifyCount, modifySigOpsCost); }
+
+ private:
+ int64_t modifySize;
+ CAmount modifyFee;
+ int64_t modifyCount;
+ int64_t modifySigOpsCost;
+};
+
+struct update_fee_delta
+{
+ explicit update_fee_delta(int64_t _feeDelta) : feeDelta(_feeDelta) { }
+
+ void operator() (CTxMemPoolEntry &e) { e.UpdateFeeDelta(feeDelta); }
+
+private:
+ int64_t feeDelta;
+};
+
+struct update_lock_points
+{
+ explicit update_lock_points(const LockPoints& _lp) : lp(_lp) { }
+
+ void operator() (CTxMemPoolEntry &e) { e.UpdateLockPoints(lp); }
+
+private:
+ const LockPoints& lp;
+};
+
CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef& tx, CAmount fee,
int64_t time, unsigned int entry_height,
bool spends_coinbase, int64_t sigops_cost, LockPoints lp)
@@ -276,7 +329,7 @@ bool CTxMemPool::CalculateMemPoolAncestors(const CTxMemPoolEntry &entry,
void CTxMemPool::UpdateAncestorsOf(bool add, txiter it, setEntries &setAncestors)
{
- CTxMemPoolEntry::Parents parents = it->GetMemPoolParents();
+ const CTxMemPoolEntry::Parents& parents = it->GetMemPoolParentsConst();
// add or remove this tx as a child of each parent
for (const CTxMemPoolEntry& parent : parents) {
UpdateChild(mapTx.iterator_to(parent), it, add);
@@ -671,16 +724,7 @@ void CTxMemPool::clear()
_clear();
}
-static void CheckInputsAndUpdateCoins(const CTransaction& tx, CCoinsViewCache& mempoolDuplicate, const int64_t spendheight)
-{
- TxValidationState dummy_state; // Not used. CheckTxInputs() should always pass
- CAmount txfee = 0;
- bool fCheckResult = tx.IsCoinBase() || Consensus::CheckTxInputs(tx, dummy_state, mempoolDuplicate, spendheight, txfee);
- assert(fCheckResult);
- UpdateCoins(tx, mempoolDuplicate, std::numeric_limits<int>::max());
-}
-
-void CTxMemPool::check(CChainState& active_chainstate) const
+void CTxMemPool::check(const CCoinsViewCache& active_coins_tip, int64_t spendheight) const
{
if (m_check_ratio == 0) return;
@@ -693,20 +737,16 @@ void CTxMemPool::check(CChainState& active_chainstate) const
uint64_t checkTotal = 0;
CAmount check_total_fee{0};
uint64_t innerUsage = 0;
+ uint64_t prev_ancestor_count{0};
- CCoinsViewCache& active_coins_tip = active_chainstate.CoinsTip();
CCoinsViewCache mempoolDuplicate(const_cast<CCoinsViewCache*>(&active_coins_tip));
- const int64_t spendheight = active_chainstate.m_chain.Height() + 1;
- std::list<const CTxMemPoolEntry*> waitingOnDependants;
- for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
- unsigned int i = 0;
+ for (const auto& it : GetSortedDepthAndScore()) {
checkTotal += it->GetTxSize();
check_total_fee += it->GetFee();
innerUsage += it->DynamicMemoryUsage();
const CTransaction& tx = it->GetTx();
innerUsage += memusage::DynamicUsage(it->GetMemPoolParentsConst()) + memusage::DynamicUsage(it->GetMemPoolChildrenConst());
- bool fDependsWait = false;
CTxMemPoolEntry::Parents setParentCheck;
for (const CTxIn &txin : tx.vin) {
// Check that every mempool transaction's inputs refer to available coins, or other mempool tx's.
@@ -714,17 +754,17 @@ void CTxMemPool::check(CChainState& active_chainstate) const
if (it2 != mapTx.end()) {
const CTransaction& tx2 = it2->GetTx();
assert(tx2.vout.size() > txin.prevout.n && !tx2.vout[txin.prevout.n].IsNull());
- fDependsWait = true;
setParentCheck.insert(*it2);
- } else {
- assert(active_coins_tip.HaveCoin(txin.prevout));
}
+ // We are iterating through the mempool entries sorted in order by ancestor count.
+ // All parents must have been checked before their children and their coins added to
+ // the mempoolDuplicate coins cache.
+ assert(mempoolDuplicate.HaveCoin(txin.prevout));
// Check whether its inputs are marked in mapNextTx.
auto it3 = mapNextTx.find(txin.prevout);
assert(it3 != mapNextTx.end());
assert(it3->first == &txin.prevout);
assert(it3->second == &tx);
- i++;
}
auto comp = [](const CTxMemPoolEntry& a, const CTxMemPoolEntry& b) -> bool {
return a.GetTx().GetHash() == b.GetTx().GetHash();
@@ -751,6 +791,9 @@ void CTxMemPool::check(CChainState& active_chainstate) const
assert(it->GetSizeWithAncestors() == nSizeCheck);
assert(it->GetSigOpCostWithAncestors() == nSigOpCheck);
assert(it->GetModFeesWithAncestors() == nFeesCheck);
+ // Sanity check: we are walking in ascending ancestor count order.
+ assert(prev_ancestor_count <= it->GetCountWithAncestors());
+ prev_ancestor_count = it->GetCountWithAncestors();
// Check children against mapNextTx
CTxMemPoolEntry::Children setChildrenCheck;
@@ -769,24 +812,12 @@ void CTxMemPool::check(CChainState& active_chainstate) const
// just a sanity check, not definitive that this calc is correct...
assert(it->GetSizeWithDescendants() >= child_sizes + it->GetTxSize());
- if (fDependsWait)
- waitingOnDependants.push_back(&(*it));
- else {
- CheckInputsAndUpdateCoins(tx, mempoolDuplicate, spendheight);
- }
- }
- unsigned int stepsSinceLastRemove = 0;
- while (!waitingOnDependants.empty()) {
- const CTxMemPoolEntry* entry = waitingOnDependants.front();
- waitingOnDependants.pop_front();
- if (!mempoolDuplicate.HaveInputs(entry->GetTx())) {
- waitingOnDependants.push_back(entry);
- stepsSinceLastRemove++;
- assert(stepsSinceLastRemove < waitingOnDependants.size());
- } else {
- CheckInputsAndUpdateCoins(entry->GetTx(), mempoolDuplicate, spendheight);
- stepsSinceLastRemove = 0;
- }
+ TxValidationState dummy_state; // Not used. CheckTxInputs() should always pass
+ CAmount txfee = 0;
+ assert(!tx.IsCoinBase());
+ assert(Consensus::CheckTxInputs(tx, dummy_state, mempoolDuplicate, spendheight, txfee));
+ for (const auto& input: tx.vin) mempoolDuplicate.SpendCoin(input.prevout);
+ AddCoins(mempoolDuplicate, tx, std::numeric_limits<int>::max());
}
for (auto it = mapNextTx.cbegin(); it != mapNextTx.cend(); it++) {
uint256 hash = it->second->GetHash();
@@ -895,8 +926,6 @@ TxMempoolInfo CTxMemPool::info(const GenTxid& gtxid) const
return GetInfo(i);
}
-TxMempoolInfo CTxMemPool::info(const uint256& txid) const { return info(GenTxid{false, txid}); }
-
void CTxMemPool::PrioritiseTransaction(const uint256& hash, const CAmount& nFeeDelta)
{
{
@@ -969,7 +998,7 @@ CTxMemPool::setEntries CTxMemPool::GetIterSet(const std::set<uint256>& hashes) c
bool CTxMemPool::HasNoInputsOf(const CTransaction &tx) const
{
for (unsigned int i = 0; i < tx.vin.size(); i++)
- if (exists(tx.vin[i].prevout.hash))
+ if (exists(GenTxid::Txid(tx.vin[i].prevout.hash)))
return false;
return true;
}
@@ -1140,7 +1169,7 @@ void CTxMemPool::TrimToSize(size_t sizelimit, std::vector<COutPoint>* pvNoSpends
if (pvNoSpendsRemaining) {
for (const CTransaction& tx : txn) {
for (const CTxIn& txin : tx.vin) {
- if (exists(txin.prevout.hash)) continue;
+ if (exists(GenTxid::Txid(txin.prevout.hash))) continue;
pvNoSpendsRemaining->push_back(txin.prevout);
}
}
diff --git a/src/txmempool.h b/src/txmempool.h
index 0be51db181..85417ac3fc 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -159,58 +159,6 @@ public:
mutable Epoch::Marker m_epoch_marker; //!< epoch when last touched, useful for graph algorithms
};
-// Helpers for modifying CTxMemPool::mapTx, which is a boost multi_index.
-struct update_descendant_state
-{
- update_descendant_state(int64_t _modifySize, CAmount _modifyFee, int64_t _modifyCount) :
- modifySize(_modifySize), modifyFee(_modifyFee), modifyCount(_modifyCount)
- {}
-
- void operator() (CTxMemPoolEntry &e)
- { e.UpdateDescendantState(modifySize, modifyFee, modifyCount); }
-
- private:
- int64_t modifySize;
- CAmount modifyFee;
- int64_t modifyCount;
-};
-
-struct update_ancestor_state
-{
- update_ancestor_state(int64_t _modifySize, CAmount _modifyFee, int64_t _modifyCount, int64_t _modifySigOpsCost) :
- modifySize(_modifySize), modifyFee(_modifyFee), modifyCount(_modifyCount), modifySigOpsCost(_modifySigOpsCost)
- {}
-
- void operator() (CTxMemPoolEntry &e)
- { e.UpdateAncestorState(modifySize, modifyFee, modifyCount, modifySigOpsCost); }
-
- private:
- int64_t modifySize;
- CAmount modifyFee;
- int64_t modifyCount;
- int64_t modifySigOpsCost;
-};
-
-struct update_fee_delta
-{
- explicit update_fee_delta(int64_t _feeDelta) : feeDelta(_feeDelta) { }
-
- void operator() (CTxMemPoolEntry &e) { e.UpdateFeeDelta(feeDelta); }
-
-private:
- int64_t feeDelta;
-};
-
-struct update_lock_points
-{
- explicit update_lock_points(const LockPoints& _lp) : lp(_lp) { }
-
- void operator() (CTxMemPoolEntry &e) { e.UpdateLockPoints(lp); }
-
-private:
- const LockPoints& lp;
-};
-
// extracts a transaction hash from CTxMemPoolEntry or CTransactionRef
struct mempoolentry_txid
{
@@ -542,7 +490,7 @@ public:
* By design, it is guaranteed that:
*
* 1. Locking both `cs_main` and `mempool.cs` will give a view of mempool
- * that is consistent with current chain tip (`::ChainActive()` and
+ * that is consistent with current chain tip (`ActiveChain()` and
* `CoinsTip()`) and is fully populated. Fully populated means that if the
* current active chain is missing transactions that were present in a
* previously active chain, all the missing transactions will have been
@@ -622,7 +570,7 @@ public:
* all inputs are in the mapNextTx array). If sanity-checking is turned off,
* check does nothing.
*/
- void check(CChainState& active_chainstate) const EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
+ void check(const CCoinsViewCache& active_coins_tip, int64_t spendheight) const EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
// addUnchecked must updated state for all ancestors of a given transaction,
// to track size/count of descendant transactions. First version of
@@ -782,7 +730,6 @@ public:
}
return (mapTx.count(gtxid.GetHash()) != 0);
}
- bool exists(const uint256& txid) const { return exists(GenTxid{false, txid}); }
CTransactionRef get(const uint256& hash) const;
txiter get_iter_from_wtxid(const uint256& wtxid) const EXCLUSIVE_LOCKS_REQUIRED(cs)
@@ -790,7 +737,6 @@ public:
AssertLockHeld(cs);
return mapTx.project<0>(mapTx.get<index_by_wtxid>().find(wtxid));
}
- TxMempoolInfo info(const uint256& hash) const;
TxMempoolInfo info(const GenTxid& gtxid) const;
std::vector<TxMempoolInfo> infoAll() const;
@@ -802,7 +748,7 @@ public:
LOCK(cs);
// Sanity check the transaction is in the mempool & insert into
// unbroadcast set.
- if (exists(txid)) m_unbroadcast_txids.insert(txid);
+ if (exists(GenTxid::Txid(txid))) m_unbroadcast_txids.insert(txid);
};
/** Removes a transaction from the unbroadcast set */
diff --git a/src/txrequest.cpp b/src/txrequest.cpp
index f8d7a1ece8..7d478a5b26 100644
--- a/src/txrequest.cpp
+++ b/src/txrequest.cpp
@@ -300,7 +300,7 @@ std::map<uint256, TxHashInfo> ComputeTxHashInfo(const Index& index, const Priori
GenTxid ToGenTxid(const Announcement& ann)
{
- return {ann.m_is_wtxid, ann.m_txhash};
+ return ann.m_is_wtxid ? GenTxid::Wtxid(ann.m_txhash) : GenTxid::Txid(ann.m_txhash);
}
} // namespace
diff --git a/src/univalue/.cirrus.yml b/src/univalue/.cirrus.yml
new file mode 100644
index 0000000000..f140fee12b
--- /dev/null
+++ b/src/univalue/.cirrus.yml
@@ -0,0 +1,44 @@
+env:
+ MAKEJOBS: "-j4"
+ RUN_TESTS: "true"
+ BASE_OUTDIR: "$CIRRUS_WORKING_DIR/out_dir_base"
+ DEBIAN_FRONTEND: "noninteractive"
+
+task:
+ container:
+ image: ubuntu:focal
+ cpu: 1
+ memory: 1G
+ greedy: true # https://medium.com/cirruslabs/introducing-greedy-container-instances-29aad06dc2b4
+
+ matrix:
+ - name: "gcc"
+ env:
+ CC: "gcc"
+ CXX: "g++"
+ APT_PKGS: "gcc"
+ - name: "clang"
+ env:
+ CC: "clang"
+ CXX: "clang++"
+ APT_PKGS: "clang"
+ - name: "mingw"
+ env:
+ CC: ""
+ CXX: ""
+ UNIVALUE_CONFIG: "--host=x86_64-w64-mingw32"
+ APT_PKGS: "g++-mingw-w64-x86-64 gcc-mingw-w64-x86-64 binutils-mingw-w64-x86-64"
+ RUN_TESTS: "false"
+
+ install_script:
+ - apt update
+ - apt install -y pkg-config build-essential libtool autotools-dev automake bsdmainutils
+ - apt install -y $APT_PKGS
+ autogen_script:
+ - ./autogen.sh
+ configure_script:
+ - ./configure --cache-file=config.cache --bindir=$BASE_OUTDIR/bin --libdir=$BASE_OUTDIR/lib $UNIVALUE_CONFIG
+ make_script:
+ - make $MAKEJOBS V=1
+ test_script:
+ - if [ "$RUN_TESTS" = "true" ]; then make $MAKEJOBS distcheck; fi
diff --git a/src/univalue/.travis.yml b/src/univalue/.travis.yml
deleted file mode 100644
index 43a1ed362e..0000000000
--- a/src/univalue/.travis.yml
+++ /dev/null
@@ -1,51 +0,0 @@
-language: cpp
-
-compiler:
- - clang
- - gcc
-
-os:
- - linux
- - osx
-
-sudo: false
-
-env:
- global:
- - MAKEJOBS=-j3
- - RUN_TESTS=true
- - BASE_OUTDIR=$TRAVIS_BUILD_DIR/out
-
-cache:
- apt: true
-
-addons:
- apt:
- packages:
- - pkg-config
-
-before_script:
- - if [ -n "$USE_SHELL" ]; then export CONFIG_SHELL="$USE_SHELL"; fi
- - test -n "$USE_SHELL" && eval '"$USE_SHELL" -c "./autogen.sh"' || ./autogen.sh
-
-script:
- - if [ -n "$UNIVALUE_CONFIG" ]; then unset CC; unset CXX; fi
- - OUTDIR=$BASE_OUTDIR/$TRAVIS_PULL_REQUEST/$TRAVIS_JOB_NUMBER-$HOST
- - UNIVALUE_CONFIG_ALL="--prefix=$TRAVIS_BUILD_DIR/depends/$HOST --bindir=$OUTDIR/bin --libdir=$OUTDIR/lib"
- - ./configure --cache-file=config.cache $UNIVALUE_CONFIG_ALL $UNIVALUE_CONFIG || ( cat config.log && false)
- - make -s $MAKEJOBS $GOAL || ( echo "Build failure. Verbose build follows." && make $GOAL ; false )
- - export LD_LIBRARY_PATH=$TRAVIS_BUILD_DIR/depends/$HOST/lib
- - if [ "$RUN_TESTS" = "true" ]; then make $MAKEJOBS distcheck; fi
-
-matrix:
- fast_finish: true
- include:
- - os: linux
- compiler: gcc
- env: UNIVALUE_CONFIG=--host=x86_64-w64-mingw32 RUN_TESTS=false
- addons:
- apt:
- packages:
- - g++-mingw-w64-x86-64
- - gcc-mingw-w64-x86-64
- - binutils-mingw-w64-x86-64
diff --git a/src/univalue/Makefile.am b/src/univalue/Makefile.am
index 0f5ba59954..476f14b922 100644
--- a/src/univalue/Makefile.am
+++ b/src/univalue/Makefile.am
@@ -1,20 +1,17 @@
+include sources.mk
ACLOCAL_AMFLAGS = -I build-aux/m4
-.PHONY: gen
+.PHONY: gen FORCE
.INTERMEDIATE: $(GENBIN)
-include_HEADERS = include/univalue.h
-noinst_HEADERS = lib/univalue_escapes.h lib/univalue_utffilter.h
+include_HEADERS = $(UNIVALUE_DIST_HEADERS_INT)
+noinst_HEADERS = $(UNIVALUE_LIB_HEADERS_INT)
lib_LTLIBRARIES = libunivalue.la
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = pc/libunivalue.pc
-libunivalue_la_SOURCES = \
- lib/univalue.cpp \
- lib/univalue_get.cpp \
- lib/univalue_read.cpp \
- lib/univalue_write.cpp
+libunivalue_la_SOURCES = $(UNIVALUE_LIB_SOURCES_INT)
libunivalue_la_LDFLAGS = \
-version-info $(LIBUNIVALUE_CURRENT):$(LIBUNIVALUE_REVISION):$(LIBUNIVALUE_AGE) \
@@ -30,89 +27,32 @@ $(GENBIN): $(GEN_SRCS)
@echo Building $@
$(AM_V_at)c++ -I$(top_srcdir)/include -o $@ $<
-gen: lib/univalue_escapes.h $(GENBIN)
- @echo Updating $<
+gen: $(GENBIN) FORCE
+ @echo Updating lib/univalue_escapes.h
$(AM_V_at)$(GENBIN) > lib/univalue_escapes.h
noinst_PROGRAMS = $(TESTS) test/test_json
-TEST_DATA_DIR=test
-
-test_unitester_SOURCES = test/unitester.cpp
+test_unitester_SOURCES = $(UNIVALUE_TEST_UNITESTER_INT)
test_unitester_LDADD = libunivalue.la
-test_unitester_CXXFLAGS = -I$(top_srcdir)/include -DJSON_TEST_SRC=\"$(srcdir)/$(TEST_DATA_DIR)\"
+test_unitester_CXXFLAGS = -I$(top_srcdir)/include -DJSON_TEST_SRC=\"$(srcdir)/$(UNIVALUE_TEST_DATA_DIR_INT)\"
test_unitester_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS)
-test_test_json_SOURCES = test/test_json.cpp
+test_test_json_SOURCES = $(UNIVALUE_TEST_JSON_INT)
test_test_json_LDADD = libunivalue.la
test_test_json_CXXFLAGS = -I$(top_srcdir)/include
test_test_json_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS)
-test_no_nul_SOURCES = test/no_nul.cpp
+test_no_nul_SOURCES = $(UNIVALUE_TEST_NO_NUL_INT)
test_no_nul_LDADD = libunivalue.la
test_no_nul_CXXFLAGS = -I$(top_srcdir)/include
test_no_nul_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS)
-test_object_SOURCES = test/object.cpp
+test_object_SOURCES = $(UNIVALUE_TEST_OBJECT_INT)
test_object_LDADD = libunivalue.la
test_object_CXXFLAGS = -I$(top_srcdir)/include
test_object_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS)
-TEST_FILES = \
- $(TEST_DATA_DIR)/fail10.json \
- $(TEST_DATA_DIR)/fail11.json \
- $(TEST_DATA_DIR)/fail12.json \
- $(TEST_DATA_DIR)/fail13.json \
- $(TEST_DATA_DIR)/fail14.json \
- $(TEST_DATA_DIR)/fail15.json \
- $(TEST_DATA_DIR)/fail16.json \
- $(TEST_DATA_DIR)/fail17.json \
- $(TEST_DATA_DIR)/fail18.json \
- $(TEST_DATA_DIR)/fail19.json \
- $(TEST_DATA_DIR)/fail1.json \
- $(TEST_DATA_DIR)/fail20.json \
- $(TEST_DATA_DIR)/fail21.json \
- $(TEST_DATA_DIR)/fail22.json \
- $(TEST_DATA_DIR)/fail23.json \
- $(TEST_DATA_DIR)/fail24.json \
- $(TEST_DATA_DIR)/fail25.json \
- $(TEST_DATA_DIR)/fail26.json \
- $(TEST_DATA_DIR)/fail27.json \
- $(TEST_DATA_DIR)/fail28.json \
- $(TEST_DATA_DIR)/fail29.json \
- $(TEST_DATA_DIR)/fail2.json \
- $(TEST_DATA_DIR)/fail30.json \
- $(TEST_DATA_DIR)/fail31.json \
- $(TEST_DATA_DIR)/fail32.json \
- $(TEST_DATA_DIR)/fail33.json \
- $(TEST_DATA_DIR)/fail34.json \
- $(TEST_DATA_DIR)/fail35.json \
- $(TEST_DATA_DIR)/fail36.json \
- $(TEST_DATA_DIR)/fail37.json \
- $(TEST_DATA_DIR)/fail38.json \
- $(TEST_DATA_DIR)/fail39.json \
- $(TEST_DATA_DIR)/fail40.json \
- $(TEST_DATA_DIR)/fail41.json \
- $(TEST_DATA_DIR)/fail42.json \
- $(TEST_DATA_DIR)/fail44.json \
- $(TEST_DATA_DIR)/fail45.json \
- $(TEST_DATA_DIR)/fail3.json \
- $(TEST_DATA_DIR)/fail4.json \
- $(TEST_DATA_DIR)/fail5.json \
- $(TEST_DATA_DIR)/fail6.json \
- $(TEST_DATA_DIR)/fail7.json \
- $(TEST_DATA_DIR)/fail8.json \
- $(TEST_DATA_DIR)/fail9.json \
- $(TEST_DATA_DIR)/pass1.json \
- $(TEST_DATA_DIR)/pass2.json \
- $(TEST_DATA_DIR)/pass3.json \
- $(TEST_DATA_DIR)/pass4.json \
- $(TEST_DATA_DIR)/round1.json \
- $(TEST_DATA_DIR)/round2.json \
- $(TEST_DATA_DIR)/round3.json \
- $(TEST_DATA_DIR)/round4.json \
- $(TEST_DATA_DIR)/round5.json \
- $(TEST_DATA_DIR)/round6.json \
- $(TEST_DATA_DIR)/round7.json
-
-EXTRA_DIST=$(TEST_FILES) $(GEN_SRCS)
+TEST_FILES = $(UNIVALUE_TEST_FILES_INT)
+
+EXTRA_DIST=$(UNIVALUE_TEST_FILES_INT) $(GEN_SRCS)
diff --git a/src/univalue/build-aux/m4/ax_cxx_compile_stdcxx.m4 b/src/univalue/build-aux/m4/ax_cxx_compile_stdcxx.m4
new file mode 100644
index 0000000000..f7e5137003
--- /dev/null
+++ b/src/univalue/build-aux/m4/ax_cxx_compile_stdcxx.m4
@@ -0,0 +1,962 @@
+# ===========================================================================
+# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional])
+#
+# DESCRIPTION
+#
+# Check for baseline language coverage in the compiler for the specified
+# version of the C++ standard. If necessary, add switches to CXX and
+# CXXCPP to enable support. VERSION may be '11' (for the C++11 standard)
+# or '14' (for the C++14 standard).
+#
+# The second argument, if specified, indicates whether you insist on an
+# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g.
+# -std=c++11). If neither is specified, you get whatever works, with
+# preference for no added switch, and then for an extended mode.
+#
+# The third argument, if specified 'mandatory' or if left unspecified,
+# indicates that baseline support for the specified C++ standard is
+# required and that the macro should error out if no mode with that
+# support is found. If specified 'optional', then configuration proceeds
+# regardless, after defining HAVE_CXX${VERSION} if and only if a
+# supporting mode is found.
+#
+# LICENSE
+#
+# Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
+# Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
+# Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
+# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
+# Copyright (c) 2015 Paul Norman <penorman@mac.com>
+# Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
+# Copyright (c) 2016, 2018 Krzesimir Nowak <qdlacz@gmail.com>
+# Copyright (c) 2019 Enji Cooper <yaneurabeya@gmail.com>
+# Copyright (c) 2020 Jason Merrill <jason@redhat.com>
+#
+# Copying and distribution of this file, with or without modification, are
+# permitted in any medium without royalty provided the copyright notice
+# and this notice are preserved. This file is offered as-is, without any
+# warranty.
+
+#serial 12
+
+dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro
+dnl (serial version number 13).
+
+AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
+ m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"],
+ [$1], [14], [ax_cxx_compile_alternatives="14 1y"],
+ [$1], [17], [ax_cxx_compile_alternatives="17 1z"],
+ [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl
+ m4_if([$2], [], [],
+ [$2], [ext], [],
+ [$2], [noext], [],
+ [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl
+ m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true],
+ [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true],
+ [$3], [optional], [ax_cxx_compile_cxx$1_required=false],
+ [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])])
+ AC_LANG_PUSH([C++])dnl
+ ac_success=no
+
+ m4_if([$2], [], [dnl
+ AC_CACHE_CHECK(whether $CXX supports C++$1 features by default,
+ ax_cv_cxx_compile_cxx$1,
+ [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
+ [ax_cv_cxx_compile_cxx$1=yes],
+ [ax_cv_cxx_compile_cxx$1=no])])
+ if test x$ax_cv_cxx_compile_cxx$1 = xyes; then
+ ac_success=yes
+ fi])
+
+ m4_if([$2], [noext], [], [dnl
+ if test x$ac_success = xno; then
+ for alternative in ${ax_cxx_compile_alternatives}; do
+ switch="-std=gnu++${alternative}"
+ cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
+ AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
+ $cachevar,
+ [ac_save_CXX="$CXX"
+ CXX="$CXX $switch"
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
+ [eval $cachevar=yes],
+ [eval $cachevar=no])
+ CXX="$ac_save_CXX"])
+ if eval test x\$$cachevar = xyes; then
+ CXX="$CXX $switch"
+ if test -n "$CXXCPP" ; then
+ CXXCPP="$CXXCPP $switch"
+ fi
+ ac_success=yes
+ break
+ fi
+ done
+ fi])
+
+ m4_if([$2], [ext], [], [dnl
+ if test x$ac_success = xno; then
+ dnl HP's aCC needs +std=c++11 according to:
+ dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf
+ dnl Cray's crayCC needs "-h std=c++11"
+ for alternative in ${ax_cxx_compile_alternatives}; do
+ for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do
+ cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
+ AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
+ $cachevar,
+ [ac_save_CXX="$CXX"
+ CXX="$CXX $switch"
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
+ [eval $cachevar=yes],
+ [eval $cachevar=no])
+ CXX="$ac_save_CXX"])
+ if eval test x\$$cachevar = xyes; then
+ CXX="$CXX $switch"
+ if test -n "$CXXCPP" ; then
+ CXXCPP="$CXXCPP $switch"
+ fi
+ ac_success=yes
+ break
+ fi
+ done
+ if test x$ac_success = xyes; then
+ break
+ fi
+ done
+ fi])
+ AC_LANG_POP([C++])
+ if test x$ax_cxx_compile_cxx$1_required = xtrue; then
+ if test x$ac_success = xno; then
+ AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.])
+ fi
+ fi
+ if test x$ac_success = xno; then
+ HAVE_CXX$1=0
+ AC_MSG_NOTICE([No compiler with C++$1 support was found])
+ else
+ HAVE_CXX$1=1
+ AC_DEFINE(HAVE_CXX$1,1,
+ [define if the compiler supports basic C++$1 syntax])
+ fi
+ AC_SUBST(HAVE_CXX$1)
+])
+
+
+dnl Test body for checking C++11 support
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11],
+ _AX_CXX_COMPILE_STDCXX_testbody_new_in_11
+)
+
+
+dnl Test body for checking C++14 support
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14],
+ _AX_CXX_COMPILE_STDCXX_testbody_new_in_11
+ _AX_CXX_COMPILE_STDCXX_testbody_new_in_14
+)
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17],
+ _AX_CXX_COMPILE_STDCXX_testbody_new_in_11
+ _AX_CXX_COMPILE_STDCXX_testbody_new_in_14
+ _AX_CXX_COMPILE_STDCXX_testbody_new_in_17
+)
+
+dnl Tests for new features in C++11
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[
+
+// If the compiler admits that it is not ready for C++11, why torture it?
+// Hopefully, this will speed up the test.
+
+#ifndef __cplusplus
+
+#error "This is not a C++ compiler"
+
+#elif __cplusplus < 201103L
+
+#error "This is not a C++11 compiler"
+
+#else
+
+namespace cxx11
+{
+
+ namespace test_static_assert
+ {
+
+ template <typename T>
+ struct check
+ {
+ static_assert(sizeof(int) <= sizeof(T), "not big enough");
+ };
+
+ }
+
+ namespace test_final_override
+ {
+
+ struct Base
+ {
+ virtual ~Base() {}
+ virtual void f() {}
+ };
+
+ struct Derived : public Base
+ {
+ virtual ~Derived() override {}
+ virtual void f() override {}
+ };
+
+ }
+
+ namespace test_double_right_angle_brackets
+ {
+
+ template < typename T >
+ struct check {};
+
+ typedef check<void> single_type;
+ typedef check<check<void>> double_type;
+ typedef check<check<check<void>>> triple_type;
+ typedef check<check<check<check<void>>>> quadruple_type;
+
+ }
+
+ namespace test_decltype
+ {
+
+ int
+ f()
+ {
+ int a = 1;
+ decltype(a) b = 2;
+ return a + b;
+ }
+
+ }
+
+ namespace test_type_deduction
+ {
+
+ template < typename T1, typename T2 >
+ struct is_same
+ {
+ static const bool value = false;
+ };
+
+ template < typename T >
+ struct is_same<T, T>
+ {
+ static const bool value = true;
+ };
+
+ template < typename T1, typename T2 >
+ auto
+ add(T1 a1, T2 a2) -> decltype(a1 + a2)
+ {
+ return a1 + a2;
+ }
+
+ int
+ test(const int c, volatile int v)
+ {
+ static_assert(is_same<int, decltype(0)>::value == true, "");
+ static_assert(is_same<int, decltype(c)>::value == false, "");
+ static_assert(is_same<int, decltype(v)>::value == false, "");
+ auto ac = c;
+ auto av = v;
+ auto sumi = ac + av + 'x';
+ auto sumf = ac + av + 1.0;
+ static_assert(is_same<int, decltype(ac)>::value == true, "");
+ static_assert(is_same<int, decltype(av)>::value == true, "");
+ static_assert(is_same<int, decltype(sumi)>::value == true, "");
+ static_assert(is_same<int, decltype(sumf)>::value == false, "");
+ static_assert(is_same<int, decltype(add(c, v))>::value == true, "");
+ return (sumf > 0.0) ? sumi : add(c, v);
+ }
+
+ }
+
+ namespace test_noexcept
+ {
+
+ int f() { return 0; }
+ int g() noexcept { return 0; }
+
+ static_assert(noexcept(f()) == false, "");
+ static_assert(noexcept(g()) == true, "");
+
+ }
+
+ namespace test_constexpr
+ {
+
+ template < typename CharT >
+ unsigned long constexpr
+ strlen_c_r(const CharT *const s, const unsigned long acc) noexcept
+ {
+ return *s ? strlen_c_r(s + 1, acc + 1) : acc;
+ }
+
+ template < typename CharT >
+ unsigned long constexpr
+ strlen_c(const CharT *const s) noexcept
+ {
+ return strlen_c_r(s, 0UL);
+ }
+
+ static_assert(strlen_c("") == 0UL, "");
+ static_assert(strlen_c("1") == 1UL, "");
+ static_assert(strlen_c("example") == 7UL, "");
+ static_assert(strlen_c("another\0example") == 7UL, "");
+
+ }
+
+ namespace test_rvalue_references
+ {
+
+ template < int N >
+ struct answer
+ {
+ static constexpr int value = N;
+ };
+
+ answer<1> f(int&) { return answer<1>(); }
+ answer<2> f(const int&) { return answer<2>(); }
+ answer<3> f(int&&) { return answer<3>(); }
+
+ void
+ test()
+ {
+ int i = 0;
+ const int c = 0;
+ static_assert(decltype(f(i))::value == 1, "");
+ static_assert(decltype(f(c))::value == 2, "");
+ static_assert(decltype(f(0))::value == 3, "");
+ }
+
+ }
+
+ namespace test_uniform_initialization
+ {
+
+ struct test
+ {
+ static const int zero {};
+ static const int one {1};
+ };
+
+ static_assert(test::zero == 0, "");
+ static_assert(test::one == 1, "");
+
+ }
+
+ namespace test_lambdas
+ {
+
+ void
+ test1()
+ {
+ auto lambda1 = [](){};
+ auto lambda2 = lambda1;
+ lambda1();
+ lambda2();
+ }
+
+ int
+ test2()
+ {
+ auto a = [](int i, int j){ return i + j; }(1, 2);
+ auto b = []() -> int { return '0'; }();
+ auto c = [=](){ return a + b; }();
+ auto d = [&](){ return c; }();
+ auto e = [a, &b](int x) mutable {
+ const auto identity = [](int y){ return y; };
+ for (auto i = 0; i < a; ++i)
+ a += b--;
+ return x + identity(a + b);
+ }(0);
+ return a + b + c + d + e;
+ }
+
+ int
+ test3()
+ {
+ const auto nullary = [](){ return 0; };
+ const auto unary = [](int x){ return x; };
+ using nullary_t = decltype(nullary);
+ using unary_t = decltype(unary);
+ const auto higher1st = [](nullary_t f){ return f(); };
+ const auto higher2nd = [unary](nullary_t f1){
+ return [unary, f1](unary_t f2){ return f2(unary(f1())); };
+ };
+ return higher1st(nullary) + higher2nd(nullary)(unary);
+ }
+
+ }
+
+ namespace test_variadic_templates
+ {
+
+ template <int...>
+ struct sum;
+
+ template <int N0, int... N1toN>
+ struct sum<N0, N1toN...>
+ {
+ static constexpr auto value = N0 + sum<N1toN...>::value;
+ };
+
+ template <>
+ struct sum<>
+ {
+ static constexpr auto value = 0;
+ };
+
+ static_assert(sum<>::value == 0, "");
+ static_assert(sum<1>::value == 1, "");
+ static_assert(sum<23>::value == 23, "");
+ static_assert(sum<1, 2>::value == 3, "");
+ static_assert(sum<5, 5, 11>::value == 21, "");
+ static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, "");
+
+ }
+
+ // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae
+ // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function
+ // because of this.
+ namespace test_template_alias_sfinae
+ {
+
+ struct foo {};
+
+ template<typename T>
+ using member = typename T::member_type;
+
+ template<typename T>
+ void func(...) {}
+
+ template<typename T>
+ void func(member<T>*) {}
+
+ void test();
+
+ void test() { func<foo>(0); }
+
+ }
+
+} // namespace cxx11
+
+#endif // __cplusplus >= 201103L
+
+]])
+
+
+dnl Tests for new features in C++14
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[
+
+// If the compiler admits that it is not ready for C++14, why torture it?
+// Hopefully, this will speed up the test.
+
+#ifndef __cplusplus
+
+#error "This is not a C++ compiler"
+
+#elif __cplusplus < 201402L
+
+#error "This is not a C++14 compiler"
+
+#else
+
+namespace cxx14
+{
+
+ namespace test_polymorphic_lambdas
+ {
+
+ int
+ test()
+ {
+ const auto lambda = [](auto&&... args){
+ const auto istiny = [](auto x){
+ return (sizeof(x) == 1UL) ? 1 : 0;
+ };
+ const int aretiny[] = { istiny(args)... };
+ return aretiny[0];
+ };
+ return lambda(1, 1L, 1.0f, '1');
+ }
+
+ }
+
+ namespace test_binary_literals
+ {
+
+ constexpr auto ivii = 0b0000000000101010;
+ static_assert(ivii == 42, "wrong value");
+
+ }
+
+ namespace test_generalized_constexpr
+ {
+
+ template < typename CharT >
+ constexpr unsigned long
+ strlen_c(const CharT *const s) noexcept
+ {
+ auto length = 0UL;
+ for (auto p = s; *p; ++p)
+ ++length;
+ return length;
+ }
+
+ static_assert(strlen_c("") == 0UL, "");
+ static_assert(strlen_c("x") == 1UL, "");
+ static_assert(strlen_c("test") == 4UL, "");
+ static_assert(strlen_c("another\0test") == 7UL, "");
+
+ }
+
+ namespace test_lambda_init_capture
+ {
+
+ int
+ test()
+ {
+ auto x = 0;
+ const auto lambda1 = [a = x](int b){ return a + b; };
+ const auto lambda2 = [a = lambda1(x)](){ return a; };
+ return lambda2();
+ }
+
+ }
+
+ namespace test_digit_separators
+ {
+
+ constexpr auto ten_million = 100'000'000;
+ static_assert(ten_million == 100000000, "");
+
+ }
+
+ namespace test_return_type_deduction
+ {
+
+ auto f(int& x) { return x; }
+ decltype(auto) g(int& x) { return x; }
+
+ template < typename T1, typename T2 >
+ struct is_same
+ {
+ static constexpr auto value = false;
+ };
+
+ template < typename T >
+ struct is_same<T, T>
+ {
+ static constexpr auto value = true;
+ };
+
+ int
+ test()
+ {
+ auto x = 0;
+ static_assert(is_same<int, decltype(f(x))>::value, "");
+ static_assert(is_same<int&, decltype(g(x))>::value, "");
+ return x;
+ }
+
+ }
+
+} // namespace cxx14
+
+#endif // __cplusplus >= 201402L
+
+]])
+
+
+dnl Tests for new features in C++17
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[
+
+// If the compiler admits that it is not ready for C++17, why torture it?
+// Hopefully, this will speed up the test.
+
+#ifndef __cplusplus
+
+#error "This is not a C++ compiler"
+
+#elif __cplusplus < 201703L
+
+#error "This is not a C++17 compiler"
+
+#else
+
+#include <initializer_list>
+#include <utility>
+#include <type_traits>
+
+namespace cxx17
+{
+
+ namespace test_constexpr_lambdas
+ {
+
+ constexpr int foo = [](){return 42;}();
+
+ }
+
+ namespace test::nested_namespace::definitions
+ {
+
+ }
+
+ namespace test_fold_expression
+ {
+
+ template<typename... Args>
+ int multiply(Args... args)
+ {
+ return (args * ... * 1);
+ }
+
+ template<typename... Args>
+ bool all(Args... args)
+ {
+ return (args && ...);
+ }
+
+ }
+
+ namespace test_extended_static_assert
+ {
+
+ static_assert (true);
+
+ }
+
+ namespace test_auto_brace_init_list
+ {
+
+ auto foo = {5};
+ auto bar {5};
+
+ static_assert(std::is_same<std::initializer_list<int>, decltype(foo)>::value);
+ static_assert(std::is_same<int, decltype(bar)>::value);
+ }
+
+ namespace test_typename_in_template_template_parameter
+ {
+
+ template<template<typename> typename X> struct D;
+
+ }
+
+ namespace test_fallthrough_nodiscard_maybe_unused_attributes
+ {
+
+ int f1()
+ {
+ return 42;
+ }
+
+ [[nodiscard]] int f2()
+ {
+ [[maybe_unused]] auto unused = f1();
+
+ switch (f1())
+ {
+ case 17:
+ f1();
+ [[fallthrough]];
+ case 42:
+ f1();
+ }
+ return f1();
+ }
+
+ }
+
+ namespace test_extended_aggregate_initialization
+ {
+
+ struct base1
+ {
+ int b1, b2 = 42;
+ };
+
+ struct base2
+ {
+ base2() {
+ b3 = 42;
+ }
+ int b3;
+ };
+
+ struct derived : base1, base2
+ {
+ int d;
+ };
+
+ derived d1 {{1, 2}, {}, 4}; // full initialization
+ derived d2 {{}, {}, 4}; // value-initialized bases
+
+ }
+
+ namespace test_general_range_based_for_loop
+ {
+
+ struct iter
+ {
+ int i;
+
+ int& operator* ()
+ {
+ return i;
+ }
+
+ const int& operator* () const
+ {
+ return i;
+ }
+
+ iter& operator++()
+ {
+ ++i;
+ return *this;
+ }
+ };
+
+ struct sentinel
+ {
+ int i;
+ };
+
+ bool operator== (const iter& i, const sentinel& s)
+ {
+ return i.i == s.i;
+ }
+
+ bool operator!= (const iter& i, const sentinel& s)
+ {
+ return !(i == s);
+ }
+
+ struct range
+ {
+ iter begin() const
+ {
+ return {0};
+ }
+
+ sentinel end() const
+ {
+ return {5};
+ }
+ };
+
+ void f()
+ {
+ range r {};
+
+ for (auto i : r)
+ {
+ [[maybe_unused]] auto v = i;
+ }
+ }
+
+ }
+
+ namespace test_lambda_capture_asterisk_this_by_value
+ {
+
+ struct t
+ {
+ int i;
+ int foo()
+ {
+ return [*this]()
+ {
+ return i;
+ }();
+ }
+ };
+
+ }
+
+ namespace test_enum_class_construction
+ {
+
+ enum class byte : unsigned char
+ {};
+
+ byte foo {42};
+
+ }
+
+ namespace test_constexpr_if
+ {
+
+ template <bool cond>
+ int f ()
+ {
+ if constexpr(cond)
+ {
+ return 13;
+ }
+ else
+ {
+ return 42;
+ }
+ }
+
+ }
+
+ namespace test_selection_statement_with_initializer
+ {
+
+ int f()
+ {
+ return 13;
+ }
+
+ int f2()
+ {
+ if (auto i = f(); i > 0)
+ {
+ return 3;
+ }
+
+ switch (auto i = f(); i + 4)
+ {
+ case 17:
+ return 2;
+
+ default:
+ return 1;
+ }
+ }
+
+ }
+
+ namespace test_template_argument_deduction_for_class_templates
+ {
+
+ template <typename T1, typename T2>
+ struct pair
+ {
+ pair (T1 p1, T2 p2)
+ : m1 {p1},
+ m2 {p2}
+ {}
+
+ T1 m1;
+ T2 m2;
+ };
+
+ void f()
+ {
+ [[maybe_unused]] auto p = pair{13, 42u};
+ }
+
+ }
+
+ namespace test_non_type_auto_template_parameters
+ {
+
+ template <auto n>
+ struct B
+ {};
+
+ B<5> b1;
+ B<'a'> b2;
+
+ }
+
+ namespace test_structured_bindings
+ {
+
+ int arr[2] = { 1, 2 };
+ std::pair<int, int> pr = { 1, 2 };
+
+ auto f1() -> int(&)[2]
+ {
+ return arr;
+ }
+
+ auto f2() -> std::pair<int, int>&
+ {
+ return pr;
+ }
+
+ struct S
+ {
+ int x1 : 2;
+ volatile double y1;
+ };
+
+ S f3()
+ {
+ return {};
+ }
+
+ auto [ x1, y1 ] = f1();
+ auto& [ xr1, yr1 ] = f1();
+ auto [ x2, y2 ] = f2();
+ auto& [ xr2, yr2 ] = f2();
+ const auto [ x3, y3 ] = f3();
+
+ }
+
+ namespace test_exception_spec_type_system
+ {
+
+ struct Good {};
+ struct Bad {};
+
+ void g1() noexcept;
+ void g2();
+
+ template<typename T>
+ Bad
+ f(T*, T*);
+
+ template<typename T1, typename T2>
+ Good
+ f(T1*, T2*);
+
+ static_assert (std::is_same_v<Good, decltype(f(g1, g2))>);
+
+ }
+
+ namespace test_inline_variables
+ {
+
+ template<class T> void f(T)
+ {}
+
+ template<class T> inline T g(T)
+ {
+ return T{};
+ }
+
+ template<> inline void f<>(int)
+ {}
+
+ template<> int g<>(int)
+ {
+ return 5;
+ }
+
+ }
+
+} // namespace cxx17
+
+#endif // __cplusplus < 201703L
+
+]])
diff --git a/src/univalue/configure.ac b/src/univalue/configure.ac
index 8298332ac1..495b25a53d 100644
--- a/src/univalue/configure.ac
+++ b/src/univalue/configure.ac
@@ -1,7 +1,7 @@
m4_define([libunivalue_major_version], [1])
m4_define([libunivalue_minor_version], [1])
-m4_define([libunivalue_micro_version], [3])
-m4_define([libunivalue_interface_age], [3])
+m4_define([libunivalue_micro_version], [4])
+m4_define([libunivalue_interface_age], [4])
# If you need a modifier for the version number.
# Normally empty, but can be used to make "fixup" releases.
m4_define([libunivalue_extraversion], [])
@@ -14,7 +14,7 @@ m4_define([libunivalue_age], [m4_eval(libunivalue_binary_age - libunivalue_inter
m4_define([libunivalue_version], [libunivalue_major_version().libunivalue_minor_version().libunivalue_micro_version()libunivalue_extraversion()])
-AC_INIT([univalue], [1.0.3],
+AC_INIT([univalue], [1.0.4],
[http://github.com/jgarzik/univalue/])
dnl make the compilation flags quiet unless V=1 is used
@@ -45,6 +45,9 @@ AC_SUBST(LIBUNIVALUE_AGE)
LT_INIT
LT_LANG([C++])
+dnl Require C++11 compiler (no GNU extensions)
+AX_CXX_COMPILE_STDCXX([11], [noext], [mandatory], [nodefault])
+
case $host in
*mingw*)
LIBTOOL_APP_LDFLAGS="$LIBTOOL_APP_LDFLAGS -all-static"
diff --git a/src/univalue/gen/gen.cpp b/src/univalue/gen/gen.cpp
index 85fe20924a..b8a6c73f4e 100644
--- a/src/univalue/gen/gen.cpp
+++ b/src/univalue/gen/gen.cpp
@@ -1,6 +1,6 @@
// Copyright 2014 BitPay Inc.
// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+// file COPYING or https://opensource.org/licenses/mit-license.php.
//
// To re-create univalue_escapes.h:
@@ -45,7 +45,7 @@ static void outputEscape()
for (unsigned int i = 0; i < 256; i++) {
if (escapes[i].empty()) {
- printf("\tNULL,\n");
+ printf("\tnullptr,\n");
} else {
printf("\t\"");
diff --git a/src/univalue/include/univalue.h b/src/univalue/include/univalue.h
index 048e162f7d..fc5cf402be 100644
--- a/src/univalue/include/univalue.h
+++ b/src/univalue/include/univalue.h
@@ -1,7 +1,7 @@
// Copyright 2014 BitPay Inc.
// Copyright 2015 Bitcoin Core Developers
// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+// file COPYING or https://opensource.org/licenses/mit-license.php.
#ifndef __UNIVALUE_H__
#define __UNIVALUE_H__
@@ -14,8 +14,6 @@
#include <map>
#include <cassert>
-#include <sstream> // .get_int64()
-
class UniValue {
public:
enum VType { VNULL, VOBJ, VARR, VSTR, VNUM, VBOOL, };
diff --git a/src/univalue/lib/univalue.cpp b/src/univalue/lib/univalue.cpp
index 4c9c15d63e..c4e59fae74 100644
--- a/src/univalue/lib/univalue.cpp
+++ b/src/univalue/lib/univalue.cpp
@@ -1,7 +1,7 @@
// Copyright 2014 BitPay Inc.
// Copyright 2015 Bitcoin Core Developers
// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+// file COPYING or https://opensource.org/licenses/mit-license.php.
#include <stdint.h>
#include <iomanip>
@@ -178,17 +178,19 @@ bool UniValue::findKey(const std::string& key, size_t& retIdx) const
bool UniValue::checkObject(const std::map<std::string,UniValue::VType>& t) const
{
- if (typ != VOBJ)
+ if (typ != VOBJ) {
return false;
+ }
- for (std::map<std::string,UniValue::VType>::const_iterator it = t.begin();
- it != t.end(); ++it) {
+ for (const auto& object: t) {
size_t idx = 0;
- if (!findKey(it->first, idx))
+ if (!findKey(object.first, idx)) {
return false;
+ }
- if (values.at(idx).getType() != it->second)
+ if (values.at(idx).getType() != object.second) {
return false;
+ }
}
return true;
@@ -228,7 +230,7 @@ const char *uvTypeName(UniValue::VType t)
}
// not reached
- return NULL;
+ return nullptr;
}
const UniValue& find_value(const UniValue& obj, const std::string& name)
diff --git a/src/univalue/lib/univalue_escapes.h b/src/univalue/lib/univalue_escapes.h
index 74596aab6d..3f714f8e5b 100644
--- a/src/univalue/lib/univalue_escapes.h
+++ b/src/univalue/lib/univalue_escapes.h
@@ -34,229 +34,229 @@ static const char *escapes[256] = {
"\\u001d",
"\\u001e",
"\\u001f",
- NULL,
- NULL,
+ nullptr,
+ nullptr,
"\\\"",
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
"\\\\",
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
"\\u007f",
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
};
#endif // BITCOIN_UNIVALUE_UNIVALUE_ESCAPES_H
diff --git a/src/univalue/lib/univalue_get.cpp b/src/univalue/lib/univalue_get.cpp
index 0ad6146545..5af89a3561 100644
--- a/src/univalue/lib/univalue_get.cpp
+++ b/src/univalue/lib/univalue_get.cpp
@@ -1,7 +1,7 @@
// Copyright 2014 BitPay Inc.
// Copyright 2015 Bitcoin Core Developers
// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+// file COPYING or https://opensource.org/licenses/mit-license.php.
#include <stdint.h>
#include <errno.h>
@@ -11,6 +11,7 @@
#include <vector>
#include <limits>
#include <string>
+#include <sstream>
#include "univalue.h"
@@ -31,7 +32,7 @@ bool ParseInt32(const std::string& str, int32_t *out)
{
if (!ParsePrechecks(str))
return false;
- char *endp = NULL;
+ char *endp = nullptr;
errno = 0; // strtol will not set errno if valid
long int n = strtol(str.c_str(), &endp, 10);
if(out) *out = (int32_t)n;
@@ -47,7 +48,7 @@ bool ParseInt64(const std::string& str, int64_t *out)
{
if (!ParsePrechecks(str))
return false;
- char *endp = NULL;
+ char *endp = nullptr;
errno = 0; // strtoll will not set errno if valid
long long int n = strtoll(str.c_str(), &endp, 10);
if(out) *out = (int64_t)n;
diff --git a/src/univalue/lib/univalue_read.cpp b/src/univalue/lib/univalue_read.cpp
index 5c6a1acf75..be39bfe57a 100644
--- a/src/univalue/lib/univalue_read.cpp
+++ b/src/univalue/lib/univalue_read.cpp
@@ -1,6 +1,6 @@
// Copyright 2014 BitPay Inc.
// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+// file COPYING or https://opensource.org/licenses/mit-license.php.
#include <string.h>
#include <vector>
@@ -227,7 +227,7 @@ enum jtokentype getJsonToken(std::string& tokenVal, unsigned int& consumed,
}
else {
- writer.push_back(*raw);
+ writer.push_back(static_cast<unsigned char>(*raw));
raw++;
}
}
@@ -244,7 +244,7 @@ enum jtokentype getJsonToken(std::string& tokenVal, unsigned int& consumed,
}
}
-enum expect_bits {
+enum expect_bits : unsigned {
EXP_OBJ_NAME = (1U << 0),
EXP_COLON = (1U << 1),
EXP_ARR_VALUE = (1U << 2),
diff --git a/src/univalue/lib/univalue_utffilter.h b/src/univalue/lib/univalue_utffilter.h
index 20d4043009..c24ac58eaf 100644
--- a/src/univalue/lib/univalue_utffilter.h
+++ b/src/univalue/lib/univalue_utffilter.h
@@ -1,6 +1,6 @@
// Copyright 2016 Wladimir J. van der Laan
// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+// file COPYING or https://opensource.org/licenses/mit-license.php.
#ifndef UNIVALUE_UTFFILTER_H
#define UNIVALUE_UTFFILTER_H
diff --git a/src/univalue/lib/univalue_write.cpp b/src/univalue/lib/univalue_write.cpp
index 827eb9b271..3a2c580c7f 100644
--- a/src/univalue/lib/univalue_write.cpp
+++ b/src/univalue/lib/univalue_write.cpp
@@ -1,9 +1,8 @@
// Copyright 2014 BitPay Inc.
// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+// file COPYING or https://opensource.org/licenses/mit-license.php.
#include <iomanip>
-#include <sstream>
#include <stdio.h>
#include "univalue.h"
#include "univalue_escapes.h"
@@ -14,13 +13,13 @@ static std::string json_escape(const std::string& inS)
outS.reserve(inS.size() * 2);
for (unsigned int i = 0; i < inS.size(); i++) {
- unsigned char ch = inS[i];
+ unsigned char ch = static_cast<unsigned char>(inS[i]);
const char *escStr = escapes[ch];
if (escStr)
outS += escStr;
else
- outS += ch;
+ outS += static_cast<char>(ch);
}
return outS;
diff --git a/src/univalue/sources.mk b/src/univalue/sources.mk
new file mode 100644
index 0000000000..efab6d277f
--- /dev/null
+++ b/src/univalue/sources.mk
@@ -0,0 +1,95 @@
+# - All variables are namespaced with UNIVALUE_ to avoid colliding with
+# downstream makefiles.
+# - All Variables ending in _HEADERS or _SOURCES confuse automake, so the
+# _INT postfix is applied.
+# - Convenience variables, for example a UNIVALUE_TEST_DIR should not be used
+# as they interfere with automatic dependency generation
+# - The %reldir% is the relative path from the Makefile.am. This allows
+# downstreams to use these variables without having to manually account for
+# the path change.
+
+UNIVALUE_INCLUDE_DIR_INT = %reldir%/include
+
+UNIVALUE_DIST_HEADERS_INT =
+UNIVALUE_DIST_HEADERS_INT += %reldir%/include/univalue.h
+
+UNIVALUE_LIB_HEADERS_INT =
+UNIVALUE_LIB_HEADERS_INT += %reldir%/lib/univalue_utffilter.h
+UNIVALUE_LIB_HEADERS_INT += %reldir%/lib/univalue_escapes.h
+
+UNIVALUE_LIB_SOURCES_INT =
+UNIVALUE_LIB_SOURCES_INT += %reldir%/lib/univalue.cpp
+UNIVALUE_LIB_SOURCES_INT += %reldir%/lib/univalue_get.cpp
+UNIVALUE_LIB_SOURCES_INT += %reldir%/lib/univalue_read.cpp
+UNIVALUE_LIB_SOURCES_INT += %reldir%/lib/univalue_write.cpp
+
+UNIVALUE_TEST_DATA_DIR_INT = %reldir%/test
+
+UNIVALUE_TEST_UNITESTER_INT =
+UNIVALUE_TEST_UNITESTER_INT += %reldir%/test/unitester.cpp
+
+UNIVALUE_TEST_JSON_INT =
+UNIVALUE_TEST_JSON_INT += %reldir%/test/test_json.cpp
+
+UNIVALUE_TEST_NO_NUL_INT =
+UNIVALUE_TEST_NO_NUL_INT += %reldir%/test/no_nul.cpp
+
+UNIVALUE_TEST_OBJECT_INT =
+UNIVALUE_TEST_OBJECT_INT += %reldir%/test/object.cpp
+
+UNIVALUE_TEST_FILES_INT =
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail1.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail2.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail3.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail4.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail5.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail6.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail7.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail8.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail9.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail10.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail11.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail12.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail13.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail14.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail15.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail16.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail17.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail18.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail19.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail20.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail21.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail22.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail23.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail24.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail25.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail26.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail27.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail28.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail29.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail30.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail31.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail32.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail33.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail34.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail35.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail36.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail37.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail38.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail39.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail40.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail41.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail42.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail44.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/fail45.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/pass1.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/pass2.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/pass3.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/pass4.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/round1.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/round2.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/round3.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/round4.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/round5.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/round6.json
+UNIVALUE_TEST_FILES_INT += %reldir%/test/round7.json
diff --git a/src/univalue/test/object.cpp b/src/univalue/test/object.cpp
index ccc1344836..c2f52f83ac 100644
--- a/src/univalue/test/object.cpp
+++ b/src/univalue/test/object.cpp
@@ -1,7 +1,7 @@
// Copyright (c) 2014 BitPay Inc.
// Copyright (c) 2014-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+// file COPYING or https://opensource.org/licenses/mit-license.php.
#include <stdint.h>
#include <vector>
diff --git a/src/univalue/test/unitester.cpp b/src/univalue/test/unitester.cpp
index 2308afbcdf..02e1a83c6d 100644
--- a/src/univalue/test/unitester.cpp
+++ b/src/univalue/test/unitester.cpp
@@ -1,6 +1,6 @@
// Copyright 2014 BitPay Inc.
// Distributed under the MIT/X11 software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+// file COPYING or https://opensource.org/licenses/mit-license.php.
#include <stdlib.h>
#include <stdio.h>
@@ -58,7 +58,7 @@ static void runtest_file(const char *filename_)
std::string basename(filename_);
std::string filename = srcdir + "/" + basename;
FILE *f = fopen(filename.c_str(), "r");
- assert(f != NULL);
+ assert(f != nullptr);
std::string jdata;
diff --git a/src/util/asmap.cpp b/src/util/asmap.cpp
index 5695c62012..b696c65e9d 100644
--- a/src/util/asmap.cpp
+++ b/src/util/asmap.cpp
@@ -201,7 +201,7 @@ std::vector<bool> DecodeAsmap(fs::path path)
}
fseek(filestr, 0, SEEK_END);
int length = ftell(filestr);
- LogPrintf("Opened asmap file %s (%d bytes) from disk\n", path, length);
+ LogPrintf("Opened asmap file %s (%d bytes) from disk\n", fs::quoted(fs::PathToString(path)), length);
fseek(filestr, 0, SEEK_SET);
uint8_t cur_byte;
for (int i = 0; i < length; ++i) {
@@ -211,7 +211,7 @@ std::vector<bool> DecodeAsmap(fs::path path)
}
}
if (!SanityCheckASMap(bits, 128)) {
- LogPrintf("Sanity check of asmap file %s failed\n", path);
+ LogPrintf("Sanity check of asmap file %s failed\n", fs::quoted(fs::PathToString(path)));
return {};
}
return bits;
diff --git a/src/util/settings.cpp b/src/util/settings.cpp
index 846b34089d..7fb35c073e 100644
--- a/src/util/settings.cpp
+++ b/src/util/settings.cpp
@@ -66,24 +66,24 @@ bool ReadSettings(const fs::path& path, std::map<std::string, SettingsValue>& va
fsbridge::ifstream file;
file.open(path);
if (!file.is_open()) {
- errors.emplace_back(strprintf("%s. Please check permissions.", path.string()));
+ errors.emplace_back(strprintf("%s. Please check permissions.", fs::PathToString(path)));
return false;
}
SettingsValue in;
if (!in.read(std::string{std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()})) {
- errors.emplace_back(strprintf("Unable to parse settings file %s", path.string()));
+ errors.emplace_back(strprintf("Unable to parse settings file %s", fs::PathToString(path)));
return false;
}
if (file.fail()) {
- errors.emplace_back(strprintf("Failed reading settings file %s", path.string()));
+ errors.emplace_back(strprintf("Failed reading settings file %s", fs::PathToString(path)));
return false;
}
file.close(); // Done with file descriptor. Release while copying data.
if (!in.isObject()) {
- errors.emplace_back(strprintf("Found non-object value %s in settings file %s", in.write(), path.string()));
+ errors.emplace_back(strprintf("Found non-object value %s in settings file %s", in.write(), fs::PathToString(path)));
return false;
}
@@ -92,7 +92,7 @@ bool ReadSettings(const fs::path& path, std::map<std::string, SettingsValue>& va
for (size_t i = 0; i < in_keys.size(); ++i) {
auto inserted = values.emplace(in_keys[i], in_values[i]);
if (!inserted.second) {
- errors.emplace_back(strprintf("Found duplicate key %s in settings file %s", in_keys[i], path.string()));
+ errors.emplace_back(strprintf("Found duplicate key %s in settings file %s", in_keys[i], fs::PathToString(path)));
}
}
return errors.empty();
@@ -109,7 +109,7 @@ bool WriteSettings(const fs::path& path,
fsbridge::ofstream file;
file.open(path);
if (file.fail()) {
- errors.emplace_back(strprintf("Error: Unable to open settings file %s for writing", path.string()));
+ errors.emplace_back(strprintf("Error: Unable to open settings file %s for writing", fs::PathToString(path)));
return false;
}
file << out.write(/* prettyIndent= */ 1, /* indentLevel= */ 4) << std::endl;
diff --git a/src/util/strencodings.h b/src/util/strencodings.h
index 1f7762aeef..eedb5ec2f8 100644
--- a/src/util/strencodings.h
+++ b/src/util/strencodings.h
@@ -72,7 +72,7 @@ void SplitHostPort(std::string in, uint16_t& portOut, std::string& hostOut);
// LocaleIndependentAtoi is provided for backwards compatibility reasons.
//
-// New code should use the ParseInt64/ParseUInt64/ParseInt32/ParseUInt32 functions
+// New code should use ToIntegral or the ParseInt* functions
// which provide parse error feedback.
//
// The goal of LocaleIndependentAtoi is to replicate the exact defined behaviour
@@ -125,7 +125,7 @@ constexpr inline bool IsSpace(char c) noexcept {
/**
* Convert string to integral type T. Leading whitespace, a leading +, or any
* trailing character fail the parsing. The required format expressed as regex
- * is `-?[0-9]+`.
+ * is `-?[0-9]+`. The minus sign is only permitted for signed integer types.
*
* @returns std::nullopt if the entire string could not be parsed, or if the
* parsed value is not in the range representable by the type T.
diff --git a/src/util/syscall_sandbox.cpp b/src/util/syscall_sandbox.cpp
index 7e76ecbf3a..bc69df44f4 100644
--- a/src/util/syscall_sandbox.cpp
+++ b/src/util/syscall_sandbox.cpp
@@ -46,6 +46,12 @@ bool g_syscall_sandbox_log_violation_before_terminating{false};
// Define system call numbers for x86_64 that are referenced in the system call profile
// but not provided by the kernel headers used in the GUIX build.
+// Usually, they can be found via "grep name /usr/include/x86_64-linux-gnu/asm/unistd_64.h"
+
+#ifndef __NR_clone3
+#define __NR_clone3 435
+#endif
+
#ifndef __NR_statx
#define __NR_statx 332
#endif
@@ -115,6 +121,7 @@ const std::map<uint32_t, std::string> LINUX_SYSCALLS{
{__NR_clock_nanosleep, "clock_nanosleep"},
{__NR_clock_settime, "clock_settime"},
{__NR_clone, "clone"},
+ {__NR_clone3, "clone3"},
{__NR_close, "close"},
{__NR_connect, "connect"},
{__NR_copy_file_range, "copy_file_range"},
@@ -162,6 +169,10 @@ const std::map<uint32_t, std::string> LINUX_SYSCALLS{
{__NR_ftruncate, "ftruncate"},
{__NR_futex, "futex"},
{__NR_futimesat, "futimesat"},
+ {__NR_get_kernel_syms, "get_kernel_syms"},
+ {__NR_get_mempolicy, "get_mempolicy"},
+ {__NR_get_robust_list, "get_robust_list"},
+ {__NR_get_thread_area, "get_thread_area"},
{__NR_getcpu, "getcpu"},
{__NR_getcwd, "getcwd"},
{__NR_getdents, "getdents"},
@@ -171,8 +182,6 @@ const std::map<uint32_t, std::string> LINUX_SYSCALLS{
{__NR_getgid, "getgid"},
{__NR_getgroups, "getgroups"},
{__NR_getitimer, "getitimer"},
- {__NR_get_kernel_syms, "get_kernel_syms"},
- {__NR_get_mempolicy, "get_mempolicy"},
{__NR_getpeername, "getpeername"},
{__NR_getpgid, "getpgid"},
{__NR_getpgrp, "getpgrp"},
@@ -184,12 +193,10 @@ const std::map<uint32_t, std::string> LINUX_SYSCALLS{
{__NR_getresgid, "getresgid"},
{__NR_getresuid, "getresuid"},
{__NR_getrlimit, "getrlimit"},
- {__NR_get_robust_list, "get_robust_list"},
{__NR_getrusage, "getrusage"},
{__NR_getsid, "getsid"},
{__NR_getsockname, "getsockname"},
{__NR_getsockopt, "getsockopt"},
- {__NR_get_thread_area, "get_thread_area"},
{__NR_gettid, "gettid"},
{__NR_gettimeofday, "gettimeofday"},
{__NR_getuid, "getuid"},
@@ -200,15 +207,15 @@ const std::map<uint32_t, std::string> LINUX_SYSCALLS{
{__NR_inotify_init1, "inotify_init1"},
{__NR_inotify_rm_watch, "inotify_rm_watch"},
{__NR_io_cancel, "io_cancel"},
- {__NR_ioctl, "ioctl"},
{__NR_io_destroy, "io_destroy"},
{__NR_io_getevents, "io_getevents"},
+ {__NR_io_setup, "io_setup"},
+ {__NR_io_submit, "io_submit"},
+ {__NR_ioctl, "ioctl"},
{__NR_ioperm, "ioperm"},
{__NR_iopl, "iopl"},
{__NR_ioprio_get, "ioprio_get"},
{__NR_ioprio_set, "ioprio_set"},
- {__NR_io_setup, "io_setup"},
- {__NR_io_submit, "io_submit"},
{__NR_kcmp, "kcmp"},
{__NR_kexec_file_load, "kexec_file_load"},
{__NR_kexec_load, "kexec_load"},
@@ -264,8 +271,8 @@ const std::map<uint32_t, std::string> LINUX_SYSCALLS{
{__NR_newfstatat, "newfstatat"},
{__NR_nfsservctl, "nfsservctl"},
{__NR_open, "open"},
- {__NR_openat, "openat"},
{__NR_open_by_handle_at, "open_by_handle_at"},
+ {__NR_openat, "openat"},
{__NR_pause, "pause"},
{__NR_perf_event_open, "perf_event_open"},
{__NR_personality, "personality"},
@@ -300,6 +307,7 @@ const std::map<uint32_t, std::string> LINUX_SYSCALLS{
#ifdef __NR_pwritev2
{__NR_pwritev2, "pwritev2"},
#endif
+ {__NR__sysctl, "_sysctl"},
{__NR_query_module, "query_module"},
{__NR_quotactl, "quotactl"},
{__NR_read, "read"},
@@ -327,11 +335,11 @@ const std::map<uint32_t, std::string> LINUX_SYSCALLS{
{__NR_rt_sigsuspend, "rt_sigsuspend"},
{__NR_rt_sigtimedwait, "rt_sigtimedwait"},
{__NR_rt_tgsigqueueinfo, "rt_tgsigqueueinfo"},
+ {__NR_sched_get_priority_max, "sched_get_priority_max"},
+ {__NR_sched_get_priority_min, "sched_get_priority_min"},
{__NR_sched_getaffinity, "sched_getaffinity"},
{__NR_sched_getattr, "sched_getattr"},
{__NR_sched_getparam, "sched_getparam"},
- {__NR_sched_get_priority_max, "sched_get_priority_max"},
- {__NR_sched_get_priority_min, "sched_get_priority_min"},
{__NR_sched_getscheduler, "sched_getscheduler"},
{__NR_sched_rr_get_interval, "sched_rr_get_interval"},
{__NR_sched_setaffinity, "sched_setaffinity"},
@@ -350,6 +358,10 @@ const std::map<uint32_t, std::string> LINUX_SYSCALLS{
{__NR_sendmmsg, "sendmmsg"},
{__NR_sendmsg, "sendmsg"},
{__NR_sendto, "sendto"},
+ {__NR_set_mempolicy, "set_mempolicy"},
+ {__NR_set_robust_list, "set_robust_list"},
+ {__NR_set_thread_area, "set_thread_area"},
+ {__NR_set_tid_address, "set_tid_address"},
{__NR_setdomainname, "setdomainname"},
{__NR_setfsgid, "setfsgid"},
{__NR_setfsuid, "setfsuid"},
@@ -357,7 +369,6 @@ const std::map<uint32_t, std::string> LINUX_SYSCALLS{
{__NR_setgroups, "setgroups"},
{__NR_sethostname, "sethostname"},
{__NR_setitimer, "setitimer"},
- {__NR_set_mempolicy, "set_mempolicy"},
{__NR_setns, "setns"},
{__NR_setpgid, "setpgid"},
{__NR_setpriority, "setpriority"},
@@ -366,11 +377,8 @@ const std::map<uint32_t, std::string> LINUX_SYSCALLS{
{__NR_setresuid, "setresuid"},
{__NR_setreuid, "setreuid"},
{__NR_setrlimit, "setrlimit"},
- {__NR_set_robust_list, "set_robust_list"},
{__NR_setsid, "setsid"},
{__NR_setsockopt, "setsockopt"},
- {__NR_set_thread_area, "set_thread_area"},
- {__NR_set_tid_address, "set_tid_address"},
{__NR_settimeofday, "settimeofday"},
{__NR_setuid, "setuid"},
{__NR_setxattr, "setxattr"},
@@ -395,7 +403,6 @@ const std::map<uint32_t, std::string> LINUX_SYSCALLS{
{__NR_sync, "sync"},
{__NR_sync_file_range, "sync_file_range"},
{__NR_syncfs, "syncfs"},
- {__NR__sysctl, "_sysctl"},
{__NR_sysfs, "sysfs"},
{__NR_sysinfo, "sysinfo"},
{__NR_syslog, "syslog"},
@@ -404,12 +411,12 @@ const std::map<uint32_t, std::string> LINUX_SYSCALLS{
{__NR_time, "time"},
{__NR_timer_create, "timer_create"},
{__NR_timer_delete, "timer_delete"},
- {__NR_timerfd_create, "timerfd_create"},
- {__NR_timerfd_gettime, "timerfd_gettime"},
- {__NR_timerfd_settime, "timerfd_settime"},
{__NR_timer_getoverrun, "timer_getoverrun"},
{__NR_timer_gettime, "timer_gettime"},
{__NR_timer_settime, "timer_settime"},
+ {__NR_timerfd_create, "timerfd_create"},
+ {__NR_timerfd_gettime, "timerfd_gettime"},
+ {__NR_timerfd_settime, "timerfd_settime"},
{__NR_times, "times"},
{__NR_tkill, "tkill"},
{__NR_truncate, "truncate"},
@@ -540,6 +547,7 @@ public:
allowed_syscalls.insert(__NR_brk); // change data segment size
allowed_syscalls.insert(__NR_madvise); // give advice about use of memory
allowed_syscalls.insert(__NR_membarrier); // issue memory barriers on a set of threads
+ allowed_syscalls.insert(__NR_mincore); // check if virtual memory is in RAM
allowed_syscalls.insert(__NR_mlock); // lock memory
allowed_syscalls.insert(__NR_mmap); // map files or devices into memory
allowed_syscalls.insert(__NR_mprotect); // set protection on a region of memory
@@ -642,6 +650,7 @@ public:
{
allowed_syscalls.insert(__NR_clock_getres); // find the resolution (precision) of the specified clock
allowed_syscalls.insert(__NR_clock_gettime); // retrieve the time of the specified clock
+ allowed_syscalls.insert(__NR_gettimeofday); // get timeval
}
void AllowGlobalProcessEnvironment()
@@ -705,6 +714,7 @@ public:
void AllowProcessStartOrDeath()
{
allowed_syscalls.insert(__NR_clone); // create a child process
+ allowed_syscalls.insert(__NR_clone3); // create a child process
allowed_syscalls.insert(__NR_exit); // terminate the calling process
allowed_syscalls.insert(__NR_exit_group); // exit all threads in a process
allowed_syscalls.insert(__NR_fork); // create a child process
diff --git a/src/util/system.cpp b/src/util/system.cpp
index 79c08816fa..99d111b066 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -71,6 +71,7 @@
#endif
#include <boost/algorithm/string/replace.hpp>
+#include <optional>
#include <thread>
#include <typeinfo>
#include <univalue.h>
@@ -98,7 +99,7 @@ bool LockDirectory(const fs::path& directory, const std::string lockfile_name, b
fs::path pathLockFile = directory / lockfile_name;
// If a lock for this directory already exists in the map, don't try to re-lock it
- if (dir_locks.count(pathLockFile.string())) {
+ if (dir_locks.count(fs::PathToString(pathLockFile))) {
return true;
}
@@ -107,11 +108,11 @@ bool LockDirectory(const fs::path& directory, const std::string lockfile_name, b
if (file) fclose(file);
auto lock = std::make_unique<fsbridge::FileLock>(pathLockFile);
if (!lock->TryLock()) {
- return error("Error while attempting to lock directory %s: %s", directory.string(), lock->GetReason());
+ return error("Error while attempting to lock directory %s: %s", fs::PathToString(directory), lock->GetReason());
}
if (!probe_only) {
// Lock successful and we're not just probing, put it into the map
- dir_locks.emplace(pathLockFile.string(), std::move(lock));
+ dir_locks.emplace(fs::PathToString(pathLockFile), std::move(lock));
}
return true;
}
@@ -119,7 +120,7 @@ bool LockDirectory(const fs::path& directory, const std::string lockfile_name, b
void UnlockDirectory(const fs::path& directory, const std::string& lockfile_name)
{
LOCK(cs_dir_locks);
- dir_locks.erase((directory / lockfile_name).string());
+ dir_locks.erase(fs::PathToString(directory / lockfile_name));
}
void ReleaseDirectoryLocks()
@@ -182,67 +183,72 @@ static std::string SettingName(const std::string& arg)
return arg.size() > 0 && arg[0] == '-' ? arg.substr(1) : arg;
}
+struct KeyInfo {
+ std::string name;
+ std::string section;
+ bool negated{false};
+};
+
/**
- * Interpret -nofoo as if the user supplied -foo=0.
- *
- * This method also tracks when the -no form was supplied, and if so,
- * checks whether there was a double-negative (-nofoo=0 -> -foo=1).
- *
- * If there was not a double negative, it removes the "no" from the key
- * and returns false.
+ * Parse "name", "section.name", "noname", "section.noname" settings keys.
*
- * If there was a double negative, it removes "no" from the key, and
- * returns true.
- *
- * If there was no "no", it returns the string value untouched.
- *
- * Where an option was negated can be later checked using the
+ * @note Where an option was negated can be later checked using the
* IsArgNegated() method. One use case for this is to have a way to disable
* options that are not normally boolean (e.g. using -nodebuglogfile to request
* that debug log output is not sent to any file at all).
*/
-
-static util::SettingsValue InterpretOption(std::string& section, std::string& key, const std::string& value)
+KeyInfo InterpretKey(std::string key)
{
+ KeyInfo result;
// Split section name from key name for keys like "testnet.foo" or "regtest.bar"
size_t option_index = key.find('.');
if (option_index != std::string::npos) {
- section = key.substr(0, option_index);
+ result.section = key.substr(0, option_index);
key.erase(0, option_index + 1);
}
if (key.substr(0, 2) == "no") {
key.erase(0, 2);
- // Double negatives like -nofoo=0 are supported (but discouraged)
- if (!InterpretBool(value)) {
- LogPrintf("Warning: parsed potentially confusing double-negative -%s=%s\n", key, value);
- return true;
- }
- return false;
+ result.negated = true;
}
- return value;
+ result.name = key;
+ return result;
}
/**
- * Check settings value validity according to flags.
+ * Interpret settings value based on registered flags.
+ *
+ * @param[in] key key information to know if key was negated
+ * @param[in] value string value of setting to be parsed
+ * @param[in] flags ArgsManager registered argument flags
+ * @param[out] error Error description if settings value is not valid
*
- * TODO: Add more meaningful error checks here in the future
- * See "here's how the flags are meant to behave" in
- * https://github.com/bitcoin/bitcoin/pull/16097#issuecomment-514627823
+ * @return parsed settings value if it is valid, otherwise nullopt accompanied
+ * by a descriptive error string
*/
-static bool CheckValid(const std::string& key, const util::SettingsValue& val, unsigned int flags, std::string& error)
-{
- if (val.isBool() && !(flags & ArgsManager::ALLOW_BOOL)) {
- error = strprintf("Negating of -%s is meaningless and therefore forbidden", key);
+static std::optional<util::SettingsValue> InterpretValue(const KeyInfo& key, const std::string& value,
+ unsigned int flags, std::string& error)
+{
+ // Return negated settings as false values.
+ if (key.negated) {
+ if (flags & ArgsManager::DISALLOW_NEGATION) {
+ error = strprintf("Negating of -%s is meaningless and therefore forbidden", key.name);
+ return std::nullopt;
+ }
+ // Double negatives like -nofoo=0 are supported (but discouraged)
+ if (!InterpretBool(value)) {
+ LogPrintf("Warning: parsed potentially confusing double-negative -%s=%s\n", key.name, value);
+ return true;
+ }
return false;
}
- return true;
+ return value;
}
namespace {
fs::path StripRedundantLastElementsOfPath(const fs::path& path)
{
auto result = path;
- while (result.filename().string() == ".") {
+ while (fs::PathToString(result.filename()) == ".") {
result = result.parent_path();
}
@@ -351,21 +357,21 @@ bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::strin
// Transform -foo to foo
key.erase(0, 1);
- std::string section;
- util::SettingsValue value = InterpretOption(section, key, val);
- std::optional<unsigned int> flags = GetArgFlags('-' + key);
+ KeyInfo keyinfo = InterpretKey(key);
+ std::optional<unsigned int> flags = GetArgFlags('-' + keyinfo.name);
// Unknown command line options and command line options with dot
- // characters (which are returned from InterpretOption with nonempty
+ // characters (which are returned from InterpretKey with nonempty
// section strings) are not valid.
- if (!flags || !section.empty()) {
+ if (!flags || !keyinfo.section.empty()) {
error = strprintf("Invalid parameter %s", argv[i]);
return false;
}
- if (!CheckValid(key, value, *flags, error)) return false;
+ std::optional<util::SettingsValue> value = InterpretValue(keyinfo, val, *flags, error);
+ if (!value) return false;
- m_settings.command_line_options[key].push_back(value);
+ m_settings.command_line_options[keyinfo.name].push_back(*value);
}
// we do not allow -includeconf from command line, only -noincludeconf
@@ -402,7 +408,7 @@ const fs::path& ArgsManager::GetBlocksDirPath() const
if (!path.empty()) return path;
if (IsArgSet("-blocksdir")) {
- path = fs::system_complete(GetArg("-blocksdir", ""));
+ path = fs::system_complete(fs::PathFromString(GetArg("-blocksdir", "")));
if (!fs::is_directory(path)) {
path = "";
return path;
@@ -411,7 +417,7 @@ const fs::path& ArgsManager::GetBlocksDirPath() const
path = GetDataDirBase();
}
- path /= BaseParams().DataDir();
+ path /= fs::PathFromString(BaseParams().DataDir());
path /= "blocks";
fs::create_directories(path);
path = StripRedundantLastElementsOfPath(path);
@@ -429,7 +435,7 @@ const fs::path& ArgsManager::GetDataDir(bool net_specific) const
std::string datadir = GetArg("-datadir", "");
if (!datadir.empty()) {
- path = fs::system_complete(datadir);
+ path = fs::system_complete(fs::PathFromString(datadir));
if (!fs::is_directory(path)) {
path = "";
return path;
@@ -438,7 +444,7 @@ const fs::path& ArgsManager::GetDataDir(bool net_specific) const
path = GetDefaultDataDir();
}
if (net_specific)
- path /= BaseParams().DataDir();
+ path /= fs::PathFromString(BaseParams().DataDir());
if (fs::create_directories(path)) {
// This is the first run, create wallets subdirectory too
@@ -517,7 +523,7 @@ bool ArgsManager::GetSettingsPath(fs::path* filepath, bool temp) const
}
if (filepath) {
std::string settings = GetArg("-settings", BITCOIN_SETTINGS_FILENAME);
- *filepath = fsbridge::AbsPathJoin(GetDataDirNet(), temp ? settings + ".tmp" : settings);
+ *filepath = fsbridge::AbsPathJoin(GetDataDirNet(), fs::PathFromString(temp ? settings + ".tmp" : settings));
}
return true;
}
@@ -548,10 +554,8 @@ bool ArgsManager::ReadSettingsFile(std::vector<std::string>* errors)
return false;
}
for (const auto& setting : m_settings.rw_settings) {
- std::string section;
- std::string key = setting.first;
- (void)InterpretOption(section, key, /* value */ {}); // Split setting key into section and argname
- if (!GetArgFlags('-' + key)) {
+ KeyInfo key = InterpretKey(setting.first); // Split setting key into section and argname
+ if (!GetArgFlags('-' + key.name)) {
LogPrintf("Ignoring unknown rw_settings value %s\n", setting.first);
}
}
@@ -572,7 +576,7 @@ bool ArgsManager::WriteSettingsFile(std::vector<std::string>* errors) const
return false;
}
if (!RenameOver(path_tmp, path)) {
- SaveErrors({strprintf("Failed renaming settings file %s to %s\n", path_tmp.string(), path.string())}, errors);
+ SaveErrors({strprintf("Failed renaming settings file %s to %s\n", fs::PathToString(path_tmp), fs::PathToString(path))}, errors);
return false;
}
return true;
@@ -809,12 +813,12 @@ fs::path GetDefaultDataDir()
bool CheckDataDirOption()
{
std::string datadir = gArgs.GetArg("-datadir", "");
- return datadir.empty() || fs::is_directory(fs::system_complete(datadir));
+ return datadir.empty() || fs::is_directory(fs::system_complete(fs::PathFromString(datadir)));
}
fs::path GetConfigFile(const std::string& confPath)
{
- return AbsPathForConfigVal(fs::path(confPath), false);
+ return AbsPathForConfigVal(fs::PathFromString(confPath), false);
}
static bool GetConfigOptions(std::istream& stream, const std::string& filepath, std::string& error, std::vector<std::pair<std::string, std::string>>& options, std::list<SectionInfo>& sections)
@@ -870,15 +874,14 @@ bool ArgsManager::ReadConfigStream(std::istream& stream, const std::string& file
return false;
}
for (const std::pair<std::string, std::string>& option : options) {
- std::string section;
- std::string key = option.first;
- util::SettingsValue value = InterpretOption(section, key, option.second);
- std::optional<unsigned int> flags = GetArgFlags('-' + key);
+ KeyInfo key = InterpretKey(option.first);
+ std::optional<unsigned int> flags = GetArgFlags('-' + key.name);
if (flags) {
- if (!CheckValid(key, value, *flags, error)) {
+ std::optional<util::SettingsValue> value = InterpretValue(key, option.second, *flags, error);
+ if (!value) {
return false;
}
- m_settings.ro_config[section][key].push_back(value);
+ m_settings.ro_config[key.section][key.name].push_back(*value);
} else {
if (ignore_invalid_keys) {
LogPrintf("Ignoring unknown configuration value %s\n", option.first);
@@ -1065,7 +1068,7 @@ bool RenameOver(fs::path src, fs::path dest)
return MoveFileExW(src.wstring().c_str(), dest.wstring().c_str(),
MOVEFILE_REPLACE_EXISTING) != 0;
#else
- int rc = std::rename(src.string().c_str(), dest.string().c_str());
+ int rc = std::rename(src.c_str(), dest.c_str());
return (rc == 0);
#endif /* WIN32 */
}
diff --git a/src/util/system.h b/src/util/system.h
index 2e217f6f90..37d976221b 100644
--- a/src/util/system.h
+++ b/src/util/system.h
@@ -158,12 +158,18 @@ struct SectionInfo
class ArgsManager
{
public:
+ /**
+ * Flags controlling how config and command line arguments are validated and
+ * interpreted.
+ */
enum Flags : uint32_t {
- // Boolean options can accept negation syntax -noOPTION or -noOPTION=1
- ALLOW_BOOL = 0x01,
- ALLOW_INT = 0x02,
- ALLOW_STRING = 0x04,
- ALLOW_ANY = ALLOW_BOOL | ALLOW_INT | ALLOW_STRING,
+ ALLOW_ANY = 0x01, //!< disable validation
+ // ALLOW_BOOL = 0x02, //!< unimplemented, draft implementation in #16545
+ // ALLOW_INT = 0x04, //!< unimplemented, draft implementation in #16545
+ // ALLOW_STRING = 0x08, //!< unimplemented, draft implementation in #16545
+ // ALLOW_LIST = 0x10, //!< unimplemented, draft implementation in #16545
+ DISALLOW_NEGATION = 0x20, //!< disallow -nofoo syntax
+
DEBUG_ONLY = 0x100,
/* Some options would cause cross-contamination if values for
* mainnet were used while running on regtest/testnet (or vice-versa).
diff --git a/src/validation.cpp b/src/validation.cpp
index 14dcd2c24b..207cdc8233 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -219,7 +219,7 @@ bool TestLockPointValidity(CChain& active_chain, const LockPoints* lp)
// If there are relative lock times then the maxInputBlock will be set
// If there are no relative lock times, the LockPoints don't depend on the chain
if (lp->maxInputBlock) {
- // Check whether ::ChainActive() is an extension of the block at which the LockPoints
+ // Check whether active_chain is an extension of the block at which the LockPoints
// calculation was valid. If not LockPoints are no longer valid
if (!active_chain.Contains(lp->maxInputBlock)) {
return false;
@@ -355,7 +355,7 @@ void CChainState::MaybeUpdateMempoolForReorg(
// If the transaction doesn't make it in to the mempool, remove any
// transactions that depend on it (which would now be orphans).
m_mempool->removeRecursive(**it, MemPoolRemovalReason::REORG);
- } else if (m_mempool->exists((*it)->GetHash())) {
+ } else if (m_mempool->exists(GenTxid::Txid((*it)->GetHash()))) {
vHashUpdate.push_back((*it)->GetHash());
}
++it;
@@ -585,10 +585,10 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
if (!CheckFinalTx(m_active_chainstate.m_chain.Tip(), tx, STANDARD_LOCKTIME_VERIFY_FLAGS))
return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "non-final");
- if (m_pool.exists(GenTxid(true, tx.GetWitnessHash()))) {
+ if (m_pool.exists(GenTxid::Wtxid(tx.GetWitnessHash()))) {
// Exact transaction already exists in the mempool.
return state.Invalid(TxValidationResult::TX_CONFLICT, "txn-already-in-mempool");
- } else if (m_pool.exists(GenTxid(false, tx.GetHash()))) {
+ } else if (m_pool.exists(GenTxid::Txid(tx.GetHash()))) {
// Transaction with the same non-witness data but different witness (same txid, different
// wtxid) already exists in the mempool.
return state.Invalid(TxValidationResult::TX_CONFLICT, "txn-same-nonwitness-data-in-mempool");
@@ -908,7 +908,7 @@ bool MemPoolAccept::Finalize(const ATMPArgs& args, Workspace& ws)
// trim mempool and check if tx was trimmed
if (!bypass_limits) {
LimitMempoolSize(m_pool, m_active_chainstate.CoinsTip(), gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, std::chrono::hours{gArgs.GetIntArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)});
- if (!m_pool.exists(hash))
+ if (!m_pool.exists(GenTxid::Txid(hash)))
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "mempool full");
}
return true;
@@ -1236,12 +1236,6 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txund
AddCoins(inputs, tx, nHeight);
}
-void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight)
-{
- CTxUndo txundo;
- UpdateCoins(tx, inputs, txundo, nHeight);
-}
-
bool CScriptCheck::operator()() {
const CScript &scriptSig = ptxTo->vin[nIn].scriptSig;
const CScriptWitness *witness = &ptxTo->vin[nIn].scriptWitness;
@@ -1877,14 +1871,13 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
int64_t nTime5 = GetTimeMicros(); nTimeIndex += nTime5 - nTime4;
LogPrint(BCLog::BENCH, " - Index writing: %.2fms [%.2fs (%.2fms/blk)]\n", MILLI * (nTime5 - nTime4), nTimeIndex * MICRO, nTimeIndex * MILLI / nBlocksTotal);
- TRACE7(validation, block_connected,
- block.GetHash().ToString().c_str(),
+ TRACE6(validation, block_connected,
+ block.GetHash().data(),
pindex->nHeight,
block.vtx.size(),
nInputs,
nSigOpsCost,
- GetTimeMicros() - nTimeStart, // in microseconds (µs)
- block.GetHash().data()
+ GetTimeMicros() - nTimeStart // in microseconds (µs)
);
return true;
@@ -2029,8 +2022,8 @@ bool CChainState::FlushStateToDisk(
}
// Flush best chain related state. This can only be done if the blocks / block index write was also done.
if (fDoFullFlush && !CoinsTip().GetBestBlock().IsNull()) {
- LOG_TIME_SECONDS(strprintf("write coins cache to disk (%d coins, %.2fkB)",
- coins_count, coins_mem_usage / 1000));
+ LOG_TIME_MILLIS_WITH_CATEGORY(strprintf("write coins cache to disk (%d coins, %.2fkB)",
+ coins_count, coins_mem_usage / 1000), BCLog::BENCH);
// Typical Coin structures on disk are around 48 bytes in size.
// Pushing a new one to the database can cause it to be written
@@ -2488,7 +2481,7 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, CBlockIndex
// any disconnected transactions back to the mempool.
MaybeUpdateMempoolForReorg(disconnectpool, true);
}
- if (m_mempool) m_mempool->check(*this);
+ if (m_mempool) m_mempool->check(this->CoinsTip(), this->m_chain.Height() + 1);
CheckForkWarningConditions();
@@ -3205,7 +3198,7 @@ bool BlockManager::AcceptBlockHeader(const CBlockHeader& block, BlockValidationS
if (ppindex)
*ppindex = pindex;
if (pindex->nStatus & BLOCK_FAILED_MASK) {
- LogPrintf("ERROR: %s: block %s is marked invalid\n", __func__, hash.ToString());
+ LogPrint(BCLog::VALIDATION, "%s: block %s is marked invalid\n", __func__, hash.ToString());
return state.Invalid(BlockValidationResult::BLOCK_CACHED_INVALID, "duplicate");
}
return true;
@@ -3220,16 +3213,18 @@ bool BlockManager::AcceptBlockHeader(const CBlockHeader& block, BlockValidationS
CBlockIndex* pindexPrev = nullptr;
BlockMap::iterator mi = m_block_index.find(block.hashPrevBlock);
if (mi == m_block_index.end()) {
- LogPrintf("ERROR: %s: prev block not found\n", __func__);
+ LogPrint(BCLog::VALIDATION, "%s: %s prev block not found\n", __func__, hash.ToString());
return state.Invalid(BlockValidationResult::BLOCK_MISSING_PREV, "prev-blk-not-found");
}
pindexPrev = (*mi).second;
if (pindexPrev->nStatus & BLOCK_FAILED_MASK) {
- LogPrintf("ERROR: %s: prev block invalid\n", __func__);
+ LogPrint(BCLog::VALIDATION, "%s: %s prev block invalid\n", __func__, hash.ToString());
return state.Invalid(BlockValidationResult::BLOCK_INVALID_PREV, "bad-prevblk");
}
- if (!ContextualCheckBlockHeader(block, state, *this, chainparams, pindexPrev, GetAdjustedTime()))
- return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", __func__, hash.ToString(), state.ToString());
+ if (!ContextualCheckBlockHeader(block, state, *this, chainparams, pindexPrev, GetAdjustedTime())) {
+ LogPrint(BCLog::VALIDATION, "%s: Consensus::ContextualCheckBlockHeader: %s, %s\n", __func__, hash.ToString(), state.ToString());
+ return false;
+ }
/* Determine if this block descends from any block which has been found
* invalid (m_failed_blocks), then mark pindexPrev and any blocks between
@@ -3264,7 +3259,7 @@ bool BlockManager::AcceptBlockHeader(const CBlockHeader& block, BlockValidationS
setDirtyBlockIndex.insert(invalid_walk);
invalid_walk = invalid_walk->pprev;
}
- LogPrintf("ERROR: %s: prev block invalid\n", __func__);
+ LogPrint(BCLog::VALIDATION, "%s: %s prev block invalid\n", __func__, hash.ToString());
return state.Invalid(BlockValidationResult::BLOCK_INVALID_PREV, "bad-prevblk");
}
}
@@ -4499,7 +4494,7 @@ bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate, FopenFn mocka
// wallet(s) having loaded it while we were processing
// mempool transactions; consider these as valid, instead of
// failed, but mark them as 'already there'
- if (pool.exists(tx->GetHash())) {
+ if (pool.exists(GenTxid::Txid(tx->GetHash()))) {
++already_there;
} else {
++failed;
diff --git a/src/validation.h b/src/validation.h
index b10050f931..4da8ec8d24 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -75,7 +75,7 @@ static const char* const DEFAULT_BLOCKFILTERINDEX = "0";
static const bool DEFAULT_PERSIST_MEMPOOL = true;
/** Default for -stopatheight */
static const int DEFAULT_STOPATHEIGHT = 0;
-/** Block files containing a block-height within MIN_BLOCKS_TO_KEEP of ::ChainActive().Tip() will not be pruned. */
+/** Block files containing a block-height within MIN_BLOCKS_TO_KEEP of ActiveChain().Tip() will not be pruned. */
static const unsigned int MIN_BLOCKS_TO_KEEP = 288;
static const signed int DEFAULT_CHECKBLOCKS = 6;
static const unsigned int DEFAULT_CHECKLEVEL = 3;
@@ -229,9 +229,6 @@ PackageMempoolAcceptResult ProcessNewPackage(CChainState& active_chainstate, CTx
const Package& txns, bool test_accept)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-/** Apply the effects of this transaction on the UTXO set represented by view */
-void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight);
-
/** Transaction validation functions */
/**
diff --git a/src/wallet/bdb.cpp b/src/wallet/bdb.cpp
index 2eb4d3106c..74fc10ab25 100644
--- a/src/wallet/bdb.cpp
+++ b/src/wallet/bdb.cpp
@@ -61,9 +61,9 @@ bool WalletDatabaseFileId::operator==(const WalletDatabaseFileId& rhs) const
std::shared_ptr<BerkeleyEnvironment> GetBerkeleyEnv(const fs::path& env_directory)
{
LOCK(cs_db);
- auto inserted = g_dbenvs.emplace(env_directory.string(), std::weak_ptr<BerkeleyEnvironment>());
+ auto inserted = g_dbenvs.emplace(fs::PathToString(env_directory), std::weak_ptr<BerkeleyEnvironment>());
if (inserted.second) {
- auto env = std::make_shared<BerkeleyEnvironment>(env_directory.string());
+ auto env = std::make_shared<BerkeleyEnvironment>(env_directory);
inserted.first->second = env;
return env;
}
@@ -101,7 +101,7 @@ void BerkeleyEnvironment::Close()
if (error_file) fclose(error_file);
- UnlockDirectory(strPath, ".walletlock");
+ UnlockDirectory(fs::PathFromString(strPath), ".walletlock");
}
void BerkeleyEnvironment::Reset()
@@ -111,7 +111,7 @@ void BerkeleyEnvironment::Reset()
fMockDb = false;
}
-BerkeleyEnvironment::BerkeleyEnvironment(const fs::path& dir_path) : strPath(dir_path.string())
+BerkeleyEnvironment::BerkeleyEnvironment(const fs::path& dir_path) : strPath(fs::PathToString(dir_path))
{
Reset();
}
@@ -129,24 +129,24 @@ bool BerkeleyEnvironment::Open(bilingual_str& err)
return true;
}
- fs::path pathIn = strPath;
+ fs::path pathIn = fs::PathFromString(strPath);
TryCreateDirectories(pathIn);
if (!LockDirectory(pathIn, ".walletlock")) {
- LogPrintf("Cannot obtain a lock on wallet directory %s. Another instance of bitcoin may be using it.\n", strPath);
- err = strprintf(_("Error initializing wallet database environment %s!"), Directory());
+ LogPrintf("Cannot obtain a lock on wallet directory %s. Another instance may be using it.\n", strPath);
+ err = strprintf(_("Error initializing wallet database environment %s!"), fs::quoted(fs::PathToString(Directory())));
return false;
}
fs::path pathLogDir = pathIn / "database";
TryCreateDirectories(pathLogDir);
fs::path pathErrorFile = pathIn / "db.log";
- LogPrintf("BerkeleyEnvironment::Open: LogDir=%s ErrorFile=%s\n", pathLogDir.string(), pathErrorFile.string());
+ LogPrintf("BerkeleyEnvironment::Open: LogDir=%s ErrorFile=%s\n", fs::PathToString(pathLogDir), fs::PathToString(pathErrorFile));
unsigned int nEnvFlags = 0;
if (gArgs.GetBoolArg("-privdb", DEFAULT_WALLET_PRIVDB))
nEnvFlags |= DB_PRIVATE;
- dbenv->set_lg_dir(pathLogDir.string().c_str());
+ dbenv->set_lg_dir(fs::PathToString(pathLogDir).c_str());
dbenv->set_cachesize(0, 0x100000, 1); // 1 MiB should be enough for just the wallet
dbenv->set_lg_bsize(0x10000);
dbenv->set_lg_max(1048576);
@@ -173,7 +173,7 @@ bool BerkeleyEnvironment::Open(bilingual_str& err)
LogPrintf("BerkeleyEnvironment::Open: Error %d closing failed database environment: %s\n", ret2, DbEnv::strerror(ret2));
}
Reset();
- err = strprintf(_("Error initializing wallet database environment %s!"), Directory());
+ err = strprintf(_("Error initializing wallet database environment %s!"), fs::quoted(fs::PathToString(Directory())));
if (ret == DB_RUNRECOVERY) {
err += Untranslated(" ") + _("This error could occur if this wallet was not shutdown cleanly and was last loaded using a build with a newer version of Berkeley DB. If so, please use the software that last loaded this wallet");
}
@@ -261,7 +261,7 @@ bool BerkeleyDatabase::Verify(bilingual_str& errorStr)
fs::path file_path = walletDir / strFile;
LogPrintf("Using BerkeleyDB version %s\n", BerkeleyDatabaseVersion());
- LogPrintf("Using wallet %s\n", file_path.string());
+ LogPrintf("Using wallet %s\n", fs::PathToString(file_path));
if (!env->Open(errorStr)) {
return false;
@@ -274,7 +274,7 @@ bool BerkeleyDatabase::Verify(bilingual_str& errorStr)
Db db(env->dbenv.get(), 0);
int result = db.verify(strFile.c_str(), nullptr, nullptr, 0);
if (result != 0) {
- errorStr = strprintf(_("%s corrupt. Try using the wallet tool bitcoin-wallet to salvage or restoring a backup."), file_path);
+ errorStr = strprintf(_("%s corrupt. Try using the wallet tool bitcoin-wallet to salvage or restoring a backup."), fs::quoted(fs::PathToString(file_path)));
return false;
}
}
@@ -566,7 +566,7 @@ void BerkeleyEnvironment::Flush(bool fShutdown)
dbenv->log_archive(&listp, DB_ARCH_REMOVE);
Close();
if (!fMockDb) {
- fs::remove_all(fs::path(strPath) / "database");
+ fs::remove_all(fs::PathFromString(strPath) / "database");
}
}
}
@@ -614,21 +614,21 @@ bool BerkeleyDatabase::Backup(const std::string& strDest) const
// Copy wallet file
fs::path pathSrc = env->Directory() / strFile;
- fs::path pathDest(strDest);
+ fs::path pathDest(fs::PathFromString(strDest));
if (fs::is_directory(pathDest))
- pathDest /= strFile;
+ pathDest /= fs::PathFromString(strFile);
try {
if (fs::equivalent(pathSrc, pathDest)) {
- LogPrintf("cannot backup to wallet source file %s\n", pathDest.string());
+ LogPrintf("cannot backup to wallet source file %s\n", fs::PathToString(pathDest));
return false;
}
fs::copy_file(pathSrc, pathDest, fs::copy_option::overwrite_if_exists);
- LogPrintf("copied %s to %s\n", strFile, pathDest.string());
+ LogPrintf("copied %s to %s\n", strFile, fs::PathToString(pathDest));
return true;
} catch (const fs::filesystem_error& e) {
- LogPrintf("error copying %s to %s - %s\n", strFile, pathDest.string(), fsbridge::get_filesystem_error_message(e));
+ LogPrintf("error copying %s to %s - %s\n", strFile, fs::PathToString(pathDest), fsbridge::get_filesystem_error_message(e));
return false;
}
}
@@ -828,10 +828,10 @@ std::unique_ptr<BerkeleyDatabase> MakeBerkeleyDatabase(const fs::path& path, con
std::unique_ptr<BerkeleyDatabase> db;
{
LOCK(cs_db); // Lock env.m_databases until insert in BerkeleyDatabase constructor
- std::string data_filename = data_file.filename().string();
+ std::string data_filename = fs::PathToString(data_file.filename());
std::shared_ptr<BerkeleyEnvironment> env = GetBerkeleyEnv(data_file.parent_path());
if (env->m_databases.count(data_filename)) {
- error = Untranslated(strprintf("Refusing to load database. Data file '%s' is already loaded.", (env->Directory() / data_filename).string()));
+ error = Untranslated(strprintf("Refusing to load database. Data file '%s' is already loaded.", fs::PathToString(env->Directory() / data_filename)));
status = DatabaseStatus::FAILED_ALREADY_LOADED;
return nullptr;
}
diff --git a/src/wallet/bdb.h b/src/wallet/bdb.h
index a8209587d7..b666a8e73a 100644
--- a/src/wallet/bdb.h
+++ b/src/wallet/bdb.h
@@ -63,7 +63,7 @@ public:
bool IsMock() const { return fMockDb; }
bool IsInitialized() const { return fDbEnvInit; }
- fs::path Directory() const { return strPath; }
+ fs::path Directory() const { return fs::PathFromString(strPath); }
bool Open(bilingual_str& error);
void Close();
@@ -141,7 +141,7 @@ public:
bool Verify(bilingual_str& error);
/** Return path to main database filename */
- std::string Filename() override { return (env->Directory() / strFile).string(); }
+ std::string Filename() override { return fs::PathToString(env->Directory() / strFile); }
std::string Format() override { return "bdb"; }
/**
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index 8d5316e0af..305f6c31fc 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -12,7 +12,6 @@
std::vector<fs::path> ListDatabases(const fs::path& wallet_dir)
{
- const size_t offset = wallet_dir.string().size() + (wallet_dir == wallet_dir.root_name() ? 0 : 1);
std::vector<fs::path> paths;
boost::system::error_code ec;
@@ -20,17 +19,15 @@ std::vector<fs::path> ListDatabases(const fs::path& wallet_dir)
if (ec) {
if (fs::is_directory(*it)) {
it.no_push();
- LogPrintf("%s: %s %s -- skipping.\n", __func__, ec.message(), it->path().string());
+ LogPrintf("%s: %s %s -- skipping.\n", __func__, ec.message(), fs::PathToString(it->path()));
} else {
- LogPrintf("%s: %s %s\n", __func__, ec.message(), it->path().string());
+ LogPrintf("%s: %s %s\n", __func__, ec.message(), fs::PathToString(it->path()));
}
continue;
}
try {
- // Get wallet path relative to walletdir by removing walletdir from the wallet path.
- // This can be replaced by boost::filesystem::lexically_relative once boost is bumped to 1.60.
- const fs::path path = it->path().string().substr(offset);
+ const fs::path path{it->path().lexically_relative(wallet_dir)};
if (it->status().type() == fs::directory_file &&
(IsBDBFile(BDBDataFile(it->path())) || IsSQLiteFile(SQLiteDataFile(it->path())))) {
@@ -50,7 +47,7 @@ std::vector<fs::path> ListDatabases(const fs::path& wallet_dir)
}
}
} catch (const std::exception& e) {
- LogPrintf("%s: Error scanning %s: %s\n", __func__, it->path().string(), e.what());
+ LogPrintf("%s: Error scanning %s: %s\n", __func__, fs::PathToString(it->path()), e.what());
it.no_push();
}
}
@@ -85,7 +82,7 @@ bool IsBDBFile(const fs::path& path)
// This check also prevents opening lock files.
boost::system::error_code ec;
auto size = fs::file_size(path, ec);
- if (ec) LogPrintf("%s: %s %s\n", __func__, ec.message(), path.string());
+ if (ec) LogPrintf("%s: %s %s\n", __func__, ec.message(), fs::PathToString(path));
if (size < 4096) return false;
fsbridge::ifstream file(path, std::ios::binary);
@@ -109,7 +106,7 @@ bool IsSQLiteFile(const fs::path& path)
// A SQLite Database file is at least 512 bytes.
boost::system::error_code ec;
auto size = fs::file_size(path, ec);
- if (ec) LogPrintf("%s: %s %s\n", __func__, ec.message(), path.string());
+ if (ec) LogPrintf("%s: %s %s\n", __func__, ec.message(), fs::PathToString(path));
if (size < 512) return false;
fsbridge::ifstream file(path, std::ios::binary);
diff --git a/src/wallet/dump.cpp b/src/wallet/dump.cpp
index c39c0c7e73..08d94b76d9 100644
--- a/src/wallet/dump.cpp
+++ b/src/wallet/dump.cpp
@@ -19,16 +19,16 @@ bool DumpWallet(CWallet& wallet, bilingual_str& error)
return false;
}
- fs::path path = dump_filename;
+ fs::path path = fs::PathFromString(dump_filename);
path = fs::absolute(path);
if (fs::exists(path)) {
- error = strprintf(_("File %s already exists. If you are sure this is what you want, move it out of the way first."), path.string());
+ error = strprintf(_("File %s already exists. If you are sure this is what you want, move it out of the way first."), fs::PathToString(path));
return false;
}
fsbridge::ofstream dump_file;
dump_file.open(path);
if (dump_file.fail()) {
- error = strprintf(_("Unable to open %s for writing"), path.string());
+ error = strprintf(_("Unable to open %s for writing"), fs::PathToString(path));
return false;
}
@@ -114,10 +114,10 @@ bool CreateFromDump(const std::string& name, const fs::path& wallet_path, biling
return false;
}
- fs::path dump_path = dump_filename;
+ fs::path dump_path = fs::PathFromString(dump_filename);
dump_path = fs::absolute(dump_path);
if (!fs::exists(dump_path)) {
- error = strprintf(_("Dump file %s does not exist."), dump_path.string());
+ error = strprintf(_("Dump file %s does not exist."), fs::PathToString(dump_path));
return false;
}
fsbridge::ifstream dump_file(dump_path);
diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp
index 59a59f9794..7a5526a4cb 100644
--- a/src/wallet/init.cpp
+++ b/src/wallet/init.cpp
@@ -84,7 +84,7 @@ void WalletInit::AddWalletOptions(ArgsManager& argsman) const
#endif
#ifdef USE_SQLITE
- argsman.AddArg("-unsafesqlitesync", "Set SQLite synchronous=OFF to disable waiting for the database to sync to disk. This is unsafe and can cause data loss and corruption. This option is only used by tests to improve their performance (default: false)", ArgsManager::ALLOW_BOOL | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST);
+ argsman.AddArg("-unsafesqlitesync", "Set SQLite synchronous=OFF to disable waiting for the database to sync to disk. This is unsafe and can cause data loss and corruption. This option is only used by tests to improve their performance (default: false)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST);
#else
argsman.AddHiddenArgs({"-unsafesqlitesync"});
#endif
diff --git a/src/wallet/interfaces.cpp b/src/wallet/interfaces.cpp
index 7d0cdb6934..57f1a6a67a 100644
--- a/src/wallet/interfaces.cpp
+++ b/src/wallet/interfaces.cpp
@@ -551,13 +551,13 @@ public:
}
std::string getWalletDir() override
{
- return GetWalletDir().string();
+ return fs::PathToString(GetWalletDir());
}
std::vector<std::string> listWalletDir() override
{
std::vector<std::string> paths;
for (auto& path : ListDatabases(GetWalletDir())) {
- paths.push_back(path.string());
+ paths.push_back(fs::PathToString(path));
}
return paths;
}
diff --git a/src/wallet/load.cpp b/src/wallet/load.cpp
index 1b841026b8..7ef5a0cf55 100644
--- a/src/wallet/load.cpp
+++ b/src/wallet/load.cpp
@@ -25,25 +25,25 @@ bool VerifyWallets(WalletContext& context)
ArgsManager& args = *Assert(context.args);
if (args.IsArgSet("-walletdir")) {
- fs::path wallet_dir = args.GetArg("-walletdir", "");
+ fs::path wallet_dir = fs::PathFromString(args.GetArg("-walletdir", ""));
boost::system::error_code error;
// The canonical path cleans the path, preventing >1 Berkeley environment instances for the same directory
fs::path canonical_wallet_dir = fs::canonical(wallet_dir, error);
if (error || !fs::exists(wallet_dir)) {
- chain.initError(strprintf(_("Specified -walletdir \"%s\" does not exist"), wallet_dir.string()));
+ chain.initError(strprintf(_("Specified -walletdir \"%s\" does not exist"), fs::PathToString(wallet_dir)));
return false;
} else if (!fs::is_directory(wallet_dir)) {
- chain.initError(strprintf(_("Specified -walletdir \"%s\" is not a directory"), wallet_dir.string()));
+ chain.initError(strprintf(_("Specified -walletdir \"%s\" is not a directory"), fs::PathToString(wallet_dir)));
return false;
// The canonical path transforms relative paths into absolute ones, so we check the non-canonical version
} else if (!wallet_dir.is_absolute()) {
- chain.initError(strprintf(_("Specified -walletdir \"%s\" is a relative path"), wallet_dir.string()));
+ chain.initError(strprintf(_("Specified -walletdir \"%s\" is a relative path"), fs::PathToString(wallet_dir)));
return false;
}
- args.ForceSetArg("-walletdir", canonical_wallet_dir.string());
+ args.ForceSetArg("-walletdir", fs::PathToString(canonical_wallet_dir));
}
- LogPrintf("Using wallet directory %s\n", GetWalletDir().string());
+ LogPrintf("Using wallet directory %s\n", fs::PathToString(GetWalletDir()));
chain.initMessage(_("Verifying wallet(s)…").translated);
@@ -70,7 +70,7 @@ bool VerifyWallets(WalletContext& context)
for (const auto& wallet : chain.getSettingsList("wallet")) {
const auto& wallet_file = wallet.get_str();
- const fs::path path = fsbridge::AbsPathJoin(GetWalletDir(), wallet_file);
+ const fs::path path = fsbridge::AbsPathJoin(GetWalletDir(), fs::PathFromString(wallet_file));
if (!wallet_paths.insert(path).second) {
chain.initWarning(strprintf(_("Ignoring duplicate -wallet %s."), wallet_file));
@@ -102,7 +102,7 @@ bool LoadWallets(WalletContext& context)
std::set<fs::path> wallet_paths;
for (const auto& wallet : chain.getSettingsList("wallet")) {
const auto& name = wallet.get_str();
- if (!wallet_paths.insert(name).second) {
+ if (!wallet_paths.insert(fs::PathFromString(name)).second) {
continue;
}
DatabaseOptions options;
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index 4d7fb2d38c..1f13b80f3e 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -550,7 +550,7 @@ RPCHelpMan importwallet()
EnsureWalletIsUnlocked(*pwallet);
fsbridge::ifstream file;
- file.open(request.params[0].get_str(), std::ios::in | std::ios::ate);
+ file.open(fs::u8path(request.params[0].get_str()), std::ios::in | std::ios::ate);
if (!file.is_open()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");
}
@@ -745,7 +745,7 @@ RPCHelpMan dumpwallet()
EnsureWalletIsUnlocked(wallet);
- fs::path filepath = request.params[0].get_str();
+ fs::path filepath = fs::u8path(request.params[0].get_str());
filepath = fs::absolute(filepath);
/* Prevent arbitrary files from being overwritten. There have been reports
@@ -754,7 +754,7 @@ RPCHelpMan dumpwallet()
* It may also avoid other security issues.
*/
if (fs::exists(filepath)) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, filepath.string() + " already exists. If you are sure this is what you want, move it out of the way first");
+ throw JSONRPCError(RPC_INVALID_PARAMETER, filepath.u8string() + " already exists. If you are sure this is what you want, move it out of the way first");
}
fsbridge::ofstream file;
@@ -844,7 +844,7 @@ RPCHelpMan dumpwallet()
file.close();
UniValue reply(UniValue::VOBJ);
- reply.pushKV("filename", filepath.string());
+ reply.pushKV("filename", filepath.u8string());
return reply;
},
@@ -1788,7 +1788,7 @@ RPCHelpMan listdescriptors()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
if (!wallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 8b481bc29c..86bfa10d88 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -103,7 +103,7 @@ std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& reques
std::string wallet_name;
if (GetWalletNameFromJSONRPCRequest(request, wallet_name)) {
- std::shared_ptr<CWallet> pwallet = GetWallet(context, wallet_name);
+ const std::shared_ptr<CWallet> pwallet = GetWallet(context, wallet_name);
if (!pwallet) throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded");
return pwallet;
}
@@ -570,7 +570,7 @@ static RPCHelpMan listaddressgroupings()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
// Make sure the results are valid at least up to the most recent block
@@ -627,7 +627,7 @@ static RPCHelpMan signmessage()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
LOCK(pwallet->cs_wallet);
@@ -729,7 +729,7 @@ static RPCHelpMan getreceivedbyaddress()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
// Make sure the results are valid at least up to the most recent block
@@ -767,7 +767,7 @@ static RPCHelpMan getreceivedbylabel()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
// Make sure the results are valid at least up to the most recent block
@@ -807,7 +807,7 @@ static RPCHelpMan getbalance()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
// Make sure the results are valid at least up to the most recent block
@@ -846,7 +846,7 @@ static RPCHelpMan getunconfirmedbalance()
RPCExamples{""},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
// Make sure the results are valid at least up to the most recent block
@@ -1234,7 +1234,7 @@ static RPCHelpMan listreceivedbyaddress()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
// Make sure the results are valid at least up to the most recent block
@@ -1276,7 +1276,7 @@ static RPCHelpMan listreceivedbylabel()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
// Make sure the results are valid at least up to the most recent block
@@ -1389,8 +1389,9 @@ static const std::vector<RPCResult> TransactionDescriptionString()
{
return{{RPCResult::Type::NUM, "confirmations", "The number of confirmations for the transaction. Negative confirmations means the\n"
"transaction conflicted that many blocks ago."},
- {RPCResult::Type::BOOL, "generated", /* optional */ true, "Only present if transaction only input is a coinbase one."},
- {RPCResult::Type::BOOL, "trusted", /* optional */ true, "Only present if we consider transaction to be trusted and so safe to spend from."},
+ {RPCResult::Type::BOOL, "generated", /* optional */ true, "Only present if the transaction's only input is a coinbase one."},
+ {RPCResult::Type::BOOL, "trusted", /* optional */ true, "Whether we consider the transaction to be trusted and safe to spend from.\n"
+ "Only present when the transaction has 0 confirmations (or negative confirmations, if conflicted)."},
{RPCResult::Type::STR_HEX, "blockhash", /* optional */ true, "The block hash containing the transaction."},
{RPCResult::Type::NUM, "blockheight", /* optional */ true, "The block height containing the transaction."},
{RPCResult::Type::NUM, "blockindex", /* optional */ true, "The index of the transaction in the block that includes it."},
@@ -1408,7 +1409,7 @@ static const std::vector<RPCResult> TransactionDescriptionString()
{RPCResult::Type::NUM_TIME, "timereceived", "The time received expressed in " + UNIX_EPOCH_TIME + "."},
{RPCResult::Type::STR, "comment", /* optional */ true, "If a comment is associated with the transaction, only present if not empty."},
{RPCResult::Type::STR, "bip125-replaceable", "(\"yes|no|unknown\") Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n"
- "may be unknown for unconfirmed transactions not in the mempool"}};
+ "may be unknown for unconfirmed transactions not in the mempool."}};
}
static RPCHelpMan listtransactions()
@@ -1460,7 +1461,7 @@ static RPCHelpMan listtransactions()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
// Make sure the results are valid at least up to the most recent block
@@ -1576,7 +1577,7 @@ static RPCHelpMan listsinceblock()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
const CWallet& wallet = *pwallet;
@@ -1717,7 +1718,7 @@ static RPCHelpMan gettransaction()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
// Make sure the results are valid at least up to the most recent block
@@ -1828,7 +1829,7 @@ static RPCHelpMan backupwallet()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
// Make sure the results are valid at least up to the most recent block
@@ -1854,7 +1855,7 @@ static RPCHelpMan keypoolrefill()
"\nFills the keypool."+
HELP_REQUIRING_PASSPHRASE,
{
- {"newsize", RPCArg::Type::NUM, RPCArg::Default{100}, "The new keypool size"},
+ {"newsize", RPCArg::Type::NUM, RPCArg::DefaultHint{strprintf("%u, or as set by -keypool", DEFAULT_KEYPOOL_SIZE)}, "The new keypool size"},
},
RPCResult{RPCResult::Type::NONE, "", ""},
RPCExamples{
@@ -1893,6 +1894,33 @@ static RPCHelpMan keypoolrefill()
}
+static RPCHelpMan newkeypool()
+{
+ return RPCHelpMan{"newkeypool",
+ "\nEntirely clears and refills the keypool."+
+ HELP_REQUIRING_PASSPHRASE,
+ {},
+ RPCResult{RPCResult::Type::NONE, "", ""},
+ RPCExamples{
+ HelpExampleCli("newkeypool", "")
+ + HelpExampleRpc("newkeypool", "")
+ },
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
+
+ LOCK(pwallet->cs_wallet);
+
+ LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet, true);
+ spk_man.NewKeyPool();
+
+ return NullUniValue;
+},
+ };
+}
+
+
static RPCHelpMan walletpassphrase()
{
return RPCHelpMan{"walletpassphrase",
@@ -2303,7 +2331,7 @@ static RPCHelpMan listlockunspent()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
LOCK(pwallet->cs_wallet);
@@ -2396,9 +2424,9 @@ static RPCHelpMan getbalances()
HelpExampleRpc("getbalances", "")},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const rpc_wallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> rpc_wallet = GetWalletForJSONRPCRequest(request);
if (!rpc_wallet) return NullUniValue;
- CWallet& wallet = *rpc_wallet;
+ const CWallet& wallet = *rpc_wallet;
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
@@ -2472,7 +2500,7 @@ static RPCHelpMan getwalletinfo()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
// Make sure the results are valid at least up to the most recent block
@@ -2555,7 +2583,7 @@ static RPCHelpMan listwalletdir()
UniValue wallets(UniValue::VARR);
for (const auto& path : ListDatabases(GetWalletDir())) {
UniValue wallet(UniValue::VOBJ);
- wallet.pushKV("name", path.string());
+ wallet.pushKV("name", path.u8string());
wallets.push_back(wallet);
}
@@ -2743,7 +2771,7 @@ static RPCHelpMan createwallet()
{"blank", RPCArg::Type::BOOL, RPCArg::Default{false}, "Create a blank wallet. A blank wallet has no keys or HD seed. One can be set using sethdseed."},
{"passphrase", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "Encrypt the wallet with this passphrase."},
{"avoid_reuse", RPCArg::Type::BOOL, RPCArg::Default{false}, "Keep track of coin reuse, and treat dirty and clean coins differently with privacy considerations in mind."},
- {"descriptors", RPCArg::Type::BOOL, RPCArg::Default{false}, "Create a native descriptor wallet. The wallet will use descriptors internally to handle address creation"},
+ {"descriptors", RPCArg::Type::BOOL, RPCArg::Default{true}, "Create a native descriptor wallet. The wallet will use descriptors internally to handle address creation"},
{"load_on_startup", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED_NAMED_ARG, "Save wallet name to persistent settings and load on startup. True to add wallet to startup list, false to remove, null to leave unchanged."},
{"external_signer", RPCArg::Type::BOOL, RPCArg::Default{false}, "Use an external signer such as a hardware wallet. Requires -signer to be configured. Wallet creation will fail if keys cannot be fetched. Requires disable_private_keys and descriptors set to true."},
},
@@ -2785,12 +2813,11 @@ static RPCHelpMan createwallet()
if (!request.params[4].isNull() && request.params[4].get_bool()) {
flags |= WALLET_FLAG_AVOID_REUSE;
}
- if (!request.params[5].isNull() && request.params[5].get_bool()) {
+ if (request.params[5].isNull() || request.params[5].get_bool()) {
#ifndef USE_SQLITE
throw JSONRPCError(RPC_WALLET_ERROR, "Compiled without sqlite support (required for descriptor wallets)");
#endif
flags |= WALLET_FLAG_DESCRIPTORS;
- warnings.emplace_back(Untranslated("Wallet is an experimental descriptor wallet"));
}
if (!request.params[7].isNull() && request.params[7].get_bool()) {
#ifdef ENABLE_EXTERNAL_SIGNER
@@ -2813,7 +2840,7 @@ static RPCHelpMan createwallet()
options.create_passphrase = passphrase;
bilingual_str error;
std::optional<bool> load_on_start = request.params[6].isNull() ? std::nullopt : std::optional<bool>(request.params[6].get_bool());
- std::shared_ptr<CWallet> wallet = CreateWallet(context, request.params[0].get_str(), load_on_start, options, status, error, warnings);
+ const std::shared_ptr<CWallet> wallet = CreateWallet(context, request.params[0].get_str(), load_on_start, options, status, error, warnings);
if (!wallet) {
RPCErrorCode code = status == DatabaseStatus::FAILED_ENCRYPT ? RPC_WALLET_ENCRYPTION_FAILED : RPC_WALLET_ERROR;
throw JSONRPCError(code, error.original);
@@ -2856,7 +2883,7 @@ static RPCHelpMan restorewallet()
WalletContext& context = EnsureWalletContext(request.context);
- std::string backup_file = request.params[1].get_str();
+ auto backup_file = fs::u8path(request.params[1].get_str());
if (!fs::exists(backup_file)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Backup file does not exist");
@@ -2864,14 +2891,14 @@ static RPCHelpMan restorewallet()
std::string wallet_name = request.params[0].get_str();
- const fs::path wallet_path = fsbridge::AbsPathJoin(GetWalletDir(), wallet_name);
+ const fs::path wallet_path = fsbridge::AbsPathJoin(GetWalletDir(), fs::u8path(wallet_name));
if (fs::exists(wallet_path)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Wallet name already exists.");
}
if (!TryCreateDirectories(wallet_path)) {
- throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Failed to create database path '%s'. Database already exists.", wallet_path.string()));
+ throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Failed to create database path '%s'. Database already exists.", wallet_path.u8string()));
}
auto wallet_file = wallet_path / "wallet.dat";
@@ -3003,7 +3030,7 @@ static RPCHelpMan listunspent()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
int nMinDepth = 1;
@@ -3566,7 +3593,7 @@ RPCHelpMan signrawtransactionwithwallet()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR, UniValue::VSTR}, true);
@@ -4031,7 +4058,7 @@ RPCHelpMan getaddressinfo()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
LOCK(pwallet->cs_wallet);
@@ -4138,7 +4165,7 @@ static RPCHelpMan getaddressesbylabel()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
LOCK(pwallet->cs_wallet);
@@ -4199,7 +4226,7 @@ static RPCHelpMan listlabels()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
LOCK(pwallet->cs_wallet);
@@ -4528,7 +4555,7 @@ static RPCHelpMan walletprocesspsbt()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
const CWallet& wallet{*pwallet};
@@ -4875,6 +4902,7 @@ static const CRPCCommand commands[] =
{ "wallet", &listwallets, },
{ "wallet", &loadwallet, },
{ "wallet", &lockunspent, },
+ { "wallet", &newkeypool, },
{ "wallet", &removeprunedfunds, },
{ "wallet", &rescanblockchain, },
{ "wallet", &send, },
diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp
index fdfb36bb0a..619ebc8b4f 100644
--- a/src/wallet/scriptpubkeyman.cpp
+++ b/src/wallet/scriptpubkeyman.cpp
@@ -489,7 +489,7 @@ bool LegacyScriptPubKeyMan::Upgrade(int prev_version, int new_version, bilingual
}
// Regenerate the keypool if upgraded to HD
if (hd_upgrade) {
- if (!TopUp()) {
+ if (!NewKeyPool()) {
error = _("Unable to generate keys");
return false;
}
diff --git a/src/wallet/sqlite.cpp b/src/wallet/sqlite.cpp
index 815d17967c..c493b96248 100644
--- a/src/wallet/sqlite.cpp
+++ b/src/wallet/sqlite.cpp
@@ -67,7 +67,7 @@ static void SetPragma(sqlite3* db, const std::string& key, const std::string& va
}
SQLiteDatabase::SQLiteDatabase(const fs::path& dir_path, const fs::path& file_path, bool mock)
- : WalletDatabase(), m_mock(mock), m_dir_path(dir_path.string()), m_file_path(file_path.string())
+ : WalletDatabase(), m_mock(mock), m_dir_path(fs::PathToString(dir_path)), m_file_path(fs::PathToString(file_path))
{
{
LOCK(g_sqlite_mutex);
@@ -206,7 +206,7 @@ void SQLiteDatabase::Open()
if (m_db == nullptr) {
if (!m_mock) {
- TryCreateDirectories(m_dir_path);
+ TryCreateDirectories(fs::PathFromString(m_dir_path));
}
int ret = sqlite3_open_v2(m_file_path.c_str(), &m_db, flags, nullptr);
if (ret != SQLITE_OK) {
@@ -228,7 +228,7 @@ void SQLiteDatabase::Open()
// Now begin a transaction to acquire the exclusive lock. This lock won't be released until we close because of the exclusive locking mode.
int ret = sqlite3_exec(m_db, "BEGIN EXCLUSIVE TRANSACTION", nullptr, nullptr, nullptr);
if (ret != SQLITE_OK) {
- throw std::runtime_error("SQLiteDatabase: Unable to obtain an exclusive lock on the database, is it being used by another bitcoind?\n");
+ throw std::runtime_error("SQLiteDatabase: Unable to obtain an exclusive lock on the database, is it being used by another instance of " PACKAGE_NAME "?\n");
}
ret = sqlite3_exec(m_db, "COMMIT", nullptr, nullptr, nullptr);
if (ret != SQLITE_OK) {
diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp
index f80c4637b8..8606924bb3 100644
--- a/src/wallet/test/coinselector_tests.cpp
+++ b/src/wallet/test/coinselector_tests.cpp
@@ -28,19 +28,9 @@ BOOST_FIXTURE_TEST_SUITE(coinselector_tests, WalletTestingSetup)
typedef std::set<CInputCoin> CoinSet;
-static std::vector<COutput> vCoins;
-static NodeContext testNode;
-static auto testChain = interfaces::MakeChain(testNode);
-static CWallet testWallet(testChain.get(), "", CreateDummyWalletDatabase());
-static CAmount balance = 0;
-
-CoinEligibilityFilter filter_standard(1, 6, 0);
-CoinEligibilityFilter filter_confirmed(1, 1, 0);
-CoinEligibilityFilter filter_standard_extra(6, 6, 0);
-CoinSelectionParams coin_selection_params(/* change_output_size= */ 0,
- /* change_spend_size= */ 0, /* effective_feerate= */ CFeeRate(0),
- /* long_term_feerate= */ CFeeRate(0), /* discard_feerate= */ CFeeRate(0),
- /* tx_noinputs_size= */ 0, /* avoid_partial= */ false);
+static const CoinEligibilityFilter filter_standard(1, 6, 0);
+static const CoinEligibilityFilter filter_confirmed(1, 1, 0);
+static const CoinEligibilityFilter filter_standard_extra(6, 6, 0);
static void add_coin(const CAmount& nValue, int nInput, std::vector<CInputCoin>& set)
{
@@ -62,9 +52,8 @@ static void add_coin(const CAmount& nValue, int nInput, CoinSet& set, CAmount fe
set.insert(coin);
}
-static void add_coin(CWallet& wallet, const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0, bool spendable = false)
+static void add_coin(std::vector<COutput>& coins, CWallet& wallet, const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0, bool spendable = false)
{
- balance += nValue;
static int nextLockTime = 0;
CMutableTransaction tx;
tx.nLockTime = nextLockTime++; // so all transactions get different hashes
@@ -82,24 +71,19 @@ static void add_coin(CWallet& wallet, const CAmount& nValue, int nAge = 6*24, bo
// so stop vin being empty, and cache a non-zero Debit to fake out IsFromMe()
tx.vin.resize(1);
}
- CWalletTx* wtx = wallet.AddToWallet(MakeTransactionRef(std::move(tx)), /* confirm= */ {});
+ uint256 txid = tx.GetHash();
+
+ LOCK(wallet.cs_wallet);
+ auto ret = wallet.mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(txid), std::forward_as_tuple(MakeTransactionRef(std::move(tx))));
+ assert(ret.second);
+ CWalletTx& wtx = (*ret.first).second;
if (fIsFromMe)
{
- wtx->m_amounts[CWalletTx::DEBIT].Set(ISMINE_SPENDABLE, 1);
- wtx->m_is_cache_empty = false;
+ wtx.m_amounts[CWalletTx::DEBIT].Set(ISMINE_SPENDABLE, 1);
+ wtx.m_is_cache_empty = false;
}
- COutput output(wallet, *wtx, nInput, nAge, true /* spendable */, true /* solvable */, true /* safe */);
- vCoins.push_back(output);
-}
-static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0, bool spendable = false)
-{
- add_coin(testWallet, nValue, nAge, fIsFromMe, nInput, spendable);
-}
-
-static void empty_wallet(void)
-{
- vCoins.clear();
- balance = 0;
+ COutput output(wallet, wtx, nInput, nAge, true /* spendable */, true /* solvable */, true /* safe */);
+ coins.push_back(output);
}
static bool equal_sets(CoinSet a, CoinSet b)
@@ -142,20 +126,20 @@ inline std::vector<OutputGroup>& GroupCoins(const std::vector<COutput>& coins)
return static_groups;
}
-inline std::vector<OutputGroup>& KnapsackGroupOutputs(const CoinEligibilityFilter& filter)
+inline std::vector<OutputGroup>& KnapsackGroupOutputs(const std::vector<COutput>& coins, CWallet& wallet, const CoinEligibilityFilter& filter)
{
+ CoinSelectionParams coin_selection_params(/* change_output_size= */ 0,
+ /* change_spend_size= */ 0, /* effective_feerate= */ CFeeRate(0),
+ /* long_term_feerate= */ CFeeRate(0), /* discard_feerate= */ CFeeRate(0),
+ /* tx_noinputs_size= */ 0, /* avoid_partial= */ false);
static std::vector<OutputGroup> static_groups;
- static_groups = GroupOutputs(testWallet, vCoins, coin_selection_params, filter, /* positive_only */false);
+ static_groups = GroupOutputs(wallet, coins, coin_selection_params, filter, /* positive_only */false);
return static_groups;
}
// Branch and bound coin selection tests
BOOST_AUTO_TEST_CASE(bnb_search_test)
{
-
- LOCK(testWallet.cs_wallet);
- testWallet.SetupLegacyScriptPubKeyMan();
-
// Setup
std::vector<CInputCoin> utxo_pool;
CoinSet selection;
@@ -288,196 +272,213 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
/* change_spend_size= */ 0, /* effective_feerate= */ CFeeRate(3000),
/* long_term_feerate= */ CFeeRate(1000), /* discard_feerate= */ CFeeRate(1000),
/* tx_noinputs_size= */ 0, /* avoid_partial= */ false);
- CoinSet setCoinsRet;
- CAmount nValueRet;
- empty_wallet();
- add_coin(1);
- vCoins.at(0).nInputBytes = 40; // Make sure that it has a negative effective value. The next check should assert if this somehow got through. Otherwise it will fail
- BOOST_CHECK(!SelectCoinsBnB(GroupCoins(vCoins), 1 * CENT, coin_selection_params_bnb.m_cost_of_change, setCoinsRet, nValueRet));
-
- // Test fees subtracted from output:
- empty_wallet();
- add_coin(1 * CENT);
- vCoins.at(0).nInputBytes = 40;
- coin_selection_params_bnb.m_subtract_fee_outputs = true;
- BOOST_CHECK(SelectCoinsBnB(GroupCoins(vCoins), 1 * CENT, coin_selection_params_bnb.m_cost_of_change, setCoinsRet, nValueRet));
- BOOST_CHECK_EQUAL(nValueRet, 1 * CENT);
-
- // Make sure that can use BnB when there are preset inputs
- empty_wallet();
{
std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", CreateMockWalletDatabase());
wallet->LoadWallet();
- wallet->SetupLegacyScriptPubKeyMan();
LOCK(wallet->cs_wallet);
- add_coin(*wallet, 5 * CENT, 6 * 24, false, 0, true);
- add_coin(*wallet, 3 * CENT, 6 * 24, false, 0, true);
- add_coin(*wallet, 2 * CENT, 6 * 24, false, 0, true);
+ wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
+ wallet->SetupDescriptorScriptPubKeyMans();
+
+ std::vector<COutput> coins;
+ CoinSet setCoinsRet;
+ CAmount nValueRet;
+
+ add_coin(coins, *wallet, 1);
+ coins.at(0).nInputBytes = 40; // Make sure that it has a negative effective value. The next check should assert if this somehow got through. Otherwise it will fail
+ BOOST_CHECK(!SelectCoinsBnB(GroupCoins(coins), 1 * CENT, coin_selection_params_bnb.m_cost_of_change, setCoinsRet, nValueRet));
+
+ // Test fees subtracted from output:
+ coins.clear();
+ add_coin(coins, *wallet, 1 * CENT);
+ coins.at(0).nInputBytes = 40;
+ coin_selection_params_bnb.m_subtract_fee_outputs = true;
+ BOOST_CHECK(SelectCoinsBnB(GroupCoins(coins), 1 * CENT, coin_selection_params_bnb.m_cost_of_change, setCoinsRet, nValueRet));
+ BOOST_CHECK_EQUAL(nValueRet, 1 * CENT);
+ }
+
+ {
+ std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", CreateMockWalletDatabase());
+ wallet->LoadWallet();
+ LOCK(wallet->cs_wallet);
+ wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
+ wallet->SetupDescriptorScriptPubKeyMans();
+
+ std::vector<COutput> coins;
+ CoinSet setCoinsRet;
+ CAmount nValueRet;
+
+ add_coin(coins, *wallet, 5 * CENT, 6 * 24, false, 0, true);
+ add_coin(coins, *wallet, 3 * CENT, 6 * 24, false, 0, true);
+ add_coin(coins, *wallet, 2 * CENT, 6 * 24, false, 0, true);
CCoinControl coin_control;
coin_control.fAllowOtherInputs = true;
- coin_control.Select(COutPoint(vCoins.at(0).tx->GetHash(), vCoins.at(0).i));
+ coin_control.Select(COutPoint(coins.at(0).tx->GetHash(), coins.at(0).i));
coin_selection_params_bnb.m_effective_feerate = CFeeRate(0);
- BOOST_CHECK(SelectCoins(*wallet, vCoins, 10 * CENT, setCoinsRet, nValueRet, coin_control, coin_selection_params_bnb));
+ BOOST_CHECK(SelectCoins(*wallet, coins, 10 * CENT, setCoinsRet, nValueRet, coin_control, coin_selection_params_bnb));
}
}
BOOST_AUTO_TEST_CASE(knapsack_solver_test)
{
+ std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", CreateMockWalletDatabase());
+ wallet->LoadWallet();
+ LOCK(wallet->cs_wallet);
+ wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
+ wallet->SetupDescriptorScriptPubKeyMans();
+
CoinSet setCoinsRet, setCoinsRet2;
CAmount nValueRet;
-
- LOCK(testWallet.cs_wallet);
- testWallet.SetupLegacyScriptPubKeyMan();
+ std::vector<COutput> coins;
// test multiple times to allow for differences in the shuffle order
for (int i = 0; i < RUN_TESTS; i++)
{
- empty_wallet();
+ coins.clear();
// with an empty wallet we can't even pay one cent
- BOOST_CHECK(!KnapsackSolver(1 * CENT, KnapsackGroupOutputs(filter_standard), setCoinsRet, nValueRet));
+ BOOST_CHECK(!KnapsackSolver(1 * CENT, KnapsackGroupOutputs(coins, *wallet, filter_standard), setCoinsRet, nValueRet));
- add_coin(1*CENT, 4); // add a new 1 cent coin
+ add_coin(coins, *wallet, 1*CENT, 4); // add a new 1 cent coin
// with a new 1 cent coin, we still can't find a mature 1 cent
- BOOST_CHECK(!KnapsackSolver(1 * CENT, KnapsackGroupOutputs(filter_standard), setCoinsRet, nValueRet));
+ BOOST_CHECK(!KnapsackSolver(1 * CENT, KnapsackGroupOutputs(coins, *wallet, filter_standard), setCoinsRet, nValueRet));
// but we can find a new 1 cent
- BOOST_CHECK(KnapsackSolver(1 * CENT, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(1 * CENT, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 1 * CENT);
- add_coin(2*CENT); // add a mature 2 cent coin
+ add_coin(coins, *wallet, 2*CENT); // add a mature 2 cent coin
// we can't make 3 cents of mature coins
- BOOST_CHECK(!KnapsackSolver(3 * CENT, KnapsackGroupOutputs(filter_standard), setCoinsRet, nValueRet));
+ BOOST_CHECK(!KnapsackSolver(3 * CENT, KnapsackGroupOutputs(coins, *wallet, filter_standard), setCoinsRet, nValueRet));
// we can make 3 cents of new coins
- BOOST_CHECK(KnapsackSolver(3 * CENT, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(3 * CENT, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 3 * CENT);
- add_coin(5*CENT); // add a mature 5 cent coin,
- add_coin(10*CENT, 3, true); // a new 10 cent coin sent from one of our own addresses
- add_coin(20*CENT); // and a mature 20 cent coin
+ add_coin(coins, *wallet, 5*CENT); // add a mature 5 cent coin,
+ add_coin(coins, *wallet, 10*CENT, 3, true); // a new 10 cent coin sent from one of our own addresses
+ add_coin(coins, *wallet, 20*CENT); // and a mature 20 cent coin
// now we have new: 1+10=11 (of which 10 was self-sent), and mature: 2+5+20=27. total = 38
// we can't make 38 cents only if we disallow new coins:
- BOOST_CHECK(!KnapsackSolver(38 * CENT, KnapsackGroupOutputs(filter_standard), setCoinsRet, nValueRet));
+ BOOST_CHECK(!KnapsackSolver(38 * CENT, KnapsackGroupOutputs(coins, *wallet, filter_standard), setCoinsRet, nValueRet));
// we can't even make 37 cents if we don't allow new coins even if they're from us
- BOOST_CHECK(!KnapsackSolver(38 * CENT, KnapsackGroupOutputs(filter_standard_extra), setCoinsRet, nValueRet));
+ BOOST_CHECK(!KnapsackSolver(38 * CENT, KnapsackGroupOutputs(coins, *wallet, filter_standard_extra), setCoinsRet, nValueRet));
// but we can make 37 cents if we accept new coins from ourself
- BOOST_CHECK(KnapsackSolver(37 * CENT, KnapsackGroupOutputs(filter_standard), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(37 * CENT, KnapsackGroupOutputs(coins, *wallet, filter_standard), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 37 * CENT);
// and we can make 38 cents if we accept all new coins
- BOOST_CHECK(KnapsackSolver(38 * CENT, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(38 * CENT, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 38 * CENT);
// try making 34 cents from 1,2,5,10,20 - we can't do it exactly
- BOOST_CHECK(KnapsackSolver(34 * CENT, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(34 * CENT, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 35 * CENT); // but 35 cents is closest
BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U); // the best should be 20+10+5. it's incredibly unlikely the 1 or 2 got included (but possible)
// when we try making 7 cents, the smaller coins (1,2,5) are enough. We should see just 2+5
- BOOST_CHECK(KnapsackSolver(7 * CENT, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(7 * CENT, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 7 * CENT);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U);
// when we try making 8 cents, the smaller coins (1,2,5) are exactly enough.
- BOOST_CHECK(KnapsackSolver(8 * CENT, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(8 * CENT, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
BOOST_CHECK(nValueRet == 8 * CENT);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U);
// when we try making 9 cents, no subset of smaller coins is enough, and we get the next bigger coin (10)
- BOOST_CHECK(KnapsackSolver(9 * CENT, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(9 * CENT, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 10 * CENT);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
// now clear out the wallet and start again to test choosing between subsets of smaller coins and the next biggest coin
- empty_wallet();
+ coins.clear();
- add_coin( 6*CENT);
- add_coin( 7*CENT);
- add_coin( 8*CENT);
- add_coin(20*CENT);
- add_coin(30*CENT); // now we have 6+7+8+20+30 = 71 cents total
+ add_coin(coins, *wallet, 6*CENT);
+ add_coin(coins, *wallet, 7*CENT);
+ add_coin(coins, *wallet, 8*CENT);
+ add_coin(coins, *wallet, 20*CENT);
+ add_coin(coins, *wallet, 30*CENT); // now we have 6+7+8+20+30 = 71 cents total
// check that we have 71 and not 72
- BOOST_CHECK(KnapsackSolver(71 * CENT, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
- BOOST_CHECK(!KnapsackSolver(72 * CENT, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(71 * CENT, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
+ BOOST_CHECK(!KnapsackSolver(72 * CENT, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
// now try making 16 cents. the best smaller coins can do is 6+7+8 = 21; not as good at the next biggest coin, 20
- BOOST_CHECK(KnapsackSolver(16 * CENT, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(16 * CENT, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 20 * CENT); // we should get 20 in one coin
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
- add_coin( 5*CENT); // now we have 5+6+7+8+20+30 = 75 cents total
+ add_coin(coins, *wallet, 5*CENT); // now we have 5+6+7+8+20+30 = 75 cents total
// now if we try making 16 cents again, the smaller coins can make 5+6+7 = 18 cents, better than the next biggest coin, 20
- BOOST_CHECK(KnapsackSolver(16 * CENT, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(16 * CENT, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 18 * CENT); // we should get 18 in 3 coins
BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U);
- add_coin( 18*CENT); // now we have 5+6+7+8+18+20+30
+ add_coin(coins, *wallet, 18*CENT); // now we have 5+6+7+8+18+20+30
// and now if we try making 16 cents again, the smaller coins can make 5+6+7 = 18 cents, the same as the next biggest coin, 18
- BOOST_CHECK(KnapsackSolver(16 * CENT, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(16 * CENT, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 18 * CENT); // we should get 18 in 1 coin
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); // because in the event of a tie, the biggest coin wins
// now try making 11 cents. we should get 5+6
- BOOST_CHECK(KnapsackSolver(11 * CENT, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(11 * CENT, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 11 * CENT);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U);
// check that the smallest bigger coin is used
- add_coin( 1*COIN);
- add_coin( 2*COIN);
- add_coin( 3*COIN);
- add_coin( 4*COIN); // now we have 5+6+7+8+18+20+30+100+200+300+400 = 1094 cents
- BOOST_CHECK(KnapsackSolver(95 * CENT, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ add_coin(coins, *wallet, 1*COIN);
+ add_coin(coins, *wallet, 2*COIN);
+ add_coin(coins, *wallet, 3*COIN);
+ add_coin(coins, *wallet, 4*COIN); // now we have 5+6+7+8+18+20+30+100+200+300+400 = 1094 cents
+ BOOST_CHECK(KnapsackSolver(95 * CENT, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 1 * COIN); // we should get 1 BTC in 1 coin
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
- BOOST_CHECK(KnapsackSolver(195 * CENT, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(195 * CENT, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 2 * COIN); // we should get 2 BTC in 1 coin
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
// empty the wallet and start again, now with fractions of a cent, to test small change avoidance
- empty_wallet();
- add_coin(MIN_CHANGE * 1 / 10);
- add_coin(MIN_CHANGE * 2 / 10);
- add_coin(MIN_CHANGE * 3 / 10);
- add_coin(MIN_CHANGE * 4 / 10);
- add_coin(MIN_CHANGE * 5 / 10);
+ coins.clear();
+ add_coin(coins, *wallet, MIN_CHANGE * 1 / 10);
+ add_coin(coins, *wallet, MIN_CHANGE * 2 / 10);
+ add_coin(coins, *wallet, MIN_CHANGE * 3 / 10);
+ add_coin(coins, *wallet, MIN_CHANGE * 4 / 10);
+ add_coin(coins, *wallet, MIN_CHANGE * 5 / 10);
// try making 1 * MIN_CHANGE from the 1.5 * MIN_CHANGE
// we'll get change smaller than MIN_CHANGE whatever happens, so can expect MIN_CHANGE exactly
- BOOST_CHECK(KnapsackSolver(MIN_CHANGE, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(MIN_CHANGE, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, MIN_CHANGE);
// but if we add a bigger coin, small change is avoided
- add_coin(1111*MIN_CHANGE);
+ add_coin(coins, *wallet, 1111*MIN_CHANGE);
// try making 1 from 0.1 + 0.2 + 0.3 + 0.4 + 0.5 + 1111 = 1112.5
- BOOST_CHECK(KnapsackSolver(1 * MIN_CHANGE, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(1 * MIN_CHANGE, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 1 * MIN_CHANGE); // we should get the exact amount
// if we add more small coins:
- add_coin(MIN_CHANGE * 6 / 10);
- add_coin(MIN_CHANGE * 7 / 10);
+ add_coin(coins, *wallet, MIN_CHANGE * 6 / 10);
+ add_coin(coins, *wallet, MIN_CHANGE * 7 / 10);
// and try again to make 1.0 * MIN_CHANGE
- BOOST_CHECK(KnapsackSolver(1 * MIN_CHANGE, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(1 * MIN_CHANGE, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 1 * MIN_CHANGE); // we should get the exact amount
// run the 'mtgox' test (see https://blockexplorer.com/tx/29a3efd3ef04f9153d47a990bd7b048a4b2d213daaa5fb8ed670fb85f13bdbcf)
// they tried to consolidate 10 50k coins into one 500k coin, and ended up with 50k in change
- empty_wallet();
+ coins.clear();
for (int j = 0; j < 20; j++)
- add_coin(50000 * COIN);
+ add_coin(coins, *wallet, 50000 * COIN);
- BOOST_CHECK(KnapsackSolver(500000 * COIN, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(500000 * COIN, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 500000 * COIN); // we should get the exact amount
BOOST_CHECK_EQUAL(setCoinsRet.size(), 10U); // in ten coins
@@ -485,79 +486,79 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
// we need to try finding an exact subset anyway
// sometimes it will fail, and so we use the next biggest coin:
- empty_wallet();
- add_coin(MIN_CHANGE * 5 / 10);
- add_coin(MIN_CHANGE * 6 / 10);
- add_coin(MIN_CHANGE * 7 / 10);
- add_coin(1111 * MIN_CHANGE);
- BOOST_CHECK(KnapsackSolver(1 * MIN_CHANGE, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ coins.clear();
+ add_coin(coins, *wallet, MIN_CHANGE * 5 / 10);
+ add_coin(coins, *wallet, MIN_CHANGE * 6 / 10);
+ add_coin(coins, *wallet, MIN_CHANGE * 7 / 10);
+ add_coin(coins, *wallet, 1111 * MIN_CHANGE);
+ BOOST_CHECK(KnapsackSolver(1 * MIN_CHANGE, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 1111 * MIN_CHANGE); // we get the bigger coin
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
// but sometimes it's possible, and we use an exact subset (0.4 + 0.6 = 1.0)
- empty_wallet();
- add_coin(MIN_CHANGE * 4 / 10);
- add_coin(MIN_CHANGE * 6 / 10);
- add_coin(MIN_CHANGE * 8 / 10);
- add_coin(1111 * MIN_CHANGE);
- BOOST_CHECK(KnapsackSolver(MIN_CHANGE, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ coins.clear();
+ add_coin(coins, *wallet, MIN_CHANGE * 4 / 10);
+ add_coin(coins, *wallet, MIN_CHANGE * 6 / 10);
+ add_coin(coins, *wallet, MIN_CHANGE * 8 / 10);
+ add_coin(coins, *wallet, 1111 * MIN_CHANGE);
+ BOOST_CHECK(KnapsackSolver(MIN_CHANGE, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, MIN_CHANGE); // we should get the exact amount
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); // in two coins 0.4+0.6
// test avoiding small change
- empty_wallet();
- add_coin(MIN_CHANGE * 5 / 100);
- add_coin(MIN_CHANGE * 1);
- add_coin(MIN_CHANGE * 100);
+ coins.clear();
+ add_coin(coins, *wallet, MIN_CHANGE * 5 / 100);
+ add_coin(coins, *wallet, MIN_CHANGE * 1);
+ add_coin(coins, *wallet, MIN_CHANGE * 100);
// trying to make 100.01 from these three coins
- BOOST_CHECK(KnapsackSolver(MIN_CHANGE * 10001 / 100, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(MIN_CHANGE * 10001 / 100, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, MIN_CHANGE * 10105 / 100); // we should get all coins
BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U);
// but if we try to make 99.9, we should take the bigger of the two small coins to avoid small change
- BOOST_CHECK(KnapsackSolver(MIN_CHANGE * 9990 / 100, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(MIN_CHANGE * 9990 / 100, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 101 * MIN_CHANGE);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U);
- }
-
- // test with many inputs
- for (CAmount amt=1500; amt < COIN; amt*=10) {
- empty_wallet();
- // Create 676 inputs (= (old MAX_STANDARD_TX_SIZE == 100000) / 148 bytes per input)
- for (uint16_t j = 0; j < 676; j++)
- add_coin(amt);
-
- // We only create the wallet once to save time, but we still run the coin selection RUN_TESTS times.
- for (int i = 0; i < RUN_TESTS; i++) {
- BOOST_CHECK(KnapsackSolver(2000, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
-
- if (amt - 2000 < MIN_CHANGE) {
- // needs more than one input:
- uint16_t returnSize = std::ceil((2000.0 + MIN_CHANGE)/amt);
- CAmount returnValue = amt * returnSize;
- BOOST_CHECK_EQUAL(nValueRet, returnValue);
- BOOST_CHECK_EQUAL(setCoinsRet.size(), returnSize);
- } else {
- // one input is sufficient:
- BOOST_CHECK_EQUAL(nValueRet, amt);
- BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
- }
- }
- }
-
- // test randomness
- {
- empty_wallet();
- for (int i2 = 0; i2 < 100; i2++)
- add_coin(COIN);
-
- // Again, we only create the wallet once to save time, but we still run the coin selection RUN_TESTS times.
- for (int i = 0; i < RUN_TESTS; i++) {
+ }
+
+ // test with many inputs
+ for (CAmount amt=1500; amt < COIN; amt*=10) {
+ coins.clear();
+ // Create 676 inputs (= (old MAX_STANDARD_TX_SIZE == 100000) / 148 bytes per input)
+ for (uint16_t j = 0; j < 676; j++)
+ add_coin(coins, *wallet, amt);
+
+ // We only create the wallet once to save time, but we still run the coin selection RUN_TESTS times.
+ for (int i = 0; i < RUN_TESTS; i++) {
+ BOOST_CHECK(KnapsackSolver(2000, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
+
+ if (amt - 2000 < MIN_CHANGE) {
+ // needs more than one input:
+ uint16_t returnSize = std::ceil((2000.0 + MIN_CHANGE)/amt);
+ CAmount returnValue = amt * returnSize;
+ BOOST_CHECK_EQUAL(nValueRet, returnValue);
+ BOOST_CHECK_EQUAL(setCoinsRet.size(), returnSize);
+ } else {
+ // one input is sufficient:
+ BOOST_CHECK_EQUAL(nValueRet, amt);
+ BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
+ }
+ }
+ }
+
+ // test randomness
+ {
+ coins.clear();
+ for (int i2 = 0; i2 < 100; i2++)
+ add_coin(coins, *wallet, COIN);
+
+ // Again, we only create the wallet once to save time, but we still run the coin selection RUN_TESTS times.
+ for (int i = 0; i < RUN_TESTS; i++) {
// picking 50 from 100 coins doesn't depend on the shuffle,
// but does depend on randomness in the stochastic approximation code
- BOOST_CHECK(KnapsackSolver(50 * COIN, GroupCoins(vCoins), setCoinsRet, nValueRet));
- BOOST_CHECK(KnapsackSolver(50 * COIN, GroupCoins(vCoins), setCoinsRet2, nValueRet));
+ BOOST_CHECK(KnapsackSolver(50 * COIN, GroupCoins(coins), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(50 * COIN, GroupCoins(coins), setCoinsRet2, nValueRet));
BOOST_CHECK(!equal_sets(setCoinsRet, setCoinsRet2));
int fails = 0;
@@ -567,66 +568,67 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
// When choosing 1 from 100 identical coins, 1% of the time, this test will choose the same coin twice
// which will cause it to fail.
// To avoid that issue, run the test RANDOM_REPEATS times and only complain if all of them fail
- BOOST_CHECK(KnapsackSolver(COIN, GroupCoins(vCoins), setCoinsRet, nValueRet));
- BOOST_CHECK(KnapsackSolver(COIN, GroupCoins(vCoins), setCoinsRet2, nValueRet));
+ BOOST_CHECK(KnapsackSolver(COIN, GroupCoins(coins), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(COIN, GroupCoins(coins), setCoinsRet2, nValueRet));
if (equal_sets(setCoinsRet, setCoinsRet2))
fails++;
}
BOOST_CHECK_NE(fails, RANDOM_REPEATS);
- }
-
- // add 75 cents in small change. not enough to make 90 cents,
- // then try making 90 cents. there are multiple competing "smallest bigger" coins,
- // one of which should be picked at random
- add_coin(5 * CENT);
- add_coin(10 * CENT);
- add_coin(15 * CENT);
- add_coin(20 * CENT);
- add_coin(25 * CENT);
-
- for (int i = 0; i < RUN_TESTS; i++) {
+ }
+
+ // add 75 cents in small change. not enough to make 90 cents,
+ // then try making 90 cents. there are multiple competing "smallest bigger" coins,
+ // one of which should be picked at random
+ add_coin(coins, *wallet, 5 * CENT);
+ add_coin(coins, *wallet, 10 * CENT);
+ add_coin(coins, *wallet, 15 * CENT);
+ add_coin(coins, *wallet, 20 * CENT);
+ add_coin(coins, *wallet, 25 * CENT);
+
+ for (int i = 0; i < RUN_TESTS; i++) {
int fails = 0;
for (int j = 0; j < RANDOM_REPEATS; j++)
{
- BOOST_CHECK(KnapsackSolver(90*CENT, GroupCoins(vCoins), setCoinsRet, nValueRet));
- BOOST_CHECK(KnapsackSolver(90*CENT, GroupCoins(vCoins), setCoinsRet2, nValueRet));
+ BOOST_CHECK(KnapsackSolver(90*CENT, GroupCoins(coins), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(90*CENT, GroupCoins(coins), setCoinsRet2, nValueRet));
if (equal_sets(setCoinsRet, setCoinsRet2))
fails++;
}
BOOST_CHECK_NE(fails, RANDOM_REPEATS);
- }
- }
-
- empty_wallet();
+ }
+ }
}
BOOST_AUTO_TEST_CASE(ApproximateBestSubset)
{
+ std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", CreateMockWalletDatabase());
+ wallet->LoadWallet();
+ LOCK(wallet->cs_wallet);
+ wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
+ wallet->SetupDescriptorScriptPubKeyMans();
+
CoinSet setCoinsRet;
CAmount nValueRet;
-
- LOCK(testWallet.cs_wallet);
- testWallet.SetupLegacyScriptPubKeyMan();
-
- empty_wallet();
+ std::vector<COutput> coins;
// Test vValue sort order
for (int i = 0; i < 1000; i++)
- add_coin(1000 * COIN);
- add_coin(3 * COIN);
+ add_coin(coins, *wallet, 1000 * COIN);
+ add_coin(coins, *wallet, 3 * COIN);
- BOOST_CHECK(KnapsackSolver(1003 * COIN, KnapsackGroupOutputs(filter_standard), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(1003 * COIN, KnapsackGroupOutputs(coins, *wallet, filter_standard), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 1003 * COIN);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U);
-
- empty_wallet();
}
// Tests that with the ideal conditions, the coin selector will always be able to find a solution that can pay the target value
BOOST_AUTO_TEST_CASE(SelectCoins_test)
{
- LOCK(testWallet.cs_wallet);
- testWallet.SetupLegacyScriptPubKeyMan();
+ std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", CreateMockWalletDatabase());
+ wallet->LoadWallet();
+ LOCK(wallet->cs_wallet);
+ wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
+ wallet->SetupDescriptorScriptPubKeyMans();
// Random generator stuff
std::default_random_engine generator;
@@ -636,12 +638,15 @@ BOOST_AUTO_TEST_CASE(SelectCoins_test)
// Run this test 100 times
for (int i = 0; i < 100; ++i)
{
- empty_wallet();
+ std::vector<COutput> coins;
+ CAmount balance{0};
// Make a wallet with 1000 exponentially distributed random inputs
for (int j = 0; j < 1000; ++j)
{
- add_coin((CAmount)(distribution(generator)*10000000));
+ CAmount val = distribution(generator)*10000000;
+ add_coin(coins, *wallet, val);
+ balance += val;
}
// Generate a random fee rate in the range of 100 - 400
@@ -658,7 +663,7 @@ BOOST_AUTO_TEST_CASE(SelectCoins_test)
CoinSet out_set;
CAmount out_value = 0;
CCoinControl cc;
- BOOST_CHECK(SelectCoins(testWallet, vCoins, target, out_set, out_value, cc, cs_params));
+ BOOST_CHECK(SelectCoins(*wallet, coins, target, out_set, out_value, cc, cs_params));
BOOST_CHECK_GE(out_value, target);
}
}
diff --git a/src/wallet/test/db_tests.cpp b/src/wallet/test/db_tests.cpp
index 16cb7e0baf..dba3f35025 100644
--- a/src/wallet/test/db_tests.cpp
+++ b/src/wallet/test/db_tests.cpp
@@ -16,7 +16,7 @@ BOOST_FIXTURE_TEST_SUITE(db_tests, BasicTestingSetup)
static std::shared_ptr<BerkeleyEnvironment> GetWalletEnv(const fs::path& path, std::string& database_filename)
{
fs::path data_file = BDBDataFile(path);
- database_filename = data_file.filename().string();
+ database_filename = fs::PathToString(data_file.filename());
return GetBerkeleyEnv(data_file.parent_path());
}
@@ -25,11 +25,7 @@ BOOST_AUTO_TEST_CASE(getwalletenv_file)
std::string test_name = "test_name.dat";
const fs::path datadir = gArgs.GetDataDirNet();
fs::path file_path = datadir / test_name;
-#if BOOST_VERSION >= 107700
- std::ofstream f(BOOST_FILESYSTEM_C_STR(file_path));
-#else
- std::ofstream f(file_path.BOOST_FILESYSTEM_C_STR);
-#endif // BOOST_VERSION >= 107700
+ fs::ofstream f(file_path);
f.close();
std::string filename;
diff --git a/src/wallet/test/init_test_fixture.cpp b/src/wallet/test/init_test_fixture.cpp
index 53c972c46d..170675c035 100644
--- a/src/wallet/test/init_test_fixture.cpp
+++ b/src/wallet/test/init_test_fixture.cpp
@@ -32,11 +32,7 @@ InitWalletDirTestingSetup::InitWalletDirTestingSetup(const std::string& chainNam
fs::create_directories(m_walletdir_path_cases["default"]);
fs::create_directories(m_walletdir_path_cases["custom"]);
fs::create_directories(m_walletdir_path_cases["relative"]);
-#if BOOST_VERSION >= 107700
- std::ofstream f(BOOST_FILESYSTEM_C_STR(m_walletdir_path_cases["file"]));
-#else
- std::ofstream f(m_walletdir_path_cases["file"].BOOST_FILESYSTEM_C_STR);
-#endif // BOOST_VERSION >= 107700
+ fs::ofstream f(m_walletdir_path_cases["file"]);
f.close();
}
@@ -50,5 +46,5 @@ InitWalletDirTestingSetup::~InitWalletDirTestingSetup()
void InitWalletDirTestingSetup::SetWalletDir(const fs::path& walletdir_path)
{
- gArgs.ForceSetArg("-walletdir", walletdir_path.string());
+ gArgs.ForceSetArg("-walletdir", fs::PathToString(walletdir_path));
}
diff --git a/src/wallet/test/init_tests.cpp b/src/wallet/test/init_tests.cpp
index 45e1b8c4b8..222c2bf4b7 100644
--- a/src/wallet/test/init_tests.cpp
+++ b/src/wallet/test/init_tests.cpp
@@ -17,7 +17,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_default)
SetWalletDir(m_walletdir_path_cases["default"]);
bool result = m_wallet_client->verify();
BOOST_CHECK(result == true);
- fs::path walletdir = gArgs.GetArg("-walletdir", "");
+ fs::path walletdir = fs::PathFromString(gArgs.GetArg("-walletdir", ""));
fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]);
BOOST_CHECK_EQUAL(walletdir, expected_path);
}
@@ -27,7 +27,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_custom)
SetWalletDir(m_walletdir_path_cases["custom"]);
bool result = m_wallet_client->verify();
BOOST_CHECK(result == true);
- fs::path walletdir = gArgs.GetArg("-walletdir", "");
+ fs::path walletdir = fs::PathFromString(gArgs.GetArg("-walletdir", ""));
fs::path expected_path = fs::canonical(m_walletdir_path_cases["custom"]);
BOOST_CHECK_EQUAL(walletdir, expected_path);
}
@@ -67,7 +67,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing)
SetWalletDir(m_walletdir_path_cases["trailing"]);
bool result = m_wallet_client->verify();
BOOST_CHECK(result == true);
- fs::path walletdir = gArgs.GetArg("-walletdir", "");
+ fs::path walletdir = fs::PathFromString(gArgs.GetArg("-walletdir", ""));
fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]);
BOOST_CHECK_EQUAL(walletdir, expected_path);
}
@@ -77,7 +77,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing2)
SetWalletDir(m_walletdir_path_cases["trailing2"]);
bool result = m_wallet_client->verify();
BOOST_CHECK(result == true);
- fs::path walletdir = gArgs.GetArg("-walletdir", "");
+ fs::path walletdir = fs::PathFromString(gArgs.GetArg("-walletdir", ""));
fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]);
BOOST_CHECK_EQUAL(walletdir, expected_path);
}
diff --git a/src/wallet/test/psbt_wallet_tests.cpp b/src/wallet/test/psbt_wallet_tests.cpp
index 8a97f7779d..dd24fa2c19 100644
--- a/src/wallet/test/psbt_wallet_tests.cpp
+++ b/src/wallet/test/psbt_wallet_tests.cpp
@@ -13,10 +13,22 @@
BOOST_FIXTURE_TEST_SUITE(psbt_wallet_tests, WalletTestingSetup)
+static void import_descriptor(CWallet& wallet, const std::string& descriptor)
+ EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
+{
+ AssertLockHeld(wallet.cs_wallet);
+ FlatSigningProvider provider;
+ std::string error;
+ std::unique_ptr<Descriptor> desc = Parse(descriptor, provider, error, /* require_checksum=*/ false);
+ assert(desc);
+ WalletDescriptor w_desc(std::move(desc), 0, 0, 10, 0);
+ wallet.AddWalletDescriptor(w_desc, provider, "", false);
+}
+
BOOST_AUTO_TEST_CASE(psbt_updater_test)
{
- auto spk_man = m_wallet.GetOrCreateLegacyScriptPubKeyMan();
- LOCK2(m_wallet.cs_wallet, spk_man->cs_KeyStore);
+ LOCK(m_wallet.cs_wallet);
+ m_wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
// Create prevtxs and add to wallet
CDataStream s_prev_tx1(ParseHex("0200000000010158e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd7501000000171600145f275f436b09a8cc9a2eb2a2f528485c68a56323feffffff02d8231f1b0100000017a914aed962d6654f9a2b36608eb9d64d2b260db4f1118700c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e88702483045022100a22edcc6e5bc511af4cc4ae0de0fcd75c7e04d8c1c3a8aa9d820ed4b967384ec02200642963597b9b1bc22c75e9f3e117284a962188bf5e8a74c895089046a20ad770121035509a48eb623e10aace8bfd0212fdb8a8e5af3c94b0b133b95e114cab89e4f7965000000"), SER_NETWORK, PROTOCOL_VERSION);
@@ -29,27 +41,10 @@ BOOST_AUTO_TEST_CASE(psbt_updater_test)
s_prev_tx2 >> prev_tx2;
m_wallet.mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(prev_tx2->GetHash()), std::forward_as_tuple(prev_tx2));
- // Add scripts
- CScript rs1;
- CDataStream s_rs1(ParseHex("475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae"), SER_NETWORK, PROTOCOL_VERSION);
- s_rs1 >> rs1;
- spk_man->AddCScript(rs1);
-
- CScript rs2;
- CDataStream s_rs2(ParseHex("2200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903"), SER_NETWORK, PROTOCOL_VERSION);
- s_rs2 >> rs2;
- spk_man->AddCScript(rs2);
-
- CScript ws1;
- CDataStream s_ws1(ParseHex("47522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae"), SER_NETWORK, PROTOCOL_VERSION);
- s_ws1 >> ws1;
- spk_man->AddCScript(ws1);
-
- // Add hd seed
- CKey key = DecodeSecret("5KSSJQ7UNfFGwVgpCZDSHm5rVNhMFcFtvWM3zQ8mW4qNDEN7LFd"); // Mainnet and uncompressed form of cUkG8i1RFfWGWy5ziR11zJ5V4U4W3viSFCfyJmZnvQaUsd1xuF3T
- CPubKey master_pub_key = spk_man->DeriveNewSeed(key);
- spk_man->SetHDSeed(master_pub_key);
- spk_man->NewKeyPool();
+ // Import descriptors for keys and scripts
+ import_descriptor(m_wallet, "sh(multi(2,xprv9s21ZrQH143K2LE7W4Xf3jATf9jECxSb7wj91ZnmY4qEJrS66Qru9RFqq8xbkgT32ya6HqYJweFdJUEDf5Q6JFV7jMiUws7kQfe6Tv4RbfN/0h/0h/0h,xprv9s21ZrQH143K2LE7W4Xf3jATf9jECxSb7wj91ZnmY4qEJrS66Qru9RFqq8xbkgT32ya6HqYJweFdJUEDf5Q6JFV7jMiUws7kQfe6Tv4RbfN/0h/0h/1h))");
+ import_descriptor(m_wallet, "sh(wsh(multi(2,xprv9s21ZrQH143K2LE7W4Xf3jATf9jECxSb7wj91ZnmY4qEJrS66Qru9RFqq8xbkgT32ya6HqYJweFdJUEDf5Q6JFV7jMiUws7kQfe6Tv4RbfN/0h/0h/2h,xprv9s21ZrQH143K2LE7W4Xf3jATf9jECxSb7wj91ZnmY4qEJrS66Qru9RFqq8xbkgT32ya6HqYJweFdJUEDf5Q6JFV7jMiUws7kQfe6Tv4RbfN/0h/0h/3h)))");
+ import_descriptor(m_wallet, "wpkh(xprv9s21ZrQH143K2LE7W4Xf3jATf9jECxSb7wj91ZnmY4qEJrS66Qru9RFqq8xbkgT32ya6HqYJweFdJUEDf5Q6JFV7jMiUws7kQfe6Tv4RbfN/0h/0h/*h)");
// Call FillPSBT
PartiallySignedTransaction psbtx;
@@ -71,7 +66,8 @@ BOOST_AUTO_TEST_CASE(psbt_updater_test)
// Try to sign the mutated input
SignatureData sigdata;
- BOOST_CHECK(spk_man->FillPSBT(psbtx, PrecomputePSBTData(psbtx), SIGHASH_ALL, true, true) != TransactionError::OK);
+ BOOST_CHECK(m_wallet.FillPSBT(psbtx, complete, SIGHASH_ALL, true, true) != TransactionError::OK);
+ //BOOST_CHECK(spk_man->FillPSBT(psbtx, PrecomputePSBTData(psbtx), SIGHASH_ALL, true, true) != TransactionError::OK);
}
BOOST_AUTO_TEST_CASE(parse_hd_keypath)
diff --git a/src/wallet/test/spend_tests.cpp b/src/wallet/test/spend_tests.cpp
index becef70729..d88d8eabdb 100644
--- a/src/wallet/test/spend_tests.cpp
+++ b/src/wallet/test/spend_tests.cpp
@@ -33,6 +33,8 @@ BOOST_FIXTURE_TEST_CASE(SubtractFee, TestChain100Setup)
CCoinControl coin_control;
coin_control.m_feerate.emplace(10000);
coin_control.fOverrideFeeRate = true;
+ // We need to use a change type with high cost of change so that the leftover amount will be dropped to fee instead of added as a change output
+ coin_control.m_change_type = OutputType::LEGACY;
FeeCalculation fee_calc;
BOOST_CHECK(CreateTransaction(*wallet, {recipient}, tx, fee, change_pos, error, coin_control, fee_calc));
BOOST_CHECK_EQUAL(tx->vout.size(), 1);
diff --git a/src/wallet/test/util.cpp b/src/wallet/test/util.cpp
index c3061b93c0..2990fc8f8d 100644
--- a/src/wallet/test/util.cpp
+++ b/src/wallet/test/util.cpp
@@ -6,6 +6,7 @@
#include <chain.h>
#include <key.h>
+#include <key_io.h>
#include <test/util/setup_common.h>
#include <wallet/wallet.h>
#include <wallet/walletdb.h>
@@ -23,9 +24,16 @@ std::unique_ptr<CWallet> CreateSyncedWallet(interfaces::Chain& chain, CChain& cc
}
wallet->LoadWallet();
{
- auto spk_man = wallet->GetOrCreateLegacyScriptPubKeyMan();
- LOCK2(wallet->cs_wallet, spk_man->cs_KeyStore);
- spk_man->AddKeyPubKey(key, key.GetPubKey());
+ LOCK(wallet->cs_wallet);
+ wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
+ wallet->SetupDescriptorScriptPubKeyMans();
+
+ FlatSigningProvider provider;
+ std::string error;
+ std::unique_ptr<Descriptor> desc = Parse("combo(" + EncodeSecret(key) + ")", provider, error, /* require_checksum=*/ false);
+ assert(desc);
+ WalletDescriptor w_desc(std::move(desc), 0, 0, 1, 1);
+ if (!wallet->AddWalletDescriptor(w_desc, provider, "", false)) assert(false);
}
WalletRescanReserver reserver(*wallet);
reserver.reserve();
diff --git a/src/wallet/test/wallet_test_fixture.cpp b/src/wallet/test/wallet_test_fixture.cpp
index fc744ebe5b..313606cc28 100644
--- a/src/wallet/test/wallet_test_fixture.cpp
+++ b/src/wallet/test/wallet_test_fixture.cpp
@@ -4,6 +4,8 @@
#include <wallet/test/wallet_test_fixture.h>
+#include <scheduler.h>
+
WalletTestingSetup::WalletTestingSetup(const std::string& chainName)
: TestingSetup(chainName),
m_wallet(m_node.chain.get(), "", CreateMockWalletDatabase())
@@ -12,3 +14,8 @@ WalletTestingSetup::WalletTestingSetup(const std::string& chainName)
m_chain_notifications_handler = m_node.chain->handleNotifications({ &m_wallet, [](CWallet*) {} });
m_wallet_client->registerRpcs();
}
+
+WalletTestingSetup::~WalletTestingSetup()
+{
+ if (m_node.scheduler) m_node.scheduler->stop();
+}
diff --git a/src/wallet/test/wallet_test_fixture.h b/src/wallet/test/wallet_test_fixture.h
index ab7fb8c42b..8bf2d36227 100644
--- a/src/wallet/test/wallet_test_fixture.h
+++ b/src/wallet/test/wallet_test_fixture.h
@@ -19,6 +19,7 @@
*/
struct WalletTestingSetup : public TestingSetup {
explicit WalletTestingSetup(const std::string& chainName = CBaseChainParams::MAIN);
+ ~WalletTestingSetup();
std::unique_ptr<interfaces::WalletClient> m_wallet_client = interfaces::MakeWalletClient(*m_node.chain, *Assert(m_node.args));
CWallet m_wallet;
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 9938380369..7a658c10a2 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -11,6 +11,7 @@
#include <vector>
#include <interfaces/chain.h>
+#include <key_io.h>
#include <node/blockstorage.h>
#include <node/context.h>
#include <policy/policy.h>
@@ -40,9 +41,10 @@ static_assert(WALLET_INCREMENTAL_RELAY_FEE >= DEFAULT_INCREMENTAL_RELAY_FEE, "wa
BOOST_FIXTURE_TEST_SUITE(wallet_tests, WalletTestingSetup)
-static std::shared_ptr<CWallet> TestLoadWallet(WalletContext& context)
+static const std::shared_ptr<CWallet> TestLoadWallet(WalletContext& context)
{
DatabaseOptions options;
+ options.create_flags = WALLET_FLAG_DESCRIPTORS;
DatabaseStatus status;
bilingual_str error;
std::vector<bilingual_str> warnings;
@@ -77,9 +79,13 @@ static CMutableTransaction TestSimpleSpend(const CTransaction& from, uint32_t in
static void AddKey(CWallet& wallet, const CKey& key)
{
- auto spk_man = wallet.GetOrCreateLegacyScriptPubKeyMan();
- LOCK2(wallet.cs_wallet, spk_man->cs_KeyStore);
- spk_man->AddKeyPubKey(key, key.GetPubKey());
+ LOCK(wallet.cs_wallet);
+ FlatSigningProvider provider;
+ std::string error;
+ std::unique_ptr<Descriptor> desc = Parse("combo(" + EncodeSecret(key) + ")", provider, error, /* require_checksum=*/ false);
+ assert(desc);
+ WalletDescriptor w_desc(std::move(desc), 0, 0, 1, 1);
+ if (!wallet.AddWalletDescriptor(w_desc, provider, "", false)) assert(false);
}
BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
@@ -95,6 +101,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
+ wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
wallet.SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}
AddKey(wallet, coinbaseKey);
@@ -114,6 +121,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
+ wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
wallet.SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}
AddKey(wallet, coinbaseKey);
@@ -140,6 +148,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
+ wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
wallet.SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}
AddKey(wallet, coinbaseKey);
@@ -165,6 +174,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
+ wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
wallet.SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}
AddKey(wallet, coinbaseKey);
@@ -198,7 +208,7 @@ BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
// before the missing block, and success for a key whose creation time is
// after.
{
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateDummyWalletDatabase());
+ const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateDummyWalletDatabase());
wallet->SetupLegacyScriptPubKeyMan();
WITH_LOCK(wallet->cs_wallet, wallet->SetLastBlockProcessed(newTip->nHeight, newTip->GetBlockHash()));
WalletContext context;
@@ -258,13 +268,13 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
SetMockTime(KEY_TIME);
m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
- std::string backup_file = (gArgs.GetDataDirNet() / "wallet.backup").string();
+ std::string backup_file = fs::PathToString(gArgs.GetDataDirNet() / "wallet.backup");
// Import key into wallet and call dumpwallet to create backup file.
{
WalletContext context;
context.args = &gArgs;
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateDummyWalletDatabase());
+ const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateDummyWalletDatabase());
{
auto spk_man = wallet->GetOrCreateLegacyScriptPubKeyMan();
LOCK2(wallet->cs_wallet, spk_man->cs_KeyStore);
@@ -286,7 +296,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
// Call importwallet RPC and verify all blocks with timestamps >= BLOCK_TIME
// were scanned, and no prior blocks were scanned.
{
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateDummyWalletDatabase());
+ const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateDummyWalletDatabase());
LOCK(wallet->cs_wallet);
wallet->SetupLegacyScriptPubKeyMan();
@@ -320,10 +330,12 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
{
CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase());
- auto spk_man = wallet.GetOrCreateLegacyScriptPubKeyMan();
CWalletTx wtx(m_coinbase_txns.back());
- LOCK2(wallet.cs_wallet, spk_man->cs_KeyStore);
+ LOCK(wallet.cs_wallet);
+ wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
+ wallet.SetupDescriptorScriptPubKeyMans();
+
wallet.SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash(), 0);
@@ -336,7 +348,7 @@ BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
// Invalidate the cached value, add the key, and make sure a new immature
// credit amount is calculated.
wtx.MarkDirty();
- BOOST_CHECK(spk_man->AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()));
+ AddKey(wallet, coinbaseKey);
BOOST_CHECK_EQUAL(CachedTxGetImmatureCredit(wallet, wtx), 50*COIN);
}
@@ -593,14 +605,26 @@ BOOST_FIXTURE_TEST_CASE(ListCoinsTest, ListCoinsTestingSetup)
BOOST_FIXTURE_TEST_CASE(wallet_disableprivkeys, TestChain100Setup)
{
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateDummyWalletDatabase());
- wallet->SetupLegacyScriptPubKeyMan();
- wallet->SetMinVersion(FEATURE_LATEST);
- wallet->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
- BOOST_CHECK(!wallet->TopUpKeyPool(1000));
- CTxDestination dest;
- bilingual_str error;
- BOOST_CHECK(!wallet->GetNewDestination(OutputType::BECH32, "", dest, error));
+ {
+ const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateDummyWalletDatabase());
+ wallet->SetupLegacyScriptPubKeyMan();
+ wallet->SetMinVersion(FEATURE_LATEST);
+ wallet->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
+ BOOST_CHECK(!wallet->TopUpKeyPool(1000));
+ CTxDestination dest;
+ bilingual_str error;
+ BOOST_CHECK(!wallet->GetNewDestination(OutputType::BECH32, "", dest, error));
+ }
+ {
+ const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateDummyWalletDatabase());
+ LOCK(wallet->cs_wallet);
+ wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
+ wallet->SetMinVersion(FEATURE_LATEST);
+ wallet->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
+ CTxDestination dest;
+ bilingual_str error;
+ BOOST_CHECK(!wallet->GetNewDestination(OutputType::BECH32, "", dest, error));
+ }
}
// Explicit calculation which is used to test the wallet constant
diff --git a/src/wallet/transaction.h b/src/wallet/transaction.h
index 6fc1bd1eed..1ccef31056 100644
--- a/src/wallet/transaction.h
+++ b/src/wallet/transaction.h
@@ -162,7 +162,8 @@ public:
int block_height;
uint256 hashBlock;
int nIndex;
- Confirmation(Status s = UNCONFIRMED, int b = 0, uint256 h = uint256(), int i = 0) : status(s), block_height(b), hashBlock(h), nIndex(i) {}
+ Confirmation(Status status = UNCONFIRMED, int block_height = 0, uint256 block_hash = uint256(), int block_index = 0)
+ : status{status}, block_height{block_height}, hashBlock{block_hash}, nIndex{block_index} {}
};
Confirmation m_confirm;
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index abfe9d7dba..4eb9d5560d 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -221,7 +221,7 @@ std::shared_ptr<CWallet> LoadWalletInternal(WalletContext& context, const std::s
}
context.chain->initMessage(_("Loading wallet…").translated);
- std::shared_ptr<CWallet> wallet = CWallet::Create(context, name, std::move(database), options.create_flags, error, warnings);
+ const std::shared_ptr<CWallet> wallet = CWallet::Create(context, name, std::move(database), options.create_flags, error, warnings);
if (!wallet) {
error = Untranslated("Wallet loading failed.") + Untranslated(" ") + error;
status = DatabaseStatus::FAILED_LOAD;
@@ -301,7 +301,7 @@ std::shared_ptr<CWallet> CreateWallet(WalletContext& context, const std::string&
// Make the wallet
context.chain->initMessage(_("Loading wallet…").translated);
- std::shared_ptr<CWallet> wallet = CWallet::Create(context, name, std::move(database), wallet_creation_flags, error, warnings);
+ const std::shared_ptr<CWallet> wallet = CWallet::Create(context, name, std::move(database), wallet_creation_flags, error, warnings);
if (!wallet) {
error = Untranslated("Wallet creation failed.") + Untranslated(" ") + error;
status = DatabaseStatus::FAILED_CREATE;
@@ -1212,7 +1212,7 @@ void CWallet::SyncTransaction(const CTransactionRef& ptx, CWalletTx::Confirmatio
void CWallet::transactionAddedToMempool(const CTransactionRef& tx, uint64_t mempool_sequence) {
LOCK(cs_wallet);
- SyncTransaction(tx, {CWalletTx::Status::UNCONFIRMED, /* block height */ 0, /* block hash */ {}, /* index */ 0});
+ SyncTransaction(tx, {CWalletTx::Status::UNCONFIRMED, /*block_height=*/0, /*block_hash=*/{}, /*block_index=*/0});
auto it = mapWallet.find(tx->GetHash());
if (it != mapWallet.end()) {
@@ -1253,7 +1253,7 @@ void CWallet::transactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRe
// distinguishing between conflicted and unconfirmed transactions are
// imperfect, and could be improved in general, see
// https://github.com/bitcoin-core/bitcoin-devwiki/wiki/Wallet-Transaction-Conflict-Tracking
- SyncTransaction(tx, {CWalletTx::Status::UNCONFIRMED, /* block height */ 0, /* block hash */ {}, /* index */ 0});
+ SyncTransaction(tx, {CWalletTx::Status::UNCONFIRMED, /*block_height=*/0, /*block_hash=*/{}, /*block_index=*/0});
}
}
@@ -1281,7 +1281,7 @@ void CWallet::blockDisconnected(const CBlock& block, int height)
m_last_block_processed_height = height - 1;
m_last_block_processed = block.hashPrevBlock;
for (const CTransactionRef& ptx : block.vtx) {
- SyncTransaction(ptx, {CWalletTx::Status::UNCONFIRMED, /* block height */ 0, /* block hash */ {}, /* index */ 0});
+ SyncTransaction(ptx, {CWalletTx::Status::UNCONFIRMED, /*block_height=*/0, /*block_hash=*/{}, /*block_index=*/0});
}
}
@@ -1294,7 +1294,7 @@ void CWallet::updatedBlockTip()
void CWallet::BlockUntilSyncedToCurrentChain() const {
AssertLockNotHeld(cs_wallet);
// Skip the queue-draining stuff if we know we're caught up with
- // ::ChainActive().Tip(), otherwise put a callback in the validation interface queue and wait
+ // chain().Tip(), otherwise put a callback in the validation interface queue and wait
// for the queue to drain enough to execute it (indicating we are caught up
// at least with the time we entered this function).
uint256 last_block_hash = WITH_LOCK(cs_wallet, return m_last_block_processed);
@@ -2515,16 +2515,16 @@ std::unique_ptr<WalletDatabase> MakeWalletDatabase(const std::string& name, cons
// 2. Path to an existing directory.
// 3. Path to a symlink to a directory.
// 4. For backwards compatibility, the name of a data file in -walletdir.
- const fs::path wallet_path = fsbridge::AbsPathJoin(GetWalletDir(), name);
+ const fs::path wallet_path = fsbridge::AbsPathJoin(GetWalletDir(), fs::PathFromString(name));
fs::file_type path_type = fs::symlink_status(wallet_path).type();
if (!(path_type == fs::file_not_found || path_type == fs::directory_file ||
(path_type == fs::symlink_file && fs::is_directory(wallet_path)) ||
- (path_type == fs::regular_file && fs::path(name).filename() == name))) {
+ (path_type == fs::regular_file && fs::PathFromString(name).filename() == fs::PathFromString(name)))) {
error_string = Untranslated(strprintf(
"Invalid -wallet path '%s'. -wallet path should point to a directory where wallet.dat and "
"database/log.?????????? files can be stored, a location where such a directory could be created, "
"or (for backwards compatibility) the name of an existing data file in -walletdir (%s)",
- name, GetWalletDir()));
+ name, fs::quoted(fs::PathToString(GetWalletDir()))));
status = DatabaseStatus::FAILED_BAD_PATH;
return nullptr;
}
@@ -2540,7 +2540,7 @@ std::shared_ptr<CWallet> CWallet::Create(WalletContext& context, const std::stri
int64_t nStart = GetTimeMillis();
// TODO: Can't use std::make_shared because we need a custom deleter but
// should be possible to use std::allocate_shared.
- std::shared_ptr<CWallet> walletInstance(new CWallet(chain, name, std::move(database)), ReleaseWallet);
+ const std::shared_ptr<CWallet> walletInstance(new CWallet(chain, name, std::move(database)), ReleaseWallet);
bool rescan_required = false;
DBErrors nLoadWalletRet = walletInstance->LoadWallet();
if (nLoadWalletRet != DBErrors::LOAD_OK) {
@@ -3235,7 +3235,8 @@ void CWallet::LoadActiveScriptPubKeyMan(uint256 id, OutputType type, bool intern
auto spk_man = m_spk_managers.at(id).get();
spk_mans[type] = spk_man;
- if (spk_mans_other[type] == spk_man) {
+ const auto it = spk_mans_other.find(type);
+ if (it != spk_mans_other.end() && it->second == spk_man) {
spk_mans_other.erase(type);
}
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 767b24adbb..c911eb461c 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -260,9 +260,9 @@ private:
void AddToSpends(const uint256& wtxid, WalletBatch* batch = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/**
- * Add a transaction to the wallet, or update it. pIndex and posInBlock should
+ * Add a transaction to the wallet, or update it. confirm.block_* should
* be set when the transaction was known to be included in a block. When
- * pIndex == nullptr, then wallet state is not updated in AddToWallet, but
+ * block_hash.IsNull(), then wallet state is not updated in AddToWallet, but
* notifications happen and cached balances are marked dirty.
*
* If fUpdate is true, existing transactions will be updated.
@@ -270,7 +270,7 @@ private:
* assumption that any further notification of a transaction that was considered
* abandoned is an indication that it is not safe to be considered abandoned.
* Abandoned state should probably be more carefully tracked via different
- * posInBlock signals or by checking mempool presence when necessary.
+ * chain notifications or by checking mempool presence when necessary.
*
* Should be called with rescanning_old_block set to true, if the transaction is
* not discovered in real time, but during a rescan of old blocks.
@@ -285,8 +285,6 @@ private:
void SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator>) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- /* Used by TransactionAddedToMemorypool/BlockConnected/Disconnected/ScanForWalletTransactions.
- * Should be called with non-zero block_hash and posInBlock if this is for a transaction that is included in a block. */
void SyncTransaction(const CTransactionRef& tx, CWalletTx::Confirmation confirm, bool update_tx = true, bool rescanning_old_block = false) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/** WalletFlags set on this wallet. */
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index 8ff09a0878..c920d4af51 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -1106,7 +1106,7 @@ std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const Databas
try {
exists = fs::symlink_status(path).type() != fs::file_not_found;
} catch (const fs::filesystem_error& e) {
- error = Untranslated(strprintf("Failed to access database path '%s': %s", path.string(), fsbridge::get_filesystem_error_message(e)));
+ error = Untranslated(strprintf("Failed to access database path '%s': %s", fs::PathToString(path), fsbridge::get_filesystem_error_message(e)));
status = DatabaseStatus::FAILED_BAD_PATH;
return nullptr;
}
@@ -1118,33 +1118,33 @@ std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const Databas
}
if (IsSQLiteFile(SQLiteDataFile(path))) {
if (format) {
- error = Untranslated(strprintf("Failed to load database path '%s'. Data is in ambiguous format.", path.string()));
+ error = Untranslated(strprintf("Failed to load database path '%s'. Data is in ambiguous format.", fs::PathToString(path)));
status = DatabaseStatus::FAILED_BAD_FORMAT;
return nullptr;
}
format = DatabaseFormat::SQLITE;
}
} else if (options.require_existing) {
- error = Untranslated(strprintf("Failed to load database path '%s'. Path does not exist.", path.string()));
+ error = Untranslated(strprintf("Failed to load database path '%s'. Path does not exist.", fs::PathToString(path)));
status = DatabaseStatus::FAILED_NOT_FOUND;
return nullptr;
}
if (!format && options.require_existing) {
- error = Untranslated(strprintf("Failed to load database path '%s'. Data is not in recognized format.", path.string()));
+ error = Untranslated(strprintf("Failed to load database path '%s'. Data is not in recognized format.", fs::PathToString(path)));
status = DatabaseStatus::FAILED_BAD_FORMAT;
return nullptr;
}
if (format && options.require_create) {
- error = Untranslated(strprintf("Failed to create database path '%s'. Database already exists.", path.string()));
+ error = Untranslated(strprintf("Failed to create database path '%s'. Database already exists.", fs::PathToString(path)));
status = DatabaseStatus::FAILED_ALREADY_EXISTS;
return nullptr;
}
// A db already exists so format is set, but options also specifies the format, so make sure they agree
if (format && options.require_format && format != options.require_format) {
- error = Untranslated(strprintf("Failed to load database path '%s'. Data is not in required format.", path.string()));
+ error = Untranslated(strprintf("Failed to load database path '%s'. Data is not in required format.", fs::PathToString(path)));
status = DatabaseStatus::FAILED_BAD_FORMAT;
return nullptr;
}
@@ -1166,7 +1166,7 @@ std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const Databas
#ifdef USE_SQLITE
return MakeSQLiteDatabase(path, options, status, error);
#endif
- error = Untranslated(strprintf("Failed to open database path '%s'. Build does not support SQLite database format.", path.string()));
+ error = Untranslated(strprintf("Failed to open database path '%s'. Build does not support SQLite database format.", fs::PathToString(path)));
status = DatabaseStatus::FAILED_BAD_FORMAT;
return nullptr;
}
@@ -1174,7 +1174,7 @@ std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const Databas
#ifdef USE_BDB
return MakeBerkeleyDatabase(path, options, status, error);
#endif
- error = Untranslated(strprintf("Failed to open database path '%s'. Build does not support Berkeley DB database format.", path.string()));
+ error = Untranslated(strprintf("Failed to open database path '%s'. Build does not support Berkeley DB database format.", fs::PathToString(path)));
status = DatabaseStatus::FAILED_BAD_FORMAT;
return nullptr;
}
@@ -1188,9 +1188,9 @@ std::unique_ptr<WalletDatabase> CreateDummyWalletDatabase()
/** Return object for accessing temporary in-memory database. */
std::unique_ptr<WalletDatabase> CreateMockWalletDatabase()
{
-#ifdef USE_BDB
- return std::make_unique<BerkeleyDatabase>(std::make_shared<BerkeleyEnvironment>(), "");
-#elif USE_SQLITE
+#ifdef USE_SQLITE
return std::make_unique<SQLiteDatabase>("", "", true);
+#elif USE_BDB
+ return std::make_unique<BerkeleyDatabase>(std::make_shared<BerkeleyEnvironment>(), "");
#endif
}
diff --git a/src/wallet/wallettool.cpp b/src/wallet/wallettool.cpp
index e3cb5cee5d..086415f152 100644
--- a/src/wallet/wallettool.cpp
+++ b/src/wallet/wallettool.cpp
@@ -40,7 +40,7 @@ static void WalletCreate(CWallet* wallet_instance, uint64_t wallet_creation_flag
wallet_instance->TopUpKeyPool();
}
-static std::shared_ptr<CWallet> MakeWallet(const std::string& name, const fs::path& path, DatabaseOptions options)
+static const std::shared_ptr<CWallet> MakeWallet(const std::string& name, const fs::path& path, DatabaseOptions options)
{
DatabaseStatus status;
bilingual_str error;
@@ -120,22 +120,38 @@ bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command)
tfm::format(std::cerr, "The -descriptors option can only be used with the 'create' command.\n");
return false;
}
+ if (args.IsArgSet("-legacy") && command != "create") {
+ tfm::format(std::cerr, "The -legacy option can only be used with the 'create' command.\n");
+ return false;
+ }
if (command == "create" && !args.IsArgSet("-wallet")) {
tfm::format(std::cerr, "Wallet name must be provided when creating a new wallet.\n");
return false;
}
const std::string name = args.GetArg("-wallet", "");
- const fs::path path = fsbridge::AbsPathJoin(GetWalletDir(), name);
+ const fs::path path = fsbridge::AbsPathJoin(GetWalletDir(), fs::PathFromString(name));
if (command == "create") {
DatabaseOptions options;
options.require_create = true;
- if (args.GetBoolArg("-descriptors", false)) {
+ // If -legacy is set, use it. Otherwise default to false.
+ bool make_legacy = args.GetBoolArg("-legacy", false);
+ // If neither -legacy nor -descriptors is set, default to true. If -descriptors is set, use its value.
+ bool make_descriptors = (!args.IsArgSet("-descriptors") && !args.IsArgSet("-legacy")) || (args.IsArgSet("-descriptors") && args.GetBoolArg("-descriptors", true));
+ if (make_legacy && make_descriptors) {
+ tfm::format(std::cerr, "Only one of -legacy or -descriptors can be set to true, not both\n");
+ return false;
+ }
+ if (!make_legacy && !make_descriptors) {
+ tfm::format(std::cerr, "One of -legacy or -descriptors must be set to true (or omitted)\n");
+ return false;
+ }
+ if (make_descriptors) {
options.create_flags |= WALLET_FLAG_DESCRIPTORS;
options.require_format = DatabaseFormat::SQLITE;
}
- std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, options);
+ const std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, options);
if (wallet_instance) {
WalletShowInfo(wallet_instance.get());
wallet_instance->Close();
@@ -143,7 +159,7 @@ bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command)
} else if (command == "info") {
DatabaseOptions options;
options.require_existing = true;
- std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, options);
+ const std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, options);
if (!wallet_instance) return false;
WalletShowInfo(wallet_instance.get());
wallet_instance->Close();
@@ -168,7 +184,7 @@ bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command)
} else if (command == "dump") {
DatabaseOptions options;
options.require_existing = true;
- std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, options);
+ const std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, options);
if (!wallet_instance) return false;
bilingual_str error;
bool ret = DumpWallet(*wallet_instance, error);
diff --git a/src/wallet/walletutil.cpp b/src/wallet/walletutil.cpp
index 1c518daba6..7f813432b3 100644
--- a/src/wallet/walletutil.cpp
+++ b/src/wallet/walletutil.cpp
@@ -12,7 +12,7 @@ fs::path GetWalletDir()
fs::path path;
if (gArgs.IsArgSet("-walletdir")) {
- path = gArgs.GetArg("-walletdir", "");
+ path = fs::PathFromString(gArgs.GetArg("-walletdir", ""));
if (!fs::is_directory(path)) {
// If the path specified doesn't exist, we return the deliberately
// invalid empty string.
diff --git a/test/README.md b/test/README.md
index acd68d8d8f..c9e15c4968 100644
--- a/test/README.md
+++ b/test/README.md
@@ -275,12 +275,15 @@ Use the `-v` option for verbose output.
#### Dependencies
-| Lint test | Dependency | Version [used by CI](../ci/lint/04_install.sh) | Installation
-|-----------|:----------:|:-------------------------------------------:|--------------
-| [`lint-python.sh`](lint/lint-python.sh) | [flake8](https://gitlab.com/pycqa/flake8) | [3.8.3](https://github.com/bitcoin/bitcoin/pull/19348) | `pip3 install flake8==3.8.3`
-| [`lint-python.sh`](lint/lint-python.sh) | [mypy](https://github.com/python/mypy) | [0.781](https://github.com/bitcoin/bitcoin/pull/19348) | `pip3 install mypy==0.781`
-| [`lint-shell.sh`](lint/lint-shell.sh) | [ShellCheck](https://github.com/koalaman/shellcheck) | [0.7.2](https://github.com/bitcoin/bitcoin/pull/21749) | [details...](https://github.com/koalaman/shellcheck#installing)
-| [`lint-spelling.sh`](lint/lint-spelling.sh) | [codespell](https://github.com/codespell-project/codespell) | [2.0.0](https://github.com/bitcoin/bitcoin/pull/20817) | `pip3 install codespell==2.0.0`
+| Lint test | Dependency |
+|-----------|:----------:|
+| [`lint-python.sh`](lint/lint-python.sh) | [flake8](https://gitlab.com/pycqa/flake8)
+| [`lint-python.sh`](lint/lint-python.sh) | [mypy](https://github.com/python/mypy)
+| [`lint-python.sh`](lint/lint-python.sh) | [pyzmq](https://github.com/zeromq/pyzmq)
+| [`lint-shell.sh`](lint/lint-shell.sh) | [ShellCheck](https://github.com/koalaman/shellcheck)
+| [`lint-spelling.sh`](lint/lint-spelling.sh) | [codespell](https://github.com/codespell-project/codespell)
+
+In use versions and install instructions are available in the [CI setup](../ci/lint/04_install.sh).
Please be aware that on Linux distributions all dependencies are usually available as packages, but could be outdated.
diff --git a/test/functional/combine_logs.py b/test/functional/combine_logs.py
index 00f2833f55..71dfb4c01a 100755
--- a/test/functional/combine_logs.py
+++ b/test/functional/combine_logs.py
@@ -188,7 +188,7 @@ def print_logs_plain(log_events, colors):
def print_logs_html(log_events):
"""Renders the iterator of log events into html."""
try:
- import jinja2
+ import jinja2 #type:ignore
except ImportError:
print("jinja2 not found. Try `pip install jinja2`")
sys.exit(1)
diff --git a/test/functional/data/__init__.py b/test/functional/data/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/functional/data/__init__.py
diff --git a/test/functional/feature_addrman.py b/test/functional/feature_addrman.py
index 93d50c1369..14a4f8abb7 100755
--- a/test/functional/feature_addrman.py
+++ b/test/functional/feature_addrman.py
@@ -18,7 +18,7 @@ from test_framework.util import assert_equal
def serialize_addrman(
*,
format=1,
- lowest_compatible=3,
+ lowest_compatible=4,
net_magic="regtest",
bucket_key=1,
len_new=None,
@@ -75,7 +75,7 @@ class AddrmanTest(BitcoinTestFramework):
expected_msg=init_error(
"Unsupported format of addrman database: 1. It is compatible with "
"formats >=111, but the maximum supported by this version of "
- f"{self.config['environment']['PACKAGE_NAME']} is 3.: (.+)"
+ f"{self.config['environment']['PACKAGE_NAME']} is 4.: (.+)"
),
match=ErrorMatch.FULL_REGEX,
)
diff --git a/test/functional/feature_filelock.py b/test/functional/feature_filelock.py
index e09107802b..0fc654e10a 100755
--- a/test/functional/feature_filelock.py
+++ b/test/functional/feature_filelock.py
@@ -35,7 +35,7 @@ class FilelockTest(BitcoinTestFramework):
wallet_dir = os.path.join(datadir, 'wallets')
self.log.info("Check that we can't start a second bitcoind instance using the same wallet")
if descriptors:
- expected_msg = "Error: SQLiteDatabase: Unable to obtain an exclusive lock on the database, is it being used by another bitcoind?"
+ expected_msg = f"Error: SQLiteDatabase: Unable to obtain an exclusive lock on the database, is it being used by another instance of {self.config['environment']['PACKAGE_NAME']}?"
else:
expected_msg = "Error: Error initializing wallet database environment"
self.nodes[1].assert_start_raises_init_error(extra_args=[f'-walletdir={wallet_dir}', f'-wallet={wallet_name}', '-noserver'], expected_msg=expected_msg, match=ErrorMatch.PARTIAL_REGEX)
diff --git a/test/functional/feature_rbf.py b/test/functional/feature_rbf.py
index 4eaaf46454..420147542e 100755
--- a/test/functional/feature_rbf.py
+++ b/test/functional/feature_rbf.py
@@ -539,7 +539,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
assert_equal(json1["vin"][0]["sequence"], 4294967295)
if self.is_wallet_compiled():
- self.init_wallet(0)
+ self.init_wallet(node=0)
rawtx2 = self.nodes[0].createrawtransaction([], outs)
frawtx2a = self.nodes[0].fundrawtransaction(rawtx2, {"replaceable": True})
frawtx2b = self.nodes[0].fundrawtransaction(rawtx2, {"replaceable": False})
diff --git a/test/functional/feature_segwit.py b/test/functional/feature_segwit.py
index 2f9ab34e99..acb7469c6a 100755
--- a/test/functional/feature_segwit.py
+++ b/test/functional/feature_segwit.py
@@ -17,6 +17,7 @@ from test_framework.blocktools import (
send_to_witness,
witness_script,
)
+from test_framework.descriptors import descsum_create
from test_framework.messages import (
COIN,
COutPoint,
@@ -29,8 +30,6 @@ from test_framework.script import (
CScript,
OP_0,
OP_1,
- OP_2,
- OP_CHECKMULTISIG,
OP_DROP,
OP_TRUE,
)
@@ -38,6 +37,7 @@ from test_framework.script_util import (
key_to_p2pk_script,
key_to_p2pkh_script,
key_to_p2wpkh_script,
+ keys_to_multisig_script,
script_to_p2sh_script,
script_to_p2wsh_script,
)
@@ -49,6 +49,9 @@ from test_framework.util import (
assert_raises_rpc_error,
try_rpc,
)
+from test_framework.wallet_util import (
+ get_generate_key,
+)
NODE_0 = 0
NODE_2 = 2
@@ -142,13 +145,39 @@ class SegWitTest(BitcoinTestFramework):
p2sh_ids = [] # p2sh_ids[NODE][TYPE] is an array of txids that spend to P2WPKH (TYPE=0) or P2WSH (TYPE=1) scripts to an address for NODE embedded in p2sh
wit_ids = [] # wit_ids[NODE][TYPE] is an array of txids that spend to P2WPKH (TYPE=0) or P2WSH (TYPE=1) scripts to an address for NODE via bare witness
for i in range(3):
- newaddress = self.nodes[i].getnewaddress()
- self.pubkey.append(self.nodes[i].getaddressinfo(newaddress)["pubkey"])
- multiscript = CScript([OP_1, bytes.fromhex(self.pubkey[-1]), OP_1, OP_CHECKMULTISIG])
- p2sh_ms_addr = self.nodes[i].addmultisigaddress(1, [self.pubkey[-1]], '', 'p2sh-segwit')['address']
- bip173_ms_addr = self.nodes[i].addmultisigaddress(1, [self.pubkey[-1]], '', 'bech32')['address']
+ key = get_generate_key()
+ self.pubkey.append(key.pubkey)
+
+ multiscript = keys_to_multisig_script([self.pubkey[-1]])
+ p2sh_ms_addr = self.nodes[i].createmultisig(1, [self.pubkey[-1]], 'p2sh-segwit')['address']
+ bip173_ms_addr = self.nodes[i].createmultisig(1, [self.pubkey[-1]], 'bech32')['address']
assert_equal(p2sh_ms_addr, script_to_p2sh_p2wsh(multiscript))
assert_equal(bip173_ms_addr, script_to_p2wsh(multiscript))
+
+ p2sh_ms_desc = descsum_create(f"sh(wsh(multi(1,{key.privkey})))")
+ bip173_ms_desc = descsum_create(f"wsh(multi(1,{key.privkey}))")
+ assert_equal(self.nodes[i].deriveaddresses(p2sh_ms_desc)[0], p2sh_ms_addr)
+ assert_equal(self.nodes[i].deriveaddresses(bip173_ms_desc)[0], bip173_ms_addr)
+
+ sh_wpkh_desc = descsum_create(f"sh(wpkh({key.privkey}))")
+ wpkh_desc = descsum_create(f"wpkh({key.privkey})")
+ assert_equal(self.nodes[i].deriveaddresses(sh_wpkh_desc)[0], key.p2sh_p2wpkh_addr)
+ assert_equal(self.nodes[i].deriveaddresses(wpkh_desc)[0], key.p2wpkh_addr)
+
+ if self.options.descriptors:
+ res = self.nodes[i].importdescriptors([
+ {"desc": p2sh_ms_desc, "timestamp": "now"},
+ {"desc": bip173_ms_desc, "timestamp": "now"},
+ {"desc": sh_wpkh_desc, "timestamp": "now"},
+ {"desc": wpkh_desc, "timestamp": "now"},
+ ])
+ else:
+ # The nature of the legacy wallet is that this import results in also adding all of the necessary scripts
+ res = self.nodes[i].importmulti([
+ {"desc": p2sh_ms_desc, "timestamp": "now"},
+ ])
+ assert all([r["success"] for r in res])
+
p2sh_ids.append([])
wit_ids.append([])
for _ in range(2):
@@ -311,284 +340,285 @@ class SegWitTest(BitcoinTestFramework):
# Mine a block to clear the gbt cache again.
self.generate(self.nodes[0], 1)
- self.log.info("Verify behaviour of importaddress and listunspent")
-
- # Some public keys to be used later
- pubkeys = [
- "0363D44AABD0F1699138239DF2F042C3282C0671CC7A76826A55C8203D90E39242", # cPiM8Ub4heR9NBYmgVzJQiUH1if44GSBGiqaeJySuL2BKxubvgwb
- "02D3E626B3E616FC8662B489C123349FECBFC611E778E5BE739B257EAE4721E5BF", # cPpAdHaD6VoYbW78kveN2bsvb45Q7G5PhaPApVUGwvF8VQ9brD97
- "04A47F2CBCEFFA7B9BCDA184E7D5668D3DA6F9079AD41E422FA5FD7B2D458F2538A62F5BD8EC85C2477F39650BD391EA6250207065B2A81DA8B009FC891E898F0E", # 91zqCU5B9sdWxzMt1ca3VzbtVm2YM6Hi5Rxn4UDtxEaN9C9nzXV
- "02A47F2CBCEFFA7B9BCDA184E7D5668D3DA6F9079AD41E422FA5FD7B2D458F2538", # cPQFjcVRpAUBG8BA9hzr2yEzHwKoMgLkJZBBtK9vJnvGJgMjzTbd
- "036722F784214129FEB9E8129D626324F3F6716555B603FFE8300BBCB882151228", # cQGtcm34xiLjB1v7bkRa4V3aAc9tS2UTuBZ1UnZGeSeNy627fN66
- "0266A8396EE936BF6D99D17920DB21C6C7B1AB14C639D5CD72B300297E416FD2EC", # cTW5mR5M45vHxXkeChZdtSPozrFwFgmEvTNnanCW6wrqwaCZ1X7K
- "0450A38BD7F0AC212FEBA77354A9B036A32E0F7C81FC4E0C5ADCA7C549C4505D2522458C2D9AE3CEFD684E039194B72C8A10F9CB9D4764AB26FCC2718D421D3B84", # 92h2XPssjBpsJN5CqSP7v9a7cf2kgDunBC6PDFwJHMACM1rrVBJ
- ]
-
- # Import a compressed key and an uncompressed key, generate some multisig addresses
- self.nodes[0].importprivkey("92e6XLo5jVAVwrQKPNTs93oQco8f8sDNBcpv73Dsrs397fQtFQn")
- uncompressed_spendable_address = ["mvozP4UwyGD2mGZU4D2eMvMLPB9WkMmMQu"]
- self.nodes[0].importprivkey("cNC8eQ5dg3mFAVePDX4ddmPYpPbw41r9bm2jd1nLJT77e6RrzTRR")
- compressed_spendable_address = ["mmWQubrDomqpgSYekvsU7HWEVjLFHAakLe"]
- assert not self.nodes[0].getaddressinfo(uncompressed_spendable_address[0])['iscompressed']
- assert self.nodes[0].getaddressinfo(compressed_spendable_address[0])['iscompressed']
-
- self.nodes[0].importpubkey(pubkeys[0])
- compressed_solvable_address = [key_to_p2pkh(pubkeys[0])]
- self.nodes[0].importpubkey(pubkeys[1])
- compressed_solvable_address.append(key_to_p2pkh(pubkeys[1]))
- self.nodes[0].importpubkey(pubkeys[2])
- uncompressed_solvable_address = [key_to_p2pkh(pubkeys[2])]
-
- spendable_anytime = [] # These outputs should be seen anytime after importprivkey and addmultisigaddress
- spendable_after_importaddress = [] # These outputs should be seen after importaddress
- solvable_after_importaddress = [] # These outputs should be seen after importaddress but not spendable
- unsolvable_after_importaddress = [] # These outputs should be unsolvable after importaddress
- solvable_anytime = [] # These outputs should be solvable after importpubkey
- unseen_anytime = [] # These outputs should never be seen
-
- uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [uncompressed_spendable_address[0], compressed_spendable_address[0]])['address'])
- uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [uncompressed_spendable_address[0], uncompressed_spendable_address[0]])['address'])
- compressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_spendable_address[0], compressed_spendable_address[0]])['address'])
- uncompressed_solvable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_spendable_address[0], uncompressed_solvable_address[0]])['address'])
- compressed_solvable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_spendable_address[0], compressed_solvable_address[0]])['address'])
- compressed_solvable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_solvable_address[0], compressed_solvable_address[1]])['address'])
-
- # Test multisig_without_privkey
- # We have 2 public keys without private keys, use addmultisigaddress to add to wallet.
- # Money sent to P2SH of multisig of this should only be seen after importaddress with the BASE58 P2SH address.
-
- multisig_without_privkey_address = self.nodes[0].addmultisigaddress(2, [pubkeys[3], pubkeys[4]])['address']
- script = CScript([OP_2, bytes.fromhex(pubkeys[3]), bytes.fromhex(pubkeys[4]), OP_2, OP_CHECKMULTISIG])
- solvable_after_importaddress.append(script_to_p2sh_script(script))
-
- for i in compressed_spendable_address:
- v = self.nodes[0].getaddressinfo(i)
- if v['isscript']:
- [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
- # p2sh multisig with compressed keys should always be spendable
- spendable_anytime.extend([p2sh])
- # bare multisig can be watched and signed, but is not treated as ours
- solvable_after_importaddress.extend([bare])
- # P2WSH and P2SH(P2WSH) multisig with compressed keys are spendable after direct importaddress
- spendable_after_importaddress.extend([p2wsh, p2sh_p2wsh])
- else:
- [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
- # normal P2PKH and P2PK with compressed keys should always be spendable
- spendable_anytime.extend([p2pkh, p2pk])
- # P2SH_P2PK, P2SH_P2PKH with compressed keys are spendable after direct importaddress
- spendable_after_importaddress.extend([p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh])
- # P2WPKH and P2SH_P2WPKH with compressed keys should always be spendable
- spendable_anytime.extend([p2wpkh, p2sh_p2wpkh])
-
- for i in uncompressed_spendable_address:
- v = self.nodes[0].getaddressinfo(i)
- if v['isscript']:
- [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
- # p2sh multisig with uncompressed keys should always be spendable
- spendable_anytime.extend([p2sh])
- # bare multisig can be watched and signed, but is not treated as ours
- solvable_after_importaddress.extend([bare])
- # P2WSH and P2SH(P2WSH) multisig with uncompressed keys are never seen
- unseen_anytime.extend([p2wsh, p2sh_p2wsh])
- else:
- [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
- # normal P2PKH and P2PK with uncompressed keys should always be spendable
- spendable_anytime.extend([p2pkh, p2pk])
- # P2SH_P2PK and P2SH_P2PKH are spendable after direct importaddress
- spendable_after_importaddress.extend([p2sh_p2pk, p2sh_p2pkh])
- # Witness output types with uncompressed keys are never seen
- unseen_anytime.extend([p2wpkh, p2sh_p2wpkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh])
-
- for i in compressed_solvable_address:
- v = self.nodes[0].getaddressinfo(i)
- if v['isscript']:
- # Multisig without private is not seen after addmultisigaddress, but seen after importaddress
- [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
- solvable_after_importaddress.extend([bare, p2sh, p2wsh, p2sh_p2wsh])
- else:
- [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
- # normal P2PKH, P2PK, P2WPKH and P2SH_P2WPKH with compressed keys should always be seen
- solvable_anytime.extend([p2pkh, p2pk, p2wpkh, p2sh_p2wpkh])
- # P2SH_P2PK, P2SH_P2PKH with compressed keys are seen after direct importaddress
- solvable_after_importaddress.extend([p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh])
-
- for i in uncompressed_solvable_address:
- v = self.nodes[0].getaddressinfo(i)
- if v['isscript']:
- [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
- # Base uncompressed multisig without private is not seen after addmultisigaddress, but seen after importaddress
- solvable_after_importaddress.extend([bare, p2sh])
- # P2WSH and P2SH(P2WSH) multisig with uncompressed keys are never seen
- unseen_anytime.extend([p2wsh, p2sh_p2wsh])
- else:
- [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
- # normal P2PKH and P2PK with uncompressed keys should always be seen
- solvable_anytime.extend([p2pkh, p2pk])
- # P2SH_P2PK, P2SH_P2PKH with uncompressed keys are seen after direct importaddress
- solvable_after_importaddress.extend([p2sh_p2pk, p2sh_p2pkh])
- # Witness output types with uncompressed keys are never seen
- unseen_anytime.extend([p2wpkh, p2sh_p2wpkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh])
-
- op1 = CScript([OP_1])
- op0 = CScript([OP_0])
- # 2N7MGY19ti4KDMSzRfPAssP6Pxyuxoi6jLe is the P2SH(P2PKH) version of mjoE3sSrb8ByYEvgnC3Aox86u1CHnfJA4V
- unsolvable_address_key = bytes.fromhex("02341AEC7587A51CDE5279E0630A531AEA2615A9F80B17E8D9376327BAEAA59E3D")
- unsolvablep2pkh = key_to_p2pkh_script(unsolvable_address_key)
- unsolvablep2wshp2pkh = script_to_p2wsh_script(unsolvablep2pkh)
- p2shop0 = script_to_p2sh_script(op0)
- p2wshop1 = script_to_p2wsh_script(op1)
- unsolvable_after_importaddress.append(unsolvablep2pkh)
- unsolvable_after_importaddress.append(unsolvablep2wshp2pkh)
- unsolvable_after_importaddress.append(op1) # OP_1 will be imported as script
- unsolvable_after_importaddress.append(p2wshop1)
- unseen_anytime.append(op0) # OP_0 will be imported as P2SH address with no script provided
- unsolvable_after_importaddress.append(p2shop0)
-
- spendable_txid = []
- solvable_txid = []
- spendable_txid.append(self.mine_and_test_listunspent(spendable_anytime, 2))
- solvable_txid.append(self.mine_and_test_listunspent(solvable_anytime, 1))
- self.mine_and_test_listunspent(spendable_after_importaddress + solvable_after_importaddress + unseen_anytime + unsolvable_after_importaddress, 0)
-
- importlist = []
- for i in compressed_spendable_address + uncompressed_spendable_address + compressed_solvable_address + uncompressed_solvable_address:
- v = self.nodes[0].getaddressinfo(i)
- if v['isscript']:
- bare = bytes.fromhex(v['hex'])
- importlist.append(bare.hex())
- importlist.append(script_to_p2wsh_script(bare).hex())
- else:
- pubkey = bytes.fromhex(v['pubkey'])
- p2pk = key_to_p2pk_script(pubkey)
- p2pkh = key_to_p2pkh_script(pubkey)
- importlist.append(p2pk.hex())
- importlist.append(p2pkh.hex())
- importlist.append(key_to_p2wpkh_script(pubkey).hex())
- importlist.append(script_to_p2wsh_script(p2pk).hex())
- importlist.append(script_to_p2wsh_script(p2pkh).hex())
-
- importlist.append(unsolvablep2pkh.hex())
- importlist.append(unsolvablep2wshp2pkh.hex())
- importlist.append(op1.hex())
- importlist.append(p2wshop1.hex())
-
- for i in importlist:
- # import all generated addresses. The wallet already has the private keys for some of these, so catch JSON RPC
- # exceptions and continue.
- try_rpc(-4, "The wallet already contains the private key for this address or script", self.nodes[0].importaddress, i, "", False, True)
-
- self.nodes[0].importaddress(script_to_p2sh(op0)) # import OP_0 as address only
- self.nodes[0].importaddress(multisig_without_privkey_address) # Test multisig_without_privkey
-
- spendable_txid.append(self.mine_and_test_listunspent(spendable_anytime + spendable_after_importaddress, 2))
- solvable_txid.append(self.mine_and_test_listunspent(solvable_anytime + solvable_after_importaddress, 1))
- self.mine_and_test_listunspent(unsolvable_after_importaddress, 1)
- self.mine_and_test_listunspent(unseen_anytime, 0)
-
- spendable_txid.append(self.mine_and_test_listunspent(spendable_anytime + spendable_after_importaddress, 2))
- solvable_txid.append(self.mine_and_test_listunspent(solvable_anytime + solvable_after_importaddress, 1))
- self.mine_and_test_listunspent(unsolvable_after_importaddress, 1)
- self.mine_and_test_listunspent(unseen_anytime, 0)
-
- # Repeat some tests. This time we don't add witness scripts with importaddress
- # Import a compressed key and an uncompressed key, generate some multisig addresses
- self.nodes[0].importprivkey("927pw6RW8ZekycnXqBQ2JS5nPyo1yRfGNN8oq74HeddWSpafDJH")
- uncompressed_spendable_address = ["mguN2vNSCEUh6rJaXoAVwY3YZwZvEmf5xi"]
- self.nodes[0].importprivkey("cMcrXaaUC48ZKpcyydfFo8PxHAjpsYLhdsp6nmtB3E2ER9UUHWnw")
- compressed_spendable_address = ["n1UNmpmbVUJ9ytXYXiurmGPQ3TRrXqPWKL"]
-
- self.nodes[0].importpubkey(pubkeys[5])
- compressed_solvable_address = [key_to_p2pkh(pubkeys[5])]
- self.nodes[0].importpubkey(pubkeys[6])
- uncompressed_solvable_address = [key_to_p2pkh(pubkeys[6])]
-
- unseen_anytime = [] # These outputs should never be seen
- solvable_anytime = [] # These outputs should be solvable after importpubkey
- unseen_anytime = [] # These outputs should never be seen
-
- uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [uncompressed_spendable_address[0], compressed_spendable_address[0]])['address'])
- uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [uncompressed_spendable_address[0], uncompressed_spendable_address[0]])['address'])
- compressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_spendable_address[0], compressed_spendable_address[0]])['address'])
- uncompressed_solvable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_solvable_address[0], uncompressed_solvable_address[0]])['address'])
- compressed_solvable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_spendable_address[0], compressed_solvable_address[0]])['address'])
-
- premature_witaddress = []
-
- for i in compressed_spendable_address:
- v = self.nodes[0].getaddressinfo(i)
- if v['isscript']:
- [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
- premature_witaddress.append(script_to_p2sh(p2wsh))
- else:
- [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
- # P2WPKH, P2SH_P2WPKH are always spendable
- spendable_anytime.extend([p2wpkh, p2sh_p2wpkh])
-
- for i in uncompressed_spendable_address + uncompressed_solvable_address:
- v = self.nodes[0].getaddressinfo(i)
- if v['isscript']:
- [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
- # P2WSH and P2SH(P2WSH) multisig with uncompressed keys are never seen
- unseen_anytime.extend([p2wsh, p2sh_p2wsh])
- else:
- [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
- # P2WPKH, P2SH_P2WPKH with uncompressed keys are never seen
- unseen_anytime.extend([p2wpkh, p2sh_p2wpkh])
-
- for i in compressed_solvable_address:
- v = self.nodes[0].getaddressinfo(i)
- if v['isscript']:
- [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
- premature_witaddress.append(script_to_p2sh(p2wsh))
- else:
- [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
- # P2SH_P2PK, P2SH_P2PKH with compressed keys are always solvable
- solvable_anytime.extend([p2wpkh, p2sh_p2wpkh])
-
- self.mine_and_test_listunspent(spendable_anytime, 2)
- self.mine_and_test_listunspent(solvable_anytime, 1)
- self.mine_and_test_listunspent(unseen_anytime, 0)
-
- # Check that createrawtransaction/decoderawtransaction with non-v0 Bech32 works
- v1_addr = program_to_witness(1, [3, 5])
- v1_tx = self.nodes[0].createrawtransaction([getutxo(spendable_txid[0])], {v1_addr: 1})
- v1_decoded = self.nodes[1].decoderawtransaction(v1_tx)
- assert_equal(v1_decoded['vout'][0]['scriptPubKey']['address'], v1_addr)
- assert_equal(v1_decoded['vout'][0]['scriptPubKey']['hex'], "51020305")
-
- # Check that spendable outputs are really spendable
- self.create_and_mine_tx_from_txids(spendable_txid)
-
- # import all the private keys so solvable addresses become spendable
- self.nodes[0].importprivkey("cPiM8Ub4heR9NBYmgVzJQiUH1if44GSBGiqaeJySuL2BKxubvgwb")
- self.nodes[0].importprivkey("cPpAdHaD6VoYbW78kveN2bsvb45Q7G5PhaPApVUGwvF8VQ9brD97")
- self.nodes[0].importprivkey("91zqCU5B9sdWxzMt1ca3VzbtVm2YM6Hi5Rxn4UDtxEaN9C9nzXV")
- self.nodes[0].importprivkey("cPQFjcVRpAUBG8BA9hzr2yEzHwKoMgLkJZBBtK9vJnvGJgMjzTbd")
- self.nodes[0].importprivkey("cQGtcm34xiLjB1v7bkRa4V3aAc9tS2UTuBZ1UnZGeSeNy627fN66")
- self.nodes[0].importprivkey("cTW5mR5M45vHxXkeChZdtSPozrFwFgmEvTNnanCW6wrqwaCZ1X7K")
- self.create_and_mine_tx_from_txids(solvable_txid)
-
- # Test that importing native P2WPKH/P2WSH scripts works
- for use_p2wsh in [False, True]:
- if use_p2wsh:
- scriptPubKey = "00203a59f3f56b713fdcf5d1a57357f02c44342cbf306ffe0c4741046837bf90561a"
- transaction = "01000000000100e1f505000000002200203a59f3f56b713fdcf5d1a57357f02c44342cbf306ffe0c4741046837bf90561a00000000"
- else:
- scriptPubKey = "a9142f8c469c2f0084c48e11f998ffbe7efa7549f26d87"
- transaction = "01000000000100e1f5050000000017a9142f8c469c2f0084c48e11f998ffbe7efa7549f26d8700000000"
-
- self.nodes[1].importaddress(scriptPubKey, "", False)
- rawtxfund = self.nodes[1].fundrawtransaction(transaction)['hex']
- rawtxfund = self.nodes[1].signrawtransactionwithwallet(rawtxfund)["hex"]
- txid = self.nodes[1].sendrawtransaction(rawtxfund)
-
- assert_equal(self.nodes[1].gettransaction(txid, True)["txid"], txid)
- assert_equal(self.nodes[1].listtransactions("*", 1, 0, True)[0]["txid"], txid)
-
- # Assert it is properly saved
- self.restart_node(1)
- assert_equal(self.nodes[1].gettransaction(txid, True)["txid"], txid)
- assert_equal(self.nodes[1].listtransactions("*", 1, 0, True)[0]["txid"], txid)
+ if not self.options.descriptors:
+ self.log.info("Verify behaviour of importaddress and listunspent")
+
+ # Some public keys to be used later
+ pubkeys = [
+ "0363D44AABD0F1699138239DF2F042C3282C0671CC7A76826A55C8203D90E39242", # cPiM8Ub4heR9NBYmgVzJQiUH1if44GSBGiqaeJySuL2BKxubvgwb
+ "02D3E626B3E616FC8662B489C123349FECBFC611E778E5BE739B257EAE4721E5BF", # cPpAdHaD6VoYbW78kveN2bsvb45Q7G5PhaPApVUGwvF8VQ9brD97
+ "04A47F2CBCEFFA7B9BCDA184E7D5668D3DA6F9079AD41E422FA5FD7B2D458F2538A62F5BD8EC85C2477F39650BD391EA6250207065B2A81DA8B009FC891E898F0E", # 91zqCU5B9sdWxzMt1ca3VzbtVm2YM6Hi5Rxn4UDtxEaN9C9nzXV
+ "02A47F2CBCEFFA7B9BCDA184E7D5668D3DA6F9079AD41E422FA5FD7B2D458F2538", # cPQFjcVRpAUBG8BA9hzr2yEzHwKoMgLkJZBBtK9vJnvGJgMjzTbd
+ "036722F784214129FEB9E8129D626324F3F6716555B603FFE8300BBCB882151228", # cQGtcm34xiLjB1v7bkRa4V3aAc9tS2UTuBZ1UnZGeSeNy627fN66
+ "0266A8396EE936BF6D99D17920DB21C6C7B1AB14C639D5CD72B300297E416FD2EC", # cTW5mR5M45vHxXkeChZdtSPozrFwFgmEvTNnanCW6wrqwaCZ1X7K
+ "0450A38BD7F0AC212FEBA77354A9B036A32E0F7C81FC4E0C5ADCA7C549C4505D2522458C2D9AE3CEFD684E039194B72C8A10F9CB9D4764AB26FCC2718D421D3B84", # 92h2XPssjBpsJN5CqSP7v9a7cf2kgDunBC6PDFwJHMACM1rrVBJ
+ ]
+
+ # Import a compressed key and an uncompressed key, generate some multisig addresses
+ self.nodes[0].importprivkey("92e6XLo5jVAVwrQKPNTs93oQco8f8sDNBcpv73Dsrs397fQtFQn")
+ uncompressed_spendable_address = ["mvozP4UwyGD2mGZU4D2eMvMLPB9WkMmMQu"]
+ self.nodes[0].importprivkey("cNC8eQ5dg3mFAVePDX4ddmPYpPbw41r9bm2jd1nLJT77e6RrzTRR")
+ compressed_spendable_address = ["mmWQubrDomqpgSYekvsU7HWEVjLFHAakLe"]
+ assert not self.nodes[0].getaddressinfo(uncompressed_spendable_address[0])['iscompressed']
+ assert self.nodes[0].getaddressinfo(compressed_spendable_address[0])['iscompressed']
+
+ self.nodes[0].importpubkey(pubkeys[0])
+ compressed_solvable_address = [key_to_p2pkh(pubkeys[0])]
+ self.nodes[0].importpubkey(pubkeys[1])
+ compressed_solvable_address.append(key_to_p2pkh(pubkeys[1]))
+ self.nodes[0].importpubkey(pubkeys[2])
+ uncompressed_solvable_address = [key_to_p2pkh(pubkeys[2])]
+
+ spendable_anytime = [] # These outputs should be seen anytime after importprivkey and addmultisigaddress
+ spendable_after_importaddress = [] # These outputs should be seen after importaddress
+ solvable_after_importaddress = [] # These outputs should be seen after importaddress but not spendable
+ unsolvable_after_importaddress = [] # These outputs should be unsolvable after importaddress
+ solvable_anytime = [] # These outputs should be solvable after importpubkey
+ unseen_anytime = [] # These outputs should never be seen
+
+ uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [uncompressed_spendable_address[0], compressed_spendable_address[0]])['address'])
+ uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [uncompressed_spendable_address[0], uncompressed_spendable_address[0]])['address'])
+ compressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_spendable_address[0], compressed_spendable_address[0]])['address'])
+ uncompressed_solvable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_spendable_address[0], uncompressed_solvable_address[0]])['address'])
+ compressed_solvable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_spendable_address[0], compressed_solvable_address[0]])['address'])
+ compressed_solvable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_solvable_address[0], compressed_solvable_address[1]])['address'])
+
+ # Test multisig_without_privkey
+ # We have 2 public keys without private keys, use addmultisigaddress to add to wallet.
+ # Money sent to P2SH of multisig of this should only be seen after importaddress with the BASE58 P2SH address.
+
+ multisig_without_privkey_address = self.nodes[0].addmultisigaddress(2, [pubkeys[3], pubkeys[4]])['address']
+ script = keys_to_multisig_script([pubkeys[3], pubkeys[4]])
+ solvable_after_importaddress.append(script_to_p2sh_script(script))
+
+ for i in compressed_spendable_address:
+ v = self.nodes[0].getaddressinfo(i)
+ if v['isscript']:
+ [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
+ # p2sh multisig with compressed keys should always be spendable
+ spendable_anytime.extend([p2sh])
+ # bare multisig can be watched and signed, but is not treated as ours
+ solvable_after_importaddress.extend([bare])
+ # P2WSH and P2SH(P2WSH) multisig with compressed keys are spendable after direct importaddress
+ spendable_after_importaddress.extend([p2wsh, p2sh_p2wsh])
+ else:
+ [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
+ # normal P2PKH and P2PK with compressed keys should always be spendable
+ spendable_anytime.extend([p2pkh, p2pk])
+ # P2SH_P2PK, P2SH_P2PKH with compressed keys are spendable after direct importaddress
+ spendable_after_importaddress.extend([p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh])
+ # P2WPKH and P2SH_P2WPKH with compressed keys should always be spendable
+ spendable_anytime.extend([p2wpkh, p2sh_p2wpkh])
+
+ for i in uncompressed_spendable_address:
+ v = self.nodes[0].getaddressinfo(i)
+ if v['isscript']:
+ [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
+ # p2sh multisig with uncompressed keys should always be spendable
+ spendable_anytime.extend([p2sh])
+ # bare multisig can be watched and signed, but is not treated as ours
+ solvable_after_importaddress.extend([bare])
+ # P2WSH and P2SH(P2WSH) multisig with uncompressed keys are never seen
+ unseen_anytime.extend([p2wsh, p2sh_p2wsh])
+ else:
+ [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
+ # normal P2PKH and P2PK with uncompressed keys should always be spendable
+ spendable_anytime.extend([p2pkh, p2pk])
+ # P2SH_P2PK and P2SH_P2PKH are spendable after direct importaddress
+ spendable_after_importaddress.extend([p2sh_p2pk, p2sh_p2pkh])
+ # Witness output types with uncompressed keys are never seen
+ unseen_anytime.extend([p2wpkh, p2sh_p2wpkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh])
+
+ for i in compressed_solvable_address:
+ v = self.nodes[0].getaddressinfo(i)
+ if v['isscript']:
+ # Multisig without private is not seen after addmultisigaddress, but seen after importaddress
+ [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
+ solvable_after_importaddress.extend([bare, p2sh, p2wsh, p2sh_p2wsh])
+ else:
+ [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
+ # normal P2PKH, P2PK, P2WPKH and P2SH_P2WPKH with compressed keys should always be seen
+ solvable_anytime.extend([p2pkh, p2pk, p2wpkh, p2sh_p2wpkh])
+ # P2SH_P2PK, P2SH_P2PKH with compressed keys are seen after direct importaddress
+ solvable_after_importaddress.extend([p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh])
+
+ for i in uncompressed_solvable_address:
+ v = self.nodes[0].getaddressinfo(i)
+ if v['isscript']:
+ [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
+ # Base uncompressed multisig without private is not seen after addmultisigaddress, but seen after importaddress
+ solvable_after_importaddress.extend([bare, p2sh])
+ # P2WSH and P2SH(P2WSH) multisig with uncompressed keys are never seen
+ unseen_anytime.extend([p2wsh, p2sh_p2wsh])
+ else:
+ [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
+ # normal P2PKH and P2PK with uncompressed keys should always be seen
+ solvable_anytime.extend([p2pkh, p2pk])
+ # P2SH_P2PK, P2SH_P2PKH with uncompressed keys are seen after direct importaddress
+ solvable_after_importaddress.extend([p2sh_p2pk, p2sh_p2pkh])
+ # Witness output types with uncompressed keys are never seen
+ unseen_anytime.extend([p2wpkh, p2sh_p2wpkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh])
+
+ op1 = CScript([OP_1])
+ op0 = CScript([OP_0])
+ # 2N7MGY19ti4KDMSzRfPAssP6Pxyuxoi6jLe is the P2SH(P2PKH) version of mjoE3sSrb8ByYEvgnC3Aox86u1CHnfJA4V
+ unsolvable_address_key = bytes.fromhex("02341AEC7587A51CDE5279E0630A531AEA2615A9F80B17E8D9376327BAEAA59E3D")
+ unsolvablep2pkh = key_to_p2pkh_script(unsolvable_address_key)
+ unsolvablep2wshp2pkh = script_to_p2wsh_script(unsolvablep2pkh)
+ p2shop0 = script_to_p2sh_script(op0)
+ p2wshop1 = script_to_p2wsh_script(op1)
+ unsolvable_after_importaddress.append(unsolvablep2pkh)
+ unsolvable_after_importaddress.append(unsolvablep2wshp2pkh)
+ unsolvable_after_importaddress.append(op1) # OP_1 will be imported as script
+ unsolvable_after_importaddress.append(p2wshop1)
+ unseen_anytime.append(op0) # OP_0 will be imported as P2SH address with no script provided
+ unsolvable_after_importaddress.append(p2shop0)
+
+ spendable_txid = []
+ solvable_txid = []
+ spendable_txid.append(self.mine_and_test_listunspent(spendable_anytime, 2))
+ solvable_txid.append(self.mine_and_test_listunspent(solvable_anytime, 1))
+ self.mine_and_test_listunspent(spendable_after_importaddress + solvable_after_importaddress + unseen_anytime + unsolvable_after_importaddress, 0)
+
+ importlist = []
+ for i in compressed_spendable_address + uncompressed_spendable_address + compressed_solvable_address + uncompressed_solvable_address:
+ v = self.nodes[0].getaddressinfo(i)
+ if v['isscript']:
+ bare = bytes.fromhex(v['hex'])
+ importlist.append(bare.hex())
+ importlist.append(script_to_p2wsh_script(bare).hex())
+ else:
+ pubkey = bytes.fromhex(v['pubkey'])
+ p2pk = key_to_p2pk_script(pubkey)
+ p2pkh = key_to_p2pkh_script(pubkey)
+ importlist.append(p2pk.hex())
+ importlist.append(p2pkh.hex())
+ importlist.append(key_to_p2wpkh_script(pubkey).hex())
+ importlist.append(script_to_p2wsh_script(p2pk).hex())
+ importlist.append(script_to_p2wsh_script(p2pkh).hex())
+
+ importlist.append(unsolvablep2pkh.hex())
+ importlist.append(unsolvablep2wshp2pkh.hex())
+ importlist.append(op1.hex())
+ importlist.append(p2wshop1.hex())
+
+ for i in importlist:
+ # import all generated addresses. The wallet already has the private keys for some of these, so catch JSON RPC
+ # exceptions and continue.
+ try_rpc(-4, "The wallet already contains the private key for this address or script", self.nodes[0].importaddress, i, "", False, True)
+
+ self.nodes[0].importaddress(script_to_p2sh(op0)) # import OP_0 as address only
+ self.nodes[0].importaddress(multisig_without_privkey_address) # Test multisig_without_privkey
+
+ spendable_txid.append(self.mine_and_test_listunspent(spendable_anytime + spendable_after_importaddress, 2))
+ solvable_txid.append(self.mine_and_test_listunspent(solvable_anytime + solvable_after_importaddress, 1))
+ self.mine_and_test_listunspent(unsolvable_after_importaddress, 1)
+ self.mine_and_test_listunspent(unseen_anytime, 0)
+
+ spendable_txid.append(self.mine_and_test_listunspent(spendable_anytime + spendable_after_importaddress, 2))
+ solvable_txid.append(self.mine_and_test_listunspent(solvable_anytime + solvable_after_importaddress, 1))
+ self.mine_and_test_listunspent(unsolvable_after_importaddress, 1)
+ self.mine_and_test_listunspent(unseen_anytime, 0)
+
+ # Repeat some tests. This time we don't add witness scripts with importaddress
+ # Import a compressed key and an uncompressed key, generate some multisig addresses
+ self.nodes[0].importprivkey("927pw6RW8ZekycnXqBQ2JS5nPyo1yRfGNN8oq74HeddWSpafDJH")
+ uncompressed_spendable_address = ["mguN2vNSCEUh6rJaXoAVwY3YZwZvEmf5xi"]
+ self.nodes[0].importprivkey("cMcrXaaUC48ZKpcyydfFo8PxHAjpsYLhdsp6nmtB3E2ER9UUHWnw")
+ compressed_spendable_address = ["n1UNmpmbVUJ9ytXYXiurmGPQ3TRrXqPWKL"]
+
+ self.nodes[0].importpubkey(pubkeys[5])
+ compressed_solvable_address = [key_to_p2pkh(pubkeys[5])]
+ self.nodes[0].importpubkey(pubkeys[6])
+ uncompressed_solvable_address = [key_to_p2pkh(pubkeys[6])]
+
+ unseen_anytime = [] # These outputs should never be seen
+ solvable_anytime = [] # These outputs should be solvable after importpubkey
+ unseen_anytime = [] # These outputs should never be seen
+
+ uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [uncompressed_spendable_address[0], compressed_spendable_address[0]])['address'])
+ uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [uncompressed_spendable_address[0], uncompressed_spendable_address[0]])['address'])
+ compressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_spendable_address[0], compressed_spendable_address[0]])['address'])
+ uncompressed_solvable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_solvable_address[0], uncompressed_solvable_address[0]])['address'])
+ compressed_solvable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_spendable_address[0], compressed_solvable_address[0]])['address'])
+
+ premature_witaddress = []
+
+ for i in compressed_spendable_address:
+ v = self.nodes[0].getaddressinfo(i)
+ if v['isscript']:
+ [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
+ premature_witaddress.append(script_to_p2sh(p2wsh))
+ else:
+ [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
+ # P2WPKH, P2SH_P2WPKH are always spendable
+ spendable_anytime.extend([p2wpkh, p2sh_p2wpkh])
+
+ for i in uncompressed_spendable_address + uncompressed_solvable_address:
+ v = self.nodes[0].getaddressinfo(i)
+ if v['isscript']:
+ [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
+ # P2WSH and P2SH(P2WSH) multisig with uncompressed keys are never seen
+ unseen_anytime.extend([p2wsh, p2sh_p2wsh])
+ else:
+ [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
+ # P2WPKH, P2SH_P2WPKH with uncompressed keys are never seen
+ unseen_anytime.extend([p2wpkh, p2sh_p2wpkh])
+
+ for i in compressed_solvable_address:
+ v = self.nodes[0].getaddressinfo(i)
+ if v['isscript']:
+ [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
+ premature_witaddress.append(script_to_p2sh(p2wsh))
+ else:
+ [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
+ # P2SH_P2PK, P2SH_P2PKH with compressed keys are always solvable
+ solvable_anytime.extend([p2wpkh, p2sh_p2wpkh])
+
+ self.mine_and_test_listunspent(spendable_anytime, 2)
+ self.mine_and_test_listunspent(solvable_anytime, 1)
+ self.mine_and_test_listunspent(unseen_anytime, 0)
+
+ # Check that createrawtransaction/decoderawtransaction with non-v0 Bech32 works
+ v1_addr = program_to_witness(1, [3, 5])
+ v1_tx = self.nodes[0].createrawtransaction([getutxo(spendable_txid[0])], {v1_addr: 1})
+ v1_decoded = self.nodes[1].decoderawtransaction(v1_tx)
+ assert_equal(v1_decoded['vout'][0]['scriptPubKey']['address'], v1_addr)
+ assert_equal(v1_decoded['vout'][0]['scriptPubKey']['hex'], "51020305")
+
+ # Check that spendable outputs are really spendable
+ self.create_and_mine_tx_from_txids(spendable_txid)
+
+ # import all the private keys so solvable addresses become spendable
+ self.nodes[0].importprivkey("cPiM8Ub4heR9NBYmgVzJQiUH1if44GSBGiqaeJySuL2BKxubvgwb")
+ self.nodes[0].importprivkey("cPpAdHaD6VoYbW78kveN2bsvb45Q7G5PhaPApVUGwvF8VQ9brD97")
+ self.nodes[0].importprivkey("91zqCU5B9sdWxzMt1ca3VzbtVm2YM6Hi5Rxn4UDtxEaN9C9nzXV")
+ self.nodes[0].importprivkey("cPQFjcVRpAUBG8BA9hzr2yEzHwKoMgLkJZBBtK9vJnvGJgMjzTbd")
+ self.nodes[0].importprivkey("cQGtcm34xiLjB1v7bkRa4V3aAc9tS2UTuBZ1UnZGeSeNy627fN66")
+ self.nodes[0].importprivkey("cTW5mR5M45vHxXkeChZdtSPozrFwFgmEvTNnanCW6wrqwaCZ1X7K")
+ self.create_and_mine_tx_from_txids(solvable_txid)
+
+ # Test that importing native P2WPKH/P2WSH scripts works
+ for use_p2wsh in [False, True]:
+ if use_p2wsh:
+ scriptPubKey = "00203a59f3f56b713fdcf5d1a57357f02c44342cbf306ffe0c4741046837bf90561a"
+ transaction = "01000000000100e1f505000000002200203a59f3f56b713fdcf5d1a57357f02c44342cbf306ffe0c4741046837bf90561a00000000"
+ else:
+ scriptPubKey = "a9142f8c469c2f0084c48e11f998ffbe7efa7549f26d87"
+ transaction = "01000000000100e1f5050000000017a9142f8c469c2f0084c48e11f998ffbe7efa7549f26d8700000000"
+
+ self.nodes[1].importaddress(scriptPubKey, "", False)
+ rawtxfund = self.nodes[1].fundrawtransaction(transaction)['hex']
+ rawtxfund = self.nodes[1].signrawtransactionwithwallet(rawtxfund)["hex"]
+ txid = self.nodes[1].sendrawtransaction(rawtxfund)
+
+ assert_equal(self.nodes[1].gettransaction(txid, True)["txid"], txid)
+ assert_equal(self.nodes[1].listtransactions("*", 1, 0, True)[0]["txid"], txid)
+
+ # Assert it is properly saved
+ self.restart_node(1)
+ assert_equal(self.nodes[1].gettransaction(txid, True)["txid"], txid)
+ assert_equal(self.nodes[1].listtransactions("*", 1, 0, True)[0]["txid"], txid)
def mine_and_test_listunspent(self, script_list, ismine):
utxo = find_spendable_utxo(self.nodes[0], 50)
diff --git a/test/functional/interface_rest.py b/test/functional/interface_rest.py
index e0716fc54a..868bb42604 100755
--- a/test/functional/interface_rest.py
+++ b/test/functional/interface_rest.py
@@ -279,6 +279,13 @@ class RESTTest (BitcoinTestFramework):
json_obj = self.test_rest_request(f"/headers/5/{bb_hash}")
assert_equal(len(json_obj), 5) # now we should have 5 header objects
+ # Test number parsing
+ for num in ['5a', '-5', '0', '2001', '99999999999999999999999999999999999']:
+ assert_equal(
+ bytes(f'Header count out of range: {num}\r\n', 'ascii'),
+ self.test_rest_request(f"/headers/{num}/{bb_hash}", ret_type=RetType.BYTES, status=400),
+ )
+
self.log.info("Test tx inclusion in the /mempool and /block URIs")
# Make 3 tx and mine them on node 1
@@ -311,6 +318,15 @@ class RESTTest (BitcoinTestFramework):
if 'coinbase' not in tx['vin'][0]}
assert_equal(non_coinbase_txs, set(txs))
+ # Verify that the non-coinbase tx has "prevout" key set
+ for tx_obj in json_obj["tx"]:
+ for vin in tx_obj["vin"]:
+ if "coinbase" not in vin:
+ assert "prevout" in vin
+ assert_equal(vin["prevout"]["generated"], False)
+ else:
+ assert "prevout" not in vin
+
# Check the same but without tx details
json_obj = self.test_rest_request(f"/block/notxdetails/{newblockhash[0]}")
for tx in txs:
diff --git a/test/functional/mempool_accept.py b/test/functional/mempool_accept.py
index 2ee440bcb7..71be2b4a82 100755
--- a/test/functional/mempool_accept.py
+++ b/test/functional/mempool_accept.py
@@ -22,13 +22,11 @@ from test_framework.messages import (
from test_framework.script import (
CScript,
OP_0,
- OP_2,
- OP_3,
- OP_CHECKMULTISIG,
OP_HASH160,
OP_RETURN,
)
from test_framework.script_util import (
+ keys_to_multisig_script,
script_to_p2sh_script,
)
from test_framework.util import (
@@ -283,7 +281,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
key = ECKey()
key.generate()
pubkey = key.get_pubkey().get_bytes()
- tx.vout[0].scriptPubKey = CScript([OP_2, pubkey, pubkey, pubkey, OP_3, OP_CHECKMULTISIG]) # Some bare multisig script (2-of-3)
+ tx.vout[0].scriptPubKey = keys_to_multisig_script([pubkey] * 3, k=2) # Some bare multisig script (2-of-3)
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'bare-multisig'}],
rawtxs=[tx.serialize().hex()],
diff --git a/test/functional/mempool_limit.py b/test/functional/mempool_limit.py
index c82dbb3f3d..79f6f9dc70 100755
--- a/test/functional/mempool_limit.py
+++ b/test/functional/mempool_limit.py
@@ -7,8 +7,14 @@
from decimal import Decimal
from test_framework.blocktools import COINBASE_MATURITY
+from test_framework.messages import COIN
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, assert_greater_than, assert_raises_rpc_error, gen_return_txouts
+from test_framework.util import (
+ assert_equal,
+ assert_greater_than,
+ assert_raises_rpc_error,
+ gen_return_txouts,
+)
from test_framework.wallet import MiniWallet
@@ -23,16 +29,19 @@ class MempoolLimitTest(BitcoinTestFramework):
]]
self.supports_cli = False
- def send_large_txs(self, node, miniwallet, txouts, fee_rate, tx_batch_size):
+ def send_large_txs(self, node, miniwallet, txouts, fee, tx_batch_size):
for _ in range(tx_batch_size):
- tx = miniwallet.create_self_transfer(from_node=node, fee_rate=fee_rate)['tx']
+ tx = miniwallet.create_self_transfer(from_node=node, fee_rate=0, mempool_valid=False)['tx']
for txout in txouts:
tx.vout.append(txout)
+ tx.vout[0].nValue -= int(fee * COIN)
+ res = node.testmempoolaccept([tx.serialize().hex()])[0]
+ assert_equal(res['fees']['base'], fee)
miniwallet.sendrawtransaction(from_node=node, tx_hex=tx.serialize().hex())
def run_test(self):
txouts = gen_return_txouts()
- node=self.nodes[0]
+ node = self.nodes[0]
miniwallet = MiniWallet(node)
relayfee = node.getnetworkinfo()['relayfee']
@@ -54,13 +63,15 @@ class MempoolLimitTest(BitcoinTestFramework):
self.log.info('Create a mempool tx that will be evicted')
tx_to_be_evicted_id = miniwallet.send_self_transfer(from_node=node, fee_rate=relayfee)["txid"]
- # Increase the tx fee rate massively to give the subsequent transactions a higher priority in the mempool
- base_fee = relayfee * 1000
+ # Increase the tx fee rate to give the subsequent transactions a higher priority in the mempool
+ # The tx has an approx. vsize of 65k, i.e. multiplying the previous fee rate (in sats/kvB)
+ # by 130 should result in a fee that corresponds to 2x of that fee rate
+ base_fee = relayfee * 130
self.log.info("Fill up the mempool with txs with higher fee rate")
for batch_of_txid in range(num_of_batches):
- fee_rate=(batch_of_txid + 1) * base_fee
- self.send_large_txs(node, miniwallet, txouts, fee_rate, tx_batch_size)
+ fee = (batch_of_txid + 1) * base_fee
+ self.send_large_txs(node, miniwallet, txouts, fee, tx_batch_size)
self.log.info('The tx should be evicted by now')
# The number of transactions created should be greater than the ones present in the mempool
diff --git a/test/functional/mempool_package_limits.py b/test/functional/mempool_package_limits.py
index 2217628858..89a5c83826 100755
--- a/test/functional/mempool_package_limits.py
+++ b/test/functional/mempool_package_limits.py
@@ -244,7 +244,7 @@ class MempoolPackageLimitsTest(BitcoinTestFramework):
assert_equal(txres["package-error"], "package-mempool-limits")
# Clear mempool and check that the package passes now
- node.generate(1)
+ self.generate(node, 1)
assert all([res["allowed"] for res in node.testmempoolaccept(rawtxs=package_hex)])
def test_anc_count_limits(self):
diff --git a/test/functional/mempool_packages.py b/test/functional/mempool_packages.py
index 3943bba489..ff5e45519f 100755
--- a/test/functional/mempool_packages.py
+++ b/test/functional/mempool_packages.py
@@ -65,8 +65,7 @@ class MempoolPackagesTest(BitcoinTestFramework):
value = sent_value
chain.append(txid)
# We need the wtxids to check P2P announcements
- fulltx = self.nodes[0].getrawtransaction(txid)
- witnesstx = self.nodes[0].decoderawtransaction(fulltx, True)
+ witnesstx = self.nodes[0].gettransaction(txid=txid, verbose=True)['decoded']
witness_chain.append(witnesstx['hash'])
# Check that listunspent ancestor{count, size, fees} yield the correct results
diff --git a/test/functional/mining_prioritisetransaction.py b/test/functional/mining_prioritisetransaction.py
index da85ee54be..35274d3500 100755
--- a/test/functional/mining_prioritisetransaction.py
+++ b/test/functional/mining_prioritisetransaction.py
@@ -13,7 +13,7 @@ from test_framework.util import assert_equal, assert_raises_rpc_error, create_co
class PrioritiseTransactionTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
- self.num_nodes = 2
+ self.num_nodes = 1
self.extra_args = [[
"-printpriority=1",
"-acceptnonstdtxn=1",
diff --git a/test/functional/p2p_addr_relay.py b/test/functional/p2p_addr_relay.py
index 15b90fa61f..9df74ad3a0 100755
--- a/test/functional/p2p_addr_relay.py
+++ b/test/functional/p2p_addr_relay.py
@@ -152,7 +152,6 @@ class AddrTest(BitcoinTestFramework):
msg = self.setup_addr_msg(num_ipv4_addrs)
with self.nodes[0].assert_debug_log(
[
- 'Added {} addresses from 127.0.0.1: 0 tried'.format(num_ipv4_addrs),
'received: addr (301 bytes) peer=1',
]
):
diff --git a/test/functional/p2p_addrv2_relay.py b/test/functional/p2p_addrv2_relay.py
index 3833c58680..f4be893d2c 100755
--- a/test/functional/p2p_addrv2_relay.py
+++ b/test/functional/p2p_addrv2_relay.py
@@ -72,9 +72,6 @@ class AddrTest(BitcoinTestFramework):
addr_receiver = self.nodes[0].add_p2p_connection(AddrReceiver())
msg.addrs = ADDRS
with self.nodes[0].assert_debug_log([
- # The I2P address is not added to node's own addrman because it has no
- # I2P reachability (thus 10 - 1 = 9).
- 'Added 9 addresses from 127.0.0.1: 0 tried',
'received: addrv2 (159 bytes) peer=0',
'sending addrv2 (159 bytes) peer=1',
]):
diff --git a/test/functional/p2p_invalid_messages.py b/test/functional/p2p_invalid_messages.py
index f3b80abb59..82c7e94c59 100755
--- a/test/functional/p2p_invalid_messages.py
+++ b/test/functional/p2p_invalid_messages.py
@@ -209,7 +209,7 @@ class InvalidMessagesTest(BitcoinTestFramework):
self.test_addrv2('unrecognized network',
[
'received: addrv2 (25 bytes)',
- 'IP 9.9.9.9 mapped',
+ '9.9.9.9:8333 mapped',
'Added 1 addresses',
],
bytes.fromhex(
diff --git a/test/functional/p2p_ping.py b/test/functional/p2p_ping.py
index 888e986fba..d67e97acf7 100755
--- a/test/functional/p2p_ping.py
+++ b/test/functional/p2p_ping.py
@@ -30,11 +30,16 @@ class NodeNoPong(P2PInterface):
pass
+TIMEOUT_INTERVAL = 20 * 60
+
+
class PingPongTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 1
- self.extra_args = [['-peertimeout=3']]
+ # Set the peer connection timeout low. It does not matter for this
+ # test, as long as it is less than TIMEOUT_INTERVAL.
+ self.extra_args = [['-peertimeout=1']]
def check_peer_info(self, *, pingtime, minping, pingwait):
stats = self.nodes[0].getpeerinfo()[0]
@@ -110,8 +115,11 @@ class PingPongTest(BitcoinTestFramework):
self.nodes[0].ping()
no_pong_node.wait_until(lambda: 'ping' in no_pong_node.last_message)
with self.nodes[0].assert_debug_log(['ping timeout: 1201.000000s']):
- self.mock_forward(20 * 60 + 1)
- time.sleep(4) # peertimeout + 1
+ self.mock_forward(TIMEOUT_INTERVAL // 2)
+ # Check that sending a ping does not prevent the disconnect
+ no_pong_node.sync_with_ping()
+ self.mock_forward(TIMEOUT_INTERVAL // 2 + 1)
+ no_pong_node.wait_for_disconnect()
if __name__ == '__main__':
diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py
index 4e21d08e5c..5195c9fd04 100755
--- a/test/functional/p2p_segwit.py
+++ b/test/functional/p2p_segwit.py
@@ -8,7 +8,12 @@ import random
import struct
import time
-from test_framework.blocktools import create_block, create_coinbase, add_witness_commitment, WITNESS_COMMITMENT_HEADER
+from test_framework.blocktools import (
+ WITNESS_COMMITMENT_HEADER,
+ add_witness_commitment,
+ create_block,
+ create_coinbase,
+)
from test_framework.key import ECKey
from test_framework.messages import (
BIP125_SEQUENCE_NUMBER,
@@ -194,7 +199,7 @@ class SegWitTest(BitcoinTestFramework):
self.num_nodes = 2
# This test tests SegWit both pre and post-activation, so use the normal BIP9 activation.
self.extra_args = [
- ["-acceptnonstdtxn=1", f"-testactivationheight=segwit@{SEGWIT_HEIGHT}", "-whitelist=noban@127.0.0.1"],
+ ["-acceptnonstdtxn=1", f"-testactivationheight=segwit@{SEGWIT_HEIGHT}", "-whitelist=noban@127.0.0.1", "-par=1"],
["-acceptnonstdtxn=0", f"-testactivationheight=segwit@{SEGWIT_HEIGHT}"],
]
self.supports_cli = False
@@ -505,8 +510,8 @@ class SegWitTest(BitcoinTestFramework):
# 'block-validation-failed' (if script check threads > 1) or
# 'non-mandatory-script-verify-flag (Witness program was passed an
# empty witness)' (otherwise).
- # TODO: support multiple acceptable reject reasons.
- test_witness_block(self.nodes[0], self.test_node, block, accepted=False, with_witness=False)
+ test_witness_block(self.nodes[0], self.test_node, block, accepted=False, with_witness=False,
+ reason='non-mandatory-script-verify-flag (Witness program was passed an empty witness)')
self.utxo.pop(0)
self.utxo.append(UTXO(txid, 2, value))
@@ -786,7 +791,7 @@ class SegWitTest(BitcoinTestFramework):
block_3.rehash()
block_3.solve()
- test_witness_block(self.nodes[0], self.test_node, block_3, accepted=False)
+ test_witness_block(self.nodes[0], self.test_node, block_3, accepted=False, reason='bad-witness-merkle-match')
# Add a different commitment with different nonce, but in the
# right location, and with some funds burned(!).
@@ -852,7 +857,7 @@ class SegWitTest(BitcoinTestFramework):
# Change the nonce -- should not cause the block to be permanently
# failed
block.vtx[0].wit.vtxinwit[0].scriptWitness.stack = [ser_uint256(1)]
- test_witness_block(self.nodes[0], self.test_node, block, accepted=False)
+ test_witness_block(self.nodes[0], self.test_node, block, accepted=False, reason='bad-witness-merkle-match')
# Changing the witness reserved value doesn't change the block hash
block.vtx[0].wit.vtxinwit[0].scriptWitness.stack = [ser_uint256(0)]
@@ -861,7 +866,7 @@ class SegWitTest(BitcoinTestFramework):
@subtest # type: ignore
def test_witness_block_size(self):
# TODO: Test that non-witness carrying blocks can't exceed 1MB
- # Skipping this test for now; this is covered in p2p-fullblocktest.py
+ # Skipping this test for now; this is covered in feature_block.py
# Test that witness-bearing blocks are limited at ceil(base + wit/4) <= 1MB.
block = self.build_next_block()
@@ -917,7 +922,7 @@ class SegWitTest(BitcoinTestFramework):
# limit
assert len(block.serialize()) > 2 * 1024 * 1024
- test_witness_block(self.nodes[0], self.test_node, block, accepted=False)
+ test_witness_block(self.nodes[0], self.test_node, block, accepted=False, reason='bad-blk-weight')
# Now resize the second transaction to make the block fit.
cur_length = len(block.vtx[-1].wit.vtxinwit[0].scriptWitness.stack[0])
@@ -989,7 +994,8 @@ class SegWitTest(BitcoinTestFramework):
self.update_witness_block_with_transactions(block, [tx])
# Extra witness data should not be allowed.
- test_witness_block(self.nodes[0], self.test_node, block, accepted=False)
+ test_witness_block(self.nodes[0], self.test_node, block, accepted=False,
+ reason='non-mandatory-script-verify-flag (Witness provided for non-witness script)')
# Try extra signature data. Ok if we're not spending a witness output.
block.vtx[1].wit.vtxinwit = []
@@ -1014,7 +1020,8 @@ class SegWitTest(BitcoinTestFramework):
self.update_witness_block_with_transactions(block, [tx2])
# This has extra witness data, so it should fail.
- test_witness_block(self.nodes[0], self.test_node, block, accepted=False)
+ test_witness_block(self.nodes[0], self.test_node, block, accepted=False,
+ reason='non-mandatory-script-verify-flag (Stack size must be exactly one after execution)')
# Now get rid of the extra witness, but add extra scriptSig data
tx2.vin[0].scriptSig = CScript([OP_TRUE])
@@ -1026,7 +1033,8 @@ class SegWitTest(BitcoinTestFramework):
block.solve()
# This has extra signature data for a witness input, so it should fail.
- test_witness_block(self.nodes[0], self.test_node, block, accepted=False)
+ test_witness_block(self.nodes[0], self.test_node, block, accepted=False,
+ reason='non-mandatory-script-verify-flag (Witness requires empty scriptSig)')
# Now get rid of the extra scriptsig on the witness input, and verify
# success (even with extra scriptsig data in the non-witness input)
@@ -1064,7 +1072,8 @@ class SegWitTest(BitcoinTestFramework):
tx2.rehash()
self.update_witness_block_with_transactions(block, [tx, tx2])
- test_witness_block(self.nodes[0], self.test_node, block, accepted=False)
+ test_witness_block(self.nodes[0], self.test_node, block, accepted=False,
+ reason='non-mandatory-script-verify-flag (Push value size limit exceeded)')
# Now reduce the length of the stack element
tx2.wit.vtxinwit[0].scriptWitness.stack[0] = b'a' * (MAX_SCRIPT_ELEMENT_SIZE)
@@ -1104,7 +1113,8 @@ class SegWitTest(BitcoinTestFramework):
self.update_witness_block_with_transactions(block, [tx, tx2])
- test_witness_block(self.nodes[0], self.test_node, block, accepted=False)
+ test_witness_block(self.nodes[0], self.test_node, block, accepted=False,
+ reason='non-mandatory-script-verify-flag (Script is too big)')
# Try again with one less byte in the witness script
witness_script = CScript([b'a' * MAX_SCRIPT_ELEMENT_SIZE] * 19 + [OP_DROP] * 62 + [OP_TRUE])
@@ -1176,7 +1186,7 @@ class SegWitTest(BitcoinTestFramework):
block = self.build_next_block()
self.update_witness_block_with_transactions(block, [tx2])
- test_witness_block(self.nodes[0], self.test_node, block, accepted=False)
+ test_witness_block(self.nodes[0], self.test_node, block, accepted=False, reason='bad-txnmrklroot')
# Now try using a too short vtxinwit
tx2.wit.vtxinwit.pop()
@@ -1184,6 +1194,8 @@ class SegWitTest(BitcoinTestFramework):
block.vtx = [block.vtx[0]]
self.update_witness_block_with_transactions(block, [tx2])
+ # This block doesn't result in a specific reject reason, but an iostream exception:
+ # "Exception 'CDataStream::read(): end of data: unspecified iostream_category error' (...) caught"
test_witness_block(self.nodes[0], self.test_node, block, accepted=False)
# Now make one of the intermediate witnesses be incorrect
@@ -1193,7 +1205,8 @@ class SegWitTest(BitcoinTestFramework):
block.vtx = [block.vtx[0]]
self.update_witness_block_with_transactions(block, [tx2])
- test_witness_block(self.nodes[0], self.test_node, block, accepted=False)
+ test_witness_block(self.nodes[0], self.test_node, block, accepted=False,
+ reason='non-mandatory-script-verify-flag (Operation not valid with the current stack size)')
# Fix the broken witness and the block should be accepted.
tx2.wit.vtxinwit[5].scriptWitness.stack = [b'a', witness_script]
@@ -1415,7 +1428,7 @@ class SegWitTest(BitcoinTestFramework):
self.sync_blocks()
block2 = self.build_next_block()
self.update_witness_block_with_transactions(block2, [spend_tx])
- test_witness_block(self.nodes[0], self.test_node, block2, accepted=False)
+ test_witness_block(self.nodes[0], self.test_node, block2, accepted=False, reason='bad-txns-premature-spend-of-coinbase')
# Advancing one more block should allow the spend.
self.generate(self.nodes[0], 1)
@@ -1564,13 +1577,17 @@ class SegWitTest(BitcoinTestFramework):
# Too-large input value
sign_p2pk_witness_input(witness_script, tx, 0, hashtype, prev_utxo.nValue + 1, key)
self.update_witness_block_with_transactions(block, [tx])
- test_witness_block(self.nodes[0], self.test_node, block, accepted=False)
+ test_witness_block(self.nodes[0], self.test_node, block, accepted=False,
+ reason='non-mandatory-script-verify-flag (Script evaluated without error '
+ 'but finished with a false/empty top stack element')
# Too-small input value
sign_p2pk_witness_input(witness_script, tx, 0, hashtype, prev_utxo.nValue - 1, key)
block.vtx.pop() # remove last tx
self.update_witness_block_with_transactions(block, [tx])
- test_witness_block(self.nodes[0], self.test_node, block, accepted=False)
+ test_witness_block(self.nodes[0], self.test_node, block, accepted=False,
+ reason='non-mandatory-script-verify-flag (Script evaluated without error '
+ 'but finished with a false/empty top stack element')
# Now try correct value
sign_p2pk_witness_input(witness_script, tx, 0, hashtype, prev_utxo.nValue, key)
@@ -1672,7 +1689,8 @@ class SegWitTest(BitcoinTestFramework):
tx2.vin[0].scriptSig = CScript([signature, pubkey])
block = self.build_next_block()
self.update_witness_block_with_transactions(block, [tx, tx2])
- test_witness_block(self.nodes[0], self.test_node, block, accepted=False)
+ test_witness_block(self.nodes[0], self.test_node, block, accepted=False,
+ reason='non-mandatory-script-verify-flag (Witness requires empty scriptSig)')
# Move the signature to the witness.
block.vtx.pop()
@@ -1918,7 +1936,7 @@ class SegWitTest(BitcoinTestFramework):
block_2 = self.build_next_block()
self.update_witness_block_with_transactions(block_2, [tx2])
- test_witness_block(self.nodes[0], self.test_node, block_2, accepted=False)
+ test_witness_block(self.nodes[0], self.test_node, block_2, accepted=False, reason='bad-blk-sigops')
# Try dropping the last input in tx2, and add an output that has
# too many sigops (contributing to legacy sigop count).
@@ -1931,7 +1949,7 @@ class SegWitTest(BitcoinTestFramework):
tx2.rehash()
block_3 = self.build_next_block()
self.update_witness_block_with_transactions(block_3, [tx2])
- test_witness_block(self.nodes[0], self.test_node, block_3, accepted=False)
+ test_witness_block(self.nodes[0], self.test_node, block_3, accepted=False, reason='bad-blk-sigops')
# If we drop the last checksig in this output, the tx should succeed.
block_4 = self.build_next_block()
diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py
index c3c6ade684..eea9ee26cb 100755
--- a/test/functional/rpc_blockchain.py
+++ b/test/functional/rpc_blockchain.py
@@ -434,17 +434,55 @@ class BlockchainTest(BitcoinTestFramework):
miniwallet.send_self_transfer(fee_rate=fee_per_kb, from_node=node)
blockhash = self.generate(node, 1)[0]
- self.log.info("Test getblock with verbosity 1 doesn't include fee")
- block = node.getblock(blockhash, 1)
- assert 'fee' not in block['tx'][1]
-
- self.log.info('Test getblock with verbosity 2 includes expected fee')
- block = node.getblock(blockhash, 2)
- tx = block['tx'][1]
- assert 'fee' in tx
- assert_equal(tx['fee'], tx['vsize'] * fee_per_byte)
-
- self.log.info("Test getblock with verbosity 2 still works with pruned Undo data")
+ def assert_fee_not_in_block(verbosity):
+ block = node.getblock(blockhash, verbosity)
+ assert 'fee' not in block['tx'][1]
+
+ def assert_fee_in_block(verbosity):
+ block = node.getblock(blockhash, verbosity)
+ tx = block['tx'][1]
+ assert 'fee' in tx
+ assert_equal(tx['fee'], tx['vsize'] * fee_per_byte)
+
+ def assert_vin_contains_prevout(verbosity):
+ block = node.getblock(blockhash, verbosity)
+ tx = block["tx"][1]
+ total_vin = Decimal("0.00000000")
+ total_vout = Decimal("0.00000000")
+ for vin in tx["vin"]:
+ assert "prevout" in vin
+ assert_equal(set(vin["prevout"].keys()), set(("value", "height", "generated", "scriptPubKey")))
+ assert_equal(vin["prevout"]["generated"], True)
+ total_vin += vin["prevout"]["value"]
+ for vout in tx["vout"]:
+ total_vout += vout["value"]
+ assert_equal(total_vin, total_vout + tx["fee"])
+
+ def assert_vin_does_not_contain_prevout(verbosity):
+ block = node.getblock(blockhash, verbosity)
+ tx = block["tx"][1]
+ if isinstance(tx, str):
+ # In verbosity level 1, only the transaction hashes are written
+ pass
+ else:
+ for vin in tx["vin"]:
+ assert "prevout" not in vin
+
+ self.log.info("Test that getblock with verbosity 1 doesn't include fee")
+ assert_fee_not_in_block(1)
+
+ self.log.info('Test that getblock with verbosity 2 and 3 includes expected fee')
+ assert_fee_in_block(2)
+ assert_fee_in_block(3)
+
+ self.log.info("Test that getblock with verbosity 1 and 2 does not include prevout")
+ assert_vin_does_not_contain_prevout(1)
+ assert_vin_does_not_contain_prevout(2)
+
+ self.log.info("Test that getblock with verbosity 3 includes prevout")
+ assert_vin_contains_prevout(3)
+
+ self.log.info("Test that getblock with verbosity 2 and 3 still works with pruned Undo data")
datadir = get_datadir_path(self.options.tmpdir, 0)
self.log.info("Test getblock with invalid verbosity type returns proper error message")
@@ -458,8 +496,10 @@ class BlockchainTest(BitcoinTestFramework):
# Move instead of deleting so we can restore chain state afterwards
move_block_file('rev00000.dat', 'rev_wrong')
- block = node.getblock(blockhash, 2)
- assert 'fee' not in block['tx'][1]
+ assert_fee_not_in_block(2)
+ assert_fee_not_in_block(3)
+ assert_vin_does_not_contain_prevout(2)
+ assert_vin_does_not_contain_prevout(3)
# Restore chain state
move_block_file('rev_wrong', 'rev00000.dat')
diff --git a/test/functional/rpc_invalid_address_message.py b/test/functional/rpc_invalid_address_message.py
index e362642f0f..085f6582b5 100755
--- a/test/functional/rpc_invalid_address_message.py
+++ b/test/functional/rpc_invalid_address_message.py
@@ -29,9 +29,6 @@ class InvalidAddressErrorMessageTest(BitcoinTestFramework):
self.setup_clean_chain = True
self.num_nodes = 1
- def skip_test_if_missing_module(self):
- self.skip_if_no_wallet()
-
def test_validateaddress(self):
node = self.nodes[0]
@@ -60,6 +57,10 @@ class InvalidAddressErrorMessageTest(BitcoinTestFramework):
assert info['isvalid']
assert 'error' not in info
+ info = node.validateaddress(BECH32_INVALID_VERSION)
+ assert not info['isvalid']
+ assert_equal(info['error'], 'Invalid Bech32 address witness version')
+
# Base58
info = node.validateaddress(BASE58_INVALID_PREFIX)
assert not info['isvalid']
@@ -87,7 +88,10 @@ class InvalidAddressErrorMessageTest(BitcoinTestFramework):
def run_test(self):
self.test_validateaddress()
- self.test_getaddressinfo()
+
+ if self.is_wallet_compiled():
+ self.init_wallet(node=0)
+ self.test_getaddressinfo()
if __name__ == '__main__':
diff --git a/test/functional/rpc_misc.py b/test/functional/rpc_misc.py
index ac2a7a309b..e32e562bce 100755
--- a/test/functional/rpc_misc.py
+++ b/test/functional/rpc_misc.py
@@ -57,7 +57,7 @@ class RpcMiscTest(BitcoinTestFramework):
self.log.info("test logging rpc and help")
# Test logging RPC returns the expected number of logging categories.
- assert_equal(len(node.logging()), 26)
+ assert_equal(len(node.logging()), 27)
# Test toggling a logging category on/off/on with the logging RPC.
assert_equal(node.logging()['qt'], True)
diff --git a/test/functional/test_framework/blocktools.py b/test/functional/test_framework/blocktools.py
index 85e3c2a383..5d0113465f 100644
--- a/test/functional/test_framework/blocktools.py
+++ b/test/functional/test_framework/blocktools.py
@@ -32,13 +32,13 @@ from .script import (
CScriptNum,
CScriptOp,
OP_1,
- OP_CHECKMULTISIG,
OP_RETURN,
OP_TRUE,
)
from .script_util import (
key_to_p2pk_script,
key_to_p2wpkh_script,
+ keys_to_multisig_script,
script_to_p2wsh_script,
)
from .util import assert_equal
@@ -209,7 +209,7 @@ def witness_script(use_p2wsh, pubkey):
pkscript = key_to_p2wpkh_script(pubkey)
else:
# 1-of-1 multisig
- witness_script = CScript([OP_1, bytes.fromhex(pubkey), OP_1, OP_CHECKMULTISIG])
+ witness_script = keys_to_multisig_script([pubkey])
pkscript = script_to_p2wsh_script(witness_script)
return pkscript.hex()
@@ -218,7 +218,7 @@ def create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount):
Optionally wrap the segwit output using P2SH."""
if use_p2wsh:
- program = CScript([OP_1, bytes.fromhex(pubkey), OP_1, OP_CHECKMULTISIG])
+ program = keys_to_multisig_script([pubkey])
addr = script_to_p2sh_p2wsh(program) if encode_p2sh else script_to_p2wsh(program)
else:
addr = key_to_p2sh_p2wpkh(pubkey) if encode_p2sh else key_to_p2wpkh(pubkey)
diff --git a/test/functional/test_framework/script_util.py b/test/functional/test_framework/script_util.py
index 82a9067dd2..cbc4a560db 100755
--- a/test/functional/test_framework/script_util.py
+++ b/test/functional/test_framework/script_util.py
@@ -5,7 +5,9 @@
"""Useful Script constants and utils."""
from test_framework.script import (
CScript,
+ CScriptOp,
OP_0,
+ OP_CHECKMULTISIG,
OP_CHECKSIG,
OP_DUP,
OP_EQUAL,
@@ -41,6 +43,17 @@ def key_to_p2pk_script(key):
return CScript([key, OP_CHECKSIG])
+def keys_to_multisig_script(keys, *, k=None):
+ n = len(keys)
+ if k is None: # n-of-n multisig by default
+ k = n
+ assert k <= n
+ op_k = CScriptOp.encode_op_n(k)
+ op_n = CScriptOp.encode_op_n(n)
+ checked_keys = [check_key(key) for key in keys]
+ return CScript([op_k] + checked_keys + [op_n, OP_CHECKMULTISIG])
+
+
def keyhash_to_p2pkh_script(hash):
assert len(hash) == 20
return CScript([OP_DUP, OP_HASH160, hash, OP_EQUALVERIFY, OP_CHECKSIG])
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index d87d0cacfd..ec3561b1f2 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -423,12 +423,12 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
def import_deterministic_coinbase_privkeys(self):
for i in range(self.num_nodes):
- self.init_wallet(i)
+ self.init_wallet(node=i)
- def init_wallet(self, i):
- wallet_name = self.default_wallet_name if self.wallet_names is None else self.wallet_names[i] if i < len(self.wallet_names) else False
+ def init_wallet(self, *, node):
+ wallet_name = self.default_wallet_name if self.wallet_names is None else self.wallet_names[node] if node < len(self.wallet_names) else False
if wallet_name is not False:
- n = self.nodes[i]
+ n = self.nodes[node]
if wallet_name is not None:
n.createwallet(wallet_name=wallet_name, descriptors=self.options.descriptors, load_on_startup=True)
n.importprivkey(privkey=n.get_deterministic_priv_key().key, label='coinbase')
@@ -628,19 +628,19 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
self.sync_all()
def generate(self, generator, *args, **kwargs):
- blocks = generator.generate(*args, **kwargs)
+ blocks = generator.generate(*args, invalid_call=False, **kwargs)
return blocks
def generateblock(self, generator, *args, **kwargs):
- blocks = generator.generateblock(*args, **kwargs)
+ blocks = generator.generateblock(*args, invalid_call=False, **kwargs)
return blocks
def generatetoaddress(self, generator, *args, **kwargs):
- blocks = generator.generatetoaddress(*args, **kwargs)
+ blocks = generator.generatetoaddress(*args, invalid_call=False, **kwargs)
return blocks
def generatetodescriptor(self, generator, *args, **kwargs):
- blocks = generator.generatetodescriptor(*args, **kwargs)
+ blocks = generator.generatetodescriptor(*args, invalid_call=False, **kwargs)
return blocks
def sync_blocks(self, nodes=None, wait=1, timeout=60):
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index f9e2cfa2f5..e8ff41a46d 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -297,9 +297,21 @@ class TestNode():
time.sleep(1.0 / poll_per_s)
self._raise_assertion_error("Unable to retrieve cookie credentials after {}s".format(self.rpc_timeout))
- def generate(self, nblocks, maxtries=1000000):
+ def generate(self, nblocks, maxtries=1000000, **kwargs):
self.log.debug("TestNode.generate() dispatches `generate` call to `generatetoaddress`")
- return self.generatetoaddress(nblocks=nblocks, address=self.get_deterministic_priv_key().address, maxtries=maxtries)
+ return self.generatetoaddress(nblocks=nblocks, address=self.get_deterministic_priv_key().address, maxtries=maxtries, **kwargs)
+
+ def generateblock(self, *args, invalid_call, **kwargs):
+ assert not invalid_call
+ return self.__getattr__('generateblock')(*args, **kwargs)
+
+ def generatetoaddress(self, *args, invalid_call, **kwargs):
+ assert not invalid_call
+ return self.__getattr__('generatetoaddress')(*args, **kwargs)
+
+ def generatetodescriptor(self, *args, invalid_call, **kwargs):
+ assert not invalid_call
+ return self.__getattr__('generatetodescriptor')(*args, **kwargs)
def get_wallet_rpc(self, wallet_name):
if self.use_cli:
diff --git a/test/functional/test_framework/wallet.py b/test/functional/test_framework/wallet.py
index b0b578e544..d2c21f3525 100644
--- a/test/functional/test_framework/wallet.py
+++ b/test/functional/test_framework/wallet.py
@@ -88,13 +88,13 @@ class MiniWallet:
res = self._test_node.scantxoutset(action="start", scanobjects=[self.get_descriptor()])
assert_equal(True, res['success'])
for utxo in res['unspents']:
- self._utxos.append({'txid': utxo['txid'], 'vout': utxo['vout'], 'value': utxo['amount']})
+ self._utxos.append({'txid': utxo['txid'], 'vout': utxo['vout'], 'value': utxo['amount'], 'height': utxo['height']})
def scan_tx(self, tx):
"""Scan the tx for self._scriptPubKey outputs and add them to self._utxos"""
for out in tx['vout']:
if out['scriptPubKey']['hex'] == self._scriptPubKey.hex():
- self._utxos.append({'txid': tx['txid'], 'vout': out['n'], 'value': out['value']})
+ self._utxos.append({'txid': tx['txid'], 'vout': out['n'], 'value': out['value'], 'height': 0})
def sign_tx(self, tx, fixed_length=True):
"""Sign tx that has been created by MiniWallet in P2PK mode"""
@@ -111,12 +111,13 @@ class MiniWallet:
break
tx.vin[0].scriptSig = CScript([der_sig + bytes(bytearray([SIGHASH_ALL]))])
- def generate(self, num_blocks):
+ def generate(self, num_blocks, **kwargs):
"""Generate blocks with coinbase outputs to the internal address, and append the outputs to the internal list"""
- blocks = self._test_node.generatetodescriptor(num_blocks, self.get_descriptor())
+ blocks = self._test_node.generatetodescriptor(num_blocks, self.get_descriptor(), **kwargs)
for b in blocks:
- cb_tx = self._test_node.getblock(blockhash=b, verbosity=2)['tx'][0]
- self._utxos.append({'txid': cb_tx['txid'], 'vout': 0, 'value': cb_tx['vout'][0]['value']})
+ block_info = self._test_node.getblock(blockhash=b, verbosity=2)
+ cb_tx = block_info['tx'][0]
+ self._utxos.append({'txid': cb_tx['txid'], 'vout': 0, 'value': cb_tx['vout'][0]['value'], 'height': block_info['height']})
return blocks
def get_descriptor(self):
@@ -170,7 +171,7 @@ class MiniWallet:
def create_self_transfer(self, *, fee_rate=Decimal("0.003"), from_node, utxo_to_spend=None, mempool_valid=True, locktime=0, sequence=0):
"""Create and return a tx with the specified fee_rate. Fee may be exact or at most one satoshi higher than needed."""
- self._utxos = sorted(self._utxos, key=lambda k: k['value'])
+ self._utxos = sorted(self._utxos, key=lambda k: (k['value'], -k['height']))
utxo_to_spend = utxo_to_spend or self._utxos.pop() # Pick the largest utxo (if none provided) and hope it covers the fee
if self._priv_key is None:
vsize = Decimal(96) # anyone-can-spend
diff --git a/test/functional/test_framework/wallet_util.py b/test/functional/test_framework/wallet_util.py
index 1ee55aa3b7..c307ded542 100755
--- a/test/functional/test_framework/wallet_util.py
+++ b/test/functional/test_framework/wallet_util.py
@@ -15,15 +15,10 @@ from test_framework.address import (
script_to_p2wsh,
)
from test_framework.key import ECKey
-from test_framework.script import (
- CScript,
- OP_2,
- OP_3,
- OP_CHECKMULTISIG,
-)
from test_framework.script_util import (
key_to_p2pkh_script,
key_to_p2wpkh_script,
+ keys_to_multisig_script,
script_to_p2sh_script,
script_to_p2wsh_script,
)
@@ -92,7 +87,7 @@ def get_multisig(node):
addr = node.getaddressinfo(node.getnewaddress())
addrs.append(addr['address'])
pubkeys.append(addr['pubkey'])
- script_code = CScript([OP_2] + [bytes.fromhex(pubkey) for pubkey in pubkeys] + [OP_3, OP_CHECKMULTISIG])
+ script_code = keys_to_multisig_script(pubkeys, k=2)
witness_script = script_to_p2wsh_script(script_code)
return Multisig(privkeys=[node.dumpprivkey(addr) for addr in addrs],
pubkeys=pubkeys,
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index c5f08b27f2..916cd94b79 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -40,7 +40,7 @@ except UnicodeDecodeError:
CROSS = "x "
CIRCLE = "o "
-if os.name != 'nt' or sys.getwindowsversion() >= (10, 0, 14393):
+if os.name != 'nt' or sys.getwindowsversion() >= (10, 0, 14393): #type:ignore
if os.name == 'nt':
import ctypes
kernel32 = ctypes.windll.kernel32 # type: ignore
@@ -100,6 +100,7 @@ BASE_SCRIPTS = [
'p2p_compactblocks.py',
'p2p_compactblocks_blocksonly.py',
'feature_segwit.py --legacy-wallet',
+ 'feature_segwit.py --descriptors',
# vv Tests less than 2m vv
'wallet_basic.py --legacy-wallet',
'wallet_basic.py --descriptors',
@@ -207,6 +208,7 @@ BASE_SCRIPTS = [
'feature_assumevalid.py',
'example_test.py',
'wallet_txn_doublespend.py --legacy-wallet',
+ 'wallet_multisig_descriptor_psbt.py',
'wallet_txn_doublespend.py --descriptors',
'feature_backwards_compatibility.py --legacy-wallet',
'feature_backwards_compatibility.py --descriptors',
diff --git a/test/functional/tool_wallet.py b/test/functional/tool_wallet.py
index 4bf3927879..cebaa02524 100755
--- a/test/functional/tool_wallet.py
+++ b/test/functional/tool_wallet.py
@@ -31,8 +31,8 @@ class ToolWalletTest(BitcoinTestFramework):
def bitcoin_wallet_process(self, *args):
binary = self.config["environment"]["BUILDDIR"] + '/src/bitcoin-wallet' + self.config["environment"]["EXEEXT"]
default_args = ['-datadir={}'.format(self.nodes[0].datadir), '-chain=%s' % self.chain]
- if self.options.descriptors and 'create' in args:
- default_args.append('-descriptors')
+ if not self.options.descriptors and 'create' in args:
+ default_args.append('-legacy')
return subprocess.Popen([binary] + default_args + list(args), stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
@@ -193,7 +193,7 @@ class ToolWalletTest(BitcoinTestFramework):
locked_dir = os.path.join(self.options.tmpdir, "node0", "regtest", "wallets")
error = 'Error initializing wallet database environment "{}"!'.format(locked_dir)
if self.options.descriptors:
- error = "SQLiteDatabase: Unable to obtain an exclusive lock on the database, is it being used by another bitcoind?"
+ error = f"SQLiteDatabase: Unable to obtain an exclusive lock on the database, is it being used by another instance of {self.config['environment']['PACKAGE_NAME']}?"
self.assert_raises_tool_error(
error,
'-wallet=' + self.default_wallet_name,
diff --git a/test/functional/wallet_abandonconflict.py b/test/functional/wallet_abandonconflict.py
index 70ca5a2fe4..8f54e50598 100755
--- a/test/functional/wallet_abandonconflict.py
+++ b/test/functional/wallet_abandonconflict.py
@@ -120,6 +120,14 @@ class AbandonConflictTest(BitcoinTestFramework):
assert_equal(newbalance, balance + Decimal("30"))
balance = newbalance
+ self.log.info("Check abandoned transactions in listsinceblock")
+ listsinceblock = self.nodes[0].listsinceblock()
+ txAB1_listsinceblock = [d for d in listsinceblock['transactions'] if d['txid'] == txAB1 and d['category'] == 'send']
+ for tx in txAB1_listsinceblock:
+ assert_equal(tx['abandoned'], True)
+ assert_equal(tx['confirmations'], 0)
+ assert_equal(tx['trusted'], False)
+
# Verify that even with a low min relay fee, the tx is not reaccepted from wallet on startup once abandoned
self.restart_node(0, extra_args=["-minrelaytxfee=0.00001"])
assert self.nodes[0].getmempoolinfo()['loaded']
@@ -205,7 +213,7 @@ class AbandonConflictTest(BitcoinTestFramework):
#assert_equal(newbalance, balance - Decimal("10"))
self.log.info("If balance has not declined after invalidateblock then out of mempool wallet tx which is no longer")
self.log.info("conflicted has not resumed causing its inputs to be seen as spent. See Issue #7315")
- self.log.info(str(balance) + " -> " + str(newbalance) + " ?")
+ assert_equal(balance, newbalance)
if __name__ == '__main__':
diff --git a/test/functional/wallet_address_types.py b/test/functional/wallet_address_types.py
index bdee22e62b..7a448e8590 100755
--- a/test/functional/wallet_address_types.py
+++ b/test/functional/wallet_address_types.py
@@ -204,8 +204,7 @@ class AddressTypeTest(BitcoinTestFramework):
def test_change_output_type(self, node_sender, destinations, expected_type):
txid = self.nodes[node_sender].sendmany(dummy="", amounts=dict.fromkeys(destinations, 0.001))
- raw_tx = self.nodes[node_sender].getrawtransaction(txid)
- tx = self.nodes[node_sender].decoderawtransaction(raw_tx)
+ tx = self.nodes[node_sender].gettransaction(txid=txid, verbose=True)['decoded']
# Make sure the transaction has change:
assert_equal(len(tx["vout"]), len(destinations) + 1)
diff --git a/test/functional/wallet_backup.py b/test/functional/wallet_backup.py
index bc6d6206e5..a07c28c8a4 100755
--- a/test/functional/wallet_backup.py
+++ b/test/functional/wallet_backup.py
@@ -124,9 +124,9 @@ class WalletBackupTest(BitcoinTestFramework):
assert_raises_rpc_error(-8, "Wallet name already exists.", node.restorewallet, wallet_name, wallet_file)
def init_three(self):
- self.init_wallet(0)
- self.init_wallet(1)
- self.init_wallet(2)
+ self.init_wallet(node=0)
+ self.init_wallet(node=1)
+ self.init_wallet(node=2)
def run_test(self):
self.log.info("Generating initial blockchain")
diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py
index 599e506f98..92da54d97c 100755
--- a/test/functional/wallet_basic.py
+++ b/test/functional/wallet_basic.py
@@ -666,7 +666,7 @@ class WalletTest(BitcoinTestFramework):
self.generate(self.nodes[0], 1)
destination = self.nodes[1].getnewaddress()
txid = self.nodes[0].sendtoaddress(destination, 0.123)
- tx = self.nodes[0].decoderawtransaction(self.nodes[0].gettransaction(txid)['hex'])
+ tx = self.nodes[0].gettransaction(txid=txid, verbose=True)['decoded']
output_addresses = [vout['scriptPubKey']['address'] for vout in tx["vout"]]
assert len(output_addresses) > 1
for address in output_addresses:
diff --git a/test/functional/wallet_bumpfee.py b/test/functional/wallet_bumpfee.py
index a1676fffa5..46a5df4a8e 100755
--- a/test/functional/wallet_bumpfee.py
+++ b/test/functional/wallet_bumpfee.py
@@ -32,6 +32,8 @@ from test_framework.util import (
assert_greater_than,
assert_raises_rpc_error,
)
+from test_framework.wallet import MiniWallet
+
WALLET_PASSPHRASE = "test"
WALLET_PASSPHRASE_TIMEOUT = 3600
@@ -265,6 +267,14 @@ def test_bumpfee_with_descendant_fails(self, rbf_node, rbf_node_address, dest_ad
tx = rbf_node.signrawtransactionwithwallet(tx)
rbf_node.sendrawtransaction(tx["hex"])
assert_raises_rpc_error(-8, "Transaction has descendants in the wallet", rbf_node.bumpfee, parent_id)
+
+ # create tx with descendant in the mempool by using MiniWallet
+ miniwallet = MiniWallet(rbf_node)
+ parent_id = spend_one_input(rbf_node, miniwallet.get_address())
+ tx = rbf_node.gettransaction(txid=parent_id, verbose=True)['decoded']
+ miniwallet.scan_tx(tx)
+ miniwallet.send_self_transfer(from_node=rbf_node)
+ assert_raises_rpc_error(-8, "Transaction has descendants in the mempool", rbf_node.bumpfee, parent_id)
self.clear_mempool()
diff --git a/test/functional/wallet_create_tx.py b/test/functional/wallet_create_tx.py
index c8b92ef1bf..00ee08002e 100755
--- a/test/functional/wallet_create_tx.py
+++ b/test/functional/wallet_create_tx.py
@@ -34,13 +34,13 @@ class CreateTxWalletTest(BitcoinTestFramework):
self.log.info('Check that we have some (old) blocks and that anti-fee-sniping is disabled')
assert_equal(self.nodes[0].getblockchaininfo()['blocks'], 200)
txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1)
- tx = self.nodes[0].decoderawtransaction(self.nodes[0].gettransaction(txid)['hex'])
+ tx = self.nodes[0].gettransaction(txid=txid, verbose=True)['decoded']
assert_equal(tx['locktime'], 0)
self.log.info('Check that anti-fee-sniping is enabled when we mine a recent block')
self.generate(self.nodes[0], 1)
txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1)
- tx = self.nodes[0].decoderawtransaction(self.nodes[0].gettransaction(txid)['hex'])
+ tx = self.nodes[0].gettransaction(txid=txid, verbose=True)['decoded']
assert 0 < tx['locktime'] <= 201
def test_tx_size_too_large(self):
diff --git a/test/functional/wallet_descriptor.py b/test/functional/wallet_descriptor.py
index 17a4c79da3..4ec44a8a6c 100755
--- a/test/functional/wallet_descriptor.py
+++ b/test/functional/wallet_descriptor.py
@@ -84,7 +84,7 @@ class WalletDescriptorTest(BitcoinTestFramework):
send_wrpc = self.nodes[0].get_wallet_rpc("desc1")
# Generate some coins
- self.generatetoaddress(send_wrpc, COINBASE_MATURITY + 1, send_wrpc.getnewaddress())
+ self.generatetoaddress(self.nodes[0], COINBASE_MATURITY + 1, send_wrpc.getnewaddress())
# Make transactions
self.log.info("Test sending and receiving")
diff --git a/test/functional/wallet_hd.py b/test/functional/wallet_hd.py
index 974ce7f381..f54ae89c04 100755
--- a/test/functional/wallet_hd.py
+++ b/test/functional/wallet_hd.py
@@ -129,7 +129,7 @@ class WalletHDTest(BitcoinTestFramework):
# send a tx and make sure its using the internal chain for the changeoutput
txid = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1)
- outs = self.nodes[1].decoderawtransaction(self.nodes[1].gettransaction(txid)['hex'])['vout']
+ outs = self.nodes[1].gettransaction(txid=txid, verbose=True)['decoded']['vout']
keypath = ""
for out in outs:
if out['value'] != 1:
diff --git a/test/functional/wallet_import_rescan.py b/test/functional/wallet_import_rescan.py
index cbe3e9bfdd..27a2a42dac 100755
--- a/test/functional/wallet_import_rescan.py
+++ b/test/functional/wallet_import_rescan.py
@@ -99,7 +99,7 @@ class Variant(collections.namedtuple("Variant", "call data address_type rescan p
assert_equal(tx["label"], self.label)
assert_equal(tx["txid"], txid)
assert_equal(tx["confirmations"], 1 + current_height - confirmation_height)
- assert_equal("trusted" not in tx, True)
+ assert "trusted" not in tx
address, = [ad for ad in addresses if txid in ad["txids"]]
assert_equal(address["address"], self.address["address"])
diff --git a/test/functional/wallet_importdescriptors.py b/test/functional/wallet_importdescriptors.py
index d86c3737fe..fc9eac1d74 100755
--- a/test/functional/wallet_importdescriptors.py
+++ b/test/functional/wallet_importdescriptors.py
@@ -74,7 +74,7 @@ class ImportDescriptorsTest(BitcoinTestFramework):
assert_equal(wpriv.getwalletinfo()['keypoolsize'], 0)
self.log.info('Mining coins')
- self.generatetoaddress(w0, COINBASE_MATURITY + 1, w0.getnewaddress())
+ self.generatetoaddress(self.nodes[0], COINBASE_MATURITY + 1, w0.getnewaddress())
# RPC importdescriptors -----------------------------------------------
@@ -405,7 +405,7 @@ class ImportDescriptorsTest(BitcoinTestFramework):
solvable=True,
ismine=True)
txid = w0.sendtoaddress(address, 49.99995540)
- self.generatetoaddress(w0, 6, w0.getnewaddress())
+ self.generatetoaddress(self.nodes[0], 6, w0.getnewaddress())
self.sync_blocks()
tx = wpriv.createrawtransaction([{"txid": txid, "vout": 0}], {w0.getnewaddress(): 49.999})
signed_tx = wpriv.signrawtransactionwithwallet(tx)
@@ -454,7 +454,7 @@ class ImportDescriptorsTest(BitcoinTestFramework):
self.generate(self.nodes[0], 6)
self.sync_all()
send_txid = wmulti_priv.sendtoaddress(w0.getnewaddress(), 8)
- decoded = wmulti_priv.decoderawtransaction(wmulti_priv.gettransaction(send_txid)['hex'])
+ decoded = wmulti_priv.gettransaction(txid=send_txid, verbose=True)['decoded']
assert_equal(len(decoded['vin'][0]['txinwitness']), 4)
self.generate(self.nodes[0], 6)
self.sync_all()
@@ -586,7 +586,7 @@ class ImportDescriptorsTest(BitcoinTestFramework):
self.sync_all()
# It is standard and would relay.
txid = wmulti_priv_big.sendtoaddress(w0.getnewaddress(), 9.999)
- decoded = wmulti_priv_big.decoderawtransaction(wmulti_priv_big.gettransaction(txid)['hex'])
+ decoded = wmulti_priv_big.gettransaction(txid=txid, verbose=True)['decoded']
# 20 sigs + dummy + witness script
assert_equal(len(decoded['vin'][0]['txinwitness']), 22)
@@ -620,12 +620,8 @@ class ImportDescriptorsTest(BitcoinTestFramework):
self.generate(self.nodes[0], 6)
self.sync_all()
# It is standard and would relay.
- txid = multi_priv_big.sendtoaddress(w0.getnewaddress(), 10, "", "",
- True)
- decoded = multi_priv_big.decoderawtransaction(
- multi_priv_big.gettransaction(txid)['hex']
- )
-
+ txid = multi_priv_big.sendtoaddress(w0.getnewaddress(), 10, "", "", True)
+ decoded = multi_priv_big.gettransaction(txid=txid, verbose=True)['decoded']
self.log.info("Amending multisig with new private keys")
self.nodes[1].createwallet(wallet_name="wmulti_priv3", descriptors=True)
diff --git a/test/functional/wallet_keypool.py b/test/functional/wallet_keypool.py
index ada97417e4..5807a92b9d 100755
--- a/test/functional/wallet_keypool.py
+++ b/test/functional/wallet_keypool.py
@@ -138,6 +138,20 @@ class KeyPoolTest(BitcoinTestFramework):
assert_equal(wi['keypoolsize_hd_internal'], 100)
assert_equal(wi['keypoolsize'], 100)
+ if not self.options.descriptors:
+ # Check that newkeypool entirely flushes the keypool
+ start_keypath = nodes[0].getaddressinfo(nodes[0].getnewaddress())['hdkeypath']
+ start_change_keypath = nodes[0].getaddressinfo(nodes[0].getrawchangeaddress())['hdkeypath']
+ # flush keypool and get new addresses
+ nodes[0].newkeypool()
+ end_keypath = nodes[0].getaddressinfo(nodes[0].getnewaddress())['hdkeypath']
+ end_change_keypath = nodes[0].getaddressinfo(nodes[0].getrawchangeaddress())['hdkeypath']
+ # The new keypath index should be 100 more than the old one
+ new_index = int(start_keypath.rsplit('/', 1)[1][:-1]) + 100
+ new_change_index = int(start_change_keypath.rsplit('/', 1)[1][:-1]) + 100
+ assert_equal(end_keypath, "m/0'/0'/" + str(new_index) + "'")
+ assert_equal(end_change_keypath, "m/0'/1'/" + str(new_change_index) + "'")
+
# create a blank wallet
nodes[0].createwallet(wallet_name='w2', blank=True, disable_private_keys=True)
w2 = nodes[0].get_wallet_rpc('w2')
diff --git a/test/functional/wallet_listdescriptors.py b/test/functional/wallet_listdescriptors.py
index 221f5262d9..436bbdcfcc 100755
--- a/test/functional/wallet_listdescriptors.py
+++ b/test/functional/wallet_listdescriptors.py
@@ -23,7 +23,7 @@ class ListDescriptorsTest(BitcoinTestFramework):
self.skip_if_no_sqlite()
# do not create any wallet by default
- def init_wallet(self, i):
+ def init_wallet(self, *, node):
return
def run_test(self):
diff --git a/test/functional/wallet_listsinceblock.py b/test/functional/wallet_listsinceblock.py
index bd3b29c81c..f4a00a8ec8 100755
--- a/test/functional/wallet_listsinceblock.py
+++ b/test/functional/wallet_listsinceblock.py
@@ -44,6 +44,14 @@ class ListSinceBlockTest(BitcoinTestFramework):
def test_no_blockhash(self):
self.log.info("Test no blockhash")
txid = self.nodes[2].sendtoaddress(self.nodes[0].getnewaddress(), 1)
+ self.sync_all()
+ assert_array_result(self.nodes[0].listtransactions(), {"txid": txid}, {
+ "category": "receive",
+ "amount": 1,
+ "confirmations": 0,
+ "trusted": False,
+ })
+
blockhash, = self.generate(self.nodes[2], 1)
blockheight = self.nodes[2].getblockheader(blockhash)['height']
self.sync_all()
@@ -56,6 +64,9 @@ class ListSinceBlockTest(BitcoinTestFramework):
"blockheight": blockheight,
"confirmations": 1,
})
+ assert_equal(len(txs), 1)
+ assert "trusted" not in txs[0]
+
assert_equal(
self.nodes[0].listsinceblock(),
{"lastblock": blockhash,
diff --git a/test/functional/wallet_listtransactions.py b/test/functional/wallet_listtransactions.py
index a14bfe345c..ca6a6ab540 100755
--- a/test/functional/wallet_listtransactions.py
+++ b/test/functional/wallet_listtransactions.py
@@ -31,10 +31,10 @@ class ListTransactionsTest(BitcoinTestFramework):
self.sync_all()
assert_array_result(self.nodes[0].listtransactions(),
{"txid": txid},
- {"category": "send", "amount": Decimal("-0.1"), "confirmations": 0})
+ {"category": "send", "amount": Decimal("-0.1"), "confirmations": 0, "trusted": True})
assert_array_result(self.nodes[1].listtransactions(),
{"txid": txid},
- {"category": "receive", "amount": Decimal("0.1"), "confirmations": 0})
+ {"category": "receive", "amount": Decimal("0.1"), "confirmations": 0, "trusted": False})
self.log.info("Test confirmations change after mining a block")
blockhash = self.generate(self.nodes[0], 1)[0]
blockheight = self.nodes[0].getblockheader(blockhash)['height']
@@ -204,6 +204,15 @@ class ListTransactionsTest(BitcoinTestFramework):
assert_equal(n.gettransaction(txid_3b)["bip125-replaceable"], "yes")
assert_equal(n.gettransaction(txid_4)["bip125-replaceable"], "unknown")
+ self.log.info("Test bip125-replaceable status with listsinceblock")
+ for n in self.nodes[0:2]:
+ txs = {tx['txid']: tx['bip125-replaceable'] for tx in n.listsinceblock()['transactions']}
+ assert_equal(txs[txid_1], "no")
+ assert_equal(txs[txid_2], "no")
+ assert_equal(txs[txid_3], "yes")
+ assert_equal(txs[txid_3b], "yes")
+ assert_equal(txs[txid_4], "unknown")
+
self.log.info("Test mined transactions are no longer bip125-replaceable")
self.generate(self.nodes[0], 1)
assert txid_3b not in self.nodes[0].getrawmempool()
diff --git a/test/functional/wallet_multisig_descriptor_psbt.py b/test/functional/wallet_multisig_descriptor_psbt.py
new file mode 100755
index 0000000000..ed855d2525
--- /dev/null
+++ b/test/functional/wallet_multisig_descriptor_psbt.py
@@ -0,0 +1,163 @@
+#!/usr/bin/env python3
+# Copyright (c) 2021 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Test a basic M-of-N multisig setup between multiple people using descriptor wallets and PSBTs, as well as a signing flow.
+
+This is meant to be documentation as much as functional tests, so it is kept as simple and readable as possible.
+"""
+
+from test_framework.address import base58_to_byte
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_approx,
+ assert_equal,
+)
+
+
+class WalletMultisigDescriptorPSBTTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 3
+ self.setup_clean_chain = True
+ self.wallet_names = []
+ self.extra_args = [["-keypool=100"]] * self.num_nodes
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+ self.skip_if_no_sqlite()
+
+ @staticmethod
+ def _get_xpub(wallet):
+ """Extract the wallet's xpubs using `listdescriptors` and pick the one from the `pkh` descriptor since it's least likely to be accidentally reused (legacy addresses)."""
+ descriptor = next(filter(lambda d: d["desc"].startswith("pkh"), wallet.listdescriptors()["descriptors"]))
+ return descriptor["desc"].split("]")[-1].split("/")[0]
+
+ @staticmethod
+ def _check_psbt(psbt, to, value, multisig):
+ """Helper function for any of the N participants to check the psbt with decodepsbt and verify it is OK before signing."""
+ tx = multisig.decodepsbt(psbt)["tx"]
+ amount = 0
+ for vout in tx["vout"]:
+ address = vout["scriptPubKey"]["address"]
+ assert_equal(multisig.getaddressinfo(address)["ischange"], address != to)
+ if address == to:
+ amount += vout["value"]
+ assert_approx(amount, float(value), vspan=0.001)
+
+ def participants_create_multisigs(self, xpubs):
+ """The multisig is created by importing the following descriptors. The resulting wallet is watch-only and every participant can do this."""
+ # some simple validation
+ assert_equal(len(xpubs), self.N)
+ # a sanity-check/assertion, this will throw if the base58 checksum of any of the provided xpubs are invalid
+ for xpub in xpubs:
+ base58_to_byte(xpub)
+
+ for i, node in enumerate(self.nodes):
+ node.createwallet(wallet_name=f"{self.name}_{i}", blank=True, descriptors=True, disable_private_keys=True)
+ multisig = node.get_wallet_rpc(f"{self.name}_{i}")
+ external = multisig.getdescriptorinfo(f"wsh(sortedmulti({self.M},{f'/0/*,'.join(xpubs)}/0/*))")
+ internal = multisig.getdescriptorinfo(f"wsh(sortedmulti({self.M},{f'/1/*,'.join(xpubs)}/1/*))")
+ result = multisig.importdescriptors([
+ { # receiving addresses (internal: False)
+ "desc": external["descriptor"],
+ "active": True,
+ "internal": False,
+ "timestamp": "now",
+ },
+ { # change addresses (internal: True)
+ "desc": internal["descriptor"],
+ "active": True,
+ "internal": True,
+ "timestamp": "now",
+ },
+ ])
+ assert all(r["success"] for r in result)
+ yield multisig
+
+ def run_test(self):
+ self.M = 2
+ self.N = self.num_nodes
+ self.name = f"{self.M}_of_{self.N}_multisig"
+ self.log.info(f"Testing {self.name}...")
+
+ participants = {
+ # Every participant generates an xpub. The most straightforward way is to create a new descriptor wallet.
+ # This wallet will be the participant's `signer` for the resulting multisig. Avoid reusing this wallet for any other purpose (for privacy reasons).
+ "signers": [node.get_wallet_rpc(node.createwallet(wallet_name=f"participant_{self.nodes.index(node)}", descriptors=True)["name"]) for node in self.nodes],
+ # After participants generate and exchange their xpubs they will each create their own watch-only multisig.
+ # Note: these multisigs are all the same, this justs highlights that each participant can independently verify everything on their own node.
+ "multisigs": []
+ }
+
+ self.log.info("Generate and exchange xpubs...")
+ xpubs = [self._get_xpub(signer) for signer in participants["signers"]]
+
+ self.log.info("Every participant imports the following descriptors to create the watch-only multisig...")
+ participants["multisigs"] = list(self.participants_create_multisigs(xpubs))
+
+ self.log.info("Check that every participant's multisig generates the same addresses...")
+ for _ in range(10): # we check that the first 10 generated addresses are the same for all participant's multisigs
+ receive_addresses = [multisig.getnewaddress() for multisig in participants["multisigs"]]
+ all(address == receive_addresses[0] for address in receive_addresses)
+ change_addresses = [multisig.getrawchangeaddress() for multisig in participants["multisigs"]]
+ all(address == change_addresses[0] for address in change_addresses)
+
+ self.log.info("Get a mature utxo to send to the multisig...")
+ coordinator_wallet = participants["signers"][0]
+ self.generatetoaddress(self.nodes[0], 101, coordinator_wallet.getnewaddress())
+
+ deposit_amount = 6.15
+ multisig_receiving_address = participants["multisigs"][0].getnewaddress()
+ self.log.info("Send funds to the resulting multisig receiving address...")
+ coordinator_wallet.sendtoaddress(multisig_receiving_address, deposit_amount)
+ self.generate(self.nodes[0], 1)
+ self.sync_all()
+ for participant in participants["multisigs"]:
+ assert_approx(participant.getbalance(), deposit_amount, vspan=0.001)
+
+ self.log.info("Send a transaction from the multisig!")
+ to = participants["signers"][self.N - 1].getnewaddress()
+ value = 1
+ self.log.info("First, make a sending transaction, created using `walletcreatefundedpsbt` (anyone can initiate this)...")
+ psbt = participants["multisigs"][0].walletcreatefundedpsbt(inputs=[], outputs={to: value}, options={"feeRate": 0.00010})
+
+ psbts = []
+ self.log.info("Now at least M users check the psbt with decodepsbt and (if OK) signs it with walletprocesspsbt...")
+ for m in range(self.M):
+ signers_multisig = participants["multisigs"][m]
+ self._check_psbt(psbt["psbt"], to, value, signers_multisig)
+ signing_wallet = participants["signers"][m]
+ partially_signed_psbt = signing_wallet.walletprocesspsbt(psbt["psbt"])
+ psbts.append(partially_signed_psbt["psbt"])
+
+ self.log.info("Finally, collect the signed PSBTs with combinepsbt, finalizepsbt, then broadcast the resulting transaction...")
+ combined = coordinator_wallet.combinepsbt(psbts)
+ finalized = coordinator_wallet.finalizepsbt(combined)
+ coordinator_wallet.sendrawtransaction(finalized["hex"])
+
+ self.log.info("Check that balances are correct after the transaction has been included in a block.")
+ self.generate(self.nodes[0], 1)
+ self.sync_all()
+ assert_approx(participants["multisigs"][0].getbalance(), deposit_amount - value, vspan=0.001)
+ assert_equal(participants["signers"][self.N - 1].getbalance(), value)
+
+ self.log.info("Send another transaction from the multisig, this time with a daisy chained signing flow (one after another in series)!")
+ psbt = participants["multisigs"][0].walletcreatefundedpsbt(inputs=[], outputs={to: value}, options={"feeRate": 0.00010})
+ for m in range(self.M):
+ signers_multisig = participants["multisigs"][m]
+ self._check_psbt(psbt["psbt"], to, value, signers_multisig)
+ signing_wallet = participants["signers"][m]
+ psbt = signing_wallet.walletprocesspsbt(psbt["psbt"])
+ assert_equal(psbt["complete"], m == self.M - 1)
+ finalized = coordinator_wallet.finalizepsbt(psbt["psbt"])
+ coordinator_wallet.sendrawtransaction(finalized["hex"])
+
+ self.log.info("Check that balances are correct after the transaction has been included in a block.")
+ self.generate(self.nodes[0], 1)
+ self.sync_all()
+ assert_approx(participants["multisigs"][0].getbalance(), deposit_amount - (value * 2), vspan=0.001)
+ assert_equal(participants["signers"][self.N - 1].getbalance(), value * 2)
+
+
+if __name__ == "__main__":
+ WalletMultisigDescriptorPSBTTest().main()
diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py
index d4768f5043..68ca005649 100755
--- a/test/functional/wallet_multiwallet.py
+++ b/test/functional/wallet_multiwallet.py
@@ -202,7 +202,7 @@ class MultiWalletTest(BitcoinTestFramework):
self.restart_node(0, ['-nowallet', '-walletdir=' + competing_wallet_dir])
self.nodes[0].createwallet(self.default_wallet_name)
if self.options.descriptors:
- exp_stderr = r"Error: SQLiteDatabase: Unable to obtain an exclusive lock on the database, is it being used by another bitcoind?"
+ exp_stderr = f"Error: SQLiteDatabase: Unable to obtain an exclusive lock on the database, is it being used by another instance of {self.config['environment']['PACKAGE_NAME']}?"
else:
exp_stderr = r"Error: Error initializing wallet database environment \"\S+competing_walletdir\S*\"!"
self.nodes[1].assert_start_raises_init_error(['-walletdir=' + competing_wallet_dir], exp_stderr, match=ErrorMatch.PARTIAL_REGEX)
@@ -303,7 +303,7 @@ class MultiWalletTest(BitcoinTestFramework):
# Fail to load duplicate wallets
path = os.path.join(self.options.tmpdir, "node0", "regtest", "wallets", "w1", "wallet.dat")
if self.options.descriptors:
- assert_raises_rpc_error(-4, "Wallet file verification failed. SQLiteDatabase: Unable to obtain an exclusive lock on the database, is it being used by another bitcoind?", self.nodes[0].loadwallet, wallet_names[0])
+ assert_raises_rpc_error(-4, f"Wallet file verification failed. SQLiteDatabase: Unable to obtain an exclusive lock on the database, is it being used by another instance of {self.config['environment']['PACKAGE_NAME']}?", self.nodes[0].loadwallet, wallet_names[0])
else:
assert_raises_rpc_error(-35, "Wallet file verification failed. Refusing to load database. Data file '{}' is already loaded.".format(path), self.nodes[0].loadwallet, wallet_names[0])
diff --git a/test/functional/wallet_taproot.py b/test/functional/wallet_taproot.py
index 4f84dbd125..80c125df97 100755
--- a/test/functional/wallet_taproot.py
+++ b/test/functional/wallet_taproot.py
@@ -185,7 +185,7 @@ class WalletTaprootTest(BitcoinTestFramework):
def setup_network(self):
self.setup_nodes()
- def init_wallet(self, i):
+ def init_wallet(self, *, node):
pass
@staticmethod
diff --git a/test/functional/wallet_transactiontime_rescan.py b/test/functional/wallet_transactiontime_rescan.py
index 78859e6131..afa5139da7 100755
--- a/test/functional/wallet_transactiontime_rescan.py
+++ b/test/functional/wallet_transactiontime_rescan.py
@@ -63,7 +63,7 @@ class TransactionTimeRescanTest(BitcoinTestFramework):
# generate some btc to create transactions and check blockcount
initial_mine = COINBASE_MATURITY + 1
- minernode.generatetoaddress(initial_mine, m1)
+ self.generatetoaddress(minernode, initial_mine, m1)
assert_equal(minernode.getblockcount(), initial_mine + 200)
# synchronize nodes and time
@@ -76,7 +76,7 @@ class TransactionTimeRescanTest(BitcoinTestFramework):
miner_wallet.sendtoaddress(wo1, 10)
# generate blocks and check blockcount
- minernode.generatetoaddress(COINBASE_MATURITY, m1)
+ self.generatetoaddress(minernode, COINBASE_MATURITY, m1)
assert_equal(minernode.getblockcount(), initial_mine + 300)
# synchronize nodes and time
@@ -89,7 +89,7 @@ class TransactionTimeRescanTest(BitcoinTestFramework):
miner_wallet.sendtoaddress(wo2, 5)
# generate blocks and check blockcount
- minernode.generatetoaddress(COINBASE_MATURITY, m1)
+ self.generatetoaddress(minernode, COINBASE_MATURITY, m1)
assert_equal(minernode.getblockcount(), initial_mine + 400)
# synchronize nodes and time
@@ -102,7 +102,7 @@ class TransactionTimeRescanTest(BitcoinTestFramework):
miner_wallet.sendtoaddress(wo3, 1)
# generate more blocks and check blockcount
- minernode.generatetoaddress(COINBASE_MATURITY, m1)
+ self.generatetoaddress(minernode, COINBASE_MATURITY, m1)
assert_equal(minernode.getblockcount(), initial_mine + 500)
self.log.info('Check user\'s final balance and transaction count')
diff --git a/test/functional/wallet_upgradewallet.py b/test/functional/wallet_upgradewallet.py
index ed98db55c9..5800880830 100755
--- a/test/functional/wallet_upgradewallet.py
+++ b/test/functional/wallet_upgradewallet.py
@@ -234,18 +234,13 @@ class UpgradeWalletTest(BitcoinTestFramework):
assert_equal(1, hd_chain_version)
seed_id = bytearray(seed_id)
seed_id.reverse()
- old_kvs = new_kvs
- # First 2 keys should still be non-HD
- for i in range(0, 2):
- info = wallet.getaddressinfo(wallet.getnewaddress())
- assert 'hdkeypath' not in info
- assert 'hdseedid' not in info
- # Next key should be HD
+
+ # New keys (including change) should be HD (the two old keys have been flushed)
info = wallet.getaddressinfo(wallet.getnewaddress())
assert_equal(seed_id.hex(), info['hdseedid'])
assert_equal('m/0\'/0\'/0\'', info['hdkeypath'])
prev_seed_id = info['hdseedid']
- # Change key should be the same keypool
+ # Change key should be HD and from the same keypool
info = wallet.getaddressinfo(wallet.getrawchangeaddress())
assert_equal(prev_seed_id, info['hdseedid'])
assert_equal('m/0\'/0\'/1\'', info['hdkeypath'])
@@ -291,14 +286,7 @@ class UpgradeWalletTest(BitcoinTestFramework):
hd_chain_version, external_counter, seed_id, internal_counter = struct.unpack('<iI20sI', hd_chain)
assert_equal(2, hd_chain_version)
assert_equal(2, internal_counter)
- # Drain the keypool by fetching one external key and one change key. Should still be the same keypool
- info = wallet.getaddressinfo(wallet.getnewaddress())
- assert 'hdseedid' not in info
- assert 'hdkeypath' not in info
- info = wallet.getaddressinfo(wallet.getrawchangeaddress())
- assert 'hdseedid' not in info
- assert 'hdkeypath' not in info
- # The next addresses are HD and should be on different HD chains
+ # The next addresses are HD and should be on different HD chains (the one remaining key in each pool should have been flushed)
info = wallet.getaddressinfo(wallet.getnewaddress())
ext_id = info['hdseedid']
assert_equal('m/0\'/0\'/0\'', info['hdkeypath'])
diff --git a/test/lint/lint-locale-dependence.sh b/test/lint/lint-locale-dependence.sh
index 3015c4f9b9..b119cffec8 100755
--- a/test/lint/lint-locale-dependence.sh
+++ b/test/lint/lint-locale-dependence.sh
@@ -38,13 +38,10 @@ export LC_ALL=C
# https://stackoverflow.com/a/34878283 for more details.
# TODO: Reduce KNOWN_VIOLATIONS by replacing uses of locale dependent stoul/strtol with locale
-# independent ToIntegral<T>(...).
+# independent ToIntegral<T>(...) or the ParseInt*() functions.
# TODO: Reduce KNOWN_VIOLATIONS by replacing uses of locale dependent snprintf with strprintf.
KNOWN_VIOLATIONS=(
- "src/bitcoin-tx.cpp.*stoul"
- "src/dbwrapper.cpp.*stoul"
"src/dbwrapper.cpp:.*vsnprintf"
- "src/rest.cpp:.*strtol"
"src/test/dbwrapper_tests.cpp:.*snprintf"
"src/test/fuzz/locale.cpp"
"src/test/fuzz/string.cpp"
diff --git a/test/lint/lint-python.sh b/test/lint/lint-python.sh
index c448fa6f9a..3d22407fd1 100755
--- a/test/lint/lint-python.sh
+++ b/test/lint/lint-python.sh
@@ -102,7 +102,7 @@ if ! PYTHONWARNINGS="ignore" flake8 --ignore=B,C,E,F,I,N,W --select=$(IFS=","; e
EXIT_CODE=1
fi
-if ! mypy --ignore-missing-imports --show-error-codes $(git ls-files "test/functional/*.py" "contrib/devtools/*.py"); then
+if ! mypy --show-error-codes $(git ls-files "test/functional/*.py" "contrib/devtools/*.py"); then
EXIT_CODE=1
fi
diff --git a/test/sanitizer_suppressions/ubsan b/test/sanitizer_suppressions/ubsan
index b52e105a33..1d608b9ec1 100644
--- a/test/sanitizer_suppressions/ubsan
+++ b/test/sanitizer_suppressions/ubsan
@@ -22,7 +22,7 @@ unsigned-integer-overflow:arith_uint256.h
unsigned-integer-overflow:basic_string.h
unsigned-integer-overflow:bench/bench.h
unsigned-integer-overflow:bitcoin-tx.cpp
-unsigned-integer-overflow:bloom.cpp
+unsigned-integer-overflow:common/bloom.cpp
unsigned-integer-overflow:chain.cpp
unsigned-integer-overflow:chain.h
unsigned-integer-overflow:coded_stream.h
@@ -48,7 +48,7 @@ implicit-integer-sign-change:*/new_allocator.h
implicit-integer-sign-change:addrman.h
implicit-integer-sign-change:arith_uint256.cpp
implicit-integer-sign-change:bech32.cpp
-implicit-integer-sign-change:bloom.cpp
+implicit-integer-sign-change:common/bloom.cpp
implicit-integer-sign-change:chain.cpp
implicit-integer-sign-change:chain.h
implicit-integer-sign-change:coins.h
diff --git a/test/util/data/bitcoin-util-test.json b/test/util/data/bitcoin-util-test.json
index a648c0287a..cca5732aa1 100644
--- a/test/util/data/bitcoin-util-test.json
+++ b/test/util/data/bitcoin-util-test.json
@@ -295,6 +295,12 @@
"description": "Create a new transaction with a single output script (OP_DROP) in a P2SH, wrapped in a P2SH (output as json)"
},
{ "exec": "./bitcoin-tx",
+ "args": ["-create", "outscript=0:999999999999999999999999999999"],
+ "return_code": 1,
+ "error_txt": "error: script parse error: decimal numeric value only allowed in the range -0xFFFFFFFF...0xFFFFFFFF",
+ "description": "Try to parse an output script with a decimal number above the allowed range"
+ },
+ { "exec": "./bitcoin-tx",
"args": ["-create", "outscript=0:9999999999"],
"return_code": 1,
"error_txt": "error: script parse error: decimal numeric value only allowed in the range -0xFFFFFFFF...0xFFFFFFFF",
@@ -512,6 +518,30 @@
{ "exec": "./bitcoin-tx",
"args":
["-create",
+ "in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0:11aa"],
+ "return_code": 1,
+ "error_txt": "error: invalid TX sequence id '11aa'",
+ "description": "Try to parse a sequence number outside the allowed range"
+ },
+ { "exec": "./bitcoin-tx",
+ "args":
+ ["-create",
+ "in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0:-1"],
+ "return_code": 1,
+ "error_txt": "error: invalid TX sequence id '-1'",
+ "description": "Try to parse a sequence number outside the allowed range"
+ },
+ { "exec": "./bitcoin-tx",
+ "args":
+ ["-create",
+ "in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0:4294967296"],
+ "return_code": 1,
+ "error_txt": "error: invalid TX sequence id '4294967296'",
+ "description": "Try to parse a sequence number outside the allowed range"
+ },
+ { "exec": "./bitcoin-tx",
+ "args":
+ ["-create",
"in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0:4294967293",
"outaddr=0.18:13tuJJDR2RgArmgfv6JScSdreahzgc4T6o"],
"output_cmp": "txcreatedata_seq0.hex",
@@ -519,6 +549,14 @@
},
{ "exec": "./bitcoin-tx",
"args":
+ ["-create",
+ "in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0: 4294967293 ",
+ "outaddr=0.18:13tuJJDR2RgArmgfv6JScSdreahzgc4T6o"],
+ "output_cmp": "txcreatedata_seq0.hex",
+ "description": "Creates a new transaction with one input with sequence number (+whitespace) and one address output"
+ },
+ { "exec": "./bitcoin-tx",
+ "args":
["-json",
"-create",
"in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0:4294967293",
@@ -542,14 +580,26 @@
"description": "Adds a new input with sequence number to a transaction (output in json)"
},
{ "exec": "./bitcoin-tx",
+ "args": ["-create", "outmultisig=1:-2:3:02a5:021:02df", "nversion=1"],
+ "return_code": 1,
+ "error_txt": "error: invalid multisig required number '-2'",
+ "description": "Try to parse a multisig number outside the allowed range"
+ },
+ { "exec": "./bitcoin-tx",
+ "args": ["-create", "outmultisig=1:2:3a:02a5:021:02df", "nversion=1"],
+ "return_code": 1,
+ "error_txt": "error: invalid multisig total number '3a'",
+ "description": "Try to parse a multisig number outside the allowed range"
+ },
+ { "exec": "./bitcoin-tx",
"args": ["-create", "outmultisig=1:2:3:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d:02df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb485", "nversion=1"],
"output_cmp": "txcreatemultisig1.hex",
"description": "Creates a new transaction with a single 2-of-3 multisig output"
},
{ "exec": "./bitcoin-tx",
- "args": ["-json", "-create", "outmultisig=1:2:3:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d:02df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb485", "nversion=1"],
+ "args": ["-json", "-create", "outmultisig=1: 2 : 3 :02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d:02df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb485", "nversion=1"],
"output_cmp": "txcreatemultisig1.json",
- "description": "Creates a new transaction with a single 2-of-3 multisig output (output in json)"
+ "description": "Creates a new transaction with a single 2-of-3 multisig output (with whitespace, output in json)"
},
{ "exec": "./bitcoin-tx",
"args": ["-create", "outmultisig=1:2:3:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d:02df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb485:S", "nversion=1"],