aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cirrus.yml7
-rw-r--r--Makefile.am3
-rw-r--r--build_msvc/vcpkg.json8
-rwxr-xr-xci/test/05_before_script.sh2
-rw-r--r--configure.ac38
-rw-r--r--contrib/builder-keys/README.md6
-rw-r--r--contrib/builder-keys/keys.txt1
-rw-r--r--contrib/guix/manifest.scm7
-rw-r--r--contrib/guix/patches/gcc-broken-longjmp.patch68
-rw-r--r--contrib/macdeploy/README.md12
-rwxr-xr-xcontrib/macdeploy/gen-sdk24
-rwxr-xr-xcontrib/seeds/makeseeds.py90
-rw-r--r--contrib/seeds/suspicious_hosts.txt16
-rw-r--r--contrib/testgen/README.md2
-rwxr-xr-xcontrib/testgen/gen_key_io_test_vectors.py22
-rw-r--r--depends/Makefile2
-rw-r--r--depends/README.md5
-rw-r--r--depends/funcs.mk22
-rw-r--r--doc/REST-interface.md18
-rw-r--r--doc/release-process.md3
-rw-r--r--src/Makefile.bench.include1
-rw-r--r--src/arith_uint256.cpp29
-rw-r--r--src/arith_uint256.h2
-rw-r--r--src/bench/rpc_blockchain.cpp4
-rw-r--r--src/bench/wallet_loading.cpp83
-rw-r--r--src/bitcoin-cli.cpp4
-rw-r--r--src/crypto/sha256.cpp16
-rw-r--r--src/init.cpp12
-rw-r--r--src/logging/timer.h8
-rw-r--r--src/net.cpp21
-rw-r--r--src/net_processing.cpp41
-rw-r--r--src/netbase.cpp53
-rw-r--r--src/netbase.h6
-rw-r--r--src/node/blockstorage.cpp20
-rw-r--r--src/node/blockstorage.h13
-rw-r--r--src/node/chainstate.cpp4
-rw-r--r--src/node/interfaces.cpp9
-rw-r--r--src/qt/bitcoin.cpp2
-rw-r--r--src/qt/bitcoinamountfield.cpp22
-rw-r--r--src/qt/bitcoinamountfield.h3
-rw-r--r--src/qt/bitcoingui.cpp20
-rw-r--r--src/qt/bitcoingui.h5
-rw-r--r--src/qt/bitcoinunits.cpp172
-rw-r--r--src/qt/bitcoinunits.h34
-rw-r--r--src/qt/coincontroldialog.cpp6
-rw-r--r--src/qt/guiutil.cpp18
-rw-r--r--src/qt/guiutil.h3
-rw-r--r--src/qt/notificator.cpp12
-rw-r--r--src/qt/optionsmodel.cpp31
-rw-r--r--src/qt/optionsmodel.h13
-rw-r--r--src/qt/overviewpage.cpp10
-rw-r--r--src/qt/peertablemodel.cpp3
-rw-r--r--src/qt/peertablemodel.h4
-rw-r--r--src/qt/peertablesortproxy.cpp2
-rw-r--r--src/qt/psbtoperationsdialog.cpp4
-rw-r--r--src/qt/rpcconsole.cpp13
-rw-r--r--src/qt/sendcoinsdialog.cpp3
-rw-r--r--src/qt/test/test_main.cpp42
-rw-r--r--src/qt/test/wallettests.cpp15
-rw-r--r--src/qt/transactiondesc.cpp28
-rw-r--r--src/qt/transactiondesc.h6
-rw-r--r--src/qt/transactionrecord.h21
-rw-r--r--src/qt/transactiontablemodel.cpp3
-rw-r--r--src/qt/walletview.cpp1
-rw-r--r--src/qt/walletview.h3
-rw-r--r--src/random.cpp2
-rw-r--r--src/rest.cpp11
-rw-r--r--src/rpc/blockchain.cpp45
-rw-r--r--src/rpc/blockchain.h3
-rw-r--r--src/script/miniscript.cpp3
-rw-r--r--src/sync.h4
-rw-r--r--src/test/data/key_io_invalid.json136
-rw-r--r--src/test/data/key_io_valid.json280
-rw-r--r--src/test/fuzz/util.cpp13
-rw-r--r--src/test/fuzz/util.h2
-rw-r--r--src/test/miniscript_tests.cpp11
-rw-r--r--src/test/util/logging.h2
-rw-r--r--src/test/util/net.h2
-rw-r--r--src/test/util/setup_common.cpp7
-rw-r--r--src/txmempool.cpp2
-rw-r--r--src/util/epochguard.h3
-rw-r--r--src/util/macros.h2
-rw-r--r--src/util/sock.cpp5
-rw-r--r--src/util/sock.h10
-rw-r--r--src/util/time.cpp19
-rw-r--r--src/validation.cpp34
-rw-r--r--src/validation.h6
-rw-r--r--src/wallet/rpc/wallet.cpp2
-rw-r--r--src/wallet/wallet.cpp14
-rw-r--r--test/README.md6
-rw-r--r--test/config.ini.in1
-rw-r--r--test/functional/README.md2
-rwxr-xr-xtest/functional/p2p_segwit.py25
-rwxr-xr-xtest/functional/test_framework/test_framework.py15
-rwxr-xr-xtest/functional/test_runner.py2
-rwxr-xr-xtest/functional/tool_signet_miner.py62
-rwxr-xr-xtest/functional/wallet_avoidreuse.py11
-rwxr-xr-xtest/functional/wallet_createwallet.py5
-rwxr-xr-xtest/lint/lint-git-commit-check.py63
-rwxr-xr-xtest/lint/lint-git-commit-check.sh48
-rwxr-xr-xtest/lint/lint-includes.py179
-rwxr-xr-xtest/lint/lint-includes.sh103
-rwxr-xr-xtest/lint/lint-logs.py34
-rwxr-xr-xtest/lint/lint-logs.sh28
-rwxr-xr-xtest/lint/lint-python-mutable-default-parameters.py72
-rwxr-xr-xtest/lint/lint-python-mutable-default-parameters.sh52
-rwxr-xr-xtest/lint/lint-python.py131
-rwxr-xr-xtest/lint/lint-python.sh111
-rwxr-xr-xtest/lint/lint-whitespace.py135
-rwxr-xr-xtest/lint/lint-whitespace.sh115
110 files changed, 1744 insertions, 1255 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
index 5bfdcbc9b1..c4aae3a50a 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -96,7 +96,7 @@ task:
env:
PATH: 'C:\jom;C:\Python39;C:\Python39\Scripts;C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\MSBuild\Current\Bin;%PATH%'
PYTHONUTF8: 1
- CI_VCPKG_TAG: '2022.02.23'
+ CI_VCPKG_TAG: '2022.04.12'
VCPKG_DOWNLOADS: 'C:\Users\ContainerAdministrator\AppData\Local\vcpkg\downloads'
VCPKG_DEFAULT_BINARY_CACHE: 'C:\Users\ContainerAdministrator\AppData\Local\vcpkg\archives'
CCACHE_DIR: 'C:\Users\ContainerAdministrator\AppData\Local\ccache'
@@ -106,6 +106,7 @@ task:
QT_SOURCE_DIR: 'C:\qt-everywhere-src-5.15.3'
QTBASEDIR: 'C:\Qt_static'
x64_NATIVE_TOOLS: '"C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Auxiliary\Build\vcvars64.bat"'
+ QT_CONFIGURE_COMMAND: '..\configure -release -silent -opensource -confirm-license -opengl desktop -static -static-runtime -mp -qt-zlib -qt-pcre -qt-libpng -nomake examples -nomake tests -nomake tools -no-angle -no-dbus -no-gif -no-gtk -no-ico -no-icu -no-libjpeg -no-libudev -no-sql-sqlite -no-sql-odbc -no-sqlite -no-vulkan -skip qt3d -skip qtactiveqt -skip qtandroidextras -skip qtcharts -skip qtconnectivity -skip qtdatavis3d -skip qtdeclarative -skip doc -skip qtdoc -skip qtgamepad -skip qtgraphicaleffects -skip qtimageformats -skip qtlocation -skip qtlottie -skip qtmacextras -skip qtmultimedia -skip qtnetworkauth -skip qtpurchasing -skip qtquick3d -skip qtquickcontrols -skip qtquickcontrols2 -skip qtquicktimeline -skip qtremoteobjects -skip qtscript -skip qtscxml -skip qtsensors -skip qtserialbus -skip qtserialport -skip qtspeech -skip qtsvg -skip qtvirtualkeyboard -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebglplugin -skip qtwebsockets -skip qtwebview -skip qtx11extras -skip qtxmlpatterns -no-openssl -no-feature-bearermanagement -no-feature-printdialog -no-feature-printer -no-feature-printpreviewdialog -no-feature-printpreviewwidget -no-feature-sql -no-feature-sqlmodel -no-feature-textbrowser -no-feature-textmarkdownwriter -no-feature-textodfwriter -no-feature-xml'
IgnoreWarnIntDirInTempDetected: 'true'
merge_script:
- git config --global user.email "ci@ci.ci"
@@ -120,7 +121,7 @@ task:
folder: "%QTBASEDIR%"
reupload_on_changes: false
fingerprint_script:
- - echo %QT_DOWNLOAD_URL%
+ - echo %QT_DOWNLOAD_URL% %QT_CONFIGURE_COMMAND%
- msbuild -version
populate_script:
- curl -L -o C:\jom.zip http://download.qt.io/official_releases/jom/jom.zip
@@ -132,7 +133,7 @@ task:
- cd %QT_SOURCE_DIR%
- mkdir build
- cd build
- - ..\configure -release -silent -opensource -confirm-license -opengl desktop -static -static-runtime -mp -qt-zlib -qt-pcre -qt-libpng -nomake examples -nomake tests -nomake tools -no-angle -no-dbus -no-gif -no-gtk -no-ico -no-icu -no-libjpeg -no-libudev -no-sql-sqlite -no-sql-odbc -no-sqlite -no-vulkan -skip qt3d -skip qtactiveqt -skip qtandroidextras -skip qtcharts -skip qtconnectivity -skip qtdatavis3d -skip qtdeclarative -skip doc -skip qtdoc -skip qtgamepad -skip qtgraphicaleffects -skip qtimageformats -skip qtlocation -skip qtlottie -skip qtmacextras -skip qtmultimedia -skip qtnetworkauth -skip qtpurchasing -skip qtquick3d -skip qtquickcontrols -skip qtquickcontrols2 -skip qtquicktimeline -skip qtremoteobjects -skip qtscript -skip qtscxml -skip qtsensors -skip qtserialbus -skip qtserialport -skip qtspeech -skip qtsvg -skip qtvirtualkeyboard -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebglplugin -skip qtwebsockets -skip qtwebview -skip qtx11extras -skip qtxmlpatterns -no-openssl -no-feature-bearermanagement -no-feature-printdialog -no-feature-printer -no-feature-printpreviewdialog -no-feature-printpreviewwidget -no-feature-sql -no-feature-sqlmodel -no-feature-textbrowser -no-feature-textmarkdownwriter -no-feature-textodfwriter -no-feature-xml -prefix %QTBASEDIR%
+ - '%QT_CONFIGURE_COMMAND% -prefix %QTBASEDIR%'
- jom
- jom install
vcpkg_tools_cache:
diff --git a/Makefile.am b/Makefile.am
index eec7da7d86..dd658170b3 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -46,7 +46,8 @@ DIST_CONTRIB = \
$(top_srcdir)/test/sanitizer_suppressions/tsan \
$(top_srcdir)/test/sanitizer_suppressions/ubsan \
$(top_srcdir)/contrib/linearize/linearize-data.py \
- $(top_srcdir)/contrib/linearize/linearize-hashes.py
+ $(top_srcdir)/contrib/linearize/linearize-hashes.py \
+ $(top_srcdir)/contrib/signet/miner
DIST_SHARE = \
$(top_srcdir)/share/genbuild.sh \
diff --git a/build_msvc/vcpkg.json b/build_msvc/vcpkg.json
index 48b4d1fd20..86773d1fd3 100644
--- a/build_msvc/vcpkg.json
+++ b/build_msvc/vcpkg.json
@@ -13,13 +13,5 @@
"features": ["thread"]
},
"zeromq"
- ],
- "builtin-baseline": "b86c0c35b88e2bf3557ff49dc831689c2f085090",
- "overrides": [
- {
- "name": "zeromq",
- "version": "4.3.4",
- "port-version": 3
- }
]
}
diff --git a/ci/test/05_before_script.sh b/ci/test/05_before_script.sh
index d8dcb708aa..fc2f76797c 100755
--- a/ci/test/05_before_script.sh
+++ b/ci/test/05_before_script.sh
@@ -45,7 +45,7 @@ if [ -z "$NO_DEPENDS" ]; then
else
SHELL_OPTS="CONFIG_SHELL="
fi
- CI_EXEC "$SHELL_OPTS" make "$MAKEJOBS" -C depends HOST="$HOST" "$DEP_OPTS"
+ CI_EXEC "$SHELL_OPTS" make "$MAKEJOBS" -C depends HOST="$HOST" "$DEP_OPTS" LOG=1
fi
if [ -n "$PREVIOUS_RELEASES_TO_DOWNLOAD" ]; then
CI_EXEC test/get_previous_releases.py -b -t "$PREVIOUS_RELEASES_DIR" "${PREVIOUS_RELEASES_TO_DOWNLOAD}"
diff --git a/configure.ac b/configure.ac
index c3e7657515..4c80899e5f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1299,21 +1299,6 @@ if test "$enable_fuzz" = "yes"; then
enable_fuzz_binary=yes
AX_CHECK_PREPROC_FLAG([-DABORT_ON_FAILED_ASSUME], [DEBUG_CPPFLAGS="$DEBUG_CPPFLAGS -DABORT_ON_FAILED_ASSUME"], [], [$CXXFLAG_WERROR])
-
- AC_MSG_CHECKING([whether main function is needed for fuzz binary])
- AX_CHECK_LINK_FLAG(
- [-fsanitize=$use_sanitizers],
- [AC_MSG_RESULT([no])],
- [AC_MSG_RESULT([yes]); CORE_CPPFLAGS="$CORE_CPPFLAGS -DPROVIDE_FUZZ_MAIN_FUNCTION"],
- [],
- [AC_LANG_PROGRAM([[
- #include <cstdint>
- #include <cstddef>
- extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { return 0; }
- /* comment to remove the main function ...
- ]],[[
- */ int not_main() {
- ]])])
else
BITCOIN_QT_INIT
@@ -1327,8 +1312,25 @@ else
QT_DBUS_INCLUDES=SUPPRESS_WARNINGS($QT_DBUS_INCLUDES)
QT_TEST_INCLUDES=SUPPRESS_WARNINGS($QT_TEST_INCLUDES)
fi
+fi
+
+if test "$enable_fuzz_binary" = "yes"; then
+ AC_MSG_CHECKING([whether main function is needed for fuzz binary])
+ AX_CHECK_LINK_FLAG(
+ [],
+ [AC_MSG_RESULT([no])],
+ [AC_MSG_RESULT([yes]); CORE_CPPFLAGS="$CORE_CPPFLAGS -DPROVIDE_FUZZ_MAIN_FUNCTION"],
+ [$SANITIZER_LDFLAGS],
+ [AC_LANG_PROGRAM([[
+ #include <cstdint>
+ #include <cstddef>
+ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { return 0; }
+ /* comment to remove the main function ...
+ ]],[[
+ */ int not_main() {
+ ]])])
- CORE_CPPFLAGS="$CORE_CPPFLAGS -DPROVIDE_FUZZ_MAIN_FUNCTION"
+ CHECK_RUNTIME_LIB
fi
if test "$enable_wallet" != "no"; then
@@ -1818,10 +1820,6 @@ if test "$build_bitcoin_wallet$build_bitcoin_cli$build_bitcoin_tx$build_bitcoin_
AC_MSG_ERROR([No targets! Please specify at least one of: --with-utils --with-libs --with-daemon --with-gui --enable-bench or --enable-tests])
fi
-if test "$enable_fuzz_binary" = "yes"; then
- CHECK_RUNTIME_LIB
-fi
-
AM_CONDITIONAL([TARGET_DARWIN], [test "$TARGET_OS" = "darwin"])
AM_CONDITIONAL([BUILD_DARWIN], [test "$BUILD_OS" = "darwin"])
AM_CONDITIONAL([TARGET_LINUX], [test "$TARGET_OS" = "linux"])
diff --git a/contrib/builder-keys/README.md b/contrib/builder-keys/README.md
index 56bd87d0af..a6179d6012 100644
--- a/contrib/builder-keys/README.md
+++ b/contrib/builder-keys/README.md
@@ -19,9 +19,15 @@ gpg --refresh-keys
To fetch keys of builders and active developers, feed the list of fingerprints
of the primary keys into gpg:
+On \*NIX:
```sh
while read fingerprint keyholder_name; do gpg --keyserver hkps://keys.openpgp.org --recv-keys ${fingerprint}; done < ./keys.txt
```
+On Windows (requires Gpg4win >= 4.0.0):
+```
+FOR /F "tokens=1" %i IN (keys.txt) DO gpg --keyserver hkps://keys.openpgp.org --recv-keys %i
+```
+
Add your key to the list if you provided Guix attestations for two major or
minor releases of Bitcoin Core.
diff --git a/contrib/builder-keys/keys.txt b/contrib/builder-keys/keys.txt
index 913e7d32f6..c70069b440 100644
--- a/contrib/builder-keys/keys.txt
+++ b/contrib/builder-keys/keys.txt
@@ -51,5 +51,6 @@ ED9BDF7AD6A55E232E84524257FF9BDBCC301009 Sjors Provoost (sjors)
9EDAFF80E080659604F4A76B2EBB056FD847F8A7 Stephan Oeste (Emzy)
6DEEF79B050C4072509B743F8C275BC595448867 Tomas Kanocz (KanoczTomas)
AEC1884398647C47413C1C3FB1179EB7347DC10D Warren Togami (wtogami)
+74E2DEF5D77260B98BC19438099BAD163C70FBFA Will Clark (will8clark)
79D00BAC68B56D422F945A8F8E3A8F3247DBCBBF Willy Ko (willyko)
71A3B16735405025D447E8F274810B012346C9A6 Wladimir J. van der Laan (laanwj)
diff --git a/contrib/guix/manifest.scm b/contrib/guix/manifest.scm
index 95fd178b55..9f8a4008cf 100644
--- a/contrib/guix/manifest.scm
+++ b/contrib/guix/manifest.scm
@@ -162,9 +162,10 @@ desirable for building Bitcoin Core release binaries."
(define (make-gcc-with-pthreads gcc)
(package-with-extra-configure-variable gcc "--enable-threads" "posix"))
-(define (make-mingw-w64-cross-gcc-vmov-alignment cross-gcc)
+(define (make-mingw-w64-cross-gcc cross-gcc)
(package-with-extra-patches cross-gcc
- (search-our-patches "vmov-alignment.patch")))
+ (search-our-patches "vmov-alignment.patch"
+ "gcc-broken-longjmp.patch")))
(define (make-mingw-pthreads-cross-toolchain target)
"Create a cross-compilation toolchain package for TARGET"
@@ -172,7 +173,7 @@ desirable for building Bitcoin Core release binaries."
(pthreads-xlibc mingw-w64-x86_64-winpthreads)
(pthreads-xgcc (make-gcc-with-pthreads
(cross-gcc target
- #:xgcc (make-ssp-fixed-gcc (make-mingw-w64-cross-gcc-vmov-alignment base-gcc))
+ #:xgcc (make-ssp-fixed-gcc (make-mingw-w64-cross-gcc base-gcc))
#:xbinutils xbinutils
#:libc pthreads-xlibc))))
;; Define a meta-package that propagates the resulting XBINUTILS, XLIBC, and
diff --git a/contrib/guix/patches/gcc-broken-longjmp.patch b/contrib/guix/patches/gcc-broken-longjmp.patch
new file mode 100644
index 0000000000..1cfc0918b0
--- /dev/null
+++ b/contrib/guix/patches/gcc-broken-longjmp.patch
@@ -0,0 +1,68 @@
+commit eb5698897c52702498938592d7f76e67d126451f
+Author: Eric Botcazou <ebotcazou@adacore.com>
+Date: Wed May 5 22:48:51 2021 +0200
+
+ Fix PR target/100402
+
+ This is a regression for 64-bit Windows present from mainline down to the 9
+ branch and introduced by the fix for PR target/99234. Again SEH, but with
+ a twist related to the way MinGW implements setjmp/longjmp, which turns out
+ to be piggybacked on SEH with recent versions of MinGW, i.e. the longjmp
+ performs a bona-fide unwinding of the stack, because it calls RtlUnwindEx
+ with the second argument initially passed to setjmp, which is the result of
+ __builtin_frame_address (0) in the MinGW header file:
+
+ define setjmp(BUF) _setjmp((BUF), __builtin_frame_address (0))
+
+ This means that we directly expose the frame pointer to the SEH machinery
+ here (unlike with regular exception handling where we use an intermediate
+ CFA) and thus that we cannot do whatever we want with it. The old code
+ would leave it unaligned, i.e. not multiple of 16, whereas the new code
+ aligns it, but this breaks for some reason; at least it appears that a
+ .seh_setframe directive with 0 as second argument always works, so the
+ fix aligns it this way.
+
+ gcc/
+ PR target/100402
+ * config/i386/i386.c (ix86_compute_frame_layout): For a SEH target,
+ always return the establisher frame for __builtin_frame_address (0).
+ gcc/testsuite/
+ * gcc.c-torture/execute/20210505-1.c: New test.
+
+diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
+index 2f838840e96..06ad1b2274e 100644
+--- a/gcc/config/i386/i386.c
++++ b/gcc/config/i386/i386.c
+@@ -6356,12 +6356,29 @@ ix86_compute_frame_layout (void)
+ area, see the SEH code in config/i386/winnt.c for the rationale. */
+ frame->hard_frame_pointer_offset = frame->sse_reg_save_offset;
+
+- /* If we can leave the frame pointer where it is, do so. Also, return
++ /* If we can leave the frame pointer where it is, do so; however return
+ the establisher frame for __builtin_frame_address (0) or else if the
+- frame overflows the SEH maximum frame size. */
++ frame overflows the SEH maximum frame size.
++
++ Note that the value returned by __builtin_frame_address (0) is quite
++ constrained, because setjmp is piggybacked on the SEH machinery with
++ recent versions of MinGW:
++
++ # elif defined(__SEH__)
++ # if defined(__aarch64__) || defined(_ARM64_)
++ # define setjmp(BUF) _setjmp((BUF), __builtin_sponentry())
++ # elif (__MINGW_GCC_VERSION < 40702)
++ # define setjmp(BUF) _setjmp((BUF), mingw_getsp())
++ # else
++ # define setjmp(BUF) _setjmp((BUF), __builtin_frame_address (0))
++ # endif
++
++ and the second argument passed to _setjmp, if not null, is forwarded
++ to the TargetFrame parameter of RtlUnwindEx by longjmp (after it has
++ built an ExceptionRecord on the fly describing the setjmp buffer). */
+ const HOST_WIDE_INT diff
+ = frame->stack_pointer_offset - frame->hard_frame_pointer_offset;
+- if (diff <= 255)
++ if (diff <= 255 && !crtl->accesses_prior_frames)
+ {
+ /* The resulting diff will be a multiple of 16 lower than 255,
+ i.e. at most 240 as required by the unwind data structure. */
diff --git a/contrib/macdeploy/README.md b/contrib/macdeploy/README.md
index 71f0cbfab6..fa7d953ce3 100644
--- a/contrib/macdeploy/README.md
+++ b/contrib/macdeploy/README.md
@@ -15,13 +15,16 @@ When complete, it will have produced `Bitcoin-Core.dmg`.
A free Apple Developer Account is required to proceed.
Our current macOS SDK
-(`Xcode-12.2-12B45b-extracted-SDK-with-libcxx-headers.tar.gz`) can be
-extracted from
+(`Xcode-12.2-12B45b-extracted-SDK-with-libcxx-headers.tar.gz`)
+can be extracted from
[Xcode_12.2.xip](https://download.developer.apple.com/Developer_Tools/Xcode_12.2/Xcode_12.2.xip).
+
Alternatively, after logging in to your account go to 'Downloads', then 'More'
and search for [`Xcode_12.2`](https://developer.apple.com/download/all/?q=Xcode%2012.2).
+
An Apple ID and cookies enabled for the hostname are needed to download this.
-The `sha256sum` of the archive should be `28d352f8c14a43d9b8a082ac6338dc173cb153f964c6e8fb6ba389e5be528bd0`.
+
+The `sha256sum` of the downloaded XIP archive should be `28d352f8c14a43d9b8a082ac6338dc173cb153f964c6e8fb6ba389e5be528bd0`.
After Xcode version 7.x, Apple started shipping the `Xcode.app` in a `.xip`
archive. This makes the SDK less-trivial to extract on non-macOS machines. One
@@ -55,7 +58,10 @@ previous stage) as the first argument.
./contrib/macdeploy/gen-sdk '/path/to/Xcode.app'
```
+The `sha256sum` of the generated TAR.GZ archive should be `e7ca56bc8804d16624fad68be2e71647747d6629cacaaa3de5fbfa7f444e9eae`.
+
## Deterministic macOS DMG Notes
+
Working macOS DMGs are created in Linux by combining a recent `clang`, the Apple
`binutils` (`ld`, `ar`, etc) and DMG authoring tools.
diff --git a/contrib/macdeploy/gen-sdk b/contrib/macdeploy/gen-sdk
index ebef1d2db0..d70cc8613c 100755
--- a/contrib/macdeploy/gen-sdk
+++ b/contrib/macdeploy/gen-sdk
@@ -8,6 +8,21 @@ import gzip
import os
import contextlib
+# monkey-patch Python 3.8 and older to fix wrong TAR header handling
+# see https://github.com/bitcoin/bitcoin/pull/24534
+# and https://github.com/python/cpython/pull/18080 for more info
+if sys.version_info < (3, 9):
+ _old_create_header = tarfile.TarInfo._create_header
+ def _create_header(info, format, encoding, errors):
+ buf = _old_create_header(info, format, encoding, errors)
+ # replace devmajor/devminor with binary zeroes
+ buf = buf[:329] + bytes(16) + buf[345:]
+ # recompute checksum
+ chksum = tarfile.calc_chksums(buf)[0]
+ buf = buf[:-364] + bytes("%06o\0" % chksum, "ascii") + buf[-357:]
+ return buf
+ tarfile.TarInfo._create_header = staticmethod(_create_header)
+
@contextlib.contextmanager
def cd(path):
"""Context manager that restores PWD even if an exception was raised."""
@@ -75,14 +90,21 @@ def run():
tarinfo.name = str(pathlib.Path(alt_base_dir, tarinfo.name))
if tarinfo.linkname and tarinfo.linkname.startswith("./"):
tarinfo.linkname = str(pathlib.Path(alt_base_dir, tarinfo.linkname))
+ # make metadata deterministic
+ tarinfo.mtime = 0
+ tarinfo.uid, tarinfo.uname = 0, ''
+ tarinfo.gid, tarinfo.gname = 0, ''
+ # don't use isdir() as there are also executable files present
+ tarinfo.mode = 0o0755 if tarinfo.mode & 0o0100 else 0x0644
return tarinfo
with cd(dir_to_add):
+ # recursion already adds entries in sorted order
tarfp.add(".", recursive=True, filter=change_tarinfo_base)
print("Creating output .tar.gz file...")
with out_sdktgz_path.open("wb") as fp:
with gzip.GzipFile(fileobj=fp, mode='wb', compresslevel=9, mtime=0) as gzf:
- with tarfile.open(mode="w", fileobj=gzf) as tarfp:
+ with tarfile.open(mode="w", fileobj=gzf, format=tarfile.GNU_FORMAT) as tarfp:
print("Adding MacOSX SDK {} files...".format(sdk_version))
tarfp_add_with_base_change(tarfp, sdk_dir, out_name)
print("Adding libc++ headers...")
diff --git a/contrib/seeds/makeseeds.py b/contrib/seeds/makeseeds.py
index 2b377f6c01..78eb04a836 100755
--- a/contrib/seeds/makeseeds.py
+++ b/contrib/seeds/makeseeds.py
@@ -10,18 +10,16 @@ import re
import sys
import dns.resolver
import collections
+from typing import List, Dict, Union
NSEEDS=512
-MAX_SEEDS_PER_ASN=2
-
-MIN_BLOCKS = 337600
-
-# These are hosts that have been observed to be behaving strangely (e.g.
-# aggressively connecting to every node).
-with open("suspicious_hosts.txt", mode="r", encoding="utf-8") as f:
- SUSPICIOUS_HOSTS = {s.strip() for s in f if s.strip()}
+MAX_SEEDS_PER_ASN = {
+ 'ipv4': 2,
+ 'ipv6': 10,
+}
+MIN_BLOCKS = 730000
PATTERN_IPV4 = re.compile(r"^((\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})):(\d+)$")
PATTERN_IPV6 = re.compile(r"^\[([0-9a-z:]+)\]:(\d+)$")
@@ -40,9 +38,13 @@ PATTERN_AGENT = re.compile(
r"23.99"
r")")
-def parseline(line):
+def parseline(line: str) -> Union[dict, None]:
+ """ Parses a line from `seeds_main.txt` into a dictionary of details for that line.
+ or `None`, if the line could not be parsed.
+ """
sline = line.split()
if len(sline) < 11:
+ # line too short to be valid, skip it.
return None
m = PATTERN_IPV4.match(sline[0])
sortkey = None
@@ -107,25 +109,26 @@ def parseline(line):
'sortkey': sortkey,
}
-def dedup(ips):
- '''deduplicate by address,port'''
+def dedup(ips: List[Dict]) -> List[Dict]:
+ """ Remove duplicates from `ips` where multiple ips share address and port. """
d = {}
for ip in ips:
d[ip['ip'],ip['port']] = ip
return list(d.values())
-def filtermultiport(ips):
- '''Filter out hosts with more nodes per IP'''
+def filtermultiport(ips: List[Dict]) -> List[Dict]:
+ """ Filter out hosts with more nodes per IP"""
hist = collections.defaultdict(list)
for ip in ips:
hist[ip['sortkey']].append(ip)
return [value[0] for (key,value) in list(hist.items()) if len(value)==1]
-def lookup_asn(net, ip):
- '''
- Look up the asn for an IP (4 or 6) address by querying cymru.com, or None
- if it could not be found.
- '''
+def lookup_asn(net: str, ip: str) -> Union[int, None]:
+ """ Look up the asn for an `ip` address by querying cymru.com
+ on network `net` (e.g. ipv4 or ipv6).
+
+ Returns in integer ASN or None if it could not be found.
+ """
try:
if net == 'ipv4':
ipaddr = ip
@@ -147,20 +150,33 @@ def lookup_asn(net, ip):
return None
# Based on Greg Maxwell's seed_filter.py
-def filterbyasn(ips, max_per_asn, max_per_net):
+def filterbyasn(ips: List[Dict], max_per_asn: Dict, max_per_net: int) -> List[Dict]:
+ """ Prunes `ips` by
+ (a) trimming ips to have at most `max_per_net` ips from each net (e.g. ipv4, ipv6); and
+ (b) trimming ips to have at most `max_per_asn` ips from each asn in each net.
+ """
# Sift out ips by type
ips_ipv46 = [ip for ip in ips if ip['net'] in ['ipv4', 'ipv6']]
ips_onion = [ip for ip in ips if ip['net'] == 'onion']
# Filter IPv46 by ASN, and limit to max_per_net per network
result = []
- net_count = collections.defaultdict(int)
- asn_count = collections.defaultdict(int)
- for ip in ips_ipv46:
+ net_count: Dict[str, int] = collections.defaultdict(int)
+ asn_count: Dict[int, int] = collections.defaultdict(int)
+
+ for i, ip in enumerate(ips_ipv46):
+ if i % 10 == 0:
+ # give progress update
+ print(f"{i:6d}/{len(ips_ipv46)} [{100*i/len(ips_ipv46):04.1f}%]\r", file=sys.stderr, end='', flush=True)
+
if net_count[ip['net']] == max_per_net:
+ # do not add this ip as we already too many
+ # ips from this network
continue
asn = lookup_asn(ip['net'], ip['ip'])
- if asn is None or asn_count[asn] == max_per_asn:
+ if asn is None or asn_count[asn] == max_per_asn[ip['net']]:
+ # do not add this ip as we already have too many
+ # ips from this ASN on this network
continue
asn_count[asn] += 1
net_count[ip['net']] += 1
@@ -170,35 +186,33 @@ def filterbyasn(ips, max_per_asn, max_per_net):
result.extend(ips_onion[0:max_per_net])
return result
-def ip_stats(ips):
- hist = collections.defaultdict(int)
+def ip_stats(ips: List[Dict]) -> str:
+ """ Format and return pretty string from `ips`. """
+ hist: Dict[str, int] = collections.defaultdict(int)
for ip in ips:
if ip is not None:
hist[ip['net']] += 1
- return '%6d %6d %6d' % (hist['ipv4'], hist['ipv6'], hist['onion'])
+ return f"{hist['ipv4']:6d} {hist['ipv6']:6d} {hist['onion']:6d}"
def main():
lines = sys.stdin.readlines()
ips = [parseline(line) for line in lines]
print('\x1b[7m IPv4 IPv6 Onion Pass \x1b[0m', file=sys.stderr)
- print('%s Initial' % (ip_stats(ips)), file=sys.stderr)
+ print(f'{ip_stats(ips):s} Initial', file=sys.stderr)
# Skip entries with invalid address.
ips = [ip for ip in ips if ip is not None]
- print('%s Skip entries with invalid address' % (ip_stats(ips)), file=sys.stderr)
+ print(f'{ip_stats(ips):s} Skip entries with invalid address', file=sys.stderr)
# Skip duplicates (in case multiple seeds files were concatenated)
ips = dedup(ips)
- print('%s After removing duplicates' % (ip_stats(ips)), file=sys.stderr)
- # Skip entries from suspicious hosts.
- ips = [ip for ip in ips if ip['ip'] not in SUSPICIOUS_HOSTS]
- print('%s Skip entries from suspicious hosts' % (ip_stats(ips)), file=sys.stderr)
+ print(f'{ip_stats(ips):s} After removing duplicates', file=sys.stderr)
# Enforce minimal number of blocks.
ips = [ip for ip in ips if ip['blocks'] >= MIN_BLOCKS]
- print('%s Enforce minimal number of blocks' % (ip_stats(ips)), file=sys.stderr)
+ print(f'{ip_stats(ips):s} Enforce minimal number of blocks', file=sys.stderr)
# Require service bit 1.
ips = [ip for ip in ips if (ip['service'] & 1) == 1]
- print('%s Require service bit 1' % (ip_stats(ips)), file=sys.stderr)
+ print(f'{ip_stats(ips):s} Require service bit 1', file=sys.stderr)
# Require at least 50% 30-day uptime for clearnet, 10% for onion.
req_uptime = {
'ipv4': 50,
@@ -206,18 +220,18 @@ def main():
'onion': 10,
}
ips = [ip for ip in ips if ip['uptime'] > req_uptime[ip['net']]]
- print('%s Require minimum uptime' % (ip_stats(ips)), file=sys.stderr)
+ print(f'{ip_stats(ips):s} Require minimum uptime', file=sys.stderr)
# Require a known and recent user agent.
ips = [ip for ip in ips if PATTERN_AGENT.match(ip['agent'])]
- print('%s Require a known and recent user agent' % (ip_stats(ips)), file=sys.stderr)
+ print(f'{ip_stats(ips):s} Require a known and recent user agent', file=sys.stderr)
# Sort by availability (and use last success as tie breaker)
ips.sort(key=lambda x: (x['uptime'], x['lastsuccess'], x['ip']), reverse=True)
# Filter out hosts with multiple bitcoin ports, these are likely abusive
ips = filtermultiport(ips)
- print('%s Filter out hosts with multiple bitcoin ports' % (ip_stats(ips)), file=sys.stderr)
+ print(f'{ip_stats(ips):s} Filter out hosts with multiple bitcoin ports', file=sys.stderr)
# Look up ASNs and limit results, both per ASN and globally.
ips = filterbyasn(ips, MAX_SEEDS_PER_ASN, NSEEDS)
- print('%s Look up ASNs and limit results per ASN and per net' % (ip_stats(ips)), file=sys.stderr)
+ print(f'{ip_stats(ips):s} Look up ASNs and limit results per ASN and per net', file=sys.stderr)
# Sort the results by IP address (for deterministic output).
ips.sort(key=lambda x: (x['net'], x['sortkey']))
for ip in ips:
diff --git a/contrib/seeds/suspicious_hosts.txt b/contrib/seeds/suspicious_hosts.txt
deleted file mode 100644
index 13385cc816..0000000000
--- a/contrib/seeds/suspicious_hosts.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-130.211.129.106
-148.251.238.178
-176.9.46.6
-178.63.107.226
-54.173.72.127
-54.174.10.182
-54.183.64.54
-54.194.231.211
-54.66.214.167
-54.66.220.137
-54.67.33.14
-54.77.251.214
-54.94.195.96
-54.94.200.247
-83.81.130.26
-88.198.17.7 \ No newline at end of file
diff --git a/contrib/testgen/README.md b/contrib/testgen/README.md
index 66276ec9dd..2f0288df16 100644
--- a/contrib/testgen/README.md
+++ b/contrib/testgen/README.md
@@ -2,7 +2,7 @@
Utilities to generate test vectors for the data-driven Bitcoin tests.
-Usage:
+To use inside a scripted-diff (or just execute directly):
./gen_key_io_test_vectors.py valid 70 > ../../src/test/data/key_io_valid.json
./gen_key_io_test_vectors.py invalid 70 > ../../src/test/data/key_io_invalid.json
diff --git a/contrib/testgen/gen_key_io_test_vectors.py b/contrib/testgen/gen_key_io_test_vectors.py
index 4aa7dc200b..7bfb1d76a8 100755
--- a/contrib/testgen/gen_key_io_test_vectors.py
+++ b/contrib/testgen/gen_key_io_test_vectors.py
@@ -4,10 +4,6 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
'''
Generate valid and invalid base58/bech32(m) address and private key test vectors.
-
-Usage:
- ./gen_key_io_test_vectors.py valid 70 > ../../src/test/data/key_io_valid.json
- ./gen_key_io_test_vectors.py invalid 70 > ../../src/test/data/key_io_invalid.json
'''
from itertools import islice
@@ -131,7 +127,7 @@ def is_valid_bech32(v):
def gen_valid_base58_vector(template):
'''Generate valid base58 vector'''
prefix = bytearray(template[0])
- payload = bytearray(os.urandom(template[1]))
+ payload = rand_bytes(size=template[1])
suffix = bytearray(template[2])
dst_prefix = bytearray(template[4])
dst_suffix = bytearray(template[5])
@@ -143,7 +139,7 @@ def gen_valid_bech32_vector(template):
'''Generate valid bech32 vector'''
hrp = template[0]
witver = template[1]
- witprog = bytearray(os.urandom(template[2]))
+ witprog = rand_bytes(size=template[2])
encoding = template[4]
dst_prefix = bytearray(template[5])
rv = bech32_encode(encoding, hrp, [witver] + convertbits(witprog, 8, 5))
@@ -173,17 +169,17 @@ def gen_invalid_base58_vector(template):
corrupt_suffix = randbool(0.2)
if corrupt_prefix:
- prefix = os.urandom(1)
+ prefix = rand_bytes(size=1)
else:
prefix = bytearray(template[0])
if randomize_payload_size:
- payload = os.urandom(max(int(random.expovariate(0.5)), 50))
+ payload = rand_bytes(size=max(int(random.expovariate(0.5)), 50))
else:
- payload = os.urandom(template[1])
+ payload = rand_bytes(size=template[1])
if corrupt_suffix:
- suffix = os.urandom(len(template[2]))
+ suffix = rand_bytes(size=len(template[2]))
else:
suffix = bytearray(template[2])
@@ -204,7 +200,7 @@ def gen_invalid_bech32_vector(template):
to_upper = randbool(0.1)
hrp = template[0]
witver = template[1]
- witprog = bytearray(os.urandom(template[2]))
+ witprog = rand_bytes(size=template[2])
encoding = template[3]
if no_data:
@@ -234,6 +230,9 @@ def randbool(p = 0.5):
'''Return True with P(p)'''
return random.random() < p
+def rand_bytes(*, size):
+ return bytearray(random.getrandbits(8) for _ in range(size))
+
def gen_invalid_vectors():
'''Generate invalid test vectors'''
# start with some manual edge-cases
@@ -250,6 +249,7 @@ def gen_invalid_vectors():
if __name__ == '__main__':
import json
iters = {'valid':gen_valid_vectors, 'invalid':gen_invalid_vectors}
+ random.seed(42)
try:
uiter = iters[sys.argv[1]]
except IndexError:
diff --git a/depends/Makefile b/depends/Makefile
index 723509c81d..b901533786 100644
--- a/depends/Makefile
+++ b/depends/Makefile
@@ -271,7 +271,7 @@ clean-all: clean
@rm -rf $(SOURCES_PATH) x86_64* i686* mips* arm* aarch64* powerpc* riscv32* riscv64* s390x*
clean:
- @rm -rf $(WORK_PATH) $(BASE_CACHE) $(BUILD)
+ @rm -rf $(WORK_PATH) $(BASE_CACHE) $(BUILD) *.log
install: check-packages $(host_prefix)/share/config.site
diff --git a/depends/README.md b/depends/README.md
index f7647198c6..da2a74e0e7 100644
--- a/depends/README.md
+++ b/depends/README.md
@@ -113,7 +113,10 @@ The following can be set when running make: `make FOO=bar`
- `BUILD_ID_SALT`: Optional salt to use when generating build package ids
- `FORCE_USE_SYSTEM_CLANG`: (EXPERTS ONLY) When cross-compiling for macOS, use Clang found in the
system's `$PATH` rather than the default prebuilt release of Clang
- from llvm.org. Clang 8 or later is required.
+ from llvm.org. Clang 8 or later is required
+- `LOG`: Use file-based logging for individual packages. During a package build its log file
+ resides in the `depends` directory, and the log file is printed out automatically in case
+ of build error. After successful build log files are moved along with package archives
If some packages are not built, for example `make NO_WALLET=1`, the appropriate
options will be passed to bitcoin's configure. In this case, `--disable-wallet`.
diff --git a/depends/funcs.mk b/depends/funcs.mk
index c4ad109c69..75fa1ed43f 100644
--- a/depends/funcs.mk
+++ b/depends/funcs.mk
@@ -67,6 +67,7 @@ $(1)_cached_checksum:=$(BASE_CACHE)/$(host)/$(1)/$(1)-$($(1)_version)-$($(1)_bui
$(1)_patch_dir:=$(base_build_dir)/$(host)/$(1)/$($(1)_version)-$($(1)_build_id)/.patches-$($(1)_build_id)
$(1)_prefixbin:=$($($(1)_type)_prefix)/bin/
$(1)_cached:=$(BASE_CACHE)/$(host)/$(1)/$(1)-$($(1)_version)-$($(1)_build_id).tar.gz
+$(1)_build_log:=$(BASEDIR)/$(1)-$($(1)_version)-$($(1)_build_id).log
$(1)_all_sources=$($(1)_file_name) $($(1)_extra_sources)
#stamps
@@ -85,7 +86,7 @@ $(1)_download_path_fixed=$(subst :,\:,$$($(1)_download_path))
# The default behavior for tar will try to set ownership when running as uid 0 and may not succeed, --no-same-owner disables this behavior
$(1)_fetch_cmds ?= $(call fetch_file,$(1),$(subst \:,:,$$($(1)_download_path_fixed)),$$($(1)_download_file),$($(1)_file_name),$($(1)_sha256_hash))
$(1)_extract_cmds ?= mkdir -p $$($(1)_extract_dir) && echo "$$($(1)_sha256_hash) $$($(1)_source)" > $$($(1)_extract_dir)/.$$($(1)_file_name).hash && $(build_SHA256SUM) -c $$($(1)_extract_dir)/.$$($(1)_file_name).hash && $(build_TAR) --no-same-owner --strip-components=1 -xf $$($(1)_source)
-$(1)_preprocess_cmds ?=
+$(1)_preprocess_cmds ?= true
$(1)_build_cmds ?=
$(1)_config_cmds ?=
$(1)_stage_cmds ?=
@@ -187,44 +188,48 @@ endif
endef
define int_add_cmds
+ifneq ($(LOG),)
+$(1)_logging = >>$$($(1)_build_log) 2>&1 || { if test -f $$($(1)_build_log); then cat $$($(1)_build_log); fi; exit 1; }
+endif
+
$($(1)_fetched):
mkdir -p $$(@D) $(SOURCES_PATH)
rm -f $$@
touch $$@
- cd $$(@D); $(call $(1)_fetch_cmds,$(1))
+ cd $$(@D); $($(1)_fetch_cmds)
cd $($(1)_source_dir); $(foreach source,$($(1)_all_sources),$(build_SHA256SUM) $(source) >> $$(@);)
touch $$@
$($(1)_extracted): | $($(1)_fetched)
echo Extracting $(1)...
mkdir -p $$(@D)
- cd $$(@D); $(call $(1)_extract_cmds,$(1))
+ cd $$(@D); $($(1)_extract_cmds)
touch $$@
$($(1)_preprocessed): | $($(1)_extracted)
echo Preprocessing $(1)...
mkdir -p $$(@D) $($(1)_patch_dir)
$(foreach patch,$($(1)_patches),cd $(PATCHES_PATH)/$(1); cp $(patch) $($(1)_patch_dir) ;)
- cd $$(@D); $(call $(1)_preprocess_cmds, $(1))
+ { cd $$(@D); $($(1)_preprocess_cmds); } $$($(1)_logging)
touch $$@
$($(1)_configured): | $($(1)_dependencies) $($(1)_preprocessed)
echo Configuring $(1)...
rm -rf $(host_prefix); mkdir -p $(host_prefix)/lib; cd $(host_prefix); $(foreach package,$($(1)_all_dependencies), $(build_TAR) --no-same-owner -xf $($(package)_cached); )
mkdir -p $$(@D)
- +cd $$(@D); $($(1)_config_env) $(call $(1)_config_cmds, $(1))
+ +{ cd $$(@D); $($(1)_config_env) $($(1)_config_cmds); } $$($(1)_logging)
touch $$@
$($(1)_built): | $($(1)_configured)
echo Building $(1)...
mkdir -p $$(@D)
- +cd $$(@D); $($(1)_build_env) $(call $(1)_build_cmds, $(1))
+ +{ cd $$(@D); $($(1)_build_env) $($(1)_build_cmds); } $$($(1)_logging)
touch $$@
$($(1)_staged): | $($(1)_built)
echo Staging $(1)...
mkdir -p $($(1)_staging_dir)/$(host_prefix)
- cd $($(1)_build_dir); $($(1)_stage_env) $(call $(1)_stage_cmds, $(1))
+ +{ cd $($(1)_build_dir); $($(1)_stage_env) $($(1)_stage_cmds); } $$($(1)_logging)
rm -rf $($(1)_extract_dir)
touch $$@
$($(1)_postprocessed): | $($(1)_staged)
echo Postprocessing $(1)...
- cd $($(1)_staging_prefix_dir); $(call $(1)_postprocess_cmds)
+ cd $($(1)_staging_prefix_dir); $($(1)_postprocess_cmds)
touch $$@
$($(1)_cached): | $($(1)_dependencies) $($(1)_postprocessed)
echo Caching $(1)...
@@ -233,6 +238,7 @@ $($(1)_cached): | $($(1)_dependencies) $($(1)_postprocessed)
rm -rf $$(@D) && mkdir -p $$(@D)
mv $$($(1)_staging_dir)/$$(@F) $$(@)
rm -rf $($(1)_staging_dir)
+ if test -f $($(1)_build_log); then mv $($(1)_build_log) $$(@D); fi
$($(1)_cached_checksum): $($(1)_cached)
cd $$(@D); $(build_SHA256SUM) $$(<F) > $$(@)
diff --git a/doc/REST-interface.md b/doc/REST-interface.md
index c359725faf..f11624fd28 100644
--- a/doc/REST-interface.md
+++ b/doc/REST-interface.md
@@ -82,17 +82,7 @@ Given a height: returns hash of block in best-block-chain at height provided.
Returns various state info regarding block chain processing.
Only supports JSON as output format.
-* chain : (string) current network name (main, test, signet, regtest)
-* blocks : (numeric) the current number of blocks processed in the server
-* headers : (numeric) the current number of headers we have validated
-* bestblockhash : (string) the hash of the currently best block
-* difficulty : (numeric) the current difficulty
-* mediantime : (numeric) the median time of the 11 blocks before the most recent block on the blockchain
-* verificationprogress : (numeric) estimate of verification progress [0..1]
-* chainwork : (string) total amount of work in active chain, in hexadecimal
-* pruned : (boolean) if the blocks are subject to pruning
-* pruneheight : (numeric) highest block available
-* softforks : (array) status of softforks in progress
+Refer to the `getblockchaininfo` RPC help for details.
#### Query UTXO set
`GET /rest/getutxos/<checkmempool>/<txid>-<n>/<txid>-<n>/.../<txid>-<n>.<bin|hex|json>`
@@ -127,13 +117,13 @@ $ curl localhost:18332/rest/getutxos/checkmempool/b2cdfd7b89def827ff8af7cd9bff76
#### Memory pool
`GET /rest/mempool/info.json`
-Returns various information about the TX mempool.
+Returns various information about the transaction mempool.
Only supports JSON as output format.
-Refer to the `getmempoolinfo` RPC for documentation of the fields.
+Refer to the `getmempoolinfo` RPC help for details.
`GET /rest/mempool/contents.json`
-Returns transactions in the TX mempool.
+Returns the transactions in the mempool.
Only supports JSON as output format.
Risks
diff --git a/doc/release-process.md b/doc/release-process.md
index bc80e3d072..2f3a163a8e 100644
--- a/doc/release-process.md
+++ b/doc/release-process.md
@@ -37,7 +37,8 @@ Release Process
- This update should be reviewed with a reindex-chainstate with assumevalid=0 to catch any defect
that causes rejection of blocks in the past history.
- Clear the release notes and move them to the wiki (see "Write the release notes" below).
-- Translations on Transifex
+- Translations on Transifex:
+ - Pull translations from Transifex into the master branch.
- Create [a new resource](https://www.transifex.com/bitcoin/bitcoin/content/) named after the major version with the slug `[bitcoin.qt-translation-<RRR>x]`, where `RRR` is the major branch number padded with zeros. Use `src/qt/locale/bitcoin_en.xlf` to create it.
- In the project workflow settings, ensure that [Translation Memory Fill-up](https://docs.transifex.com/translation-memory/enabling-autofill) is enabled and that [Translation Memory Context Matching](https://docs.transifex.com/translation-memory/translation-memory-with-context) is disabled.
- Update the Transifex slug in [`.tx/config`](/.tx/config) to the slug of the resource created in the first step. This identifies which resource the translations will be synchronized from.
diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include
index 5dae4374e3..58a09cd4a4 100644
--- a/src/Makefile.bench.include
+++ b/src/Makefile.bench.include
@@ -74,6 +74,7 @@ endif
if ENABLE_WALLET
bench_bench_bitcoin_SOURCES += bench/coin_selection.cpp
bench_bench_bitcoin_SOURCES += bench/wallet_balance.cpp
+bench_bench_bitcoin_SOURCES += bench/wallet_loading.cpp
endif
bench_bench_bitcoin_LDADD += $(BDB_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(MINIUPNPC_LIBS) $(NATPMP_LIBS) $(SQLITE_LIBS)
diff --git a/src/arith_uint256.cpp b/src/arith_uint256.cpp
index f7f62dfc68..e614102de3 100644
--- a/src/arith_uint256.cpp
+++ b/src/arith_uint256.cpp
@@ -146,13 +146,21 @@ double base_uint<BITS>::getdouble() const
template <unsigned int BITS>
std::string base_uint<BITS>::GetHex() const
{
- return ArithToUint256(*this).GetHex();
+ base_blob<BITS> b;
+ for (int x = 0; x < this->WIDTH; ++x) {
+ WriteLE32(b.begin() + x*4, this->pn[x]);
+ }
+ return b.GetHex();
}
template <unsigned int BITS>
void base_uint<BITS>::SetHex(const char* psz)
{
- *this = UintToArith256(uint256S(psz));
+ base_blob<BITS> b;
+ b.SetHex(psz);
+ for (int x = 0; x < this->WIDTH; ++x) {
+ this->pn[x] = ReadLE32(b.begin() + x*4);
+ }
}
template <unsigned int BITS>
@@ -164,7 +172,7 @@ void base_uint<BITS>::SetHex(const std::string& str)
template <unsigned int BITS>
std::string base_uint<BITS>::ToString() const
{
- return (GetHex());
+ return GetHex();
}
template <unsigned int BITS>
@@ -183,20 +191,7 @@ unsigned int base_uint<BITS>::bits() const
}
// Explicit instantiations for base_uint<256>
-template base_uint<256>::base_uint(const std::string&);
-template base_uint<256>& base_uint<256>::operator<<=(unsigned int);
-template base_uint<256>& base_uint<256>::operator>>=(unsigned int);
-template base_uint<256>& base_uint<256>::operator*=(uint32_t b32);
-template base_uint<256>& base_uint<256>::operator*=(const base_uint<256>& b);
-template base_uint<256>& base_uint<256>::operator/=(const base_uint<256>& b);
-template int base_uint<256>::CompareTo(const base_uint<256>&) const;
-template bool base_uint<256>::EqualTo(uint64_t) const;
-template double base_uint<256>::getdouble() const;
-template std::string base_uint<256>::GetHex() const;
-template std::string base_uint<256>::ToString() const;
-template void base_uint<256>::SetHex(const char*);
-template void base_uint<256>::SetHex(const std::string&);
-template unsigned int base_uint<256>::bits() const;
+template class base_uint<256>;
// This implementation directly uses shifts instead of going
// through an intermediate MPI representation.
diff --git a/src/arith_uint256.h b/src/arith_uint256.h
index a0a0429c2a..19193972a4 100644
--- a/src/arith_uint256.h
+++ b/src/arith_uint256.h
@@ -284,4 +284,6 @@ public:
uint256 ArithToUint256(const arith_uint256 &);
arith_uint256 UintToArith256(const uint256 &);
+extern template class base_uint<256>;
+
#endif // BITCOIN_ARITH_UINT256_H
diff --git a/src/bench/rpc_blockchain.cpp b/src/bench/rpc_blockchain.cpp
index 2143bcf950..e6fc8d21f4 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, TxVerbosity::SHOW_DETAILS_AND_PREVOUT);
+ auto univalue = blockToJSON(data.testing_setup->m_node.chainman->m_blockman, 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, TxVerbosity::SHOW_DETAILS_AND_PREVOUT);
+ auto univalue = blockToJSON(data.testing_setup->m_node.chainman->m_blockman, 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_loading.cpp b/src/bench/wallet_loading.cpp
new file mode 100644
index 0000000000..38d3460001
--- /dev/null
+++ b/src/bench/wallet_loading.cpp
@@ -0,0 +1,83 @@
+// Copyright (c) 2022 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <bench/bench.h>
+#include <interfaces/chain.h>
+#include <node/context.h>
+#include <test/util/mining.h>
+#include <test/util/setup_common.h>
+#include <test/util/wallet.h>
+#include <util/translation.h>
+#include <validationinterface.h>
+#include <wallet/context.h>
+#include <wallet/receive.h>
+#include <wallet/wallet.h>
+
+#include <optional>
+
+using wallet::CWallet;
+using wallet::DatabaseOptions;
+using wallet::DatabaseStatus;
+using wallet::ISMINE_SPENDABLE;
+using wallet::MakeWalletDatabase;
+using wallet::WALLET_FLAG_DESCRIPTORS;
+using wallet::WalletContext;
+
+static const std::shared_ptr<CWallet> BenchLoadWallet(WalletContext& context, DatabaseOptions& options)
+{
+ DatabaseStatus status;
+ bilingual_str error;
+ std::vector<bilingual_str> warnings;
+ auto database = MakeWalletDatabase("", options, status, error);
+ assert(database);
+ auto wallet = CWallet::Create(context, "", std::move(database), options.create_flags, error, warnings);
+ NotifyWalletLoaded(context, wallet);
+ if (context.chain) {
+ wallet->postInitProcess();
+ }
+ return wallet;
+}
+
+static void BenchUnloadWallet(std::shared_ptr<CWallet>&& wallet)
+{
+ SyncWithValidationInterfaceQueue();
+ wallet->m_chain_notifications_handler.reset();
+ UnloadWallet(std::move(wallet));
+}
+
+static void WalletLoading(benchmark::Bench& bench, bool legacy_wallet)
+{
+ const auto test_setup = MakeNoLogFileContext<TestingSetup>();
+
+ WalletContext context;
+ context.args = &test_setup->m_args;
+ context.chain = test_setup->m_node.chain.get();
+
+ // Setup the wallet
+ // Loading the wallet will also create it
+ DatabaseOptions options;
+ if (!legacy_wallet) options.create_flags = WALLET_FLAG_DESCRIPTORS;
+ auto wallet = BenchLoadWallet(context, options);
+
+ // Generate a bunch of transactions and addresses to put into the wallet
+ for (int i = 0; i < 5000; ++i) {
+ generatetoaddress(test_setup->m_node, getnewaddress(*wallet));
+ }
+
+ // reload the wallet for the actual benchmark
+ BenchUnloadWallet(std::move(wallet));
+
+ bench.minEpochIterations(10).run([&] {
+ wallet = BenchLoadWallet(context, options);
+
+ // Cleanup
+ BenchUnloadWallet(std::move(wallet));
+ });
+}
+
+static void WalletLoadingLegacy(benchmark::Bench& bench) { WalletLoading(bench, /*legacy_wallet=*/true); }
+static void WalletLoadingDescriptors(benchmark::Bench& bench) { WalletLoading(bench, /*legacy_wallet=*/false); }
+
+BENCHMARK(WalletLoadingLegacy);
+BENCHMARK(WalletLoadingDescriptors);
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index 6925a4c8c6..dea46693bc 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -1055,7 +1055,9 @@ static void ParseGetInfoResult(UniValue& result)
result_string += "\n";
}
- result_string += strprintf("%sWarnings:%s %s", YELLOW, RESET, result["warnings"].getValStr());
+ const std::string warnings{result["warnings"].getValStr()};
+ result_string += strprintf("%sWarnings:%s %s", YELLOW, RESET, warnings.empty() ? "(none)" : warnings);
+
result.setStr(result_string);
}
diff --git a/src/crypto/sha256.cpp b/src/crypto/sha256.cpp
index cde543e68c..196f81ea16 100644
--- a/src/crypto/sha256.cpp
+++ b/src/crypto/sha256.cpp
@@ -586,17 +586,9 @@ std::string SHA256AutoDetect()
bool have_sse4 = false;
bool have_xsave = false;
bool have_avx = false;
- bool have_avx2 = false;
- bool have_x86_shani = false;
- bool enabled_avx = false;
-
- (void)AVXEnabled;
- (void)have_sse4;
- (void)have_avx;
- (void)have_xsave;
- (void)have_avx2;
- (void)have_x86_shani;
- (void)enabled_avx;
+ [[maybe_unused]] bool have_avx2 = false;
+ [[maybe_unused]] bool have_x86_shani = false;
+ [[maybe_unused]] bool enabled_avx = false;
uint32_t eax, ebx, ecx, edx;
GetCPUID(1, 0, eax, ebx, ecx, edx);
@@ -641,7 +633,7 @@ std::string SHA256AutoDetect()
ret += ",avx2(8way)";
}
#endif
-#endif
+#endif // defined(USE_ASM) && defined(HAVE_GETCPUID)
#if defined(ENABLE_ARM_SHANI) && !defined(BUILD_BITCOIN_INTERNAL)
bool have_arm_shani = false;
diff --git a/src/init.cpp b/src/init.cpp
index 86e6ec4451..c0af2b136f 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -109,7 +109,6 @@ using node::LoadChainstate;
using node::NodeContext;
using node::ThreadImport;
using node::VerifyLoadedChainstate;
-using node::fHavePruned;
using node::fPruneMode;
using node::fReindex;
using node::nPruneTarget;
@@ -660,7 +659,8 @@ void InitParameterInteraction(ArgsManager& args)
LogPrintf("%s: parameter interaction: -connect set -> setting -listen=0\n", __func__);
}
- if (args.IsArgSet("-proxy")) {
+ std::string proxy_arg = args.GetArg("-proxy", "");
+ if (proxy_arg != "" && proxy_arg != "0") {
// to protect privacy, do not listen by default if a default proxy server is specified
if (args.SoftSetBoolArg("-listen", false))
LogPrintf("%s: parameter interaction: -proxy set -> setting -listen=0\n", __func__);
@@ -1480,7 +1480,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
try {
uiInterface.InitMessage(_("Verifying blocks…").translated);
auto check_blocks = args.GetIntArg("-checkblocks", DEFAULT_CHECKBLOCKS);
- if (fHavePruned && check_blocks > MIN_BLOCKS_TO_KEEP) {
+ if (chainman.m_blockman.m_have_pruned && check_blocks > MIN_BLOCKS_TO_KEEP) {
LogPrintf("Prune: pruned datadir may not have more than %d blocks; only checking available blocks\n",
MIN_BLOCKS_TO_KEEP);
}
@@ -1664,9 +1664,9 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
tip_info->block_time = chainman.ActiveChain().Tip() ? chainman.ActiveChain().Tip()->GetBlockTime() : Params().GenesisBlock().GetBlockTime();
tip_info->verification_progress = GuessVerificationProgress(Params().TxData(), chainman.ActiveChain().Tip());
}
- if (tip_info && ::pindexBestHeader) {
- tip_info->header_height = ::pindexBestHeader->nHeight;
- tip_info->header_time = ::pindexBestHeader->GetBlockTime();
+ if (tip_info && chainman.m_best_header) {
+ tip_info->header_height = chainman.m_best_header->nHeight;
+ tip_info->header_time = chainman.m_best_header->GetBlockTime();
}
}
LogPrintf("nBestHeight = %d\n", chain_active_height);
diff --git a/src/logging/timer.h b/src/logging/timer.h
index fc5307bc62..d954e46301 100644
--- a/src/logging/timer.h
+++ b/src/logging/timer.h
@@ -97,13 +97,13 @@ private:
#define LOG_TIME_MICROS_WITH_CATEGORY(end_msg, log_category) \
- BCLog::Timer<std::chrono::microseconds> PASTE2(logging_timer, __COUNTER__)(__func__, end_msg, log_category)
+ BCLog::Timer<std::chrono::microseconds> UNIQUE_NAME(logging_timer)(__func__, end_msg, log_category)
#define LOG_TIME_MILLIS_WITH_CATEGORY(end_msg, log_category) \
- BCLog::Timer<std::chrono::milliseconds> PASTE2(logging_timer, __COUNTER__)(__func__, end_msg, log_category)
+ BCLog::Timer<std::chrono::milliseconds> UNIQUE_NAME(logging_timer)(__func__, end_msg, log_category)
#define LOG_TIME_MILLIS_WITH_CATEGORY_MSG_ONCE(end_msg, log_category) \
- BCLog::Timer<std::chrono::milliseconds> PASTE2(logging_timer, __COUNTER__)(__func__, end_msg, log_category, /* msg_on_completion=*/false)
+ BCLog::Timer<std::chrono::milliseconds> UNIQUE_NAME(logging_timer)(__func__, end_msg, log_category, /* msg_on_completion=*/false)
#define LOG_TIME_SECONDS(end_msg) \
- BCLog::Timer<std::chrono::seconds> PASTE2(logging_timer, __COUNTER__)(__func__, end_msg)
+ BCLog::Timer<std::chrono::seconds> UNIQUE_NAME(logging_timer)(__func__, end_msg)
#endif // BITCOIN_LOGGING_TIMER_H
diff --git a/src/net.cpp b/src/net.cpp
index 602d56ab98..5a0c057c3e 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -1190,7 +1190,11 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
// According to the internet TCP_NODELAY is not carried into accepted sockets
// on all platforms. Set it again here just to be sure.
- SetSocketNoDelay(sock->Get());
+ const int on{1};
+ if (sock->SetSockOpt(IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) == SOCKET_ERROR) {
+ LogPrint(BCLog::NET, "connection from %s: unable to set TCP_NODELAY, continuing anyway\n",
+ addr.ToString());
+ }
// Don't accept connections from banned peers.
bool banned = m_banman && m_banman->IsBanned(addr);
@@ -2395,17 +2399,26 @@ bool CConnman::BindListenPort(const CService& addrBind, bilingual_str& strError,
// Allow binding if the port is still in TIME_WAIT state after
// the program was closed and restarted.
- setsockopt(sock->Get(), SOL_SOCKET, SO_REUSEADDR, (sockopt_arg_type)&nOne, sizeof(int));
+ if (sock->SetSockOpt(SOL_SOCKET, SO_REUSEADDR, (sockopt_arg_type)&nOne, sizeof(int)) == SOCKET_ERROR) {
+ strError = strprintf(Untranslated("Error setting SO_REUSEADDR on socket: %s, continuing anyway"), NetworkErrorString(WSAGetLastError()));
+ LogPrintf("%s\n", strError.original);
+ }
// some systems don't have IPV6_V6ONLY but are always v6only; others do have the option
// and enable it by default or not. Try to enable it, if possible.
if (addrBind.IsIPv6()) {
#ifdef IPV6_V6ONLY
- setsockopt(sock->Get(), IPPROTO_IPV6, IPV6_V6ONLY, (sockopt_arg_type)&nOne, sizeof(int));
+ if (sock->SetSockOpt(IPPROTO_IPV6, IPV6_V6ONLY, (sockopt_arg_type)&nOne, sizeof(int)) == SOCKET_ERROR) {
+ strError = strprintf(Untranslated("Error setting IPV6_V6ONLY on socket: %s, continuing anyway"), NetworkErrorString(WSAGetLastError()));
+ LogPrintf("%s\n", strError.original);
+ }
#endif
#ifdef WIN32
int nProtLevel = PROTECTION_LEVEL_UNRESTRICTED;
- setsockopt(sock->Get(), IPPROTO_IPV6, IPV6_PROTECTION_LEVEL, (const char*)&nProtLevel, sizeof(int));
+ if (sock->SetSockOpt(IPPROTO_IPV6, IPV6_PROTECTION_LEVEL, (const char*)&nProtLevel, sizeof(int)) == SOCKET_ERROR) {
+ strError = strprintf(Untranslated("Error setting IPV6_PROTECTION_LEVEL on socket: %s, continuing anyway"), NetworkErrorString(WSAGetLastError()));
+ LogPrintf("%s\n", strError.original);
+ }
#endif
}
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index ba72a11ec9..02eb3762b1 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -1499,9 +1499,9 @@ bool PeerManagerImpl::BlockRequestAllowed(const CBlockIndex* pindex)
{
AssertLockHeld(cs_main);
if (m_chainman.ActiveChain().Contains(pindex)) return true;
- return pindex->IsValid(BLOCK_VALID_SCRIPTS) && (pindexBestHeader != nullptr) &&
- (pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() < STALE_RELAY_AGE_LIMIT) &&
- (GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, m_chainparams.GetConsensus()) < STALE_RELAY_AGE_LIMIT);
+ return pindex->IsValid(BLOCK_VALID_SCRIPTS) && (m_chainman.m_best_header != nullptr) &&
+ (m_chainman.m_best_header->GetBlockTime() - pindex->GetBlockTime() < STALE_RELAY_AGE_LIMIT) &&
+ (GetBlockProofEquivalentTime(*m_chainman.m_best_header, *pindex, *m_chainman.m_best_header, m_chainparams.GetConsensus()) < STALE_RELAY_AGE_LIMIT);
}
std::optional<std::string> PeerManagerImpl::FetchBlock(NodeId peer_id, const CBlockIndex& block_index)
@@ -1896,7 +1896,7 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv&
const CNetMsgMaker msgMaker(pfrom.GetCommonVersion());
// disconnect node in case we have reached the outbound limit for serving historical blocks
if (m_connman.OutboundTargetReached(true) &&
- (((pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() > HISTORICAL_BLOCK_AGE)) || inv.IsMsgFilteredBlk()) &&
+ (((m_chainman.m_best_header != nullptr) && (m_chainman.m_best_header->GetBlockTime() - pindex->GetBlockTime() > HISTORICAL_BLOCK_AGE)) || inv.IsMsgFilteredBlk()) &&
!pfrom.HasPermission(NetPermissionFlags::Download) // nodes with the download permission may exceed target
) {
LogPrint(BCLog::NET, "historical block serving limit reached, disconnect peer=%d\n", pfrom.GetId());
@@ -2173,12 +2173,12 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
// nUnconnectingHeaders gets reset back to 0.
if (!m_chainman.m_blockman.LookupBlockIndex(headers[0].hashPrevBlock) && nCount < MAX_BLOCKS_TO_ANNOUNCE) {
nodestate->nUnconnectingHeaders++;
- m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, m_chainman.ActiveChain().GetLocator(pindexBestHeader), uint256()));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, m_chainman.ActiveChain().GetLocator(m_chainman.m_best_header), uint256()));
LogPrint(BCLog::NET, "received header %s: missing prev block %s, sending getheaders (%d) to end (peer=%d, nUnconnectingHeaders=%d)\n",
- headers[0].GetHash().ToString(),
- headers[0].hashPrevBlock.ToString(),
- pindexBestHeader->nHeight,
- pfrom.GetId(), nodestate->nUnconnectingHeaders);
+ headers[0].GetHash().ToString(),
+ headers[0].hashPrevBlock.ToString(),
+ m_chainman.m_best_header->nHeight,
+ pfrom.GetId(), nodestate->nUnconnectingHeaders);
// Set hashLastUnknownBlock for this peer, so that if we
// eventually get the headers - even from a different peer -
// we can use this peer to download.
@@ -2235,7 +2235,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
if (nCount == MAX_HEADERS_RESULTS) {
// Headers message had its maximum size; the peer may have more headers.
- // TODO: optimize: if pindexLast is an ancestor of m_chainman.ActiveChain().Tip or pindexBestHeader, continue
+ // TODO: optimize: if pindexLast is an ancestor of m_chainman.ActiveChain().Tip or m_chainman.m_best_header, continue
// from there instead.
LogPrint(BCLog::NET, "more getheaders (%d) to end to peer=%d (startheight:%d)\n",
pindexLast->nHeight, pfrom.GetId(), peer.m_starting_height);
@@ -3102,8 +3102,8 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
}
if (best_block != nullptr) {
- m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, m_chainman.ActiveChain().GetLocator(pindexBestHeader), *best_block));
- LogPrint(BCLog::NET, "getheaders (%d) %s to peer=%d\n", pindexBestHeader->nHeight, best_block->ToString(), pfrom.GetId());
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, m_chainman.ActiveChain().GetLocator(m_chainman.m_best_header), *best_block));
+ LogPrint(BCLog::NET, "getheaders (%d) %s to peer=%d\n", m_chainman.m_best_header->nHeight, best_block->ToString(), pfrom.GetId());
}
return;
@@ -3549,7 +3549,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
if (!m_chainman.m_blockman.LookupBlockIndex(cmpctblock.header.hashPrevBlock)) {
// Doesn't connect (or is genesis), instead of DoSing in AcceptBlockHeader, request deeper headers
if (!m_chainman.ActiveChainstate().IsInitialBlockDownload())
- m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, m_chainman.ActiveChain().GetLocator(pindexBestHeader), uint256()));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, m_chainman.ActiveChain().GetLocator(m_chainman.m_best_header), uint256()));
return;
}
@@ -4670,28 +4670,29 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
CNodeState &state = *State(pto->GetId());
// Start block sync
- if (pindexBestHeader == nullptr)
- pindexBestHeader = m_chainman.ActiveChain().Tip();
+ if (m_chainman.m_best_header == nullptr) {
+ m_chainman.m_best_header = m_chainman.ActiveChain().Tip();
+ }
bool fFetch = state.fPreferredDownload || (nPreferredDownload == 0 && !pto->fClient && !pto->IsAddrFetchConn()); // Download if this is a nice peer, or we have no nice peers and this one might do.
if (!state.fSyncStarted && !pto->fClient && !fImporting && !fReindex) {
// Only actively request headers from a single peer, unless we're close to today.
- if ((nSyncStarted == 0 && fFetch) || pindexBestHeader->GetBlockTime() > GetAdjustedTime() - 24 * 60 * 60) {
+ if ((nSyncStarted == 0 && fFetch) || m_chainman.m_best_header->GetBlockTime() > GetAdjustedTime() - 24 * 60 * 60) {
state.fSyncStarted = true;
state.m_headers_sync_timeout = current_time + HEADERS_DOWNLOAD_TIMEOUT_BASE +
(
// Convert HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER to microseconds before scaling
// to maintain precision
std::chrono::microseconds{HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER} *
- (GetAdjustedTime() - pindexBestHeader->GetBlockTime()) / consensusParams.nPowTargetSpacing
+ (GetAdjustedTime() - m_chainman.m_best_header->GetBlockTime()) / consensusParams.nPowTargetSpacing
);
nSyncStarted++;
- const CBlockIndex *pindexStart = pindexBestHeader;
+ const CBlockIndex* pindexStart = m_chainman.m_best_header;
/* If possible, start at the block preceding the currently
best known header. This ensures that we always get a
non-empty list of headers back as long as the peer
is up-to-date. With a non-empty response, we can initialise
the peer's known best block. This wouldn't be possible
- if we requested starting at pindexBestHeader and
+ if we requested starting at m_chainman.m_best_header and
got back an empty response. */
if (pindexStart->pprev)
pindexStart = pindexStart->pprev;
@@ -5016,7 +5017,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
// Check for headers sync timeouts
if (state.fSyncStarted && state.m_headers_sync_timeout < std::chrono::microseconds::max()) {
// Detect whether this is a stalling initial-headers-sync peer
- if (pindexBestHeader->GetBlockTime() <= GetAdjustedTime() - 24 * 60 * 60) {
+ if (m_chainman.m_best_header->GetBlockTime() <= GetAdjustedTime() - 24 * 60 * 60) {
if (current_time > state.m_headers_sync_timeout && nSyncStarted == 1 && (nPreferredDownload - state.fPreferredDownload >= 1)) {
// Disconnect a peer (without NetPermissionFlags::NoBan permission) if it is our only sync peer,
// and we have others we could be using instead.
diff --git a/src/netbase.cpp b/src/netbase.cpp
index 9a0b800565..9557297df1 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -305,7 +305,7 @@ enum class IntrRecvError {
*
* @see This function can be interrupted by calling InterruptSocks5(bool).
* Sockets can be made non-blocking with SetSocketNonBlocking(const
- * SOCKET&, bool).
+ * SOCKET&).
*/
static IntrRecvError InterruptibleRecv(uint8_t* data, size_t len, int timeout, const Sock& sock)
{
@@ -499,10 +499,11 @@ std::unique_ptr<Sock> CreateSockTCP(const CService& address_family)
return nullptr;
}
+ auto sock = std::make_unique<Sock>(hSocket);
+
// Ensure that waiting for I/O on this socket won't result in undefined
// behavior.
- if (!IsSelectableSocket(hSocket)) {
- CloseSocket(hSocket);
+ if (!IsSelectableSocket(sock->Get())) {
LogPrintf("Cannot create connection: non-selectable socket created (fd >= FD_SETSIZE ?)\n");
return nullptr;
}
@@ -511,19 +512,24 @@ std::unique_ptr<Sock> CreateSockTCP(const CService& address_family)
int set = 1;
// Set the no-sigpipe option on the socket for BSD systems, other UNIXes
// should use the MSG_NOSIGNAL flag for every send.
- setsockopt(hSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&set, sizeof(int));
+ if (sock->SetSockOpt(SOL_SOCKET, SO_NOSIGPIPE, (void*)&set, sizeof(int)) == SOCKET_ERROR) {
+ LogPrintf("Error setting SO_NOSIGPIPE on socket: %s, continuing anyway\n",
+ NetworkErrorString(WSAGetLastError()));
+ }
#endif
// Set the no-delay option (disable Nagle's algorithm) on the TCP socket.
- SetSocketNoDelay(hSocket);
+ const int on{1};
+ if (sock->SetSockOpt(IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) == SOCKET_ERROR) {
+ LogPrint(BCLog::NET, "Unable to set TCP_NODELAY on a newly created socket, continuing anyway\n");
+ }
// Set the non-blocking option on the socket.
- if (!SetSocketNonBlocking(hSocket, true)) {
- CloseSocket(hSocket);
+ if (!SetSocketNonBlocking(sock->Get())) {
LogPrintf("Error setting socket to non-blocking: %s\n", NetworkErrorString(WSAGetLastError()));
return nullptr;
}
- return std::make_unique<Sock>(hSocket);
+ return sock;
}
std::function<std::unique_ptr<Sock>(const CService&)> CreateSock = CreateSockTCP;
@@ -711,40 +717,21 @@ bool LookupSubNet(const std::string& subnet_str, CSubNet& subnet_out)
return false;
}
-bool SetSocketNonBlocking(const SOCKET& hSocket, bool fNonBlocking)
+bool SetSocketNonBlocking(const SOCKET& hSocket)
{
- if (fNonBlocking) {
-#ifdef WIN32
- u_long nOne = 1;
- if (ioctlsocket(hSocket, FIONBIO, &nOne) == SOCKET_ERROR) {
-#else
- int fFlags = fcntl(hSocket, F_GETFL, 0);
- if (fcntl(hSocket, F_SETFL, fFlags | O_NONBLOCK) == SOCKET_ERROR) {
-#endif
- return false;
- }
- } else {
#ifdef WIN32
- u_long nZero = 0;
- if (ioctlsocket(hSocket, FIONBIO, &nZero) == SOCKET_ERROR) {
+ u_long nOne = 1;
+ if (ioctlsocket(hSocket, FIONBIO, &nOne) == SOCKET_ERROR) {
#else
- int fFlags = fcntl(hSocket, F_GETFL, 0);
- if (fcntl(hSocket, F_SETFL, fFlags & ~O_NONBLOCK) == SOCKET_ERROR) {
+ int fFlags = fcntl(hSocket, F_GETFL, 0);
+ if (fcntl(hSocket, F_SETFL, fFlags | O_NONBLOCK) == SOCKET_ERROR) {
#endif
- return false;
- }
+ return false;
}
return true;
}
-bool SetSocketNoDelay(const SOCKET& hSocket)
-{
- int set = 1;
- int rc = setsockopt(hSocket, IPPROTO_TCP, TCP_NODELAY, (const char*)&set, sizeof(int));
- return rc == 0;
-}
-
void InterruptSocks5(bool interrupt)
{
interruptSocks5Recv = interrupt;
diff --git a/src/netbase.h b/src/netbase.h
index f9e3872c16..bf7522210d 100644
--- a/src/netbase.h
+++ b/src/netbase.h
@@ -221,10 +221,8 @@ bool ConnectSocketDirectly(const CService &addrConnect, const Sock& sock, int nT
*/
bool ConnectThroughProxy(const Proxy& proxy, const std::string& strDest, uint16_t port, const Sock& sock, int nTimeout, bool& outProxyConnectionFailed);
-/** Disable or enable blocking-mode for a socket */
-bool SetSocketNonBlocking(const SOCKET& hSocket, bool fNonBlocking);
-/** Set the TCP_NODELAY flag on a socket */
-bool SetSocketNoDelay(const SOCKET& hSocket);
+/** Enable non-blocking mode for a socket */
+bool SetSocketNonBlocking(const SOCKET& hSocket);
void InterruptSocks5(bool interrupt);
/**
diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp
index 763fd29744..21cb0250d8 100644
--- a/src/node/blockstorage.cpp
+++ b/src/node/blockstorage.cpp
@@ -24,7 +24,6 @@
namespace node {
std::atomic_bool fImporting(false);
std::atomic_bool fReindex(false);
-bool fHavePruned = false;
bool fPruneMode = false;
uint64_t nPruneTarget = 0;
@@ -81,7 +80,7 @@ const CBlockIndex* BlockManager::LookupBlockIndex(const uint256& hash) const
return it == m_block_index.end() ? nullptr : &it->second;
}
-CBlockIndex* BlockManager::AddToBlockIndex(const CBlockHeader& block)
+CBlockIndex* BlockManager::AddToBlockIndex(const CBlockHeader& block, CBlockIndex*& best_header)
{
AssertLockHeld(cs_main);
@@ -106,8 +105,9 @@ CBlockIndex* BlockManager::AddToBlockIndex(const CBlockHeader& block)
pindexNew->nTimeMax = (pindexNew->pprev ? std::max(pindexNew->pprev->nTimeMax, pindexNew->nTime) : pindexNew->nTime);
pindexNew->nChainWork = (pindexNew->pprev ? pindexNew->pprev->nChainWork : 0) + GetBlockProof(*pindexNew);
pindexNew->RaiseValidity(BLOCK_VALID_TREE);
- if (pindexBestHeader == nullptr || pindexBestHeader->nChainWork < pindexNew->nChainWork)
- pindexBestHeader = pindexNew;
+ if (best_header == nullptr || best_header->nChainWork < pindexNew->nChainWork) {
+ best_header = pindexNew;
+ }
m_dirty_blockindex.insert(pindexNew);
@@ -285,8 +285,6 @@ bool BlockManager::LoadBlockIndex(const Consensus::Params& consensus_params)
if (pindex->pprev) {
pindex->BuildSkip();
}
- if (pindex->IsValid(BLOCK_VALID_TREE) && (pindexBestHeader == nullptr || CBlockIndexWorkComparator()(pindexBestHeader, pindex)))
- pindexBestHeader = pindex;
}
return true;
@@ -302,6 +300,8 @@ void BlockManager::Unload()
m_last_blockfile = 0;
m_dirty_blockindex.clear();
m_dirty_fileinfo.clear();
+
+ m_have_pruned = false;
}
bool BlockManager::WriteBlockIndexDB()
@@ -364,8 +364,8 @@ bool BlockManager::LoadBlockIndexDB()
}
// Check whether we have ever pruned block & undo files
- m_block_tree_db->ReadFlag("prunedblockfiles", fHavePruned);
- if (fHavePruned) {
+ m_block_tree_db->ReadFlag("prunedblockfiles", m_have_pruned);
+ if (m_have_pruned) {
LogPrintf("LoadBlockIndexDB(): Block files have previously been pruned\n");
}
@@ -391,10 +391,10 @@ const CBlockIndex* BlockManager::GetLastCheckpoint(const CCheckpointData& data)
return nullptr;
}
-bool IsBlockPruned(const CBlockIndex* pblockindex)
+bool BlockManager::IsBlockPruned(const CBlockIndex* pblockindex)
{
AssertLockHeld(::cs_main);
- return (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0);
+ return (m_have_pruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0);
}
// If we're using -prune with -reindex, then delete block files that will be ignored by the
diff --git a/src/node/blockstorage.h b/src/node/blockstorage.h
index a051e90808..11445aa22e 100644
--- a/src/node/blockstorage.h
+++ b/src/node/blockstorage.h
@@ -45,8 +45,6 @@ static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB
extern std::atomic_bool fImporting;
extern std::atomic_bool fReindex;
/** Pruning-related variables and constants */
-/** True if any block files have ever been pruned. */
-extern bool fHavePruned;
/** True if we're running in -prune mode. */
extern bool fPruneMode;
/** Number of MiB of block files that we're trying to stay below. */
@@ -147,7 +145,7 @@ public:
/** Clear all data members. */
void Unload() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- CBlockIndex* AddToBlockIndex(const CBlockHeader& block) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ CBlockIndex* AddToBlockIndex(const CBlockHeader& block, CBlockIndex*& best_header) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Create a new block index entry for a given block hash */
CBlockIndex* InsertBlockIndex(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@@ -171,15 +169,18 @@ public:
//! Returns last CBlockIndex* that is a checkpoint
const CBlockIndex* GetLastCheckpoint(const CCheckpointData& data) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ /** True if any block files have ever been pruned. */
+ bool m_have_pruned = false;
+
+ //! Check whether the block associated with this index entry is pruned or not.
+ bool IsBlockPruned(const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
+
~BlockManager()
{
Unload();
}
};
-//! Check whether the block associated with this index entry is pruned or not.
-bool IsBlockPruned(const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
-
void CleanupBlockRevFiles();
/** Open a block file (blk?????.dat) */
diff --git a/src/node/chainstate.cpp b/src/node/chainstate.cpp
index 9fdeb036fd..e43211402c 100644
--- a/src/node/chainstate.cpp
+++ b/src/node/chainstate.cpp
@@ -49,7 +49,7 @@ std::optional<ChainstateLoadingError> LoadChainstate(bool fReset,
if (shutdown_requested && shutdown_requested()) return ChainstateLoadingError::SHUTDOWN_PROBED;
- // LoadBlockIndex will load fHavePruned if we've ever removed a
+ // LoadBlockIndex will load m_have_pruned if we've ever removed a
// block file from disk.
// Note that it also sets fReindex based on the disk flag!
// From here on out fReindex and fReset mean something different!
@@ -65,7 +65,7 @@ std::optional<ChainstateLoadingError> LoadChainstate(bool fReset,
// Check for changed -prune state. What we are concerned about is a user who has pruned blocks
// in the past, but is now trying to run unpruned.
- if (fHavePruned && !fPruneMode) {
+ if (chainman.m_blockman.m_have_pruned && !fPruneMode) {
return ChainstateLoadingError::ERROR_PRUNED_NEEDS_REINDEX;
}
diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp
index 73d15652b1..954bd1c31d 100644
--- a/src/node/interfaces.cpp
+++ b/src/node/interfaces.cpp
@@ -212,9 +212,10 @@ public:
bool getHeaderTip(int& height, int64_t& block_time) override
{
LOCK(::cs_main);
- if (::pindexBestHeader) {
- height = ::pindexBestHeader->nHeight;
- block_time = ::pindexBestHeader->GetBlockTime();
+ auto best_header = chainman().m_best_header;
+ if (best_header) {
+ height = best_header->nHeight;
+ block_time = best_header->GetBlockTime();
return true;
}
return false;
@@ -644,7 +645,7 @@ public:
bool havePruned() override
{
LOCK(cs_main);
- return node::fHavePruned;
+ return m_node.chainman->m_blockman.m_have_pruned;
}
bool isReadyToBroadcast() override { return !node::fImporting && !node::fReindex && !isInitialBlockDownload(); }
bool isInitialBlockDownload() override {
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index c6b884e40a..8cac28400f 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -95,6 +95,8 @@ static void RegisterMetaTypes()
qRegisterMetaType<std::function<void()>>("std::function<void()>");
qRegisterMetaType<QMessageBox::Icon>("QMessageBox::Icon");
qRegisterMetaType<interfaces::BlockAndHeaderTipInfo>("interfaces::BlockAndHeaderTipInfo");
+
+ qRegisterMetaTypeStreamOperators<BitcoinUnit>("BitcoinUnit");
}
static QString GetLangTerritory()
diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp
index a257e250e0..c92aecd095 100644
--- a/src/qt/bitcoinamountfield.cpp
+++ b/src/qt/bitcoinamountfield.cpp
@@ -14,6 +14,9 @@
#include <QHBoxLayout>
#include <QKeyEvent>
#include <QLineEdit>
+#include <QVariant>
+
+#include <cassert>
/** QSpinBox that uses fixed-point numbers internally and uses our own
* formatting/parsing functions.
@@ -96,7 +99,7 @@ public:
setValue(val);
}
- void setDisplayUnit(int unit)
+ void setDisplayUnit(BitcoinUnit unit)
{
bool valid = false;
CAmount val = value(&valid);
@@ -122,7 +125,7 @@ public:
const QFontMetrics fm(fontMetrics());
int h = lineEdit()->minimumSizeHint().height();
- int w = GUIUtil::TextWidth(fm, BitcoinUnits::format(BitcoinUnits::BTC, BitcoinUnits::maxMoney(), false, BitcoinUnits::SeparatorStyle::ALWAYS));
+ int w = GUIUtil::TextWidth(fm, BitcoinUnits::format(BitcoinUnit::BTC, BitcoinUnits::maxMoney(), false, BitcoinUnits::SeparatorStyle::ALWAYS));
w += 2; // cursor blinking space
QStyleOptionSpinBox opt;
@@ -141,14 +144,13 @@ public:
opt.rect = rect();
- cachedMinimumSizeHint = style()->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, this)
- .expandedTo(QApplication::globalStrut());
+ cachedMinimumSizeHint = style()->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, this);
}
return cachedMinimumSizeHint;
}
private:
- int currentUnit{BitcoinUnits::BTC};
+ BitcoinUnit currentUnit{BitcoinUnit::BTC};
CAmount singleStep{CAmount(100000)}; // satoshis
mutable QSize cachedMinimumSizeHint;
bool m_allow_empty{true};
@@ -326,14 +328,14 @@ void BitcoinAmountField::unitChanged(int idx)
unit->setToolTip(unit->itemData(idx, Qt::ToolTipRole).toString());
// Determine new unit ID
- int newUnit = unit->itemData(idx, BitcoinUnits::UnitRole).toInt();
-
- amount->setDisplayUnit(newUnit);
+ QVariant new_unit = unit->currentData(BitcoinUnits::UnitRole);
+ assert(new_unit.isValid());
+ amount->setDisplayUnit(new_unit.value<BitcoinUnit>());
}
-void BitcoinAmountField::setDisplayUnit(int newUnit)
+void BitcoinAmountField::setDisplayUnit(BitcoinUnit new_unit)
{
- unit->setValue(newUnit);
+ unit->setValue(QVariant::fromValue(new_unit));
}
void BitcoinAmountField::setSingleStep(const CAmount& step)
diff --git a/src/qt/bitcoinamountfield.h b/src/qt/bitcoinamountfield.h
index 366a6fc4b5..a40cd38332 100644
--- a/src/qt/bitcoinamountfield.h
+++ b/src/qt/bitcoinamountfield.h
@@ -6,6 +6,7 @@
#define BITCOIN_QT_BITCOINAMOUNTFIELD_H
#include <consensus/amount.h>
+#include <qt/bitcoinunits.h>
#include <QWidget>
@@ -52,7 +53,7 @@ public:
bool validate();
/** Change unit used to display amount. */
- void setDisplayUnit(int unit);
+ void setDisplayUnit(BitcoinUnit new_unit);
/** Make field empty and ready for new input. */
void clear();
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 85e3c23085..81b0e711b2 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -41,6 +41,7 @@
#include <validation.h>
#include <QAction>
+#include <QActionGroup>
#include <QApplication>
#include <QComboBox>
#include <QCursor>
@@ -355,7 +356,7 @@ void BitcoinGUI::createActions()
showHelpMessageAction->setStatusTip(tr("Show the %1 help message to get a list with possible Bitcoin command-line options").arg(PACKAGE_NAME));
m_mask_values_action = new QAction(tr("&Mask values"), this);
- m_mask_values_action->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_M));
+ m_mask_values_action->setShortcut(QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_M));
m_mask_values_action->setStatusTip(tr("Mask the values in the Overview tab"));
m_mask_values_action->setCheckable(true);
@@ -426,8 +427,8 @@ void BitcoinGUI::createActions()
}
#endif // ENABLE_WALLET
- connect(new QShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_C), this), &QShortcut::activated, this, &BitcoinGUI::showDebugWindowActivateConsole);
- connect(new QShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_D), this), &QShortcut::activated, this, &BitcoinGUI::showDebugWindow);
+ connect(new QShortcut(QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_C), this), &QShortcut::activated, this, &BitcoinGUI::showDebugWindowActivateConsole);
+ connect(new QShortcut(QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_D), this), &QShortcut::activated, this, &BitcoinGUI::showDebugWindow);
}
void BitcoinGUI::createMenuBar()
@@ -1244,7 +1245,7 @@ void BitcoinGUI::showEvent(QShowEvent *event)
}
#ifdef ENABLE_WALLET
-void BitcoinGUI::incomingTransaction(const QString& date, int unit, const CAmount& amount, const QString& type, const QString& address, const QString& label, const QString& walletName)
+void BitcoinGUI::incomingTransaction(const QString& date, BitcoinUnit unit, const CAmount& amount, const QString& type, const QString& address, const QString& label, const QString& walletName)
{
// On new transaction, make an info balloon
QString msg = tr("Date: %1\n").arg(date) +
@@ -1495,11 +1496,10 @@ UnitDisplayStatusBarControl::UnitDisplayStatusBarControl(const PlatformStyle *pl
{
createContextMenu();
setToolTip(tr("Unit to show amounts in. Click to select another unit."));
- QList<BitcoinUnits::Unit> units = BitcoinUnits::availableUnits();
+ QList<BitcoinUnit> units = BitcoinUnits::availableUnits();
int max_width = 0;
const QFontMetrics fm(font());
- for (const BitcoinUnits::Unit unit : units)
- {
+ for (const BitcoinUnit unit : units) {
max_width = qMax(max_width, GUIUtil::TextWidth(fm, BitcoinUnits::longName(unit)));
}
setMinimumSize(max_width, 0);
@@ -1529,8 +1529,8 @@ void UnitDisplayStatusBarControl::changeEvent(QEvent* e)
void UnitDisplayStatusBarControl::createContextMenu()
{
menu = new QMenu(this);
- for (const BitcoinUnits::Unit u : BitcoinUnits::availableUnits()) {
- menu->addAction(BitcoinUnits::longName(u))->setData(QVariant(u));
+ for (const BitcoinUnit u : BitcoinUnits::availableUnits()) {
+ menu->addAction(BitcoinUnits::longName(u))->setData(QVariant::fromValue(u));
}
connect(menu, &QMenu::triggered, this, &UnitDisplayStatusBarControl::onMenuSelection);
}
@@ -1551,7 +1551,7 @@ void UnitDisplayStatusBarControl::setOptionsModel(OptionsModel *_optionsModel)
}
/** When Display Units are changed on OptionsModel it will refresh the display text of the control on the status bar */
-void UnitDisplayStatusBarControl::updateDisplayUnit(int newUnits)
+void UnitDisplayStatusBarControl::updateDisplayUnit(BitcoinUnit newUnits)
{
setText(BitcoinUnits::longName(newUnits));
}
diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h
index d2b29ba27b..5d9a978695 100644
--- a/src/qt/bitcoingui.h
+++ b/src/qt/bitcoingui.h
@@ -9,6 +9,7 @@
#include <config/bitcoin-config.h>
#endif
+#include <qt/bitcoinunits.h>
#include <qt/guiutil.h>
#include <qt/optionsdialog.h>
@@ -260,7 +261,7 @@ public Q_SLOTS:
bool handlePaymentRequest(const SendCoinsRecipient& recipient);
/** Show incoming transaction notification for new transactions. */
- void incomingTransaction(const QString& date, int unit, const CAmount& amount, const QString& type, const QString& address, const QString& label, const QString& walletName);
+ void incomingTransaction(const QString& date, BitcoinUnit unit, const CAmount& amount, const QString& type, const QString& address, const QString& label, const QString& walletName);
#endif // ENABLE_WALLET
private:
@@ -341,7 +342,7 @@ private:
private Q_SLOTS:
/** When Display Units are changed on OptionsModel it will refresh the display text of the control on the status bar */
- void updateDisplayUnit(int newUnits);
+ void updateDisplayUnit(BitcoinUnit newUnits);
/** Tells underlying optionsModel to update its current display unit. */
void onMenuSelection(QAction* action);
};
diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp
index 69caf64d5c..fe3eb3240b 100644
--- a/src/qt/bitcoinunits.cpp
+++ b/src/qt/bitcoinunits.cpp
@@ -18,94 +18,75 @@ BitcoinUnits::BitcoinUnits(QObject *parent):
{
}
-QList<BitcoinUnits::Unit> BitcoinUnits::availableUnits()
+QList<BitcoinUnit> BitcoinUnits::availableUnits()
{
- QList<BitcoinUnits::Unit> unitlist;
- unitlist.append(BTC);
- unitlist.append(mBTC);
- unitlist.append(uBTC);
- unitlist.append(SAT);
+ QList<BitcoinUnit> unitlist;
+ unitlist.append(Unit::BTC);
+ unitlist.append(Unit::mBTC);
+ unitlist.append(Unit::uBTC);
+ unitlist.append(Unit::SAT);
return unitlist;
}
-bool BitcoinUnits::valid(int unit)
+QString BitcoinUnits::longName(Unit unit)
{
- switch(unit)
- {
- case BTC:
- case mBTC:
- case uBTC:
- case SAT:
- return true;
- default:
- return false;
- }
-}
-
-QString BitcoinUnits::longName(int unit)
-{
- switch(unit)
- {
- case BTC: return QString("BTC");
- case mBTC: return QString("mBTC");
- case uBTC: return QString::fromUtf8("µBTC (bits)");
- case SAT: return QString("Satoshi (sat)");
- default: return QString("???");
- }
+ switch (unit) {
+ case Unit::BTC: return QString("BTC");
+ case Unit::mBTC: return QString("mBTC");
+ case Unit::uBTC: return QString::fromUtf8("µBTC (bits)");
+ case Unit::SAT: return QString("Satoshi (sat)");
+ } // no default case, so the compiler can warn about missing cases
+ assert(false);
}
-QString BitcoinUnits::shortName(int unit)
+QString BitcoinUnits::shortName(Unit unit)
{
- switch(unit)
- {
- case uBTC: return QString::fromUtf8("bits");
- case SAT: return QString("sat");
- default: return longName(unit);
- }
+ switch (unit) {
+ case Unit::BTC: return longName(unit);
+ case Unit::mBTC: return longName(unit);
+ case Unit::uBTC: return QString("bits");
+ case Unit::SAT: return QString("sat");
+ } // no default case, so the compiler can warn about missing cases
+ assert(false);
}
-QString BitcoinUnits::description(int unit)
+QString BitcoinUnits::description(Unit unit)
{
- switch(unit)
- {
- case BTC: return QString("Bitcoins");
- case mBTC: return QString("Milli-Bitcoins (1 / 1" THIN_SP_UTF8 "000)");
- case uBTC: return QString("Micro-Bitcoins (bits) (1 / 1" THIN_SP_UTF8 "000" THIN_SP_UTF8 "000)");
- case SAT: return QString("Satoshi (sat) (1 / 100" THIN_SP_UTF8 "000" THIN_SP_UTF8 "000)");
- default: return QString("???");
- }
+ switch (unit) {
+ case Unit::BTC: return QString("Bitcoins");
+ case Unit::mBTC: return QString("Milli-Bitcoins (1 / 1" THIN_SP_UTF8 "000)");
+ case Unit::uBTC: return QString("Micro-Bitcoins (bits) (1 / 1" THIN_SP_UTF8 "000" THIN_SP_UTF8 "000)");
+ case Unit::SAT: return QString("Satoshi (sat) (1 / 100" THIN_SP_UTF8 "000" THIN_SP_UTF8 "000)");
+ } // no default case, so the compiler can warn about missing cases
+ assert(false);
}
-qint64 BitcoinUnits::factor(int unit)
+qint64 BitcoinUnits::factor(Unit unit)
{
- switch(unit)
- {
- case BTC: return 100000000;
- case mBTC: return 100000;
- case uBTC: return 100;
- case SAT: return 1;
- default: return 100000000;
- }
+ switch (unit) {
+ case Unit::BTC: return 100'000'000;
+ case Unit::mBTC: return 100'000;
+ case Unit::uBTC: return 100;
+ case Unit::SAT: return 1;
+ } // no default case, so the compiler can warn about missing cases
+ assert(false);
}
-int BitcoinUnits::decimals(int unit)
+int BitcoinUnits::decimals(Unit unit)
{
- switch(unit)
- {
- case BTC: return 8;
- case mBTC: return 5;
- case uBTC: return 2;
- case SAT: return 0;
- default: return 0;
- }
+ switch (unit) {
+ case Unit::BTC: return 8;
+ case Unit::mBTC: return 5;
+ case Unit::uBTC: return 2;
+ case Unit::SAT: return 0;
+ } // no default case, so the compiler can warn about missing cases
+ assert(false);
}
-QString BitcoinUnits::format(int unit, const CAmount& nIn, bool fPlus, SeparatorStyle separators, bool justify)
+QString BitcoinUnits::format(Unit unit, const CAmount& nIn, bool fPlus, SeparatorStyle separators, bool justify)
{
// Note: not using straight sprintf here because we do NOT want
// localized number formatting.
- if(!valid(unit))
- return QString(); // Refuse to format invalid unit
qint64 n = (qint64)nIn;
qint64 coin = factor(unit);
int num_decimals = decimals(unit);
@@ -147,19 +128,19 @@ QString BitcoinUnits::format(int unit, const CAmount& nIn, bool fPlus, Separator
// Please take care to use formatHtmlWithUnit instead, when
// appropriate.
-QString BitcoinUnits::formatWithUnit(int unit, const CAmount& amount, bool plussign, SeparatorStyle separators)
+QString BitcoinUnits::formatWithUnit(Unit unit, const CAmount& amount, bool plussign, SeparatorStyle separators)
{
return format(unit, amount, plussign, separators) + QString(" ") + shortName(unit);
}
-QString BitcoinUnits::formatHtmlWithUnit(int unit, const CAmount& amount, bool plussign, SeparatorStyle separators)
+QString BitcoinUnits::formatHtmlWithUnit(Unit unit, const CAmount& amount, bool plussign, SeparatorStyle separators)
{
QString str(formatWithUnit(unit, amount, plussign, separators));
str.replace(QChar(THIN_SP_CP), QString(THIN_SP_HTML));
return QString("<span style='white-space: nowrap;'>%1</span>").arg(str);
}
-QString BitcoinUnits::formatWithPrivacy(int unit, const CAmount& amount, SeparatorStyle separators, bool privacy)
+QString BitcoinUnits::formatWithPrivacy(Unit unit, const CAmount& amount, SeparatorStyle separators, bool privacy)
{
assert(amount >= 0);
QString value;
@@ -171,10 +152,11 @@ QString BitcoinUnits::formatWithPrivacy(int unit, const CAmount& amount, Separat
return value + QString(" ") + shortName(unit);
}
-bool BitcoinUnits::parse(int unit, const QString &value, CAmount *val_out)
+bool BitcoinUnits::parse(Unit unit, const QString& value, CAmount* val_out)
{
- if(!valid(unit) || value.isEmpty())
+ if (value.isEmpty()) {
return false; // Refuse to parse invalid unit or empty string
+ }
int num_decimals = decimals(unit);
// Ignore spaces and thin spaces when parsing
@@ -210,14 +192,9 @@ bool BitcoinUnits::parse(int unit, const QString &value, CAmount *val_out)
return ok;
}
-QString BitcoinUnits::getAmountColumnTitle(int unit)
+QString BitcoinUnits::getAmountColumnTitle(Unit unit)
{
- QString amountTitle = QObject::tr("Amount");
- if (BitcoinUnits::valid(unit))
- {
- amountTitle += " ("+BitcoinUnits::shortName(unit) + ")";
- }
- return amountTitle;
+ return QObject::tr("Amount") + " (" + shortName(unit) + ")";
}
int BitcoinUnits::rowCount(const QModelIndex &parent) const
@@ -240,7 +217,7 @@ QVariant BitcoinUnits::data(const QModelIndex &index, int role) const
case Qt::ToolTipRole:
return QVariant(description(unit));
case UnitRole:
- return QVariant(static_cast<int>(unit));
+ return QVariant::fromValue(unit);
}
}
return QVariant();
@@ -250,3 +227,40 @@ CAmount BitcoinUnits::maxMoney()
{
return MAX_MONEY;
}
+
+namespace {
+qint8 ToQint8(BitcoinUnit unit)
+{
+ switch (unit) {
+ case BitcoinUnit::BTC: return 0;
+ case BitcoinUnit::mBTC: return 1;
+ case BitcoinUnit::uBTC: return 2;
+ case BitcoinUnit::SAT: return 3;
+ } // no default case, so the compiler can warn about missing cases
+ assert(false);
+}
+
+BitcoinUnit FromQint8(qint8 num)
+{
+ switch (num) {
+ case 0: return BitcoinUnit::BTC;
+ case 1: return BitcoinUnit::mBTC;
+ case 2: return BitcoinUnit::uBTC;
+ case 3: return BitcoinUnit::SAT;
+ }
+ assert(false);
+}
+} // namespace
+
+QDataStream& operator<<(QDataStream& out, const BitcoinUnit& unit)
+{
+ return out << ToQint8(unit);
+}
+
+QDataStream& operator>>(QDataStream& in, BitcoinUnit& unit)
+{
+ qint8 input;
+ in >> input;
+ unit = FromQint8(input);
+ return in;
+}
diff --git a/src/qt/bitcoinunits.h b/src/qt/bitcoinunits.h
index 9fedec0d4f..b3b5a8fc18 100644
--- a/src/qt/bitcoinunits.h
+++ b/src/qt/bitcoinunits.h
@@ -8,6 +8,7 @@
#include <consensus/amount.h>
#include <QAbstractListModel>
+#include <QDataStream>
#include <QString>
// U+2009 THIN SPACE = UTF-8 E2 80 89
@@ -38,13 +39,13 @@ public:
/** Bitcoin units.
@note Source: https://en.bitcoin.it/wiki/Units . Please add only sensible ones
*/
- enum Unit
- {
+ enum class Unit {
BTC,
mBTC,
uBTC,
SAT
};
+ Q_ENUM(Unit)
enum class SeparatorStyle
{
@@ -59,30 +60,28 @@ public:
//! Get list of units, for drop-down box
static QList<Unit> availableUnits();
- //! Is unit ID valid?
- static bool valid(int unit);
//! Long name
- static QString longName(int unit);
+ static QString longName(Unit unit);
//! Short name
- static QString shortName(int unit);
+ static QString shortName(Unit unit);
//! Longer description
- static QString description(int unit);
+ static QString description(Unit unit);
//! Number of Satoshis (1e-8) per unit
- static qint64 factor(int unit);
+ static qint64 factor(Unit unit);
//! Number of decimals left
- static int decimals(int unit);
+ static int decimals(Unit unit);
//! Format as string
- static QString format(int unit, const CAmount& amount, bool plussign = false, SeparatorStyle separators = SeparatorStyle::STANDARD, bool justify = false);
+ static QString format(Unit unit, const CAmount& amount, bool plussign = false, SeparatorStyle separators = SeparatorStyle::STANDARD, bool justify = false);
//! Format as string (with unit)
- static QString formatWithUnit(int unit, const CAmount& amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD);
+ static QString formatWithUnit(Unit unit, const CAmount& amount, bool plussign = false, SeparatorStyle separators = SeparatorStyle::STANDARD);
//! Format as HTML string (with unit)
- static QString formatHtmlWithUnit(int unit, const CAmount& amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD);
+ static QString formatHtmlWithUnit(Unit unit, const CAmount& amount, bool plussign = false, SeparatorStyle separators = SeparatorStyle::STANDARD);
//! Format as string (with unit) of fixed length to preserve privacy, if it is set.
- static QString formatWithPrivacy(int unit, const CAmount& amount, SeparatorStyle separators, bool privacy);
+ static QString formatWithPrivacy(Unit unit, const CAmount& amount, SeparatorStyle separators, bool privacy);
//! Parse string to coin amount
- static bool parse(int unit, const QString &value, CAmount *val_out);
+ static bool parse(Unit unit, const QString& value, CAmount* val_out);
//! Gets title for amount column including current display unit if optionsModel reference available */
- static QString getAmountColumnTitle(int unit);
+ static QString getAmountColumnTitle(Unit unit);
///@}
//! @name AbstractListModel implementation
@@ -107,8 +106,11 @@ public:
static CAmount maxMoney();
private:
- QList<BitcoinUnits::Unit> unitlist;
+ QList<Unit> unitlist;
};
typedef BitcoinUnits::Unit BitcoinUnit;
+QDataStream& operator<<(QDataStream& out, const BitcoinUnit& unit);
+QDataStream& operator>>(QDataStream& in, BitcoinUnit& unit);
+
#endif // BITCOIN_QT_BITCOINUNITS_H
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index d3103492a4..bd9a90a890 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -507,7 +507,7 @@ void CoinControlDialog::updateLabels(CCoinControl& m_coin_control, WalletModel *
}
// actually update labels
- int nDisplayUnit = BitcoinUnits::BTC;
+ BitcoinUnit nDisplayUnit = BitcoinUnit::BTC;
if (model && model->getOptionsModel())
nDisplayUnit = model->getOptionsModel()->getDisplayUnit();
@@ -588,9 +588,9 @@ void CoinControlDialog::updateView()
ui->treeWidget->setEnabled(false); // performance, otherwise updateLabels would be called for every checked checkbox
ui->treeWidget->setAlternatingRowColors(!treeMode);
QFlags<Qt::ItemFlag> flgCheckbox = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
- QFlags<Qt::ItemFlag> flgTristate = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsTristate;
+ QFlags<Qt::ItemFlag> flgTristate = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsAutoTristate;
- int nDisplayUnit = model->getOptionsModel()->getDisplayUnit();
+ BitcoinUnit nDisplayUnit = model->getOptionsModel()->getDisplayUnit();
for (const auto& coins : model->wallet().listCoins()) {
CCoinControlWidgetItem* itemWalletAddress{nullptr};
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index 362601b512..6fb5fce5b3 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -60,6 +60,7 @@
#include <QSettings>
#include <QShortcut>
#include <QSize>
+#include <QStandardPaths>
#include <QString>
#include <QTextDocument> // for Qt::mightBeRichText
#include <QThread>
@@ -80,6 +81,8 @@
void ForceActivation();
#endif
+using namespace std::chrono_literals;
+
namespace GUIUtil {
QString dateTimeStr(const QDateTime &date)
@@ -175,8 +178,7 @@ bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out)
{
if(!i->second.isEmpty())
{
- if(!BitcoinUnits::parse(BitcoinUnits::BTC, i->second, &rv.amount))
- {
+ if (!BitcoinUnits::parse(BitcoinUnit::BTC, i->second, &rv.amount)) {
return false;
}
}
@@ -208,7 +210,7 @@ QString formatBitcoinURI(const SendCoinsRecipient &info)
if (info.amount)
{
- ret += QString("?amount=%1").arg(BitcoinUnits::format(BitcoinUnits::BTC, info.amount, false, BitcoinUnits::SeparatorStyle::NEVER));
+ ret += QString("?amount=%1").arg(BitcoinUnits::format(BitcoinUnit::BTC, info.amount, false, BitcoinUnits::SeparatorStyle::NEVER));
paramCount++;
}
@@ -728,6 +730,16 @@ QString formatDurationStr(std::chrono::seconds dur)
return str_list.join(" ");
}
+QString FormatPeerAge(std::chrono::seconds time_connected)
+{
+ const auto time_now{GetTime<std::chrono::seconds>()};
+ const auto age{time_now - time_connected};
+ if (age >= 24h) return QObject::tr("%1 d").arg(age / 24h);
+ if (age >= 1h) return QObject::tr("%1 h").arg(age / 1h);
+ if (age >= 1min) return QObject::tr("%1 m").arg(age / 1min);
+ return QObject::tr("%1 s").arg(age / 1s);
+}
+
QString formatServicesStr(quint64 mask)
{
QStringList strList;
diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h
index 0224b18b4e..76e5207d81 100644
--- a/src/qt/guiutil.h
+++ b/src/qt/guiutil.h
@@ -223,6 +223,9 @@ namespace GUIUtil
/** Convert seconds into a QString with days, hours, mins, secs */
QString formatDurationStr(std::chrono::seconds dur);
+ /** Convert peer connection time to a QString denominated in the most relevant unit. */
+ QString FormatPeerAge(std::chrono::seconds time_connected);
+
/** Format CNodeStats.nServices bitmask into a user-readable string */
QString formatServicesStr(quint64 mask);
diff --git a/src/qt/notificator.cpp b/src/qt/notificator.cpp
index b097ef080c..51151b0be8 100644
--- a/src/qt/notificator.cpp
+++ b/src/qt/notificator.cpp
@@ -14,8 +14,9 @@
#include <QTemporaryFile>
#include <QVariant>
#ifdef USE_DBUS
-#include <stdint.h>
+#include <QDBusMetaType>
#include <QtDBus>
+#include <stdint.h>
#endif
#ifdef Q_OS_MAC
#include <qt/macnotificationhandler.h>
@@ -73,8 +74,6 @@ public:
FreedesktopImage() {}
explicit FreedesktopImage(const QImage &img);
- static int metaType();
-
// Image to variant that can be marshalled over DBus
static QVariant toVariant(const QImage &img);
@@ -136,15 +135,10 @@ const QDBusArgument &operator>>(const QDBusArgument &a, FreedesktopImage &i)
return a;
}
-int FreedesktopImage::metaType()
-{
- return qDBusRegisterMetaType<FreedesktopImage>();
-}
-
QVariant FreedesktopImage::toVariant(const QImage &img)
{
FreedesktopImage fimg(img);
- return QVariant(FreedesktopImage::metaType(), &fimg);
+ return QVariant(qDBusRegisterMetaType<FreedesktopImage>(), &fimg);
}
void Notificator::notifyDBus(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout)
diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp
index 52bda59748..40b9ed5483 100644
--- a/src/qt/optionsmodel.cpp
+++ b/src/qt/optionsmodel.cpp
@@ -24,6 +24,7 @@
#include <QLatin1Char>
#include <QSettings>
#include <QStringList>
+#include <QVariant>
const char *DEFAULT_GUI_PROXY_HOST = "127.0.0.1";
@@ -71,9 +72,16 @@ void OptionsModel::Init(bool resetSettings)
fMinimizeOnClose = settings.value("fMinimizeOnClose").toBool();
// Display
- if (!settings.contains("nDisplayUnit"))
- settings.setValue("nDisplayUnit", BitcoinUnits::BTC);
- nDisplayUnit = settings.value("nDisplayUnit").toInt();
+ if (!settings.contains("DisplayBitcoinUnit")) {
+ settings.setValue("DisplayBitcoinUnit", QVariant::fromValue(BitcoinUnit::BTC));
+ }
+ QVariant unit = settings.value("DisplayBitcoinUnit");
+ if (unit.canConvert<BitcoinUnit>()) {
+ m_display_bitcoin_unit = unit.value<BitcoinUnit>();
+ } else {
+ m_display_bitcoin_unit = BitcoinUnit::BTC;
+ settings.setValue("DisplayBitcoinUnit", QVariant::fromValue(m_display_bitcoin_unit));
+ }
if (!settings.contains("strThirdPartyTxUrls"))
settings.setValue("strThirdPartyTxUrls", "");
@@ -376,7 +384,7 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const
return m_sub_fee_from_amount;
#endif
case DisplayUnit:
- return nDisplayUnit;
+ return QVariant::fromValue(m_display_bitcoin_unit);
case ThirdPartyTxUrls:
return strThirdPartyTxUrls;
case Language:
@@ -584,16 +592,13 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in
return successful;
}
-/** Updates current unit in memory, settings and emits displayUnitChanged(newUnit) signal */
-void OptionsModel::setDisplayUnit(const QVariant &value)
+void OptionsModel::setDisplayUnit(const QVariant& new_unit)
{
- if (!value.isNull())
- {
- QSettings settings;
- nDisplayUnit = value.toInt();
- settings.setValue("nDisplayUnit", nDisplayUnit);
- Q_EMIT displayUnitChanged(nDisplayUnit);
- }
+ if (new_unit.isNull() || new_unit.value<BitcoinUnit>() == m_display_bitcoin_unit) return;
+ m_display_bitcoin_unit = new_unit.value<BitcoinUnit>();
+ QSettings settings;
+ settings.setValue("DisplayBitcoinUnit", QVariant::fromValue(m_display_bitcoin_unit));
+ Q_EMIT displayUnitChanged(m_display_bitcoin_unit);
}
void OptionsModel::setRestartRequired(bool fRequired)
diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h
index bb9a8c1f8c..510ebb5cfd 100644
--- a/src/qt/optionsmodel.h
+++ b/src/qt/optionsmodel.h
@@ -6,6 +6,7 @@
#define BITCOIN_QT_OPTIONSMODEL_H
#include <cstdint>
+#include <qt/bitcoinunits.h>
#include <qt/guiconstants.h>
#include <QAbstractListModel>
@@ -55,7 +56,7 @@ public:
ProxyUseTor, // bool
ProxyIPTor, // QString
ProxyPortTor, // int
- DisplayUnit, // BitcoinUnits::Unit
+ DisplayUnit, // BitcoinUnit
ThirdPartyTxUrls, // QString
Language, // QString
UseEmbeddedMonospacedFont, // bool
@@ -79,14 +80,14 @@ public:
int rowCount(const QModelIndex & parent = QModelIndex()) const override;
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole) override;
- /** Updates current unit in memory, settings and emits displayUnitChanged(newUnit) signal */
- void setDisplayUnit(const QVariant &value);
+ /** Updates current unit in memory, settings and emits displayUnitChanged(new_unit) signal */
+ void setDisplayUnit(const QVariant& new_unit);
/* Explicit getters */
bool getShowTrayIcon() const { return m_show_tray_icon; }
bool getMinimizeToTray() const { return fMinimizeToTray; }
bool getMinimizeOnClose() const { return fMinimizeOnClose; }
- int getDisplayUnit() const { return nDisplayUnit; }
+ BitcoinUnit getDisplayUnit() const { return m_display_bitcoin_unit; }
QString getThirdPartyTxUrls() const { return strThirdPartyTxUrls; }
bool getUseEmbeddedMonospacedFont() const { return m_use_embedded_monospaced_font; }
bool getCoinControlFeatures() const { return fCoinControlFeatures; }
@@ -112,7 +113,7 @@ private:
bool fMinimizeToTray;
bool fMinimizeOnClose;
QString language;
- int nDisplayUnit;
+ BitcoinUnit m_display_bitcoin_unit;
QString strThirdPartyTxUrls;
bool m_use_embedded_monospaced_font;
bool fCoinControlFeatures;
@@ -127,7 +128,7 @@ private:
// Check settings version and upgrade default values if required
void checkAndMigrate();
Q_SIGNALS:
- void displayUnitChanged(int unit);
+ void displayUnitChanged(BitcoinUnit unit);
void coinControlFeaturesChanged(bool);
void showTrayIconChanged(bool);
void useEmbeddedMonospacedFontChanged(bool);
diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp
index 7127706463..820bcbf3cd 100644
--- a/src/qt/overviewpage.cpp
+++ b/src/qt/overviewpage.cpp
@@ -34,9 +34,9 @@ class TxViewDelegate : public QAbstractItemDelegate
{
Q_OBJECT
public:
- explicit TxViewDelegate(const PlatformStyle *_platformStyle, QObject *parent=nullptr):
- QAbstractItemDelegate(parent), unit(BitcoinUnits::BTC),
- platformStyle(_platformStyle)
+ explicit TxViewDelegate(const PlatformStyle* _platformStyle, QObject* parent = nullptr)
+ : QAbstractItemDelegate(parent), unit(BitcoinUnit::BTC),
+ platformStyle(_platformStyle)
{
connect(this, &TxViewDelegate::width_changed, this, &TxViewDelegate::sizeHintChanged);
}
@@ -125,7 +125,7 @@ public:
return {DECORATION_SIZE + 8 + minimum_text_width, DECORATION_SIZE};
}
- int unit;
+ BitcoinUnit unit;
Q_SIGNALS:
//! An intermediate signal for emitting from the `paint() const` member function.
@@ -197,7 +197,7 @@ OverviewPage::~OverviewPage()
void OverviewPage::setBalance(const interfaces::WalletBalances& balances)
{
- int unit = walletModel->getOptionsModel()->getDisplayUnit();
+ BitcoinUnit unit = walletModel->getOptionsModel()->getDisplayUnit();
m_balances = balances;
if (walletModel->wallet().isLegacy()) {
if (walletModel->wallet().privateKeysDisabled()) {
diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp
index 563bca76e5..41c389d9cc 100644
--- a/src/qt/peertablemodel.cpp
+++ b/src/qt/peertablemodel.cpp
@@ -71,6 +71,8 @@ QVariant PeerTableModel::data(const QModelIndex& index, int role) const
switch (column) {
case NetNodeId:
return (qint64)rec->nodeStats.nodeid;
+ case Age:
+ return GUIUtil::FormatPeerAge(rec->nodeStats.m_connected);
case Address:
return QString::fromStdString(rec->nodeStats.m_addr_name);
case Direction:
@@ -96,6 +98,7 @@ QVariant PeerTableModel::data(const QModelIndex& index, int role) const
} else if (role == Qt::TextAlignmentRole) {
switch (column) {
case NetNodeId:
+ case Age:
return QVariant(Qt::AlignRight | Qt::AlignVCenter);
case Address:
return {};
diff --git a/src/qt/peertablemodel.h b/src/qt/peertablemodel.h
index 11064cdbfe..e2515de775 100644
--- a/src/qt/peertablemodel.h
+++ b/src/qt/peertablemodel.h
@@ -47,6 +47,7 @@ public:
enum ColumnIndex {
NetNodeId = 0,
+ Age,
Address,
Direction,
ConnectionType,
@@ -82,6 +83,9 @@ private:
/*: Title of Peers Table column which contains a
unique number used to identify a connection. */
tr("Peer"),
+ /*: Title of Peers Table column which indicates the duration (length of time)
+ since the peer connection started. */
+ tr("Age"),
/*: Title of Peers Table column which contains the
IP/Onion/I2P address of the connected peer. */
tr("Address"),
diff --git a/src/qt/peertablesortproxy.cpp b/src/qt/peertablesortproxy.cpp
index 26fedb4127..d87f10c365 100644
--- a/src/qt/peertablesortproxy.cpp
+++ b/src/qt/peertablesortproxy.cpp
@@ -24,6 +24,8 @@ bool PeerTableSortProxy::lessThan(const QModelIndex& left_index, const QModelInd
switch (static_cast<PeerTableModel::ColumnIndex>(left_index.column())) {
case PeerTableModel::NetNodeId:
return left_stats.nodeid < right_stats.nodeid;
+ case PeerTableModel::Age:
+ return left_stats.m_connected > right_stats.m_connected;
case PeerTableModel::Address:
return left_stats.m_addr_name.compare(right_stats.m_addr_name) < 0;
case PeerTableModel::Direction:
diff --git a/src/qt/psbtoperationsdialog.cpp b/src/qt/psbtoperationsdialog.cpp
index 6880c157c0..333766ce21 100644
--- a/src/qt/psbtoperationsdialog.cpp
+++ b/src/qt/psbtoperationsdialog.cpp
@@ -181,7 +181,7 @@ std::string PSBTOperationsDialog::renderTransaction(const PartiallySignedTransac
ExtractDestination(out.scriptPubKey, address);
totalAmount += out.nValue;
tx_description.append(tr(" * Sends %1 to %2")
- .arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, out.nValue))
+ .arg(BitcoinUnits::formatWithUnit(BitcoinUnit::BTC, out.nValue))
.arg(QString::fromStdString(EncodeDestination(address))));
tx_description.append("<br>");
}
@@ -193,7 +193,7 @@ std::string PSBTOperationsDialog::renderTransaction(const PartiallySignedTransac
tx_description.append(tr("Unable to calculate transaction fee or total transaction amount."));
} else {
tx_description.append(tr("Pays transaction fee: "));
- tx_description.append(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, *analysis.fee));
+ tx_description.append(BitcoinUnits::formatWithUnit(BitcoinUnit::BTC, *analysis.fee));
// add total amount in all subdivision units
tx_description.append("<hr />");
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index dcc4f36aaa..eb69fabe89 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -618,17 +618,16 @@ bool RPCConsole::eventFilter(QObject* obj, QEvent *event)
case Qt::Key_Down: if(obj == ui->lineEdit) { browseHistory(1); return true; } break;
case Qt::Key_PageUp: /* pass paging keys to messages widget */
case Qt::Key_PageDown:
- if(obj == ui->lineEdit)
- {
- QApplication::postEvent(ui->messagesWidget, new QKeyEvent(*keyevt));
+ if (obj == ui->lineEdit) {
+ QApplication::sendEvent(ui->messagesWidget, keyevt);
return true;
}
break;
case Qt::Key_Return:
case Qt::Key_Enter:
// forward these events to lineEdit
- if(obj == autoCompleter->popup()) {
- QApplication::postEvent(ui->lineEdit, new QKeyEvent(*keyevt));
+ if (obj == autoCompleter->popup()) {
+ QApplication::sendEvent(ui->lineEdit, keyevt);
autoCompleter->popup()->hide();
return true;
}
@@ -642,7 +641,7 @@ bool RPCConsole::eventFilter(QObject* obj, QEvent *event)
((mod & Qt::ShiftModifier) && key == Qt::Key_Insert)))
{
ui->lineEdit->setFocus();
- QApplication::postEvent(ui->lineEdit, new QKeyEvent(*keyevt));
+ QApplication::sendEvent(ui->lineEdit, keyevt);
return true;
}
}
@@ -694,6 +693,7 @@ void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_
ui->peerWidget->setColumnWidth(PeerTableModel::Subversion, SUBVERSION_COLUMN_WIDTH);
ui->peerWidget->setColumnWidth(PeerTableModel::Ping, PING_COLUMN_WIDTH);
}
+ ui->peerWidget->horizontalHeader()->setSectionResizeMode(PeerTableModel::Age, QHeaderView::ResizeToContents);
ui->peerWidget->horizontalHeader()->setStretchLastSection(true);
ui->peerWidget->setItemDelegateForColumn(PeerTableModel::NetNodeId, new PeerIdViewDelegate(this));
@@ -726,6 +726,7 @@ void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_
ui->banlistWidget->setColumnWidth(BanTableModel::Address, BANSUBNET_COLUMN_WIDTH);
ui->banlistWidget->setColumnWidth(BanTableModel::Bantime, BANTIME_COLUMN_WIDTH);
}
+ ui->banlistWidget->horizontalHeader()->setSectionResizeMode(BanTableModel::Address, QHeaderView::ResizeToContents);
ui->banlistWidget->horizontalHeader()->setStretchLastSection(true);
// create ban table context menu
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index c924789796..fd8eccb86d 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -380,8 +380,7 @@ bool SendCoinsDialog::PrepareSendText(QString& question_string, QString& informa
question_string.append("<hr />");
CAmount totalAmount = m_current_transaction->getTotalTransactionAmount() + txFee;
QStringList alternativeUnits;
- for (const BitcoinUnits::Unit u : BitcoinUnits::availableUnits())
- {
+ for (const BitcoinUnit u : BitcoinUnits::availableUnits()) {
if(u != model->getOptionsModel()->getDisplayUnit())
alternativeUnits.append(BitcoinUnits::formatHtmlWithUnit(u, totalAmount));
}
diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp
index 07d256f05a..aeedd92834 100644
--- a/src/qt/test/test_main.cpp
+++ b/src/qt/test/test_main.cpp
@@ -21,8 +21,10 @@
#endif // ENABLE_WALLET
#include <QApplication>
+#include <QDebug>
#include <QObject>
#include <QTest>
+
#include <functional>
#if defined(QT_STATICPLUGIN)
@@ -69,8 +71,6 @@ int main(int argc, char* argv[])
gArgs.ForceSetArg("-upnp", "0");
gArgs.ForceSetArg("-natpmp", "0");
- bool fInvalid = false;
-
// Prefer the "minimal" platform for the test instead of the normal default
// platform ("xcb", "windows", or "cocoa") so tests can't unintentionally
// interfere with any background GUIs and don't require extra resources.
@@ -86,32 +86,32 @@ int main(int argc, char* argv[])
app.setApplicationName("Bitcoin-Qt-test");
app.createNode(*init);
+ int num_test_failures{0};
+
AppTests app_tests(app);
- if (QTest::qExec(&app_tests) != 0) {
- fInvalid = true;
- }
+ num_test_failures += QTest::qExec(&app_tests);
+
OptionTests options_tests(app.node());
- if (QTest::qExec(&options_tests) != 0) {
- fInvalid = true;
- }
+ num_test_failures += QTest::qExec(&options_tests);
+
URITests test1;
- if (QTest::qExec(&test1) != 0) {
- fInvalid = true;
- }
+ num_test_failures += QTest::qExec(&test1);
+
RPCNestedTests test3(app.node());
- if (QTest::qExec(&test3) != 0) {
- fInvalid = true;
- }
+ num_test_failures += QTest::qExec(&test3);
+
#ifdef ENABLE_WALLET
WalletTests test5(app.node());
- if (QTest::qExec(&test5) != 0) {
- fInvalid = true;
- }
+ num_test_failures += QTest::qExec(&test5);
+
AddressBookTests test6(app.node());
- if (QTest::qExec(&test6) != 0) {
- fInvalid = true;
- }
+ num_test_failures += QTest::qExec(&test6);
#endif
- return fInvalid;
+ if (num_test_failures) {
+ qWarning("\nFailed tests: %d\n", num_test_failures);
+ } else {
+ qDebug("\nAll tests passed.\n");
+ }
+ return num_test_failures;
}
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index 6ab534764b..c4cd0f4cd1 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -7,24 +7,25 @@
#include <interfaces/chain.h>
#include <interfaces/node.h>
+#include <key_io.h>
#include <qt/bitcoinamountfield.h>
+#include <qt/bitcoinunits.h>
#include <qt/clientmodel.h>
#include <qt/optionsmodel.h>
+#include <qt/overviewpage.h>
#include <qt/platformstyle.h>
#include <qt/qvalidatedlineedit.h>
+#include <qt/receivecoinsdialog.h>
+#include <qt/receiverequestdialog.h>
+#include <qt/recentrequeststablemodel.h>
#include <qt/sendcoinsdialog.h>
#include <qt/sendcoinsentry.h>
#include <qt/transactiontablemodel.h>
#include <qt/transactionview.h>
#include <qt/walletmodel.h>
-#include <key_io.h>
#include <test/util/setup_common.h>
#include <validation.h>
#include <wallet/wallet.h>
-#include <qt/overviewpage.h>
-#include <qt/receivecoinsdialog.h>
-#include <qt/recentrequeststablemodel.h>
-#include <qt/receiverequestdialog.h>
#include <chrono>
#include <memory>
@@ -196,7 +197,7 @@ void TestGUI(interfaces::Node& node)
// Check balance in send dialog
QLabel* balanceLabel = sendCoinsDialog.findChild<QLabel*>("labelBalance");
QString balanceText = balanceLabel->text();
- int unit = walletModel.getOptionsModel()->getDisplayUnit();
+ BitcoinUnit unit = walletModel.getOptionsModel()->getDisplayUnit();
CAmount balance = walletModel.wallet().getBalance();
QString balanceComparison = BitcoinUnits::formatWithUnit(unit, balance, false, BitcoinUnits::SeparatorStyle::ALWAYS);
QCOMPARE(balanceText, balanceComparison);
@@ -222,7 +223,7 @@ void TestGUI(interfaces::Node& node)
overviewPage.setWalletModel(&walletModel);
QLabel* balanceLabel = overviewPage.findChild<QLabel*>("labelBalance");
QString balanceText = balanceLabel->text().trimmed();
- int unit = walletModel.getOptionsModel()->getDisplayUnit();
+ BitcoinUnit unit = walletModel.getOptionsModel()->getDisplayUnit();
CAmount balance = walletModel.wallet().getBalance();
QString balanceComparison = BitcoinUnits::formatWithUnit(unit, balance, false, BitcoinUnits::SeparatorStyle::ALWAYS);
QCOMPARE(balanceText, balanceComparison);
diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp
index be5851d627..9e92f89543 100644
--- a/src/qt/transactiondesc.cpp
+++ b/src/qt/transactiondesc.cpp
@@ -32,20 +32,18 @@ using wallet::ISMINE_SPENDABLE;
using wallet::ISMINE_WATCH_ONLY;
using wallet::isminetype;
-QString TransactionDesc::FormatTxStatus(const interfaces::WalletTx& wtx, const interfaces::WalletTxStatus& status, bool inMempool, int numBlocks)
+QString TransactionDesc::FormatTxStatus(const interfaces::WalletTxStatus& status, bool inMempool)
{
- {
- int nDepth = status.depth_in_main_chain;
- if (nDepth < 0) {
- return tr("conflicted with a transaction with %1 confirmations").arg(-nDepth);
- } else if (nDepth == 0) {
- const QString abandoned{status.is_abandoned ? QLatin1String(", ") + tr("abandoned") : QString()};
- return tr("0/unconfirmed, %1").arg(inMempool ? tr("in memory pool") : tr("not in memory pool")) + abandoned;
- } else if (nDepth < 6) {
- return tr("%1/unconfirmed").arg(nDepth);
- } else {
- return tr("%1 confirmations").arg(nDepth);
- }
+ int depth = status.depth_in_main_chain;
+ if (depth < 0) {
+ return tr("conflicted with a transaction with %1 confirmations").arg(-depth);
+ } else if (depth == 0) {
+ const QString abandoned{status.is_abandoned ? QLatin1String(", ") + tr("abandoned") : QString()};
+ return tr("0/unconfirmed, %1").arg(inMempool ? tr("in memory pool") : tr("not in memory pool")) + abandoned;
+ } else if (depth < 6) {
+ return tr("%1/unconfirmed").arg(depth);
+ } else {
+ return tr("%1 confirmations").arg(depth);
}
}
@@ -77,7 +75,7 @@ bool GetPaymentRequestMerchant(const std::string& pr, QString& merchant)
return false;
}
-QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wallet, TransactionRecord *rec, int unit)
+QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wallet, TransactionRecord* rec, BitcoinUnit unit)
{
int numBlocks;
interfaces::WalletTxStatus status;
@@ -95,7 +93,7 @@ QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wall
CAmount nDebit = wtx.debit;
CAmount nNet = nCredit - nDebit;
- strHTML += "<b>" + tr("Status") + ":</b> " + FormatTxStatus(wtx, status, inMempool, numBlocks);
+ strHTML += "<b>" + tr("Status") + ":</b> " + FormatTxStatus(status, inMempool);
strHTML += "<br>";
strHTML += "<b>" + tr("Date") + ":</b> " + (nTime ? GUIUtil::dateTimeStr(nTime) : "") + "<br>";
diff --git a/src/qt/transactiondesc.h b/src/qt/transactiondesc.h
index cf955a433c..803e41b699 100644
--- a/src/qt/transactiondesc.h
+++ b/src/qt/transactiondesc.h
@@ -5,6 +5,8 @@
#ifndef BITCOIN_QT_TRANSACTIONDESC_H
#define BITCOIN_QT_TRANSACTIONDESC_H
+#include <qt/bitcoinunits.h>
+
#include <QObject>
#include <QString>
@@ -24,12 +26,12 @@ class TransactionDesc: public QObject
Q_OBJECT
public:
- static QString toHTML(interfaces::Node& node, interfaces::Wallet& wallet, TransactionRecord *rec, int unit);
+ static QString toHTML(interfaces::Node& node, interfaces::Wallet& wallet, TransactionRecord* rec, BitcoinUnit unit);
private:
TransactionDesc() {}
- static QString FormatTxStatus(const interfaces::WalletTx& wtx, const interfaces::WalletTxStatus& status, bool inMempool, int numBlocks);
+ static QString FormatTxStatus(const interfaces::WalletTxStatus& status, bool inMempool);
};
#endif // BITCOIN_QT_TRANSACTIONDESC_H
diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h
index dd34656d5f..d8748d7dc9 100644
--- a/src/qt/transactionrecord.h
+++ b/src/qt/transactionrecord.h
@@ -20,13 +20,7 @@ struct WalletTxStatus;
/** UI model for transaction status. The transaction status is the part of a transaction that will change over time.
*/
-class TransactionStatus
-{
-public:
- TransactionStatus() : countsForBalance(false), sortKey(""),
- matures_in(0), status(Unconfirmed), depth(0), open_for(0)
- { }
-
+struct TransactionStatus {
enum Status {
Confirmed, /**< Have 6 or more confirmations (normal tx) or fully mature (mined tx) **/
/// Normal (sent/received) transactions
@@ -40,28 +34,25 @@ public:
};
/// Transaction counts towards available balance
- bool countsForBalance;
+ bool countsForBalance{false};
/// Sorting key based on status
std::string sortKey;
/** @name Generated (mined) transactions
@{*/
- int matures_in;
+ int matures_in{0};
/**@}*/
/** @name Reported status
@{*/
- Status status;
- qint64 depth;
- qint64 open_for; /**< Timestamp if status==OpenUntilDate, otherwise number
- of additional blocks that need to be mined before
- finalization */
+ Status status{Unconfirmed};
+ qint64 depth{0};
/**@}*/
/** Current block hash (to know whether cached status is still valid) */
uint256 m_cur_block_hash{};
- bool needsUpdate;
+ bool needsUpdate{false};
};
/** UI model for a transaction. A core transaction can be represented by multiple UI transactions if it has
diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp
index 6b0495f5a8..7b932890cf 100644
--- a/src/qt/transactiontablemodel.cpp
+++ b/src/qt/transactiontablemodel.cpp
@@ -5,6 +5,7 @@
#include <qt/transactiontablemodel.h>
#include <qt/addresstablemodel.h>
+#include <qt/bitcoinunits.h>
#include <qt/clientmodel.h>
#include <qt/guiconstants.h>
#include <qt/guiutil.h>
@@ -232,7 +233,7 @@ public:
return nullptr;
}
- QString describe(interfaces::Node& node, interfaces::Wallet& wallet, TransactionRecord *rec, int unit)
+ QString describe(interfaces::Node& node, interfaces::Wallet& wallet, TransactionRecord* rec, BitcoinUnit unit)
{
return TransactionDesc::toHTML(node, wallet, rec, unit);
}
diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp
index e7ec54721a..344bf628bb 100644
--- a/src/qt/walletview.cpp
+++ b/src/qt/walletview.cpp
@@ -23,7 +23,6 @@
#include <util/strencodings.h>
#include <QAction>
-#include <QActionGroup>
#include <QFileDialog>
#include <QHBoxLayout>
#include <QProgressDialog>
diff --git a/src/qt/walletview.h b/src/qt/walletview.h
index 2f9d344bc8..301084ffa9 100644
--- a/src/qt/walletview.h
+++ b/src/qt/walletview.h
@@ -6,6 +6,7 @@
#define BITCOIN_QT_WALLETVIEW_H
#include <consensus/amount.h>
+#include <qt/bitcoinunits.h>
#include <QStackedWidget>
@@ -115,7 +116,7 @@ Q_SIGNALS:
/** Encryption status of wallet changed */
void encryptionStatusChanged();
/** Notify that a new transaction appeared */
- void incomingTransaction(const QString& date, int unit, const CAmount& amount, const QString& type, const QString& address, const QString& label, const QString& walletName);
+ void incomingTransaction(const QString& date, BitcoinUnit unit, const CAmount& amount, const QString& type, const QString& address, const QString& label, const QString& walletName);
/** Notify that the out of sync warning icon has been pressed */
void outOfSyncWarningClicked();
};
diff --git a/src/random.cpp b/src/random.cpp
index b862510524..77d2ae4d43 100644
--- a/src/random.cpp
+++ b/src/random.cpp
@@ -13,7 +13,7 @@
#include <compat.h> // for Windows API
#include <wincrypt.h>
#endif
-#include <logging.h> // for LogPrintf()
+#include <logging.h>
#include <randomenv.h>
#include <support/allocators/secure.h>
#include <sync.h> // for Mutex
diff --git a/src/rest.cpp b/src/rest.cpp
index a8eba05c3f..2aeb9c68c3 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -37,7 +37,6 @@
#include <univalue.h>
using node::GetTransaction;
-using node::IsBlockPruned;
using node::NodeContext;
using node::ReadBlockFromDisk;
@@ -295,10 +294,10 @@ static bool rest_block(const std::any& context,
CBlock block;
const CBlockIndex* pblockindex = nullptr;
const CBlockIndex* tip = nullptr;
+ ChainstateManager* maybe_chainman = GetChainman(context, req);
+ if (!maybe_chainman) return false;
+ ChainstateManager& chainman = *maybe_chainman;
{
- ChainstateManager* maybe_chainman = GetChainman(context, req);
- if (!maybe_chainman) return false;
- ChainstateManager& chainman = *maybe_chainman;
LOCK(cs_main);
tip = chainman.ActiveChain().Tip();
pblockindex = chainman.m_blockman.LookupBlockIndex(hash);
@@ -306,7 +305,7 @@ static bool rest_block(const std::any& context,
return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
}
- if (IsBlockPruned(pblockindex))
+ if (chainman.m_blockman.IsBlockPruned(pblockindex))
return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not available (pruned data)");
if (!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus()))
@@ -333,7 +332,7 @@ static bool rest_block(const std::any& context,
}
case RESTResponseFormat::JSON: {
- UniValue objBlock = blockToJSON(block, tip, pblockindex, tx_verbosity);
+ UniValue objBlock = blockToJSON(chainman.m_blockman, block, tip, pblockindex, tx_verbosity);
std::string strJSON = objBlock.write() + "\n";
req->WriteHeader("Content-Type", "application/json");
req->WriteReply(HTTP_OK, strJSON);
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index a124e0267c..f46e5e9fef 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -54,7 +54,6 @@ using node::BlockManager;
using node::CCoinsStats;
using node::CoinStatsHashType;
using node::GetUTXOStats;
-using node::IsBlockPruned;
using node::NodeContext;
using node::ReadBlockFromDisk;
using node::SnapshotMetadata;
@@ -161,7 +160,7 @@ UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex
return result;
}
-UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, TxVerbosity verbosity)
+UniValue blockToJSON(BlockManager& blockman, const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, TxVerbosity verbosity)
{
UniValue result = blockheaderToJSON(tip, blockindex);
@@ -180,7 +179,7 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIn
case TxVerbosity::SHOW_DETAILS:
case TxVerbosity::SHOW_DETAILS_AND_PREVOUT:
CBlockUndo blockUndo;
- const bool have_undo{WITH_LOCK(::cs_main, return !IsBlockPruned(blockindex) && UndoReadFromDisk(blockUndo, blockindex))};
+ const bool have_undo{WITH_LOCK(::cs_main, return !blockman.IsBlockPruned(blockindex) && UndoReadFromDisk(blockUndo, blockindex))};
for (size_t i = 0; i < block.vtx.size(); ++i) {
const CTransactionRef& tx = block.vtx.at(i);
@@ -565,11 +564,11 @@ static RPCHelpMan getblockheader()
};
}
-static CBlock GetBlockChecked(const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
+static CBlock GetBlockChecked(BlockManager& blockman, const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
{
AssertLockHeld(::cs_main);
CBlock block;
- if (IsBlockPruned(pblockindex)) {
+ if (blockman.IsBlockPruned(pblockindex)) {
throw JSONRPCError(RPC_MISC_ERROR, "Block not available (pruned data)");
}
@@ -583,11 +582,11 @@ static CBlock GetBlockChecked(const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_RE
return block;
}
-static CBlockUndo GetUndoChecked(const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+static CBlockUndo GetUndoChecked(BlockManager& blockman, const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
AssertLockHeld(::cs_main);
CBlockUndo blockUndo;
- if (IsBlockPruned(pblockindex)) {
+ if (blockman.IsBlockPruned(pblockindex)) {
throw JSONRPCError(RPC_MISC_ERROR, "Undo data not available (pruned data)");
}
@@ -701,8 +700,8 @@ static RPCHelpMan getblock()
CBlock block;
const CBlockIndex* pblockindex;
const CBlockIndex* tip;
+ ChainstateManager& chainman = EnsureAnyChainman(request.context);
{
- ChainstateManager& chainman = EnsureAnyChainman(request.context);
LOCK(cs_main);
pblockindex = chainman.m_blockman.LookupBlockIndex(hash);
tip = chainman.ActiveChain().Tip();
@@ -711,7 +710,7 @@ static RPCHelpMan getblock()
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
- block = GetBlockChecked(pblockindex);
+ block = GetBlockChecked(chainman.m_blockman, pblockindex);
}
if (verbosity <= 0)
@@ -731,7 +730,7 @@ static RPCHelpMan getblock()
tx_verbosity = TxVerbosity::SHOW_DETAILS_AND_PREVOUT;
}
- return blockToJSON(block, tip, pblockindex, tx_verbosity);
+ return blockToJSON(chainman.m_blockman, block, tip, pblockindex, tx_verbosity);
},
};
}
@@ -1205,18 +1204,18 @@ RPCHelpMan getblockchaininfo()
CHECK_NONFATAL(tip);
const int height = tip->nHeight;
UniValue obj(UniValue::VOBJ);
- obj.pushKV("chain", Params().NetworkIDString());
- obj.pushKV("blocks", height);
- obj.pushKV("headers", pindexBestHeader ? pindexBestHeader->nHeight : -1);
- obj.pushKV("bestblockhash", tip->GetBlockHash().GetHex());
- obj.pushKV("difficulty", (double)GetDifficulty(tip));
- obj.pushKV("time", (int64_t)tip->nTime);
- obj.pushKV("mediantime", (int64_t)tip->GetMedianTimePast());
- obj.pushKV("verificationprogress", GuessVerificationProgress(Params().TxData(), tip));
- obj.pushKV("initialblockdownload", active_chainstate.IsInitialBlockDownload());
- obj.pushKV("chainwork", tip->nChainWork.GetHex());
+ obj.pushKV("chain", Params().NetworkIDString());
+ obj.pushKV("blocks", height);
+ obj.pushKV("headers", chainman.m_best_header ? chainman.m_best_header->nHeight : -1);
+ obj.pushKV("bestblockhash", tip->GetBlockHash().GetHex());
+ obj.pushKV("difficulty", (double)GetDifficulty(tip));
+ obj.pushKV("time", (int64_t)tip->nTime);
+ obj.pushKV("mediantime", (int64_t)tip->GetMedianTimePast());
+ obj.pushKV("verificationprogress", GuessVerificationProgress(Params().TxData(), tip));
+ obj.pushKV("initialblockdownload", active_chainstate.IsInitialBlockDownload());
+ obj.pushKV("chainwork", tip->nChainWork.GetHex());
obj.pushKV("size_on_disk", chainman.m_blockman.CalculateCurrentUsage());
- obj.pushKV("pruned", node::fPruneMode);
+ obj.pushKV("pruned", node::fPruneMode);
if (node::fPruneMode) {
const CBlockIndex* block = tip;
CHECK_NONFATAL(block);
@@ -1777,8 +1776,8 @@ static RPCHelpMan getblockstats()
}
}
- const CBlock block = GetBlockChecked(pindex);
- const CBlockUndo blockUndo = GetUndoChecked(pindex);
+ const CBlock block = GetBlockChecked(chainman.m_blockman, pindex);
+ const CBlockUndo blockUndo = GetUndoChecked(chainman.m_blockman, pindex);
const bool do_all = stats.size() == 0; // Calculate everything if nothing selected (default)
const bool do_mediantxsize = do_all || stats.count("mediantxsize") != 0;
diff --git a/src/rpc/blockchain.h b/src/rpc/blockchain.h
index a8c6d171cc..5fbd9d5fd3 100644
--- a/src/rpc/blockchain.h
+++ b/src/rpc/blockchain.h
@@ -10,6 +10,7 @@
#include <fs.h>
#include <streams.h>
#include <sync.h>
+#include <validation.h>
#include <any>
#include <stdint.h>
@@ -39,7 +40,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, TxVerbosity verbosity) LOCKS_EXCLUDED(cs_main);
+UniValue blockToJSON(node::BlockManager& blockman, const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, TxVerbosity verbosity) LOCKS_EXCLUDED(cs_main);
/** Block header to JSON */
UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex) LOCKS_EXCLUDED(cs_main);
diff --git a/src/script/miniscript.cpp b/src/script/miniscript.cpp
index d0bb937885..019f02f159 100644
--- a/src/script/miniscript.cpp
+++ b/src/script/miniscript.cpp
@@ -116,7 +116,8 @@ Type ComputeType(Fragment nodetype, Type x, Type y, Type z, const std::vector<Ty
"e"_mst.If(x << "f"_mst) | // e=f_x
(x & "ghijk"_mst) | // g=g_x, h=h_x, i=i_x, j=j_x, k=k_x
(x & "ms"_mst) | // m=m_x, s=s_x
- "nudx"_mst; // n, u, d, x
+ // NOTE: 'd:' is not 'u' under P2WSH as MINIMALIF is only a policy rule there.
+ "ndx"_mst; // n, d, x
case Fragment::WRAP_V: return
"V"_mst.If(x << "B"_mst) | // V=B_x
(x & "ghijk"_mst) | // g=g_x, h=h_x, i=i_x, j=j_x, k=k_x
diff --git a/src/sync.h b/src/sync.h
index af7595e6fa..c69b58741b 100644
--- a/src/sync.h
+++ b/src/sync.h
@@ -223,12 +223,12 @@ public:
friend class reverse_lock;
};
-#define REVERSE_LOCK(g) typename std::decay<decltype(g)>::type::reverse_lock PASTE2(revlock, __COUNTER__)(g, #g, __FILE__, __LINE__)
+#define REVERSE_LOCK(g) typename std::decay<decltype(g)>::type::reverse_lock UNIQUE_NAME(revlock)(g, #g, __FILE__, __LINE__)
template<typename MutexArg>
using DebugLock = UniqueLock<typename std::remove_reference<typename std::remove_pointer<MutexArg>::type>::type>;
-#define LOCK(cs) DebugLock<decltype(cs)> PASTE2(criticalblock, __COUNTER__)(cs, #cs, __FILE__, __LINE__)
+#define LOCK(cs) DebugLock<decltype(cs)> UNIQUE_NAME(criticalblock)(cs, #cs, __FILE__, __LINE__)
#define LOCK2(cs1, cs2) \
DebugLock<decltype(cs1)> criticalblock1(cs1, #cs1, __FILE__, __LINE__); \
DebugLock<decltype(cs2)> criticalblock2(cs2, #cs2, __FILE__, __LINE__);
diff --git a/src/test/data/key_io_invalid.json b/src/test/data/key_io_invalid.json
index abe07dad24..8f55abfec7 100644
--- a/src/test/data/key_io_invalid.json
+++ b/src/test/data/key_io_invalid.json
@@ -6,207 +6,207 @@
"x"
],
[
- "2v7k5Bb8Lr1MMgTgW6HAf5YHXi6BzpPjHpQ4srD4RSwHYpzXKiXmLAgiLhkXvp3JF5v7nq45EWr"
+ "1GAdfviErV2Ew95FPtZyikz2qGP3gyCB6Hyu94sedAkPpA523m3fQwps9YKUZkKgQckGPKhRsFR"
],
[
- "RAZzCGtMbiUgMiiyrZySrSpdfnQReFXA3r"
+ "37G2kMDLpmWVhimxRdzwNfE8JFvWXnJYnVcXeeGrek2qumdJuK7XArcVVpRtLLjRra3t64BEPF2"
],
[
- "NYamy7tcPQTzoU5iyQojD3sqhiz7zxkvn8"
+ "giymtio7u7oqWtmC9YnvAEKkLF3JQpAdkEFkVJKYrVDfaLbhaDpX1ihfF2vZmya1i61fwLPC3YQ"
],
[
- "geaFG555Ex5nyRf7JjW6Pj2GwZA8KYxtJJLbr1eZhVW75STbYBZeRszy3wg4pkKdF4ez9J4wQiz"
+ "8iVk9nLM3nYwRuwypjy9NK5rsuZH7BbrQRZ1pgcQmvMnjAgRXD"
],
[
- "2Cxmid3c2XQ2zvQ8SA1ha2TKqvqbJS9XFmXRsCneBS3Po7Qqb65z5zNdsoF9AfieXFcpoVPmkmfa"
+ "cPTVQ1hbo4qdoysf6Jx5GthqucNmdfqt6J2pZRFeXv8Ep7Kmjqud"
],
[
- "gaJ7UVge2njVg9tFTetJrtHgruMm7aQDiSAxfHrVEgzK8N2ooagDVmDkdph434xzc4K96Gjyxcs"
+ "cQbR2Ny85XFBzUMx3Ed6HsTLw2pVruSgPvt5AofnBUnhiv86gYeW"
],
[
- "5JN5BEVQPZ3tAiatz1RGXkrJuE3EC6bervMaPb38wTNgEuZCeqp"
+ "2UB3iG3VJbX2TRrMwm6ssWskgvU9VjFBYSqCzwqkrihCwo7mg4mtS4WuGZgxTKuxf5A3EcotYEymz"
],
[
- "3TnFbyUtBRS5rE1KTW81qLVspjJNaB3uu6uuvLjxhZo2DB6PCGh"
+ "cQe12pqwPR6ExtZKfrKf1q4b3CTh1Qi7MwuvMvzs79nWXDvESfBJ"
],
[
- "7UgSZGaMaTc4d2mdEgcGBFiMeS6eMsithGUqvBsKTQdGzD7XQDbMEYo3gojdbXEPbUdFF3CQoK72f"
+ "tc1qeul5g2xfkvdkrhcfmdursv73ad64jnkjl9c40f"
],
[
- "9261wfqQqruNDnBDhbbb4tN9oKA1KpRFHeoYeufyJApVGixyAG4V"
+ "bt1pq65rzej5glw3ra79gav6fqnx4haa0z257qr3mc8cggkefahmgvyseufhc0"
],
[
- "cS824CTUh18scFmYuqt6BgxuRhdR4dEEnCHs3fzBbcyQgbfasHbw"
+ "tb13hty4qmumlwpp6chxjvcyzza4duqgtmxw3xhm3u9ahj4nyhtwz8eq7ynrj4"
],
[
- "tc1q0ywf7wkz6t580n3yemd3ucfw8jxn93tpc6wskt"
+ "bcrt1r2qxpwuge"
],
[
- "bt1pxeeuh96wpm5c6u3kavts2qgwlv6y8um7u7ga6ltlwrhrv7w9vers8lgt3k"
+ "bc10uexgzna2dpfk0vjt35srz6a27ps6m0l89jweznt83n2sqn2fx4hvn9ym5af8wut34sfrqhk3"
],
[
- "tb130lvl2lyugsk2tf3zhwcjjv39dmwt2tt7ytqaexy8edwcuwks6p5scll5kz"
+ "tb1qum6uh0pt4q253qaf520929737v63w5gf"
],
[
- "bcrt1rhsveeudk"
+ "bcrt1q888ryfgxpvl0k7vum8zpyar2u2sexvdhkf38ue37yknmqq0ycrwpl3w48y"
],
[
- "bc10rmfwl8nxdweeyc4sf89t0tn9fv9w6qpyzsnl2r4k48vjqh03qas9asdje0rlr0phru0wqw0p"
+ "bc1qdsuzmn04k2z8vryw8l4dj8m5ygqgnne5n"
],
[
- "tb1qjqnfsuatr54e957xzg9sqk7yqcry9lns"
+ "tb1qlj8es50nc8j8r8xshrjgzmw5azx89efghmw8ju6zcqla0g6xcnrstsjz7k"
],
[
- "bcrt1q8p08mv8echkf3es027u4cdswxlylm3th76ls8v6y4zy4vwsavngpr4e4td"
+ "bcrt1qzwmyj0z924g7fzs5yvnrkc43y76RVyr2lh5t4r"
],
[
- "BC1QNC2H66VLWTWTW52DP0FYUSNU3QQG5VT4V"
+ "bc1qpu6d26mrulzetu4jqhd7rsunv9aqru26f5c4j8"
],
[
- "tb1qgk665m2auw09rc7pqyf7aulcuhmatz9xqtr5mxew7zuysacaascqs9v0vn"
+ "tb1qun6d26ufh77ghny6u5u8cwz9da7qwc6k4wkuceae9tth06eqlw0syupl4w"
],
[
- "bcrt17CAPP7"
+ "bcrt1qj7g2jps453kj9htk9cxyyc2nxe69x4kzzmth7v"
],
[
- "bc1qxmf2d6aerjzam3rur0zufqxqnyqfts5u302s7x"
+ "bc1p702xksx4z3uqf0u2phllxkfe5cgu0adxptqs0uelx0tqt8e885sqryes2l"
],
[
- "tb1qn8x5dnzpexq7nnvrvnhwr9c3wkakpcyu9wwsjzq9pstkwg0t6qhs4l3rv6"
+ "tb1z7gmh0v6pc30z4xum76lmw8w86yswrlmw"
],
[
- "BCRT1Q397G2RNVYRL5LK07CE8NCKHVKP8Z4SC9U0MVH9"
+ "bcrt1sjsrw6nun4h502cr97xmnyyuhkr22q0s6efrgtu"
],
[
- "bc1pgxwyajq0gdn389f69uwn2fw9q0z5c9s063j5dgkdd23ajaud4hpsercr9h"
+ "2UVPFpGYnLHJezFzjUo42our6PMEoozzRdM"
],
[
- "tb1z6mnmp5k542l6yk4ul0mp4rq3yvz44lfm"
+ "2MygHQjE1U33q3LSC53p69YqFjP8PihumJAF"
],
[
- "bcrt17capp7"
+ "KzNbAQ4mexfAxa6RKBzHQqfoTycaeWpv2p"
],
[
- "2D2bqvKseKHdoKjCNvjVULUgmxHu9hjKGwDbPRjTRH59tsHNLeyKwq3vyVBbo9LByY9wiapqjwFY"
+ "2jDPrDfAKihCGPbPD9ztY8TswAia4V8Bc6vx"
],
[
- "2SSjAim4wZpeQRe5zTj1qqS6Li9ttJDaZ3ze"
+ "4VQUNG1hG64QFtaNyQZQWDdwpxB275Pwb3tvyPt2HDxB8Mi2MgH8Tz3AC83YYiz9LydsLNXEZJLHY"
],
[
- "mi9H6MjLwXxy9kxe1x4ToxyLRBsmcZxgVi"
+ "39TKsUQ5QpEL1wowc6GMUqak94ijirPuP69ooV3xsFmiKQX2dau"
],
[
- "VciXoxEitcn88jy197J9n9cpJ1pZahzU3SyWUiHqLgcfjttLEEJz"
+ "2UEJjT3dSdwc8dAo7oedPzznXceXCEsBbDfAvSymqpqDrkZMv7JBEUpLyhkghioYAWC9W4sKysry"
],
[
- "KppmwADGoExPT9Eq5hjRWpWFDbzJyfzHFgsfxBiDHNpVBgWPRNuy"
+ "7VmMEkphxCFSV1y659Th4dkk6x6bJS5eQvbt8rzUYKQyd6ACgwQ4vXHtXKFUwP2kW3XULipnHJdZ7"
],
[
- "TN7EQXMxKffzvHo54yHHu9R4ks9f5gWBW3MMVf5k72zAqrgVK9ys"
+ "tc1qdlapns4zkn03juf2k9xwwpct209suj6mgcd9gh"
],
[
- "92dbrMEYzP5dD5UhQ6maNkCQ4GLG42BM4Gc6XKZzSSMSfosfkkcB"
+ "bt1psa5eptk29c4jc9yumeseat3a0l5e2fpmw635za2p4gpwdnthueysxga9je"
],
[
- "J7VQxPxyzuWEkRstQWpCz2AgysEz1APgnWCEQrFvkN3umAnCrhQF"
+ "tb13w8c43lykfj3lvm9sgp6dsnfjla3d57cm83seykunf0ltxjc9lt2q4efm4d"
],
[
- "tc1qymllj6c96v5qj2504y27ldtner6eh8ldx38t83"
+ "bcrt1rjqr2tdkm"
],
[
- "bt1flep4g"
+ "bc10lyxwnxa70l270e6fcmxr4x7dtgu2yvy7gzkurwxy4zhdvgaqrrn6pfg2flyhqzy5t5se8yu3"
],
[
- "tb13c553hwygcgj48qwmr9f8q0hgdcfklyaye5sxzcpcjnmxv4z506xs90tchn"
+ "TB1QFDFM763VXVSUNZHQLPWC0Q8FG5LJX6ZN"
],
[
- "bcrt1tyddyu"
+ "bcrt1q60chha7wfwlau4kdr4mlvyeyc8mnnh9dhxk05e0hmrxcuhghefj36uwyha"
],
[
- "bc10qssq2mknjqf0glwe2f3587wc4jysvs3f8s6chysae6hcl6fxzdm4wxyyscrl5k9f5qmnf05a"
+ "bc1gmk9yu"
],
[
- "tb1q425lmgvxdgtyl2m6xuu2pc354y4fvgg8"
+ "tb1ly0q7p"
],
[
- "bcrt1q9wp8e5d2u3u4g0pll0cy7smeeuqezdun9xl439n3p2gg4fvgfvk3hu52hj"
+ "bcrt1qdwttaw38uf42wxw40kwk3u8nguyTQH3hx6jmqp"
],
[
- "bc1qrz5acazpue8vl4zsaxn8fxtmeuqmyjkq3"
+ "bc1qtsvlht6730n04f2mpaj5vv8hrledn5n5ug8c79"
],
[
- "tb1qkeuglpgmnex9tv3fr7htzfrh3rwrk23r52rx9halxzmv9fr85lwq0fwhmp"
+ "tb1dclvmr"
],
[
- "bcrt1qd0t2wrhl7s57z99rsyaekpq0dyjcQRSSmz80r4"
+ "bcrt1q3fqvctqu48wsvggrt09vj0yk2gzzcscdp4h98u"
],
[
- "BC1QXLFDUCGX90T3E53PQCNKJ2PK25MSF3VLPMVY6T"
+ "bc1prklpq7tjcawg89cmwwqr3u5apwav36xa4zz56ady7crsllm6mpnqts7p86"
],
[
- "tb1qmycg4zszgnk34vaurx3cu8wpvteg9h40yq6cp52gt26gjel03t3su3x3xu"
+ "tb1zkm58zyhxz3ffkfgsyprflg543slsl4c4"
],
[
- "bcrt1q9hy58r4fnuxqzdqndpmq9pptc9nt2dw3rczf5e"
+ "bcrt1snzr5kaypnfhpnjanrhd20fhqcjxm3hfh7dw9fu"
],
[
- "BC1PA7682NAY6JQSLUWAJYTC0ERWTMW7A4RPWLNTUS32LCXWLHVKKKTQ2UL8CG"
+ "2GgnYKqBGuA2Mm5GnrPsMTZR81xPhNtgMYoFUZngZGiobhCuUpCaTriUHRcgFreEekNdPAR17q8d"
],
[
- "tb1z850dpxnwz2fzae5h2myatj4yvu6rq5xq"
+ "AZEah8d1EK362okRBS66e8SvdtYkrE8tsX"
],
[
- "bcrt1sp525pzjsmpqvcrawjreww36e9keg876skjvpwt"
+ "gep8xr77FyPW6zYP15RiV9W8nL6w2HyHB16cUDakfyDceMA6ZzUdhJjk2LPuLYHnLkBqkRTTi6z"
],
[
- "xcAvW5jurCpzSpLxBKEhCewCgwwuGhqJnC"
+ "2NDNP7GY59tTJPZTpbkprhM9SR99Nn5rUs7"
],
[
- "2Cvv8yp9kXbQt8EKh6Yma95yJ1uwYF9YKXuVhGJyu3dHGVsb2AVpTC62TFACZZ3KDNrALxR2CVNs"
+ "2Csgzy2T287YAjeU5tFtt1nPshBZAUFQi4WtgaWyZGKSBNnKXHy2Tmxo8QK4Mfdds977ShcDWC5o"
],
[
- "niUuL46hCuEVvkAzZKHvD746qbmLmzip9Pv3F6UZV14JxzEXBnTkVxCT4URapChJG6qAEgsZs6G"
+ "Kwjk3Vy6sdXMQDGWJzaWmqFxUNtWZCX1q4F4Kpt8jNNUoWJUUaTY"
],
[
- "2UHHgGfiipzvB8Eumnmvq6SowvrMJimjT3NwwG1839XEiUfwtpSdkUrseNsQuagXv21ce7aZu6yo"
+ "Svj8kk98bAS9V4L2crmxakbhmnPm3cJ1tJ4Je4yVzDreU8eSTFURS1SPYv5oWEQD8Q9VBDvx5uF"
],
[
- "8u9djKu4u6o3bsgeR4BKNnLK3akpo64FYzDAmA9239wKeshgF97"
+ "KNYsv6v9GtkGeD4WdQnBEJCrPKQm91PTxAbCfXr66LEd4JDmhPWC"
],
[
- "TC1QPAARXSLVMXHVRR0474LZXQYZWLGFZYPSFVL9E4"
+ "2UJ2H2xvAeXmFKfQwMyDoSdQTTPFMNCT3SsoUafBWKzoGP3NsUK1buEgQZG38viyD53jgMdpqfT7"
],
[
- "bt1pakek0n2267t9yaksxaczgr2syhv9y3xkx0wnsdwchfa6xkmjtvuqg3kgyr"
+ "6aLMfayKF4TW4ecn5SEc8FExpyJA2peKxYRGZhes6tQ4NTTzuGy"
],
[
- "tb13h83rtwq62udrhwpn87uely7cyxcjrj0azz6a4r3n9s87x5uj98ys6ufp83"
+ "7VP4FmcebU2thJns9MnXde7LWfuqR5vMizrAuUoq2GcJjzTyA4RHFcPVdZL8PLg1SbpSFdJrvLXoY5"
],
[
- "bcrt1rk5vw5qf2"
+ "tc1q5qdvt99uc92jyz663dtdpfpv6nr67ahmgwcpq2"
],
[
- "bc10d3rmtg62h747en5j6fju5g5qyvsransrkty6ghh96pu647wumctejlsngh9pf26cysrys2x2"
+ "bt1peu3ppd7x796sjjenp09r8cs22rhylqm9lhggk72qp8q22vzft0wq2a0x6j"
],
[
- "tb1qajuy2cdwqgmrzc7la85al5cwcq374tsp"
+ "tb1323z3lnz7dl3kd0nsuh6xy4he9almzl67anxgg3xdzkaxc9rwntlqdhdzd7"
],
[
- "bcrt1q3udxvj6x20chqh723mn064mzz65yr56ef00xk8czvu3jnx04ydapzk02s5"
+ "bcrt1r2gc42sky"
],
[
- "bc1qule2szwzyaq4qy0s3aa4mauucyqt6fewe"
+ "bc10fd889x4hd54tqu2ewg9t4hhft2wl7m6x50av4uswzw46xe6as0xmltfg7vrjfkvm459vld7w"
],
[
- "tb1ql0qny5vg9gh5tyzke6dw36px5ulkrp24x53x0pl2t5lpwrtejw3s2seej2"
+ "TB1QZY7V0F2AT3308YGGNGN66ULJTCN3RY6F"
],
[
- "bcrt17CAPP7"
+ "bcrt1qjg3cwht92znyw0l4r5rtctmls337nrc7g0ry9drjxmlecjd3atl3fake7c"
],
[
- "bc1qtvm6davyf725wfedc2d5mrgfewqgcrce8gjrpl"
+ "bc1qmgf8xt8xkecl79k04mma3lz34gqep7hg4"
],
[
- "tb1q5acjgtqrrw3an0dzavxxxzlex8k7aukjzjk9v2u4rmfdqxjphcyq7ge97e"
+ "TB1Q3F9WGNXE9ZMTTMDN5VKVKHYZ8Y0LCV72YV7V5LSXTJXEYHNHEHASLYL0TZ"
]
]
diff --git a/src/test/data/key_io_valid.json b/src/test/data/key_io_valid.json
index 5dee44c04b..c051f8b76b 100644
--- a/src/test/data/key_io_valid.json
+++ b/src/test/data/key_io_valid.json
@@ -1,71 +1,71 @@
[
[
- "1BShJZ8A5q53oJJfMJoEF1gfZCWdZqZwwD",
- "76a914728d4cc27d19707b0197cfcd7c412d43287864b588ac",
+ "1FsSia9rv4NeEwvJ2GvXrX7LyxYspbN2mo",
+ "76a914a31c06bd463e3923bc1aadbde48b16976c08071788ac",
{
"chain": "main",
"isPrivkey": false
}
],
[
- "3L1YkZjdeNSqaZcNKZFXQfyokx3zVYm7r6",
- "a914c8f37c3cc21561296ad81f4bec6b5de10ebc185187",
+ "36j4NfKv6Akva9amjWrLG6MuSQym1GuEmm",
+ "a914373b819a068f32b7a6b38b6b38729647cfde01c287",
{
"chain": "main",
"isPrivkey": false
}
],
[
- "mhJuoGLgnJC8gdBgBzEigsoyG4omQXejPT",
- "76a91413a92d1998e081354d36c13ce0c9dc04b865d40a88ac",
+ "mzK2FFDEhxqHcmrJw1ysqFkVyhUULo45hZ",
+ "76a914ce28b26c57472737f5c3561a1761185bd8589a4388ac",
{
"chain": "test",
"isPrivkey": false
}
],
[
- "2N5VpzKEuYvZJbmg6eUNGnfrrD1ir92FWGu",
- "a91486648cc2faaf05660e72c04c7a837bcc3e986f1787",
+ "2NC2hEhe28ULKAJkW5MjZ3jtTMJdvXmByvK",
+ "a914ce0bba75891ff9ec60148d4bd4a09ee2dc5c933187",
{
"chain": "test",
"isPrivkey": false
}
],
[
- "mtQueCtmAnP3E4aBHXCiFNEQAuPaLMuQNy",
- "76a9148d74ecd86c845baf9c6d4484d2d00e731b79e34788ac",
+ "mww4LvqtTMKvmeQvizPz2EQv26xTneWrbg",
+ "76a914b4110ba93ac54afc14da3bdd19614774a2d55d2988ac",
{
"chain": "signet",
"isPrivkey": false
}
],
[
- "2NEvWRTHjh89gV52fkperFtwzoFWQiQmiCh",
- "a914edc895152c67ccff0ba620bcc373b789ec68266f87",
+ "2N1r7aC69VHeE7yQJPDLi9T1PYq4wnwvjuT",
+ "a9145e5a35ab44b3efaea5129ba22b88ba3e2976614587",
{
"chain": "signet",
"isPrivkey": false
}
],
[
- "mngdx94qJFhSf7A7SAEgQSC9fQJuapujJp",
- "76a9144e9dba545455a80ce94c343d1cac9dec62cbf22288ac",
+ "n4fajahJrAuKbN7uNsKjLjQkz9Qn5ewJXQ",
+ "76a914fdeca3b08e38af53d7c4c60e3ad208ce5066441088ac",
{
"chain": "regtest",
"isPrivkey": false
}
],
[
- "2NBzRN3pV56k3JUvSHifaHyzjGHv7ZS9FZZ",
- "a914cd9da5642451273e5b6d088854cc1fad4a8d442187",
+ "2MxFajLApXpYk4VodBSZSt7rw8y4ryABkfA",
+ "a91436e9f191e0b75036a77f65e2eaa4752443233fbe87",
{
"chain": "regtest",
"isPrivkey": false
}
],
[
- "5KcrFZvJ2p4dM6QVUPu53cKXcCfozA1PJLHm1mNAxkDYhgThLu4",
- "ed6c796e2f62377410766214f55aa81ac9a6590ad7ed57c509c983bf648409ac",
+ "5JuW2AMDYu4xVwRG9DZW18VbzQrGcd5RCgb99sS6ehJsNQXu5b9",
+ "8f8943bf956de595665c38ffff23827e17c10cdc1c27a028caae6c9810626198",
{
"chain": "main",
"isCompressed": false,
@@ -73,8 +73,8 @@
}
],
[
- "L195WBrf2G3nCnun4CLxrb8XKk9LbCqH43THh4n4QrL5SzRzpq9j",
- "74f76c106e38d20514a99a86e4fe3bb28319e7dd2ad21dbc170cbb516a5358fa",
+ "L5nJeqKmpHp4P7F8ZYyjwc5a7P4d8EabuGAzfGJk7yC1BJyzNaEd",
+ "ff778740f88ddcf102aeb81daee289c044c4a4571c4b6f287400f4b8e0b843f8",
{
"chain": "main",
"isCompressed": true,
@@ -82,8 +82,8 @@
}
],
[
- "92z6HnMQR4tWqjfVA3UaUN5EuUMgoVMdCa5rZFYZfmgyD7wxYCw",
- "b8511e1d74549e305517d48a1d394d1be2cfa5d0f3c0d83f9f450316ffa01276",
+ "92ZdE5HoLafywnTBbzPxbvRmp75pSfzvdU3XaZGh1cToipgdHVh",
+ "80c32d81e91bdea04cd7a3819b32275fc3298af4c7ec87eb0099527d041ced5c",
{
"chain": "test",
"isCompressed": false,
@@ -91,8 +91,8 @@
}
],
[
- "cTPnaF52x4w4Tq6afPxRHux3wbYb86thS7S45A7r3oZc1AHTQ6Qm",
- "ad68c48d337181da125de9061933ececcdf7d917631af7d34f7e38082bff9a11",
+ "cV83kKisF3RQSvXbUCm9ox3kaz5JjEUBWcx8tNydfGJcyeUxuH47",
+ "e0fcd4ce4e3d0e3de091f21415bb7cd011fac288c42020a879f28c2a4387df9b",
{
"chain": "test",
"isCompressed": true,
@@ -100,8 +100,8 @@
}
],
[
- "924U35yFcYkxe2JXGmuhSRVaShGyhRDZx1ysPmw1sAHuszGMoxq",
- "3e8dfaf78d4f02b11d0b645648a4f3080d71d0d068979c47f7255c9a29eee01d",
+ "92QuSnywrhsV7WPZChTgSQA23uSmj9MCEEno1eRBDG9sg8M29cX",
+ "6cf636ed8ac1bab033b64f66feaba65f70e684731e3f39105605968d3a963801",
{
"chain": "signet",
"isCompressed": false,
@@ -109,8 +109,8 @@
}
],
[
- "cRy1qCf2LUesGPQagTkYwk2V3PyN2KCPKgxeg6k6KoJPzH7nrVjw",
- "82d4187690d6b59bcffda27dae52f2ecb87313cfc0904e0b674a27d906a65fde",
+ "cND53Dhp8eCZqG2ghe8YhSCGesXZ8fE5PGD1khrqNvEi4RBoXhEK",
+ "12b5a10f3a11e708dc5412833c47ab7c368a21b9efe19293793ec879ce683018",
{
"chain": "signet",
"isCompressed": true,
@@ -118,8 +118,8 @@
}
],
[
- "932NTcHK35Apf2C3K9Zv1ZdeZEmB1x7ZT2Ju3SjoEY6pUgUpT7H",
- "bd7dba24df9e003e145ae9b4862776413a0bb6fa5b4e42753397f2d9536e58a9",
+ "91mn1wYKEB1zyof1VFm8tMtocZx1oBrKKRCu9GCpgZvPmBLEJjp",
+ "18a86e5a6c6977ddba0daca7fba5190f67ba56ccdc1b3f31308972236c2e4776",
{
"chain": "regtest",
"isCompressed": false,
@@ -127,8 +127,8 @@
}
],
[
- "cNa75orYQ2oos52zCnMaS5PG6XbNZKc5LpGxTHacrxwWeX4WAK3E",
- "1d87e3c58b08766fea03598380ec8d59f8c88d5392bf683ab1088bd4caf073ee",
+ "cPisAUdLvqqAr6MYtXnrWvgvyUAwuNyuTvZkDGw6miPhZdaiSDNH",
+ "3fdfec1371cedcdb8c190ca6ff8ad603f817edc0d93c2a687c7b36dd66e70f2a",
{
"chain": "regtest",
"isCompressed": true,
@@ -136,8 +136,8 @@
}
],
[
- "bc1q5cuatynjmk4szh40mmunszfzh7zrc5xm9w8ccy",
- "0014a639d59272ddab015eafdef9380922bf843c50db",
+ "bc1qvyq0cc6rahyvsazfdje0twl7ez82ndmuac2lhv",
+ "00146100fc6343edc8c874496cb2f5bbfec88ea9b77c",
{
"chain": "main",
"isPrivkey": false,
@@ -145,8 +145,8 @@
}
],
[
- "bc1qkw7lz3ahms6e0ajv27mzh7g62tchjpmve4afc29u7w49tddydy2syv0087",
- "0020b3bdf147b7dc3597f64c57b62bf91a52f179076ccd7a9c28bcf3aa55b5a46915",
+ "bc1qyucykdlhp62tezs0hagqury402qwhk589q80tqs5myh3rxq34nwqhkdhv7",
+ "002027304b37f70e94bc8a0fbf500e0c957a80ebda87280ef58214d92f119811acdc",
{
"chain": "main",
"isPrivkey": false,
@@ -154,8 +154,8 @@
}
],
[
- "bc1p5rgvqejqh9dh37t9g94dd9cm8vtqns7dndgj423egwggsggcdzmsspvr7j",
- "5120a0d0c06640b95b78f965416ad6971b3b1609c3cd9b512aaa39439088211868b7",
+ "bc1p83n3au0rjylefxq2nc2xh2y4jzz4pm6zxj4mw5pagdjjr2a9f36s6jjnnu",
+ "51203c671ef1e3913f94980a9e146ba895908550ef4234abb7503d436521aba54c75",
{
"chain": "main",
"isPrivkey": false,
@@ -163,8 +163,8 @@
}
],
[
- "bc1zr4pq63udck",
- "52021d42",
+ "bc1z2rksukkjr8",
+ "520250ed",
{
"chain": "main",
"isPrivkey": false,
@@ -172,8 +172,8 @@
}
],
[
- "tb1q74fxwnvhsue0l8wremgq66xzvn48jlc5zthsvz",
- "0014f552674d978732ff9dc3ced00d68c264ea797f14",
+ "tb1qcrh3yqn4nlleplcez2yndq2ry8h9ncg3qh7n54",
+ "0014c0ef1202759fff90ff19128936814321ee59e111",
{
"chain": "test",
"isPrivkey": false,
@@ -181,8 +181,8 @@
}
],
[
- "tb1qpt7cqgq8ukv92dcraun9c3n0s3aswrt62vtv8nqmkfpa2tjfghesv9ln74",
- "00200afd802007e598553703ef265c466f847b070d7a5316c3cc1bb243d52e4945f3",
+ "tb1quyl9ujpgwr2chdzdnnalen48sup245vdfnh2jxhsuq3yx80rrwlq5hqfe4",
+ "0020e13e5e482870d58bb44d9cfbfccea78702aad18d4ceea91af0e022431de31bbe",
{
"chain": "test",
"isPrivkey": false,
@@ -190,8 +190,8 @@
}
],
[
- "tb1ph9v3e8nxct57hknlkhkz75p5pnxnkn05cw8ewpxu6tek56g29xgqydzfu7",
- "5120b9591c9e66c2e9ebda7fb5ec2f50340ccd3b4df4c38f9704dcd2f36a690a2990",
+ "tb1p35n52jy6xkm4wd905tdy8qtagrn73kqdz73xe4zxpvq9t3fp50aqk3s6gz",
+ "51208d2745489a35b75734afa2da43817d40e7e8d80d17a26cd4460b0055c521a3fa",
{
"chain": "test",
"isPrivkey": false,
@@ -199,8 +199,8 @@
}
],
[
- "tb1ray6e8gxfx49ers6c4c70l3c8lsxtcmlx",
- "5310e93593a0c9354b91c358ae3cffc707fc",
+ "tb1rgv5m6uvdk3kc7qsuz0c79v88ycr5w4wa",
+ "53104329bd718db46d8f021c13f1e2b0e726",
{
"chain": "test",
"isPrivkey": false,
@@ -208,8 +208,8 @@
}
],
[
- "tb1q0sqzfp3zj42u0perxr6jahhu4y03uw4dypk6sc",
- "00147c002486229555c7872330f52edefca91f1e3aad",
+ "tb1q3vya2h5435jkugq2few7dmktlrwq4ejmfaw7kr",
+ "00148b09d55e958d256e200a4e5de6eecbf8dc0ae65b",
{
"chain": "signet",
"isPrivkey": false,
@@ -217,8 +217,8 @@
}
],
[
- "tb1q9jv4qnawnuevqaeadn47gkq05ev78m4qg3zqejykdr9u0cm7yutq6gu5dj",
- "00202c99504fae9f32c0773d6cebe4580fa659e3eea044440cc89668cbc7e37e2716",
+ "tb1qxkhrl2s6ttrclckldruea0e8anhrehffl8xv7t0pdyrzm08v2hyqy408nf",
+ "002035ae3faa1a5ac78fe2df68f99ebf27ecee3cdd29f9cccf2de169062dbcec55c8",
{
"chain": "signet",
"isPrivkey": false,
@@ -226,8 +226,8 @@
}
],
[
- "tb1pxqf7d825wjtcftj7uep8w24jq3tz8vudfaqj20rns8ahqya56gcs92eqtu",
- "51203013e69d54749784ae5ee642772ab2045623b38d4f41253c7381fb7013b4d231",
+ "tb1pae5um27ahn8n73pgexe3kcwlp8dhswpn684h2k2w6t9a7w3eq65qephd5y",
+ "5120ee69cdabddbccf3f4428c9b31b61df09db783833d1eb75594ed2cbdf3a3906a8",
{
"chain": "signet",
"isPrivkey": false,
@@ -235,8 +235,8 @@
}
],
[
- "tb1rsrzkyvu2rt0dcgexajtazlw5nft4j7494ay396q6auw9375wxsrsgag884",
- "532080c562338a1adedc2326ec97d17dd49a57597aa5af4912e81aef1c58fa8e3407",
+ "tb1rx9n9g37az8mu236e5jpxdt0m67y4fuq8rhs0ss3djnm0kscfrwvq0ntlyg",
+ "532031665447dd11f7c54759a48266adfbd78954f0071de0f8422d94f6fb43091b98",
{
"chain": "signet",
"isPrivkey": false,
@@ -244,8 +244,8 @@
}
],
[
- "bcrt1qwf52dt9y2sv0f7fwkcpmtfjf74d4np2saeljt6",
- "00147268a6aca45418f4f92eb603b5a649f55b598550",
+ "bcrt1qdavt4j2sd7dlhqsavtnfxvzppw6k7qy97tmnu9",
+ "00146f58bac9506f9bfb821d62e69330410bb56f0085",
{
"chain": "regtest",
"isPrivkey": false,
@@ -253,8 +253,8 @@
}
],
[
- "bcrt1q0lma84unycxl4n96etffthqlf7y5axyp4fxf64kmhymvw8l6pwfs39futd",
- "00207ff7d3d793260dfaccbacad295dc1f4f894e9881aa4c9d56dbb936c71ffa0b93",
+ "bcrt1qan8gntac7z7me2ejt4hpru42ad2f759fmy0m3ejvs98656znv7eqga4uhv",
+ "0020ecce89afb8f0bdbcab325d6e11f2aaeb549f50a9d91fb8e64c814faa685367b2",
{
"chain": "regtest",
"isPrivkey": false,
@@ -262,8 +262,8 @@
}
],
[
- "bcrt1p3xat2ryucc2v0adrktqnavfzttvezrr27ngltsa2726p2ehvxz4se722v2",
- "512089bab50c9cc614c7f5a3b2c13eb1225ad9910c6af4d1f5c3aaf2b41566ec30ab",
+ "bcrt1pfwxjqvtt4tcxrtdluukfmy2dv7xd2qzdfy6kajv5nwn4yam3wxkq3553uh",
+ "51204b8d20316baaf061adbfe72c9d914d678cd5004d49356ec9949ba752777171ac",
{
"chain": "regtest",
"isPrivkey": false,
@@ -271,8 +271,8 @@
}
],
[
- "bcrt1saflydw6e26xhp29euhy5jke5jjqyywk3wvtc9ulgw9dvxyuqy9hdnxthyw755c7ldavy7u",
- "6028ea7e46bb59568d70a8b9e5c9495b349480423ad1731782f3e8715ac31380216ed9997723bd4a63df",
+ "bcrt1sx6p8njlx7h9mc2agz4yg82dzne23050ncq72cneeecez2pst8mahn8xecsf8g6hzx94420",
+ "6028368279cbe6f5cbbc2ba8154883a9a29e5517d1f3c03cac4f39ce3225060b3efb799cd9c412746ae2",
{
"chain": "regtest",
"isPrivkey": false,
@@ -280,72 +280,72 @@
}
],
[
- "16y3Q1XVRZqMR9T1XL1FkvNtD2E1bXBuYa",
- "76a9144171ec673aeb9fcf42af6094a3c82207e3b9a78188ac",
+ "1FjL87pn8ky6Vbavd1ZHeChRXtoxwRGCRd",
+ "76a914a19331b7b2627e663e25a7b001e4c0dcc5e21bc788ac",
{
"chain": "main",
"isPrivkey": false
}
],
[
- "3CmZZnAiHVQgiAKSakf864oJMxN2BP1eLC",
- "a914798575fc1041b9440c4e63c28e57e597d00b7e4387",
+ "3BZECeAH8gSKkjrTx8PwMrNQBLG18yHpvf",
+ "a9146c382dcdf5b284760c8e3fead91f7422cd76aa8787",
{
"chain": "main",
"isPrivkey": false
}
],
[
- "mtCB3SoBo7EYUv8j54kUubGY4x3aJPY8nk",
- "76a9148b0c5f9ee714e0d1d24642ad63d9d5f398d9b56588ac",
+ "n4YNbYuFdPwFrxSP8sjHFbAhUbLMUiY9jE",
+ "76a914fc8f9851f3c1e4719cd0b8e4816dd4e88c72e52888ac",
{
"chain": "test",
"isPrivkey": false
}
],
[
- "2N5ymzzKpx6EdUR4UdMZ7t9hcuwqtpHwgw5",
- "a9148badb3c3b5c0d39f906f7618e0018b7eae4baf7387",
+ "2NAeQVZayzVFAtgeC3iYJsjpjWDmsDph71A",
+ "a914bedc797342c03fd7a346c4c7857ca03d467013b687",
{
"chain": "test",
"isPrivkey": false
}
],
[
- "myXnpYbub28zgiJupDdZSWZtDbjcyfJVby",
- "76a914c59ac57661b57daadd7c0caf7318c14f54c6c0fa88ac",
+ "mnCBpkNMJEJLehgdEkzSo2eioniyJMxLpZ",
+ "76a914493c455551e48a1423263b62b127b436106a685488ac",
{
"chain": "signet",
"isPrivkey": false
}
],
[
- "2MtLg8jS5jSXm9evMzTtvpLjy26dBmjFEoT",
- "a9140c0007e89cea625d3bf9543baa5a470bb7e5b67287",
+ "2N5sNHomeNJDZv67AcFx9ES7FBZY4jx9KDA",
+ "a9148a776a0f34d56b63e7c595f2b205dbe1c393617a87",
{
"chain": "signet",
"isPrivkey": false
}
],
[
- "mzCyqdf2UNGdpgkD9NBgLcxdwXRg1i9buY",
- "76a914cd04311bdd1ef9c5c24e41930e032aade82a863a88ac",
+ "mfhE6jAUwjUDNZhaX1PAsDTKfneQF2Nshc",
+ "76a91401f15a4cc063dae4f4d56b89bfbc8bcc9ae5387c88ac",
{
"chain": "regtest",
"isPrivkey": false
}
],
[
- "2N3zGiwFku2vQjYnAqXv5Qu2ztfYRhh7tbF",
- "a91475d56d75c88e704d6c72fbe84ac1505abf736b4087",
+ "2MxNm1VHyVU4RuP3u1c1v5aQLk2dQjwy1Qk",
+ "a91438456f7c076356abadcc67b92ad777eb20fb9f8887",
{
"chain": "regtest",
"isPrivkey": false
}
],
[
- "5JUHCgyxNSHg64wwju72eNsG6ajqo4Z2fHHw9iLDLfh69rSiL7w",
- "5644d06d88855dacf3192a31df8f4acd8e4c155c52a86d2c1fa48303f5cff053",
+ "5HsL2nZuEebU5nM3RxNVQD9GcAnvNMahqQskf4fkqHe54zwd14e",
+ "06e8649790a90615a46d22dd762e0c42615336745356c2e16147c0f3d46b40d5",
{
"chain": "main",
"isCompressed": false,
@@ -353,8 +353,8 @@
}
],
[
- "L2kZaexG69VSriMe9T2m1jkS86iPe3xNbjcdfakRC1PHe7ay78Ji",
- "a50ee94aefcabf5a5d7c85be5b3844dee03c5604861dbfc77fe388c91e5a30f8",
+ "KwuVvu6hsuEMHrfFWJQV64tRrWX3QzqHH18JuAHYqYV6dqBvNKxd",
+ "147804bf8a0dfff35939a611c7f5a60ac107f33f33d6059f273d2079ab1d90f2",
{
"chain": "main",
"isCompressed": true,
@@ -362,8 +362,8 @@
}
],
[
- "927JwT1ViCr5TD2ZX8CsMNhg17dXmou5xu4y2KiH54zD7i34UJq",
- "4502a54c0026b0150281d41f40860d1e23870c63cdc32645bbed688f2ee41f64",
+ "921M1RNxghFcsVGqAJksQVbSgx36Yz4u6vebfz1wDujNvgNt93B",
+ "3777b341c45e2a9b9bf6bfb71dc7d129f64f1b9406ed4f93ade8f56065f1b732",
{
"chain": "test",
"isCompressed": false,
@@ -371,8 +371,8 @@
}
],
[
- "cTpGGNPVy2Eagawohbr4aGtRJzpLnjxGsGYh9DUcBM45f3KdKGF6",
- "ba005a0cb39587aab00bd54c848b59e8adaed47403228567ddc739c2a344ff59",
+ "cNEnbfF2fcxmmCLWqMAaq6fxJvVkwMbyU3kCbpQznz4Z1j6TZDGb",
+ "1397b0d4a03e1ab2c54dd9af99ce1ecbfb90c80a58886da95e1181a55703d96b",
{
"chain": "test",
"isCompressed": true,
@@ -380,8 +380,8 @@
}
],
[
- "932PLCLA19yPNqV67qwHBSGjxi82LVzWBF7josL9ab4Q1kxgPGF",
- "bd8677e076eb39770bf7e9f9e8d3f2cf257effab9b4c220fd3439ccfc208c984",
+ "93BcpCMKPmFCuY8bqS4k3HFrhJ1Afxi4uSsEeJFvX86GYW7PC7W",
+ "d27d1b6ef55ca2e4d475b5276f2dbb85f7a6459dceeb89c67b776fd3bb974452",
{
"chain": "signet",
"isCompressed": false,
@@ -389,8 +389,8 @@
}
],
[
- "cViUpEy8URSsLjUvxwL7cEuNgCVqM7oKBzd1ZPbA4khcQsQJuj1j",
- "f2b36ade8393e29dc71e52cb75ba1109ba210203cd7d0a5ae881ad6846516203",
+ "cUtwbyxoL1owPxUafgH2meEpydeywjhnTYv2mJaFHHchz39AaEgy",
+ "da3ed4ef1647e1733ec076919cab6156077ed9532e7c365acc425747e198b3e1",
{
"chain": "signet",
"isCompressed": true,
@@ -398,8 +398,8 @@
}
],
[
- "92jddDjJCVDmJtgvBHQ9i58PMash8kwsYhRdNo22ea2MYPXdCBE",
- "977bf8686f1bcad28f86c4c14afbd33215746bd19203647bf7ff9c6fddc9cc87",
+ "927zPWny2SiNaUmHF5NnGQXQWDwbByfFzXGgu88j91ZoutSosvE",
+ "468e0284f230153db8687d8ec23db079a5b67d72ca04174b3867b13e4ea9945e",
{
"chain": "regtest",
"isCompressed": false,
@@ -407,8 +407,8 @@
}
],
[
- "cVwAuMoUqRo399X7vXzuzQyPEvZJMXM8c82zHzRkFCxPCSGx8A6y",
- "f93acbbce02b8cb9ddca3fad495441e324cc01eb640b0a7b4c9f0e31644c822a",
+ "cRez45VGSp5EXNqm89K3NJJPSKKapJg5Kbw3atxr2337x2gtgYed",
+ "798d87586cffbe8c545ab374454e403b1eb831501ebe89f3c3b02f3137bd7b46",
{
"chain": "regtest",
"isCompressed": true,
@@ -416,8 +416,8 @@
}
],
[
- "bc1qz377zwe5awr68dnggengqx9vrjt05k98q3sw2n",
- "0014147de13b34eb87a3b66846668018ac1c96fa58a7",
+ "bc1qhxt04s5xnpy0kxw4x99n5hpdf5pmtzpqs52es2",
+ "0014b996fac2869848fb19d5314b3a5c2d4d03b58820",
{
"chain": "main",
"isPrivkey": false,
@@ -425,8 +425,8 @@
}
],
[
- "bc1qkmhskpdzg8kdkfywhu09kswwn9qan9vnkrf6mk40jvnr06s6sz5ssf82ya",
- "0020b6ef0b05a241ecdb248ebf1e5b41ce9941d99593b0d3addaaf932637ea1a80a9",
+ "bc1qgc9ljrvdf2e0zg9rmmq86xklqwfys7r6wptjlacdgrcdc7sa6ggqu4rrxf",
+ "0020460bf90d8d4ab2f120a3dec07d1adf039248787a70572ff70d40f0dc7a1dd210",
{
"chain": "main",
"isPrivkey": false,
@@ -434,8 +434,8 @@
}
],
[
- "bc1ps8cndas60cntk8x79sg9f5e5jz7x050z8agyugln2ukkks23rryqpejzkc",
- "512081f136f61a7e26bb1cde2c1054d33490bc67d1e23f504e23f3572d6b415118c8",
+ "bc1pve739yap4uxjvfk0jrey69078u0gasm2nwvv483ec6zkzulgw9xqu4w9fd",
+ "5120667d1293a1af0d2626cf90f24d15fe3f1e8ec36a9b98ca9e39c6856173e8714c",
{
"chain": "main",
"isPrivkey": false,
@@ -443,8 +443,8 @@
}
],
[
- "bc1zn4tsczge9l",
- "52029d57",
+ "bc1zmjtqxkzs89",
+ "5202dc96",
{
"chain": "main",
"isPrivkey": false,
@@ -452,8 +452,8 @@
}
],
[
- "tb1q6xw0wwd9n9d7ge87dryz4vm5vtahzhvz6yett3",
- "0014d19cf739a5995be464fe68c82ab37462fb715d82",
+ "tb1ql4k5ayv7p7w0t0ge7tpntgpkgw53g2payxkszr",
+ "0014fd6d4e919e0f9cf5bd19f2c335a03643a914283d",
{
"chain": "test",
"isPrivkey": false,
@@ -461,8 +461,8 @@
}
],
[
- "tb1qwn9zq9fu5uk35ykpgsc7rz4uawy4yh0r5m5er26768h5ur50su3qj6evun",
- "002074ca20153ca72d1a12c14431e18abceb89525de3a6e991ab5ed1ef4e0e8f8722",
+ "tb1q9jx3x2qqdpempxrcfgyrkjd5fzeacaqj4ua7cs7fe2sfd2wdaueq5wn26y",
+ "00202c8d1328006873b098784a083b49b448b3dc7412af3bec43c9caa096a9cdef32",
{
"chain": "test",
"isPrivkey": false,
@@ -470,8 +470,8 @@
}
],
[
- "tb1pmcdc5d8gr92rtemfsnhpeqanvs0nr82upn5dktxluz9n0qcv34lqxke0wq",
- "5120de1b8a34e8195435e76984ee1c83b3641f319d5c0ce8db2cdfe08b37830c8d7e",
+ "tb1pdswckwd9ym5yf5eyzg8j4jjwnzla8y0tf9cp7aasfkek0u29sz9qfr00yf",
+ "51206c1d8b39a526e844d324120f2aca4e98bfd391eb49701f77b04db367f145808a",
{
"chain": "test",
"isPrivkey": false,
@@ -479,8 +479,8 @@
}
],
[
- "tb1rgxjvtfzp0xczz6dlzqv8d5cmuykk4qkk",
- "531041a4c5a44179b02169bf101876d31be1",
+ "tb1r0ecpfxg2udhtc556gqrpwwhk4sw3f0kc",
+ "53107e7014990ae36ebc529a4006173af6ac",
{
"chain": "test",
"isPrivkey": false,
@@ -488,8 +488,8 @@
}
],
[
- "tb1qa9dlyt6fydestul4y4wh72yshh044w32np8etk",
- "0014e95bf22f49237305f3f5255d7f2890bddf5aba2a",
+ "tb1q6mwf89hnqhlu8txjgjfs4s7p93ugffn3k062ll",
+ "0014d6dc9396f305ffc3acd244930ac3c12c7884a671",
{
"chain": "signet",
"isPrivkey": false,
@@ -497,8 +497,8 @@
}
],
[
- "tb1qu4p26n0033720xm0rjgkds5ehdwf039k2fgv75um5krrvfhrrj7qckl9r2",
- "0020e542ad4def8c7ca79b6f1c9166c299bb5c97c4b65250cf539ba5863626e31cbc",
+ "tb1qafrjalu4d73dql0czau9j6z422434kef235mzljf48ckd5xz3sys09jm97",
+ "0020ea472eff956fa2d07df8177859685552ab1adb295469b17e49a9f166d0c28c09",
{
"chain": "signet",
"isPrivkey": false,
@@ -506,8 +506,8 @@
}
],
[
- "tb1pjyukm4n4flwd0ey3lrl06c9kalr60ggmlkcxq2rhhxmy4lvkmkpqexdzqy",
- "512091396dd6754fdcd7e491f8fefd60b6efc7a7a11bfdb0602877b9b64afd96dd82",
+ "tb1pwst9qszjrhuv2e7as0flcq9gm698v6gdxzz9e87p07s8rssdx3zqklm3vf",
+ "512074165040521df8c567dd83d3fc00a8de8a76690d30845c9fc17fa071c20d3444",
{
"chain": "signet",
"isPrivkey": false,
@@ -515,8 +515,8 @@
}
],
[
- "tb1r4k75s5syvewsvxufdc3xfhf4tw4u30alw39xny3dnxrl6hau7systymfdv",
- "5320adbd485204665d061b896e2264dd355babc8bfbf744a69922d9987fd5fbcf409",
+ "tb1r3ss76jtsuxe8c8c8lxsehnpak55ylrgr345pww076l536ahjr6jsydamx3",
+ "53208c21ed4970e1b27c1f07f9a19bcc3db5284f8d038d681739fed7e91d76f21ea5",
{
"chain": "signet",
"isPrivkey": false,
@@ -524,8 +524,8 @@
}
],
[
- "bcrt1qnk3tdwwj47ppc4pqmxkjdusegedn9ru5gvccwa",
- "00149da2b6b9d2af821c5420d9ad26f219465b328f94",
+ "bcrt1q65nhlm4hf2ptg3t264al57p7wjxj2c3s6kyt83",
+ "0014d5277feeb74a82b4456ad57bfa783e748d256230",
{
"chain": "regtest",
"isPrivkey": false,
@@ -533,8 +533,8 @@
}
],
[
- "bcrt1qz7prfshfkwsxuk72pt6mzr6uumq4qllxe4vmwqt89tat48d362yqlykk6a",
- "0020178234c2e9b3a06e5bca0af5b10f5ce6c1507fe6cd59b701672afaba9db1d288",
+ "bcrt1qawvc90lpytw3z3k9etdx54l0exq5f5sqfzu5e45kjnl6slwayeeqx2dyac",
+ "0020eb9982bfe122dd1146c5cada6a57efc98144d20048b94cd69694ffa87ddd2672",
{
"chain": "regtest",
"isPrivkey": false,
@@ -542,8 +542,8 @@
}
],
[
- "bcrt1pumee3wj80xvyr7wjmj7zsk26x5pn095aegy862yhx6f2j9sgc9hq6cj4cm",
- "5120e6f398ba47799841f9d2dcbc28595a350337969dca087d28973692a91608c16e",
+ "bcrt1p39a4s4vdcw9kqa8w2t0rp7aj8kfxyw7mce5sk5d70x6wnnmpvt7skf2kxy",
+ "5120897b58558dc38b6074ee52de30fbb23d92623bdbc6690b51be79b4e9cf6162fd",
{
"chain": "regtest",
"isPrivkey": false,
@@ -551,8 +551,8 @@
}
],
[
- "bcrt1szqz8hj64d2hhc6nt65v09jxal66pgff2xpcp9kj648qkk8kjzxelsts4dktd799g47uase",
- "602810047bcb556aaf7c6a6bd518f2c8ddfeb414252a307012da5aa9c16b1ed211b3f82e156d96df14a8",
+ "bcrt1s489d9fhmyel0vzfqsrmew4x7r80asuqesm5hgqacy35daflcyufh3j8cgdtflvt99ph05m",
+ "6028a9cad2a6fb267ef6092080f79754de19dfd8701986e97403b82468dea7f8271378c8f843569fb165",
{
"chain": "regtest",
"isPrivkey": false,
@@ -560,48 +560,48 @@
}
],
[
- "12agZTajtRE3STSchwWNWnrm467zzTQ916",
- "76a9141156e00f70061e5faba8b71593a8c7554b47090c88ac",
+ "1G9A9j6W8TLuh6dEeVwWeyibK1Uc5MfVFV",
+ "76a914a614da54daacdb8861f451a0b7e3c27cdf8a099e88ac",
{
"chain": "main",
"isPrivkey": false
}
],
[
- "3NXqB6iZiPYbKruNT3d9xNBTmtb73xMvvf",
- "a914e49decc9e5d97e0547d3642f3a4795b13ae62bca87",
+ "33GA3ZXbw5o5HeUrBEaqkWXFYYZmdxGRRP",
+ "a914113ca1afeb49ff3abf176ffa19c2a2b4df19712a87",
{
"chain": "main",
"isPrivkey": false
}
],
[
- "mjgt4BoCYxjzWvJFoh68x7cj5GeaKDYhyx",
- "76a9142dc11fc7b8072f733f690ffb0591c00f4062295c88ac",
+ "mwgS2HRbjyfYxFnR1nF9VKLvmdgMfFBmGq",
+ "76a914b14ce7070b53cb0e4b5b5f6e253e876990aeca2e88ac",
{
"chain": "test",
"isPrivkey": false
}
],
[
- "2NCT6FdQ5MxorHgnFxLeHyGwTGRdkHcrJDH",
- "a914d2a8ec992b0894a0d9391ca5d9c45c388c41be7e87",
+ "2MwBVrJQ76BdaGD76CTmou8cZzQYLpe4NqU",
+ "a9142b2c149cde619eae3d7fe995243b76a3417541aa87",
{
"chain": "test",
"isPrivkey": false
}
],
[
- "mpomiA7wqDnMcxaNLC23eBuXAb4U6H4ZqW",
- "76a91465e75e340415ed297c58d6a14d3c17ceeaa17bbd88ac",
+ "mfnJ8tEkqKNFE5YaHTXFxyHk2mnDK2fvDh",
+ "76a91402e6cd77e649ad8b281271f158fc964ca3f66cb088ac",
{
"chain": "signet",
"isPrivkey": false
}
],
[
- "2N1pGAA5uatbU2PKvMA9BnJmHcK6yHfMiZa",
- "a9145e008b6cc232164570befc23d216060bf4ea793b87",
+ "2My83D67ir7K8PPzeT6mE2oth3ZwNTVRS9F",
+ "a9144074d84d32ff62da7b1b3c61925b934bfeb34b0587",
{
"chain": "signet",
"isPrivkey": false
diff --git a/src/test/fuzz/util.cpp b/src/test/fuzz/util.cpp
index 6766fbf2d9..033c6e18d5 100644
--- a/src/test/fuzz/util.cpp
+++ b/src/test/fuzz/util.cpp
@@ -193,6 +193,19 @@ int FuzzedSock::GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* op
return 0;
}
+int FuzzedSock::SetSockOpt(int, int, const void*, socklen_t) const
+{
+ constexpr std::array setsockopt_errnos{
+ ENOMEM,
+ ENOBUFS,
+ };
+ if (m_fuzzed_data_provider.ConsumeBool()) {
+ SetFuzzedErrNo(m_fuzzed_data_provider, setsockopt_errnos);
+ return -1;
+ }
+ return 0;
+}
+
bool FuzzedSock::Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred) const
{
constexpr std::array wait_errnos{
diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h
index 6c91844633..580105e442 100644
--- a/src/test/fuzz/util.h
+++ b/src/test/fuzz/util.h
@@ -68,6 +68,8 @@ public:
int GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len) const override;
+ int SetSockOpt(int level, int opt_name, const void* opt_val, socklen_t opt_len) const override;
+
bool Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred = nullptr) const override;
bool IsConnected(std::string& errmsg) const override;
diff --git a/src/test/miniscript_tests.cpp b/src/test/miniscript_tests.cpp
index 949d30dfd5..930582ea24 100644
--- a/src/test/miniscript_tests.cpp
+++ b/src/test/miniscript_tests.cpp
@@ -224,7 +224,7 @@ BOOST_AUTO_TEST_CASE(fixed_tests)
Test("and_b(hash256(32ba476771d01e37807990ead8719f08af494723de1d228f2c2c07cc0aa40bac),a:and_b(hash256(131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b),a:older(1)))", "82012088aa2032ba476771d01e37807990ead8719f08af494723de1d228f2c2c07cc0aa40bac876b82012088aa20131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b876b51b26c9a6c9a", TESTMODE_VALID | TESTMODE_NONMAL, 15, 3);
Test("thresh(2,multi(2,03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00),a:multi(1,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00),ac:pk_k(022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01))", "522103a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c721036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a0052ae6b5121036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a0051ae6c936b21022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01ac6c935287", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG, 13, 7);
Test("and_n(sha256(d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c68),t:or_i(v:older(4252898),v:older(144)))", "82012088a820d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c68876400676303e2e440b26967029000b269685168", TESTMODE_VALID, 14, 3);
- Test("or_d(d:and_v(v:older(4252898),v:older(4252898)),sha256(38df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b6))", "766303e2e440b26903e2e440b26968736482012088a82038df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b68768", TESTMODE_VALID, 14, 3);
+ Test("or_d(nd:and_v(v:older(4252898),v:older(4252898)),sha256(38df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b6))", "766303e2e440b26903e2e440b2696892736482012088a82038df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b68768", TESTMODE_VALID, 15, 3);
Test("c:and_v(or_c(sha256(9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2),v:multi(1,02c44d12c7065d812e8acf28d7cbb19f9011ecd9e9fdf281b0e6a3b5e87d22e7db)),pk_k(03acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbe))", "82012088a8209267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed28764512102c44d12c7065d812e8acf28d7cbb19f9011ecd9e9fdf281b0e6a3b5e87d22e7db51af682103acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbeac", TESTMODE_VALID | TESTMODE_NEEDSIG, 8, 3);
Test("c:and_v(or_c(multi(2,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00,02352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5),v:ripemd160(1b0f3c404d12075c68c938f9f60ebea4f74941a0)),pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))", "5221036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a002102352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d552ae6482012088a6141b0f3c404d12075c68c938f9f60ebea4f74941a088682103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG, 10, 6);
Test("and_v(andor(hash256(8a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b25),v:hash256(939894f70e6c3a25da75da0cc2071b4076d9b006563cf635986ada2e93c0d735),v:older(50000)),after(499999999))", "82012088aa208a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b2587640350c300b2696782012088aa20939894f70e6c3a25da75da0cc2071b4076d9b006563cf635986ada2e93c0d735886804ff64cd1db1", TESTMODE_VALID, 14, 3);
@@ -263,6 +263,15 @@ BOOST_AUTO_TEST_CASE(fixed_tests)
BOOST_CHECK(ms_multi);
BOOST_CHECK_EQUAL(ms_multi->GetOps(), 4); // 3 pubkeys + CMS
BOOST_CHECK_EQUAL(ms_multi->GetStackSize(), 3); // 1 sig + dummy elem + script push
+ // The 'd:' wrapper leaves on the stack what was DUP'ed at the beginning of its execution.
+ // Since it contains an OP_IF just after on the same element, we can make sure that the element
+ // in question must be OP_1 if OP_IF enforces that its argument must only be OP_1 or the empty
+ // vector (since otherwise the execution would immediately fail). This is the MINIMALIF rule.
+ // Unfortunately, this rule is consensus for Taproot but only policy for P2WSH. Therefore we can't
+ // (for now) have 'd:' be 'u'. This tests we can't use a 'd:' wrapper for a thresh, which requires
+ // its subs to all be 'u' (taken from https://github.com/rust-bitcoin/rust-miniscript/discussions/341).
+ const auto ms_minimalif = miniscript::FromString("thresh(3,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),sc:pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556),sc:pk_k(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798),sdv:older(32))", CONVERTER);
+ BOOST_CHECK(!ms_minimalif);
// Timelock tests
Test("after(100)", "?", TESTMODE_VALID | TESTMODE_NONMAL); // only heightlock
diff --git a/src/test/util/logging.h b/src/test/util/logging.h
index ebe0ecf623..f477088392 100644
--- a/src/test/util/logging.h
+++ b/src/test/util/logging.h
@@ -36,6 +36,6 @@ public:
~DebugLogHelper() { check_found(); }
};
-#define ASSERT_DEBUG_LOG(message) DebugLogHelper PASTE2(debugloghelper, __COUNTER__)(message)
+#define ASSERT_DEBUG_LOG(message) DebugLogHelper UNIQUE_NAME(debugloghelper)(message)
#endif // BITCOIN_TEST_UTIL_LOGGING_H
diff --git a/src/test/util/net.h b/src/test/util/net.h
index 20c45058a1..e980fe4967 100644
--- a/src/test/util/net.h
+++ b/src/test/util/net.h
@@ -150,6 +150,8 @@ public:
return 0;
}
+ int SetSockOpt(int, int, const void*, socklen_t) const override { return 0; }
+
bool Wait(std::chrono::milliseconds timeout,
Event requested,
Event* occurred = nullptr) const override
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index 211153f06c..13f17ca277 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -15,10 +15,10 @@
#include <interfaces/chain.h>
#include <net.h>
#include <net_processing.h>
-#include <node/miner.h>
-#include <noui.h>
#include <node/blockstorage.h>
#include <node/chainstate.h>
+#include <node/miner.h>
+#include <noui.h>
#include <policy/fees.h>
#include <pow.h>
#include <rpc/blockchain.h>
@@ -28,6 +28,7 @@
#include <script/sigcache.h>
#include <shutdown.h>
#include <streams.h>
+#include <test/util/net.h>
#include <txdb.h>
#include <util/strencodings.h>
#include <util/string.h>
@@ -226,7 +227,7 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
/*deterministic=*/false,
m_node.args->GetIntArg("-checkaddrman", 0));
m_node.banman = std::make_unique<BanMan>(m_args.GetDataDirBase() / "banlist", nullptr, DEFAULT_MISBEHAVING_BANTIME);
- m_node.connman = std::make_unique<CConnman>(0x1337, 0x1337, *m_node.addrman); // Deterministic randomness for tests.
+ m_node.connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman); // Deterministic randomness for tests.
m_node.peerman = PeerManager::make(chainparams, *m_node.connman, *m_node.addrman,
m_node.banman.get(), *m_node.chainman,
*m_node.mempool, false);
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index f73cc5da5f..65c8b4ea60 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -481,8 +481,6 @@ void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry, setEntries &setAnces
indexed_transaction_set::iterator newit = mapTx.insert(entry).first;
// Update transaction for any feeDelta created by PrioritiseTransaction
- // TODO: refactor so that the fee delta is calculated before inserting
- // into mapTx.
CAmount delta{0};
ApplyDelta(entry.GetTx().GetHash(), delta);
if (delta) {
diff --git a/src/util/epochguard.h b/src/util/epochguard.h
index 0fec7d2624..7f6477fb3b 100644
--- a/src/util/epochguard.h
+++ b/src/util/epochguard.h
@@ -7,6 +7,7 @@
#define BITCOIN_UTIL_EPOCHGUARD_H
#include <threadsafety.h>
+#include <util/macros.h>
#include <cassert>
@@ -96,6 +97,6 @@ public:
}
};
-#define WITH_FRESH_EPOCH(epoch) const Epoch::Guard PASTE2(epoch_guard_, __COUNTER__)(epoch)
+#define WITH_FRESH_EPOCH(epoch) const Epoch::Guard UNIQUE_NAME(epoch_guard_)(epoch)
#endif // BITCOIN_UTIL_EPOCHGUARD_H
diff --git a/src/util/macros.h b/src/util/macros.h
index c9740c8e82..bf6ba665dc 100644
--- a/src/util/macros.h
+++ b/src/util/macros.h
@@ -8,6 +8,8 @@
#define PASTE(x, y) x ## y
#define PASTE2(x, y) PASTE(x, y)
+#define UNIQUE_NAME(name) PASTE2(name, __COUNTER__)
+
/**
* Converts the parameter X to a string after macro replacement on X has been performed.
* Don't merge these into one macro!
diff --git a/src/util/sock.cpp b/src/util/sock.cpp
index 2029d70a37..b5c1e28294 100644
--- a/src/util/sock.cpp
+++ b/src/util/sock.cpp
@@ -105,6 +105,11 @@ int Sock::GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len)
return getsockopt(m_socket, level, opt_name, static_cast<char*>(opt_val), opt_len);
}
+int Sock::SetSockOpt(int level, int opt_name, const void* opt_val, socklen_t opt_len) const
+{
+ return setsockopt(m_socket, level, opt_name, static_cast<const char*>(opt_val), opt_len);
+}
+
bool Sock::Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred) const
{
#ifdef USE_POLL
diff --git a/src/util/sock.h b/src/util/sock.h
index 7510482857..dd2913a66c 100644
--- a/src/util/sock.h
+++ b/src/util/sock.h
@@ -115,6 +115,16 @@ public:
void* opt_val,
socklen_t* opt_len) const;
+ /**
+ * setsockopt(2) wrapper. Equivalent to
+ * `setsockopt(this->Get(), level, opt_name, opt_val, opt_len)`. Code that uses this
+ * wrapper can be unit tested if this method is overridden by a mock Sock implementation.
+ */
+ [[nodiscard]] virtual int SetSockOpt(int level,
+ int opt_name,
+ const void* opt_val,
+ socklen_t opt_len) const;
+
using Event = uint8_t;
/**
diff --git a/src/util/time.cpp b/src/util/time.cpp
index f7712f0dc8..e428430bac 100644
--- a/src/util/time.cpp
+++ b/src/util/time.cpp
@@ -23,16 +23,6 @@ void UninterruptibleSleep(const std::chrono::microseconds& n) { std::this_thread
static std::atomic<int64_t> nMockTime(0); //!< For testing
-int64_t GetTime()
-{
- int64_t mocktime = nMockTime.load(std::memory_order_relaxed);
- if (mocktime) return mocktime;
-
- time_t now = time(nullptr);
- assert(now > 0);
- return now;
-}
-
bool ChronoSanityCheck()
{
// std::chrono::system_clock.time_since_epoch and time_t(0) are not guaranteed
@@ -80,11 +70,12 @@ template <typename T>
T GetTime()
{
const std::chrono::seconds mocktime{nMockTime.load(std::memory_order_relaxed)};
-
- return std::chrono::duration_cast<T>(
+ const auto ret{
mocktime.count() ?
mocktime :
- std::chrono::microseconds{GetTimeMicros()});
+ std::chrono::duration_cast<T>(std::chrono::system_clock::now().time_since_epoch())};
+ assert(ret > 0s);
+ return ret;
}
template std::chrono::seconds GetTime();
template std::chrono::milliseconds GetTime();
@@ -129,6 +120,8 @@ int64_t GetTimeSeconds()
return int64_t{GetSystemTime<std::chrono::seconds>().count()};
}
+int64_t GetTime() { return GetTime<std::chrono::seconds>().count(); }
+
std::string FormatISO8601DateTime(int64_t nTime) {
struct tm ts;
time_t time_val = nTime;
diff --git a/src/validation.cpp b/src/validation.cpp
index f4b316f67a..58686632f9 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -69,7 +69,6 @@ using node::CBlockIndexHeightOnlyComparator;
using node::CBlockIndexWorkComparator;
using node::CCoinsStats;
using node::CoinStatsHashType;
-using node::fHavePruned;
using node::fImporting;
using node::fPruneMode;
using node::fReindex;
@@ -120,7 +119,6 @@ const std::vector<std::string> CHECKLEVEL_DOC {
*/
RecursiveMutex cs_main;
-CBlockIndex *pindexBestHeader = nullptr;
Mutex g_best_block_mutex;
std::condition_variable g_best_block_cv;
uint256 g_best_block;
@@ -280,8 +278,9 @@ static bool IsCurrentForFeeEstimation(CChainState& active_chainstate) EXCLUSIVE_
return false;
if (active_chainstate.m_chain.Tip()->GetBlockTime() < count_seconds(GetTime<std::chrono::seconds>() - MAX_FEE_ESTIMATION_TIP_AGE))
return false;
- if (active_chainstate.m_chain.Height() < pindexBestHeader->nHeight - 1)
+ if (active_chainstate.m_chain.Height() < active_chainstate.m_chainman.m_best_header->nHeight - 1) {
return false;
+ }
return true;
}
@@ -1602,8 +1601,8 @@ void CChainState::InvalidChainFound(CBlockIndex* pindexNew)
if (!m_chainman.m_best_invalid || pindexNew->nChainWork > m_chainman.m_best_invalid->nChainWork) {
m_chainman.m_best_invalid = pindexNew;
}
- if (pindexBestHeader != nullptr && pindexBestHeader->GetAncestor(pindexNew->nHeight) == pindexNew) {
- pindexBestHeader = m_chain.Tip();
+ if (m_chainman.m_best_header != nullptr && m_chainman.m_best_header->GetAncestor(pindexNew->nHeight) == pindexNew) {
+ m_chainman.m_best_header = m_chain.Tip();
}
LogPrintf("%s: invalid block=%s height=%d log2_work=%f date=%s\n", __func__,
@@ -2029,8 +2028,8 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
BlockMap::const_iterator it = m_blockman.m_block_index.find(hashAssumeValid);
if (it != m_blockman.m_block_index.end()) {
if (it->second.GetAncestor(pindex->nHeight) == pindex &&
- pindexBestHeader->GetAncestor(pindex->nHeight) == pindex &&
- pindexBestHeader->nChainWork >= nMinimumChainWork) {
+ m_chainman.m_best_header->GetAncestor(pindex->nHeight) == pindex &&
+ m_chainman.m_best_header->nChainWork >= nMinimumChainWork) {
// This block is a member of the assumed verified chain and an ancestor of the best header.
// Script verification is skipped when connecting blocks under the
// assumevalid block. Assuming the assumevalid block is valid this
@@ -2045,7 +2044,7 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
// artificially set the default assumed verified block further back.
// The test against nMinimumChainWork prevents the skipping when denied access to any chain at
// least as good as the expected chain.
- fScriptChecks = (GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, m_params.GetConsensus()) <= 60 * 60 * 24 * 7 * 2);
+ fScriptChecks = (GetBlockProofEquivalentTime(*m_chainman.m_best_header, *pindex, *m_chainman.m_best_header, m_params.GetConsensus()) <= 60 * 60 * 24 * 7 * 2);
}
}
}
@@ -2358,9 +2357,9 @@ bool CChainState::FlushStateToDisk(
}
if (!setFilesToPrune.empty()) {
fFlushForPrune = true;
- if (!fHavePruned) {
+ if (!m_blockman.m_have_pruned) {
m_blockman.m_block_tree_db->WriteFlag("prunedblockfiles", true);
- fHavePruned = true;
+ m_blockman.m_have_pruned = true;
}
}
}
@@ -2904,7 +2903,7 @@ static bool NotifyHeaderTip(CChainState& chainstate) LOCKS_EXCLUDED(cs_main) {
CBlockIndex* pindexHeader = nullptr;
{
LOCK(cs_main);
- pindexHeader = pindexBestHeader;
+ pindexHeader = chainstate.m_chainman.m_best_header;
if (pindexHeader != pindexHeaderOld) {
fNotify = true;
@@ -3621,7 +3620,7 @@ bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValida
}
}
}
- CBlockIndex* pindex{m_blockman.AddToBlockIndex(block)};
+ CBlockIndex* pindex{m_blockman.AddToBlockIndex(block, m_best_header)};
if (ppindex)
*ppindex = pindex;
@@ -4122,13 +4121,11 @@ void UnloadBlockIndex(CTxMemPool* mempool, ChainstateManager& chainman)
{
AssertLockHeld(::cs_main);
chainman.Unload();
- pindexBestHeader = nullptr;
if (mempool) mempool->clear();
g_versionbitscache.Clear();
for (int b = 0; b < VERSIONBITS_NUM_BITS; b++) {
warningcache[b].clear();
}
- fHavePruned = false;
}
bool ChainstateManager::LoadBlockIndex()
@@ -4203,6 +4200,8 @@ bool ChainstateManager::LoadBlockIndex()
if (pindex->nStatus & BLOCK_FAILED_MASK && (!m_best_invalid || pindex->nChainWork > m_best_invalid->nChainWork)) {
m_best_invalid = pindex;
}
+ if (pindex->IsValid(BLOCK_VALID_TREE) && (m_best_header == nullptr || CBlockIndexWorkComparator()(m_best_header, pindex)))
+ m_best_header = pindex;
}
needs_init = m_blockman.m_block_index.empty();
@@ -4237,7 +4236,7 @@ bool CChainState::LoadGenesisBlock()
if (blockPos.IsNull()) {
return error("%s: writing genesis block to disk failed", __func__);
}
- CBlockIndex *pindex = m_blockman.AddToBlockIndex(block);
+ CBlockIndex* pindex = m_blockman.AddToBlockIndex(block, m_chainman.m_best_header);
ReceivedBlockTransactions(block, pindex, blockPos);
} catch (const std::runtime_error& e) {
return error("%s: failed to write genesis block: %s", __func__, e.what());
@@ -4448,7 +4447,7 @@ void CChainState::CheckBlockIndex()
// HAVE_DATA is only equivalent to nTx > 0 (or VALID_TRANSACTIONS) if no pruning has occurred.
// Unless these indexes are assumed valid and pending block download on a
// background chainstate.
- if (!fHavePruned && !pindex->IsAssumedValid()) {
+ if (!m_blockman.m_have_pruned && !pindex->IsAssumedValid()) {
// If we've never pruned, then HAVE_DATA should be equivalent to nTx > 0
assert(!(pindex->nStatus & BLOCK_HAVE_DATA) == (pindex->nTx == 0));
assert(pindexFirstMissing == pindexFirstNeverProcessed);
@@ -4522,7 +4521,7 @@ void CChainState::CheckBlockIndex()
if (pindexFirstMissing == nullptr) assert(!foundInUnlinked); // We aren't missing data for any parent -- cannot be in m_blocks_unlinked.
if (pindex->pprev && (pindex->nStatus & BLOCK_HAVE_DATA) && pindexFirstNeverProcessed == nullptr && pindexFirstMissing != nullptr) {
// We HAVE_DATA for this block, have received data for all parents at some point, but we're currently missing data for some parent.
- assert(fHavePruned); // We must have pruned.
+ assert(m_blockman.m_have_pruned); // We must have pruned.
// This block may have entered m_blocks_unlinked if:
// - it has a descendant that at some point had more work than the
// tip, and
@@ -5169,6 +5168,7 @@ void ChainstateManager::Unload()
m_failed_blocks.clear();
m_blockman.Unload();
+ m_best_header = nullptr;
m_best_invalid = nullptr;
}
diff --git a/src/validation.h b/src/validation.h
index 53ea2d4aea..2e7ab42f88 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -131,9 +131,6 @@ extern uint256 hashAssumeValid;
/** Minimum work we will assume exists on some valid chain. */
extern arith_uint256 nMinimumChainWork;
-/** Best header we've seen so far (used for getheaders queries' starting points). */
-extern CBlockIndex *pindexBestHeader;
-
/** Documentation for argument 'checklevel'. */
extern const std::vector<std::string> CHECKLEVEL_DOC;
@@ -883,6 +880,9 @@ public:
*/
std::set<CBlockIndex*> m_failed_blocks;
+ /** Best header we've seen so far (used for getheaders queries' starting points). */
+ CBlockIndex* m_best_header = nullptr;
+
//! The total number of bytes available for us to use across all in-memory
//! coins caches. This will be split somehow across chainstates.
int64_t m_total_coinstip_cache{0};
diff --git a/src/wallet/rpc/wallet.cpp b/src/wallet/rpc/wallet.cpp
index 4baf16fdcb..1291663847 100644
--- a/src/wallet/rpc/wallet.cpp
+++ b/src/wallet/rpc/wallet.cpp
@@ -257,7 +257,7 @@ static RPCHelpMan setwalletflag()
{
{RPCResult::Type::STR, "flag_name", "The name of the flag that was modified"},
{RPCResult::Type::BOOL, "flag_state", "The new state of the flag"},
- {RPCResult::Type::STR, "warnings", "Any warnings associated with the change"},
+ {RPCResult::Type::STR, "warnings", /*optional=*/true, "Any warnings associated with the change"},
}
},
RPCExamples{
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 2a0653c719..c3ae098aee 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -299,6 +299,13 @@ std::shared_ptr<CWallet> CreateWallet(WalletContext& context, const std::string&
return nullptr;
}
+ // Do not allow a passphrase when private keys are disabled
+ if (!passphrase.empty() && (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
+ error = Untranslated("Passphrase provided but private keys are disabled. A passphrase is only used to encrypt private keys, so cannot be used for wallets with private keys disabled.");
+ status = DatabaseStatus::FAILED_CREATE;
+ return nullptr;
+ }
+
// Wallet::Verify will check if we're trying to create a wallet with a duplicate name.
std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(name, options, status, error);
if (!database) {
@@ -307,13 +314,6 @@ std::shared_ptr<CWallet> CreateWallet(WalletContext& context, const std::string&
return nullptr;
}
- // Do not allow a passphrase when private keys are disabled
- if (!passphrase.empty() && (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
- error = Untranslated("Passphrase provided but private keys are disabled. A passphrase is only used to encrypt private keys, so cannot be used for wallets with private keys disabled.");
- status = DatabaseStatus::FAILED_CREATE;
- return nullptr;
- }
-
// Make the wallet
context.chain->initMessage(_("Loading wallet…").translated);
const std::shared_ptr<CWallet> wallet = CWallet::Create(context, name, std::move(database), wallet_creation_flags, error, warnings);
diff --git a/test/README.md b/test/README.md
index 7ff2d6d9f2..e5a184d23c 100644
--- a/test/README.md
+++ b/test/README.md
@@ -305,9 +305,9 @@ Use the `-v` option for verbose output.
| 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-python.py`](lint/lint-python.py) | [flake8](https://gitlab.com/pycqa/flake8)
+| [`lint-python.py`](lint/lint-python.py) | [mypy](https://github.com/python/mypy)
+| [`lint-python.py`](lint/lint-python.py) | [pyzmq](https://github.com/zeromq/pyzmq)
| [`lint-python-dead-code.py`](lint/lint-python-dead-code.py) | [vulture](https://github.com/jendrikseipp/vulture)
| [`lint-shell.sh`](lint/lint-shell.sh) | [ShellCheck](https://github.com/koalaman/shellcheck)
| [`lint-spelling.py`](lint/lint-spelling.py) | [codespell](https://github.com/codespell-project/codespell)
diff --git a/test/config.ini.in b/test/config.ini.in
index d7105c419b..5888ef443b 100644
--- a/test/config.ini.in
+++ b/test/config.ini.in
@@ -19,6 +19,7 @@ RPCAUTH=@abs_top_srcdir@/share/rpcauth/rpcauth.py
@USE_SQLITE_TRUE@USE_SQLITE=true
@USE_BDB_TRUE@USE_BDB=true
@BUILD_BITCOIN_CLI_TRUE@ENABLE_CLI=true
+@BUILD_BITCOIN_UTIL_TRUE@ENABLE_BITCOIN_UTIL=true
@BUILD_BITCOIN_WALLET_TRUE@ENABLE_WALLET_TOOL=true
@BUILD_BITCOIND_TRUE@ENABLE_BITCOIND=true
@ENABLE_FUZZ_TRUE@ENABLE_FUZZ=true
diff --git a/test/functional/README.md b/test/functional/README.md
index 926810cf03..914dbfd977 100644
--- a/test/functional/README.md
+++ b/test/functional/README.md
@@ -24,7 +24,7 @@ don't have test cases for.
Consider using [pyenv](https://github.com/pyenv/pyenv), which checks [.python-version](/.python-version),
to prevent accidentally introducing modern syntax from an unsupported Python version.
The CI linter job also checks this, but [possibly not in all cases](https://github.com/bitcoin/bitcoin/pull/14884#discussion_r239585126).
-- See [the python lint script](/test/lint/lint-python.sh) that checks for violations that
+- See [the python lint script](/test/lint/lint-python.py) that checks for violations that
could lead to bugs and issues in the test code.
- Use [type hints](https://docs.python.org/3/library/typing.html) in your code to improve code readability
and to detect possible bugs earlier.
diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py
index f377fbaaa6..89ddfd3bcf 100755
--- a/test/functional/p2p_segwit.py
+++ b/test/functional/p2p_segwit.py
@@ -43,7 +43,6 @@ from test_framework.messages import (
ser_uint256,
ser_vector,
sha256,
- tx_from_hex,
)
from test_framework.p2p import (
P2PInterface,
@@ -89,6 +88,8 @@ from test_framework.util import (
softfork_active,
assert_raises_rpc_error,
)
+from test_framework.wallet import MiniWallet
+
MAX_SIGOP_COST = 80000
@@ -221,9 +222,6 @@ class SegWitTest(BitcoinTestFramework):
]
self.supports_cli = False
- def skip_test_if_missing_module(self):
- self.skip_if_no_wallet()
-
# Helper functions
def build_next_block(self):
@@ -259,6 +257,7 @@ class SegWitTest(BitcoinTestFramework):
self.log.info("Starting tests before segwit activation")
self.segwit_active = False
+ self.wallet = MiniWallet(self.nodes[0])
self.test_non_witness_transaction()
self.test_v0_outputs_arent_spendable()
@@ -307,7 +306,7 @@ class SegWitTest(BitcoinTestFramework):
self.test_node.send_and_ping(msg_no_witness_block(block)) # make sure the block was processed
txid = block.vtx[0].sha256
- self.generate(self.nodes[0], 99) # let the block mature
+ self.generate(self.wallet, 99) # let the block mature
# Create a transaction that spends the coinbase
tx = CTransaction()
@@ -1999,21 +1998,13 @@ class SegWitTest(BitcoinTestFramework):
def serialize(self):
return serialize_with_bogus_witness(self.tx)
- self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(address_type='bech32'), 5)
- self.generate(self.nodes[0], 1)
- unspent = next(u for u in self.nodes[0].listunspent() if u['spendable'] and u['address'].startswith('bcrt'))
-
- raw = self.nodes[0].createrawtransaction([{"txid": unspent['txid'], "vout": unspent['vout']}], {self.nodes[0].getnewaddress(): 1})
- tx = tx_from_hex(raw)
+ tx = self.wallet.create_self_transfer(from_node=self.nodes[0])['tx']
assert_raises_rpc_error(-22, "TX decode failed", self.nodes[0].decoderawtransaction, hexstring=serialize_with_bogus_witness(tx).hex(), iswitness=True)
- with self.nodes[0].assert_debug_log(['Superfluous witness record']):
+ with self.nodes[0].assert_debug_log(['Unknown transaction optional data']):
self.test_node.send_and_ping(msg_bogus_tx(tx))
- raw = self.nodes[0].signrawtransactionwithwallet(raw)
- assert raw['complete']
- raw = raw['hex']
- tx = tx_from_hex(raw)
+ tx.wit.vtxinwit = [] # drop witness
assert_raises_rpc_error(-22, "TX decode failed", self.nodes[0].decoderawtransaction, hexstring=serialize_with_bogus_witness(tx).hex(), iswitness=True)
- with self.nodes[0].assert_debug_log(['Unknown transaction optional data']):
+ with self.nodes[0].assert_debug_log(['Superfluous witness record']):
self.test_node.send_and_ping(msg_bogus_tx(tx))
@subtest
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index 2fb9ec0942..a39ee003ef 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -244,8 +244,14 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
"src",
"bitcoin-cli" + config["environment"]["EXEEXT"],
)
+ fname_bitcoinutil = os.path.join(
+ config["environment"]["BUILDDIR"],
+ "src",
+ "bitcoin-util" + config["environment"]["EXEEXT"],
+ )
self.options.bitcoind = os.getenv("BITCOIND", default=fname_bitcoind)
self.options.bitcoincli = os.getenv("BITCOINCLI", default=fname_bitcoincli)
+ self.options.bitcoinutil = os.getenv("BITCOINUTIL", default=fname_bitcoinutil)
os.environ['PATH'] = os.pathsep.join([
os.path.join(config['environment']['BUILDDIR'], 'src'),
@@ -880,6 +886,11 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
if not self.is_wallet_tool_compiled():
raise SkipTest("bitcoin-wallet has not been compiled")
+ def skip_if_no_bitcoin_util(self):
+ """Skip the running test if bitcoin-util has not been compiled."""
+ if not self.is_bitcoin_util_compiled():
+ raise SkipTest("bitcoin-util has not been compiled")
+
def skip_if_no_cli(self):
"""Skip the running test if bitcoin-cli has not been compiled."""
if not self.is_cli_compiled():
@@ -927,6 +938,10 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
"""Checks whether bitcoin-wallet was compiled."""
return self.config["components"].getboolean("ENABLE_WALLET_TOOL")
+ def is_bitcoin_util_compiled(self):
+ """Checks whether bitcoin-util was compiled."""
+ return self.config["components"].getboolean("ENABLE_BITCOIN_UTIL")
+
def is_zmq_compiled(self):
"""Checks whether the zmq module was compiled."""
return self.config["components"].getboolean("ENABLE_ZMQ")
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 1f0f806d91..a3c938ae26 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -145,6 +145,8 @@ BASE_SCRIPTS = [
'wallet_txn_doublespend.py --mineblock',
'tool_wallet.py --legacy-wallet',
'tool_wallet.py --descriptors',
+ 'tool_signet_miner.py --legacy-wallet',
+ 'tool_signet_miner.py --descriptors',
'wallet_txn_clone.py',
'wallet_txn_clone.py --segwit',
'rpc_getchaintips.py',
diff --git a/test/functional/tool_signet_miner.py b/test/functional/tool_signet_miner.py
new file mode 100755
index 0000000000..e6fc9072ab
--- /dev/null
+++ b/test/functional/tool_signet_miner.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env python3
+# Copyright (c) 2022 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Test signet miner tool"""
+
+import os.path
+import subprocess
+import sys
+import time
+
+from test_framework.key import ECKey
+from test_framework.script_util import key_to_p2wpkh_script
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_equal
+from test_framework.wallet_util import bytes_to_wif
+
+
+CHALLENGE_PRIVATE_KEY = (42).to_bytes(32, 'big')
+
+
+class SignetMinerTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.chain = "signet"
+ self.setup_clean_chain = True
+ self.num_nodes = 1
+
+ # generate and specify signet challenge (simple p2wpkh script)
+ privkey = ECKey()
+ privkey.set(CHALLENGE_PRIVATE_KEY, True)
+ pubkey = privkey.get_pubkey().get_bytes()
+ challenge = key_to_p2wpkh_script(pubkey)
+ self.extra_args = [[f'-signetchallenge={challenge.hex()}']]
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_cli()
+ self.skip_if_no_wallet()
+ self.skip_if_no_bitcoin_util()
+
+ def run_test(self):
+ node = self.nodes[0]
+ # import private key needed for signing block
+ node.importprivkey(bytes_to_wif(CHALLENGE_PRIVATE_KEY))
+
+ # generate block with signet miner tool
+ base_dir = self.config["environment"]["SRCDIR"]
+ signet_miner_path = os.path.join(base_dir, "contrib", "signet", "miner")
+ subprocess.run([
+ sys.executable,
+ signet_miner_path,
+ f'--cli={node.cli.binary} -datadir={node.cli.datadir}',
+ 'generate',
+ f'--address={node.getnewaddress()}',
+ f'--grind-cmd={self.options.bitcoinutil} grind',
+ '--nbits=1d00ffff',
+ f'--set-block-time={int(time.time())}',
+ ], check=True, stderr=subprocess.STDOUT)
+ assert_equal(node.getblockcount(), 1)
+
+
+if __name__ == "__main__":
+ SignetMinerTest().main()
diff --git a/test/functional/wallet_avoidreuse.py b/test/functional/wallet_avoidreuse.py
index dc823c2c60..f663666f57 100755
--- a/test/functional/wallet_avoidreuse.py
+++ b/test/functional/wallet_avoidreuse.py
@@ -118,6 +118,17 @@ class AvoidReuseTest(BitcoinTestFramework):
assert_raises_rpc_error(-8, "Wallet flag is already set to false", self.nodes[0].setwalletflag, 'avoid_reuse', False)
assert_raises_rpc_error(-8, "Wallet flag is already set to true", self.nodes[1].setwalletflag, 'avoid_reuse', True)
+ # Create a wallet with avoid reuse, and test that disabling it afterwards persists
+ self.nodes[1].createwallet(wallet_name="avoid_reuse_persist", avoid_reuse=True)
+ w = self.nodes[1].get_wallet_rpc("avoid_reuse_persist")
+ assert_equal(w.getwalletinfo()["avoid_reuse"], True)
+ w.setwalletflag("avoid_reuse", False)
+ assert_equal(w.getwalletinfo()["avoid_reuse"], False)
+ w.unloadwallet()
+ self.nodes[1].loadwallet("avoid_reuse_persist")
+ assert_equal(w.getwalletinfo()["avoid_reuse"], False)
+ w.unloadwallet()
+
def test_immutable(self):
'''Test immutable wallet flags'''
self.log.info("Test immutable wallet flags")
diff --git a/test/functional/wallet_createwallet.py b/test/functional/wallet_createwallet.py
index e8234de032..dcf2e98638 100755
--- a/test/functional/wallet_createwallet.py
+++ b/test/functional/wallet_createwallet.py
@@ -26,6 +26,11 @@ class CreateWalletTest(BitcoinTestFramework):
node = self.nodes[0]
self.generate(node, 1) # Leave IBD for sethdseed
+ self.log.info("Run createwallet with invalid parameters.")
+ # Run createwallet with invalid parameters. This must not prevent a new wallet with the same name from being created with the correct parameters.
+ assert_raises_rpc_error(-4, "Passphrase provided but private keys are disabled. A passphrase is only used to encrypt private keys, so cannot be used for wallets with private keys disabled.",
+ self.nodes[0].createwallet, wallet_name='w0', descriptors=True, disable_private_keys=True, passphrase="passphrase")
+
self.nodes[0].createwallet(wallet_name='w0')
w0 = node.get_wallet_rpc('w0')
address1 = w0.getnewaddress()
diff --git a/test/lint/lint-git-commit-check.py b/test/lint/lint-git-commit-check.py
new file mode 100755
index 0000000000..a1d03370e8
--- /dev/null
+++ b/test/lint/lint-git-commit-check.py
@@ -0,0 +1,63 @@
+#!/usr/bin/env python3
+#
+# Copyright (c) 2020-2022 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#
+# Linter to check that commit messages have a new line before the body
+# or no body at all
+
+import argparse
+import os
+import sys
+
+from subprocess import check_output
+
+
+def parse_args():
+ """Parse command line arguments."""
+ parser = argparse.ArgumentParser(
+ description="""
+ Linter to check that commit messages have a new line before
+ the body or no body at all.
+ """,
+ epilog=f"""
+ You can manually set the commit-range with the COMMIT_RANGE
+ environment variable (e.g. "COMMIT_RANGE='47ba2c3...ee50c9e'
+ {sys.argv[0]}"). Defaults to current merge base when neither
+ prev-commits nor the environment variable is set.
+ """)
+
+ parser.add_argument("--prev-commits", "-p", required=False, help="The previous n commits to check")
+
+ return parser.parse_args()
+
+
+def main():
+ args = parse_args()
+ exit_code = 0
+
+ if not os.getenv("COMMIT_RANGE"):
+ if args.prev_commits:
+ commit_range = "HEAD~" + args.prev_commits + "...HEAD"
+ else:
+ # This assumes that the target branch of the pull request will be master.
+ merge_base = check_output(["git", "merge-base", "HEAD", "master"], universal_newlines=True, encoding="utf8").rstrip("\n")
+ commit_range = merge_base + "..HEAD"
+ else:
+ commit_range = os.getenv("COMMIT_RANGE")
+
+ commit_hashes = check_output(["git", "log", commit_range, "--format=%H"], universal_newlines=True, encoding="utf8").splitlines()
+
+ for hash in commit_hashes:
+ commit_info = check_output(["git", "log", "--format=%B", "-n", "1", hash], universal_newlines=True, encoding="utf8").splitlines()
+ if len(commit_info) >= 2:
+ if commit_info[1]:
+ print(f"The subject line of commit hash {hash} is followed by a non-empty line. Subject lines should always be followed by a blank line.")
+ exit_code = 1
+
+ sys.exit(exit_code)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/test/lint/lint-git-commit-check.sh b/test/lint/lint-git-commit-check.sh
deleted file mode 100755
index f77373ed00..0000000000
--- a/test/lint/lint-git-commit-check.sh
+++ /dev/null
@@ -1,48 +0,0 @@
-#!/usr/bin/env bash
-# Copyright (c) 2020-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.
-#
-# Linter to check that commit messages have a new line before the body
-# or no body at all
-
-export LC_ALL=C
-
-EXIT_CODE=0
-
-while getopts "?" opt; do
- case $opt in
- ?)
- echo "Usage: $0 [N]"
- echo " COMMIT_RANGE='<commit range>' $0"
- echo " $0 -?"
- echo "Checks unmerged commits, the previous N commits, or a commit range."
- echo "COMMIT_RANGE='47ba2c3...ee50c9e' $0"
- exit ${EXIT_CODE}
- ;;
- esac
-done
-
-if [ -z "${COMMIT_RANGE}" ]; then
- if [ -n "$1" ]; then
- COMMIT_RANGE="HEAD~$1...HEAD"
- else
- # This assumes that the target branch of the pull request will be master.
- MERGE_BASE=$(git merge-base HEAD master)
- COMMIT_RANGE="$MERGE_BASE..HEAD"
- fi
-fi
-
-while IFS= read -r commit_hash || [[ -n "$commit_hash" ]]; do
- n_line=0
- while IFS= read -r line || [[ -n "$line" ]]; do
- n_line=$((n_line+1))
- length=${#line}
- if [ $n_line -eq 2 ] && [ "$length" -ne 0 ]; then
- echo "The subject line of commit hash ${commit_hash} is followed by a non-empty line. Subject lines should always be followed by a blank line."
- EXIT_CODE=1
- fi
- done < <(git log --format=%B -n 1 "$commit_hash")
-done < <(git log "${COMMIT_RANGE}" --format=%H)
-
-exit ${EXIT_CODE}
diff --git a/test/lint/lint-includes.py b/test/lint/lint-includes.py
new file mode 100755
index 0000000000..b29c7f8b4d
--- /dev/null
+++ b/test/lint/lint-includes.py
@@ -0,0 +1,179 @@
+#!/usr/bin/env python3
+#
+# Copyright (c) 2018-2022 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#
+# Check for duplicate includes.
+# Guard against accidental introduction of new Boost dependencies.
+# Check includes: Check for duplicate includes. Enforce bracket syntax includes.
+
+import os
+import re
+import sys
+
+from subprocess import check_output, CalledProcessError
+
+
+EXCLUDED_DIRS = ["src/leveldb/",
+ "src/crc32c/",
+ "src/secp256k1/",
+ "src/minisketch/",
+ "src/univalue/"]
+
+EXPECTED_BOOST_INCLUDES = ["boost/algorithm/string.hpp",
+ "boost/algorithm/string/classification.hpp",
+ "boost/algorithm/string/replace.hpp",
+ "boost/algorithm/string/split.hpp",
+ "boost/date_time/posix_time/posix_time.hpp",
+ "boost/multi_index/hashed_index.hpp",
+ "boost/multi_index/ordered_index.hpp",
+ "boost/multi_index/sequenced_index.hpp",
+ "boost/multi_index_container.hpp",
+ "boost/process.hpp",
+ "boost/signals2/connection.hpp",
+ "boost/signals2/optional_last_value.hpp",
+ "boost/signals2/signal.hpp",
+ "boost/test/included/unit_test.hpp",
+ "boost/test/unit_test.hpp"]
+
+
+def get_toplevel():
+ return check_output(["git", "rev-parse", "--show-toplevel"], universal_newlines=True, encoding="utf8").rstrip("\n")
+
+
+def list_files_by_suffix(suffixes):
+ exclude_args = [":(exclude)" + dir for dir in EXCLUDED_DIRS]
+
+ files_list = check_output(["git", "ls-files", "src"] + exclude_args, universal_newlines=True, encoding="utf8").splitlines()
+
+ return [file for file in files_list if file.endswith(suffixes)]
+
+
+def find_duplicate_includes(include_list):
+ tempset = set()
+ duplicates = set()
+
+ for inclusion in include_list:
+ if inclusion in tempset:
+ duplicates.add(inclusion)
+ else:
+ tempset.add(inclusion)
+
+ return duplicates
+
+
+def find_included_cpps():
+ included_cpps = list()
+
+ try:
+ included_cpps = check_output(["git", "grep", "-E", r"^#include [<\"][^>\"]+\.cpp[>\"]", "--", "*.cpp", "*.h"], universal_newlines=True, encoding="utf8").splitlines()
+ except CalledProcessError as e:
+ if e.returncode > 1:
+ raise e
+
+ return included_cpps
+
+
+def find_extra_boosts():
+ included_boosts = list()
+ filtered_included_boost_set = set()
+ exclusion_set = set()
+
+ try:
+ included_boosts = check_output(["git", "grep", "-E", r"^#include <boost/", "--", "*.cpp", "*.h"], universal_newlines=True, encoding="utf8").splitlines()
+ except CalledProcessError as e:
+ if e.returncode > 1:
+ raise e
+
+ for boost in included_boosts:
+ filtered_included_boost_set.add(re.findall(r'(?<=\<).+?(?=\>)', boost)[0])
+
+ for expected_boost in EXPECTED_BOOST_INCLUDES:
+ for boost in filtered_included_boost_set:
+ if expected_boost in boost:
+ exclusion_set.add(boost)
+
+ extra_boosts = set(filtered_included_boost_set.difference(exclusion_set))
+
+ return extra_boosts
+
+
+def find_quote_syntax_inclusions():
+ exclude_args = [":(exclude)" + dir for dir in EXCLUDED_DIRS]
+ quote_syntax_inclusions = list()
+
+ try:
+ quote_syntax_inclusions = check_output(["git", "grep", r"^#include \"", "--", "*.cpp", "*.h"] + exclude_args, universal_newlines=True, encoding="utf8").splitlines()
+ except CalledProcessError as e:
+ if e.returncode > 1:
+ raise e
+
+ return quote_syntax_inclusions
+
+
+def main():
+ exit_code = 0
+
+ os.chdir(get_toplevel())
+
+ # Check for duplicate includes
+ for filename in list_files_by_suffix((".cpp", ".h")):
+ with open(filename, "r", encoding="utf8") as file:
+ include_list = [line.rstrip("\n") for line in file if re.match(r"^#include", line)]
+
+ duplicates = find_duplicate_includes(include_list)
+
+ if duplicates:
+ print(f"Duplicate include(s) in {filename}:")
+ for duplicate in duplicates:
+ print(duplicate)
+ print("")
+ exit_code = 1
+
+ # Check if code includes .cpp-files
+ included_cpps = find_included_cpps()
+
+ if included_cpps:
+ print("The following files #include .cpp files:")
+ for included_cpp in included_cpps:
+ print(included_cpp)
+ print("")
+ exit_code = 1
+
+ # Guard against accidental introduction of new Boost dependencies
+ extra_boosts = find_extra_boosts()
+
+ if extra_boosts:
+ for boost in extra_boosts:
+ print(f"A new Boost dependency in the form of \"{boost}\" appears to have been introduced:")
+ print(check_output(["git", "grep", boost, "--", "*.cpp", "*.h"], universal_newlines=True, encoding="utf8"))
+ exit_code = 1
+
+ # Check if Boost dependencies are no longer used
+ for expected_boost in EXPECTED_BOOST_INCLUDES:
+ try:
+ check_output(["git", "grep", "-q", r"^#include <%s>" % expected_boost, "--", "*.cpp", "*.h"], universal_newlines=True, encoding="utf8")
+ except CalledProcessError as e:
+ if e.returncode > 1:
+ raise e
+ else:
+ print(f"Good job! The Boost dependency \"{expected_boost}\" is no longer used. "
+ "Please remove it from EXPECTED_BOOST_INCLUDES in test/lint/lint-includes.py "
+ "to make sure this dependency is not accidentally reintroduced.\n")
+ exit_code = 1
+
+ # Enforce bracket syntax includes
+ quote_syntax_inclusions = find_quote_syntax_inclusions()
+
+ if quote_syntax_inclusions:
+ print("Please use bracket syntax includes (\"#include <foo.h>\") instead of quote syntax includes:")
+ for quote_syntax_inclusion in quote_syntax_inclusions:
+ print(quote_syntax_inclusion)
+ exit_code = 1
+
+ sys.exit(exit_code)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/test/lint/lint-includes.sh b/test/lint/lint-includes.sh
deleted file mode 100755
index 9e72831ee9..0000000000
--- a/test/lint/lint-includes.sh
+++ /dev/null
@@ -1,103 +0,0 @@
-#!/usr/bin/env bash
-#
-# Copyright (c) 2018-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.
-#
-# Check for duplicate includes.
-# Guard against accidental introduction of new Boost dependencies.
-# Check includes: Check for duplicate includes. Enforce bracket syntax includes.
-
-export LC_ALL=C
-IGNORE_REGEXP="/(leveldb|secp256k1|minisketch|univalue|crc32c)/"
-
-# cd to root folder of git repo for git ls-files to work properly
-cd "$(dirname "$0")/../.." || exit 1
-
-filter_suffix() {
- git ls-files | grep -E "^src/.*\.${1}"'$' | grep -Ev "${IGNORE_REGEXP}"
-}
-
-EXIT_CODE=0
-
-for HEADER_FILE in $(filter_suffix h); do
- DUPLICATE_INCLUDES_IN_HEADER_FILE=$(grep -E "^#include " < "${HEADER_FILE}" | sort | uniq -d)
- if [[ ${DUPLICATE_INCLUDES_IN_HEADER_FILE} != "" ]]; then
- echo "Duplicate include(s) in ${HEADER_FILE}:"
- echo "${DUPLICATE_INCLUDES_IN_HEADER_FILE}"
- echo
- EXIT_CODE=1
- fi
-done
-
-for CPP_FILE in $(filter_suffix cpp); do
- DUPLICATE_INCLUDES_IN_CPP_FILE=$(grep -E "^#include " < "${CPP_FILE}" | sort | uniq -d)
- if [[ ${DUPLICATE_INCLUDES_IN_CPP_FILE} != "" ]]; then
- echo "Duplicate include(s) in ${CPP_FILE}:"
- echo "${DUPLICATE_INCLUDES_IN_CPP_FILE}"
- echo
- EXIT_CODE=1
- fi
-done
-
-INCLUDED_CPP_FILES=$(git grep -E "^#include [<\"][^>\"]+\.cpp[>\"]" -- "*.cpp" "*.h")
-if [[ ${INCLUDED_CPP_FILES} != "" ]]; then
- echo "The following files #include .cpp files:"
- echo "${INCLUDED_CPP_FILES}"
- echo
- EXIT_CODE=1
-fi
-
-EXPECTED_BOOST_INCLUDES=(
- boost/algorithm/string.hpp
- boost/algorithm/string/classification.hpp
- boost/algorithm/string/replace.hpp
- boost/algorithm/string/split.hpp
- boost/date_time/posix_time/posix_time.hpp
- boost/multi_index/hashed_index.hpp
- boost/multi_index/ordered_index.hpp
- boost/multi_index/sequenced_index.hpp
- boost/multi_index_container.hpp
- boost/process.hpp
- boost/signals2/connection.hpp
- boost/signals2/optional_last_value.hpp
- boost/signals2/signal.hpp
- boost/test/included/unit_test.hpp
- boost/test/unit_test.hpp
-)
-
-for BOOST_INCLUDE in $(git grep '^#include <boost/' -- "*.cpp" "*.h" | cut -f2 -d: | cut -f2 -d'<' | cut -f1 -d'>' | sort -u); do
- IS_EXPECTED_INCLUDE=0
- for EXPECTED_BOOST_INCLUDE in "${EXPECTED_BOOST_INCLUDES[@]}"; do
- if [[ "${BOOST_INCLUDE}" == "${EXPECTED_BOOST_INCLUDE}" ]]; then
- IS_EXPECTED_INCLUDE=1
- break
- fi
- done
- if [[ ${IS_EXPECTED_INCLUDE} == 0 ]]; then
- EXIT_CODE=1
- echo "A new Boost dependency in the form of \"${BOOST_INCLUDE}\" appears to have been introduced:"
- git grep "${BOOST_INCLUDE}" -- "*.cpp" "*.h"
- echo
- fi
-done
-
-for EXPECTED_BOOST_INCLUDE in "${EXPECTED_BOOST_INCLUDES[@]}"; do
- if ! git grep -q "^#include <${EXPECTED_BOOST_INCLUDE}>" -- "*.cpp" "*.h"; then
- echo "Good job! The Boost dependency \"${EXPECTED_BOOST_INCLUDE}\" is no longer used."
- echo "Please remove it from EXPECTED_BOOST_INCLUDES in $0"
- echo "to make sure this dependency is not accidentally reintroduced."
- echo
- EXIT_CODE=1
- fi
-done
-
-QUOTE_SYNTAX_INCLUDES=$(git grep '^#include "' -- "*.cpp" "*.h" | grep -Ev "${IGNORE_REGEXP}")
-if [[ ${QUOTE_SYNTAX_INCLUDES} != "" ]]; then
- echo "Please use bracket syntax includes (\"#include <foo.h>\") instead of quote syntax includes:"
- echo "${QUOTE_SYNTAX_INCLUDES}"
- echo
- EXIT_CODE=1
-fi
-
-exit ${EXIT_CODE}
diff --git a/test/lint/lint-logs.py b/test/lint/lint-logs.py
new file mode 100755
index 0000000000..e6c4c068fb
--- /dev/null
+++ b/test/lint/lint-logs.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python3
+#
+# Copyright (c) 2018-2022 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#
+# Check that all logs are terminated with '\n'
+#
+# Some logs are continued over multiple lines. They should be explicitly
+# commented with /* Continued */
+
+import re
+import sys
+
+from subprocess import check_output
+
+
+def main():
+ logs_list = check_output(["git", "grep", "--extended-regexp", r"LogPrintf?\(", "--", "*.cpp"], universal_newlines=True, encoding="utf8").splitlines()
+
+ unterminated_logs = [line for line in logs_list if not re.search(r'(\\n"|/\* Continued \*/)', line)]
+
+ if unterminated_logs != []:
+ print("All calls to LogPrintf() and LogPrint() should be terminated with \\n")
+ print("")
+
+ for line in unterminated_logs:
+ print(line)
+
+ sys.exit(1)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/test/lint/lint-logs.sh b/test/lint/lint-logs.sh
deleted file mode 100755
index 6d5165f649..0000000000
--- a/test/lint/lint-logs.sh
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/usr/bin/env bash
-#
-# Copyright (c) 2018-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.
-#
-# Check that all logs are terminated with '\n'
-#
-# Some logs are continued over multiple lines. They should be explicitly
-# commented with /* Continued */
-#
-# There are some instances of LogPrintf() in comments. Those can be
-# ignored
-
-export LC_ALL=C
-UNTERMINATED_LOGS=$(git grep --extended-regexp "LogPrintf?\(" -- "*.cpp" | \
- grep -v '\\n"' | \
- grep -v '\.\.\.' | \
- grep -v "/\* Continued \*/" | \
- grep -v "LogPrint()" | \
- grep -v "LogPrintf()")
-if [[ ${UNTERMINATED_LOGS} != "" ]]; then
- # shellcheck disable=SC2028
- echo "All calls to LogPrintf() and LogPrint() should be terminated with \\n"
- echo
- echo "${UNTERMINATED_LOGS}"
- exit 1
-fi
diff --git a/test/lint/lint-python-mutable-default-parameters.py b/test/lint/lint-python-mutable-default-parameters.py
new file mode 100755
index 0000000000..7991e3630b
--- /dev/null
+++ b/test/lint/lint-python-mutable-default-parameters.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python3
+#
+# Copyright (c) 2019 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+"""
+Detect when a mutable list or dict is used as a default parameter value in a Python function.
+"""
+
+import subprocess
+import sys
+
+
+def main():
+ command = [
+ "git",
+ "grep",
+ "-E",
+ r"^\s*def [a-zA-Z0-9_]+\(.*=\s*(\[|\{)",
+ "--",
+ "*.py",
+ ]
+ output = subprocess.run(command, stdout=subprocess.PIPE, universal_newlines=True)
+ if len(output.stdout) > 0:
+ error_msg = (
+ "A mutable list or dict seems to be used as default parameter value:\n\n"
+ f"{output.stdout}\n"
+ f"{example()}"
+ )
+ print(error_msg)
+ sys.exit(1)
+ else:
+ sys.exit(0)
+
+
+def example():
+ return """This is how mutable list and dict default parameter values behave:
+
+>>> def f(i, j=[], k={}):
+... j.append(i)
+... k[i] = True
+... return j, k
+...
+>>> f(1)
+([1], {1: True})
+>>> f(1)
+([1, 1], {1: True})
+>>> f(2)
+([1, 1, 2], {1: True, 2: True})
+
+The intended behaviour was likely:
+
+>>> def f(i, j=None, k=None):
+... if j is None:
+... j = []
+... if k is None:
+... k = {}
+... j.append(i)
+... k[i] = True
+... return j, k
+...
+>>> f(1)
+([1], {1: True})
+>>> f(1)
+([1], {1: True})
+>>> f(2)
+([2], {2: True})"""
+
+
+if __name__ == "__main__":
+ main()
diff --git a/test/lint/lint-python-mutable-default-parameters.sh b/test/lint/lint-python-mutable-default-parameters.sh
deleted file mode 100755
index 1f9f035d30..0000000000
--- a/test/lint/lint-python-mutable-default-parameters.sh
+++ /dev/null
@@ -1,52 +0,0 @@
-#!/usr/bin/env bash
-#
-# Copyright (c) 2019 The Bitcoin Core developers
-# Distributed under the MIT software license, see the accompanying
-# file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#
-# Detect when a mutable list or dict is used as a default parameter value in a Python function.
-
-export LC_ALL=C
-EXIT_CODE=0
-OUTPUT=$(git grep -E '^\s*def [a-zA-Z0-9_]+\(.*=\s*(\[|\{)' -- "*.py")
-if [[ ${OUTPUT} != "" ]]; then
- echo "A mutable list or dict seems to be used as default parameter value:"
- echo
- echo "${OUTPUT}"
- echo
- cat << EXAMPLE
-This is how mutable list and dict default parameter values behave:
-
->>> def f(i, j=[], k={}):
-... j.append(i)
-... k[i] = True
-... return j, k
-...
->>> f(1)
-([1], {1: True})
->>> f(1)
-([1, 1], {1: True})
->>> f(2)
-([1, 1, 2], {1: True, 2: True})
-
-The intended behaviour was likely:
-
->>> def f(i, j=None, k=None):
-... if j is None:
-... j = []
-... if k is None:
-... k = {}
-... j.append(i)
-... k[i] = True
-... return j, k
-...
->>> f(1)
-([1], {1: True})
->>> f(1)
-([1], {1: True})
->>> f(2)
-([2], {2: True})
-EXAMPLE
- EXIT_CODE=1
-fi
-exit ${EXIT_CODE}
diff --git a/test/lint/lint-python.py b/test/lint/lint-python.py
new file mode 100755
index 0000000000..4d16facfea
--- /dev/null
+++ b/test/lint/lint-python.py
@@ -0,0 +1,131 @@
+#!/usr/bin/env python3
+#
+# Copyright (c) 2022 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+"""
+Check for specified flake8 and mypy warnings in python files.
+"""
+
+import os
+import pkg_resources
+import subprocess
+import sys
+
+DEPS = ['flake8', 'mypy', 'pyzmq']
+MYPY_CACHE_DIR = f"{os.getenv('BASE_ROOT_DIR', '')}/test/.mypy_cache"
+FILES_ARGS = ['git', 'ls-files', 'test/functional/*.py', 'contrib/devtools/*.py']
+
+ENABLED = (
+ 'E101,' # indentation contains mixed spaces and tabs
+ 'E112,' # expected an indented block
+ 'E113,' # unexpected indentation
+ 'E115,' # expected an indented block (comment)
+ 'E116,' # unexpected indentation (comment)
+ 'E125,' # continuation line with same indent as next logical line
+ 'E129,' # visually indented line with same indent as next logical line
+ 'E131,' # continuation line unaligned for hanging indent
+ 'E133,' # closing bracket is missing indentation
+ 'E223,' # tab before operator
+ 'E224,' # tab after operator
+ 'E242,' # tab after ','
+ 'E266,' # too many leading '#' for block comment
+ 'E271,' # multiple spaces after keyword
+ 'E272,' # multiple spaces before keyword
+ 'E273,' # tab after keyword
+ 'E274,' # tab before keyword
+ 'E275,' # missing whitespace after keyword
+ 'E304,' # blank lines found after function decorator
+ 'E306,' # expected 1 blank line before a nested definition
+ 'E401,' # multiple imports on one line
+ 'E402,' # module level import not at top of file
+ 'E502,' # the backslash is redundant between brackets
+ 'E701,' # multiple statements on one line (colon)
+ 'E702,' # multiple statements on one line (semicolon)
+ 'E703,' # statement ends with a semicolon
+ 'E711,' # comparison to None should be 'if cond is None:'
+ 'E714,' # test for object identity should be "is not"
+ 'E721,' # do not compare types, use "isinstance()"
+ 'E742,' # do not define classes named "l", "O", or "I"
+ 'E743,' # do not define functions named "l", "O", or "I"
+ 'E901,' # SyntaxError: invalid syntax
+ 'E902,' # TokenError: EOF in multi-line string
+ 'F401,' # module imported but unused
+ 'F402,' # import module from line N shadowed by loop variable
+ 'F403,' # 'from foo_module import *' used; unable to detect undefined names
+ 'F404,' # future import(s) name after other statements
+ 'F405,' # foo_function may be undefined, or defined from star imports: bar_module
+ 'F406,' # "from module import *" only allowed at module level
+ 'F407,' # an undefined __future__ feature name was imported
+ 'F601,' # dictionary key name repeated with different values
+ 'F602,' # dictionary key variable name repeated with different values
+ 'F621,' # too many expressions in an assignment with star-unpacking
+ 'F622,' # two or more starred expressions in an assignment (a, *b, *c = d)
+ 'F631,' # assertion test is a tuple, which are always True
+ 'F632,' # use ==/!= to compare str, bytes, and int literals
+ 'F701,' # a break statement outside of a while or for loop
+ 'F702,' # a continue statement outside of a while or for loop
+ 'F703,' # a continue statement in a finally block in a loop
+ 'F704,' # a yield or yield from statement outside of a function
+ 'F705,' # a return statement with arguments inside a generator
+ 'F706,' # a return statement outside of a function/method
+ 'F707,' # an except: block as not the last exception handler
+ 'F811,' # redefinition of unused name from line N
+ 'F812,' # list comprehension redefines 'foo' from line N
+ 'F821,' # undefined name 'Foo'
+ 'F822,' # undefined name name in __all__
+ 'F823,' # local variable name … referenced before assignment
+ 'F831,' # duplicate argument name in function definition
+ 'F841,' # local variable 'foo' is assigned to but never used
+ 'W191,' # indentation contains tabs
+ 'W291,' # trailing whitespace
+ 'W292,' # no newline at end of file
+ 'W293,' # blank line contains whitespace
+ 'W601,' # .has_key() is deprecated, use "in"
+ 'W602,' # deprecated form of raising exception
+ 'W603,' # "<>" is deprecated, use "!="
+ 'W604,' # backticks are deprecated, use "repr()"
+ 'W605,' # invalid escape sequence "x"
+ 'W606,' # 'async' and 'await' are reserved keywords starting with Python 3.7
+)
+
+
+def check_dependencies():
+ working_set = {pkg.key for pkg in pkg_resources.working_set}
+
+ for dep in DEPS:
+ if dep not in working_set:
+ print(f"Skipping Python linting since {dep} is not installed.")
+ exit(0)
+
+
+def main():
+ check_dependencies()
+
+ if len(sys.argv) > 1:
+ flake8_files = sys.argv[1:]
+ else:
+ files_args = ['git', 'ls-files', '*.py']
+ flake8_files = subprocess.check_output(files_args).decode("utf-8").splitlines()
+
+ flake8_args = ['flake8', '--ignore=B,C,E,F,I,N,W', f'--select={ENABLED}'] + flake8_files
+ flake8_env = os.environ.copy()
+ flake8_env["PYTHONWARNINGS"] = "ignore"
+
+ try:
+ subprocess.check_call(flake8_args, env=flake8_env)
+ except subprocess.CalledProcessError:
+ exit(1)
+
+ mypy_files = subprocess.check_output(FILES_ARGS).decode("utf-8").splitlines()
+ mypy_args = ['mypy', '--show-error-codes'] + mypy_files
+
+ try:
+ subprocess.check_call(mypy_args)
+ except subprocess.CalledProcessError:
+ exit(1)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/test/lint/lint-python.sh b/test/lint/lint-python.sh
deleted file mode 100755
index 7d7857d325..0000000000
--- a/test/lint/lint-python.sh
+++ /dev/null
@@ -1,111 +0,0 @@
-#!/usr/bin/env bash
-#
-# Copyright (c) 2017-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.
-#
-# Check for specified flake8 warnings in python files.
-
-export LC_ALL=C
-export MYPY_CACHE_DIR="${BASE_ROOT_DIR}/test/.mypy_cache"
-
-enabled=(
- E101 # indentation contains mixed spaces and tabs
- E112 # expected an indented block
- E113 # unexpected indentation
- E115 # expected an indented block (comment)
- E116 # unexpected indentation (comment)
- E125 # continuation line with same indent as next logical line
- E129 # visually indented line with same indent as next logical line
- E131 # continuation line unaligned for hanging indent
- E133 # closing bracket is missing indentation
- E223 # tab before operator
- E224 # tab after operator
- E242 # tab after ','
- E266 # too many leading '#' for block comment
- E271 # multiple spaces after keyword
- E272 # multiple spaces before keyword
- E273 # tab after keyword
- E274 # tab before keyword
- E275 # missing whitespace after keyword
- E304 # blank lines found after function decorator
- E306 # expected 1 blank line before a nested definition
- E401 # multiple imports on one line
- E402 # module level import not at top of file
- E502 # the backslash is redundant between brackets
- E701 # multiple statements on one line (colon)
- E702 # multiple statements on one line (semicolon)
- E703 # statement ends with a semicolon
- E711 # comparison to None should be 'if cond is None:'
- E714 # test for object identity should be "is not"
- E721 # do not compare types, use "isinstance()"
- E742 # do not define classes named "l", "O", or "I"
- E743 # do not define functions named "l", "O", or "I"
- E901 # SyntaxError: invalid syntax
- E902 # TokenError: EOF in multi-line string
- F401 # module imported but unused
- F402 # import module from line N shadowed by loop variable
- F403 # 'from foo_module import *' used; unable to detect undefined names
- F404 # future import(s) name after other statements
- F405 # foo_function may be undefined, or defined from star imports: bar_module
- F406 # "from module import *" only allowed at module level
- F407 # an undefined __future__ feature name was imported
- F601 # dictionary key name repeated with different values
- F602 # dictionary key variable name repeated with different values
- F621 # too many expressions in an assignment with star-unpacking
- F622 # two or more starred expressions in an assignment (a, *b, *c = d)
- F631 # assertion test is a tuple, which are always True
- F632 # use ==/!= to compare str, bytes, and int literals
- F701 # a break statement outside of a while or for loop
- F702 # a continue statement outside of a while or for loop
- F703 # a continue statement in a finally block in a loop
- F704 # a yield or yield from statement outside of a function
- F705 # a return statement with arguments inside a generator
- F706 # a return statement outside of a function/method
- F707 # an except: block as not the last exception handler
- F811 # redefinition of unused name from line N
- F812 # list comprehension redefines 'foo' from line N
- F821 # undefined name 'Foo'
- F822 # undefined name name in __all__
- F823 # local variable name … referenced before assignment
- F831 # duplicate argument name in function definition
- F841 # local variable 'foo' is assigned to but never used
- W191 # indentation contains tabs
- W291 # trailing whitespace
- W292 # no newline at end of file
- W293 # blank line contains whitespace
- W601 # .has_key() is deprecated, use "in"
- W602 # deprecated form of raising exception
- W603 # "<>" is deprecated, use "!="
- W604 # backticks are deprecated, use "repr()"
- W605 # invalid escape sequence "x"
- W606 # 'async' and 'await' are reserved keywords starting with Python 3.7
-)
-
-if ! command -v flake8 > /dev/null; then
- echo "Skipping Python linting since flake8 is not installed."
- exit 0
-elif PYTHONWARNINGS="ignore" flake8 --version | grep -q "Python 2"; then
- echo "Skipping Python linting since flake8 is running under Python 2. Install the Python 3 version of flake8."
- exit 0
-fi
-
-EXIT_CODE=0
-
-# shellcheck disable=SC2046
-if ! PYTHONWARNINGS="ignore" flake8 --ignore=B,C,E,F,I,N,W --select=$(IFS=","; echo "${enabled[*]}") $(
- if [[ $# == 0 ]]; then
- git ls-files "*.py"
- else
- echo "$@"
- fi
-); then
- EXIT_CODE=1
-fi
-
-mapfile -t FILES < <(git ls-files "test/functional/*.py" "contrib/devtools/*.py")
-if ! mypy --show-error-codes "${FILES[@]}"; then
- EXIT_CODE=1
-fi
-
-exit $EXIT_CODE
diff --git a/test/lint/lint-whitespace.py b/test/lint/lint-whitespace.py
new file mode 100755
index 0000000000..d98fc8d9a2
--- /dev/null
+++ b/test/lint/lint-whitespace.py
@@ -0,0 +1,135 @@
+#!/usr/bin/env python3
+#
+# Copyright (c) 2017-2022 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#
+# Check for new lines in diff that introduce trailing whitespace or
+# tab characters instead of spaces.
+
+# We can't run this check unless we know the commit range for the PR.
+
+import argparse
+import os
+import re
+import sys
+
+from subprocess import check_output
+
+EXCLUDED_DIRS = ["depends/patches/",
+ "contrib/guix/patches/",
+ "src/leveldb/",
+ "src/crc32c/",
+ "src/secp256k1/",
+ "src/minisketch/",
+ "src/univalue/",
+ "doc/release-notes/",
+ "src/qt/locale"]
+
+def parse_args():
+ """Parse command line arguments."""
+ parser = argparse.ArgumentParser(
+ description="""
+ Check for new lines in diff that introduce trailing whitespace
+ or tab characters instead of spaces in unstaged changes, the
+ previous n commits, or a commit-range.
+ """,
+ epilog=f"""
+ You can manually set the commit-range with the COMMIT_RANGE
+ environment variable (e.g. "COMMIT_RANGE='47ba2c3...ee50c9e'
+ {sys.argv[0]}"). Defaults to current merge base when neither
+ prev-commits nor the environment variable is set.
+ """)
+
+ parser.add_argument("--prev-commits", "-p", required=False, help="The previous n commits to check")
+
+ return parser.parse_args()
+
+
+def report_diff(selection):
+ filename = ""
+ seen = False
+ seenln = False
+
+ print("The following changes were suspected:")
+
+ for line in selection:
+ if re.match(r"^diff", line):
+ filename = line
+ seen = False
+ elif re.match(r"^@@", line):
+ linenumber = line
+ seenln = False
+ else:
+ if not seen:
+ # The first time a file is seen with trailing whitespace or a tab character, we print the
+ # filename (preceded by a newline).
+ print("")
+ print(filename)
+ seen = True
+ if not seenln:
+ print(linenumber)
+ seenln = True
+ print(line)
+
+
+def get_diff(commit_range, check_only_code):
+ exclude_args = [":(exclude)" + dir for dir in EXCLUDED_DIRS]
+
+ if check_only_code:
+ what_files = ["*.cpp", "*.h", "*.md", "*.py", "*.sh"]
+ else:
+ what_files = ["."]
+
+ diff = check_output(["git", "diff", "-U0", commit_range, "--"] + what_files + exclude_args, universal_newlines=True, encoding="utf8")
+
+ return diff
+
+
+def main():
+ args = parse_args()
+
+ if not os.getenv("COMMIT_RANGE"):
+ if args.prev_commits:
+ commit_range = "HEAD~" + args.prev_commits + "...HEAD"
+ else:
+ # This assumes that the target branch of the pull request will be master.
+ merge_base = check_output(["git", "merge-base", "HEAD", "master"], universal_newlines=True, encoding="utf8").rstrip("\n")
+ commit_range = merge_base + "..HEAD"
+ else:
+ commit_range = os.getenv("COMMIT_RANGE")
+
+ whitespace_selection = []
+ tab_selection = []
+
+ # Check if trailing whitespace was found in the diff.
+ for line in get_diff(commit_range, check_only_code=False).splitlines():
+ if re.match(r"^(diff --git|\@@|^\+.*\s+$)", line):
+ whitespace_selection.append(line)
+
+ whitespace_additions = [i for i in whitespace_selection if i.startswith("+")]
+
+ # Check if tab characters were found in the diff.
+ for line in get_diff(commit_range, check_only_code=True).splitlines():
+ if re.match(r"^(diff --git|\@@|^\+.*\t)", line):
+ tab_selection.append(line)
+
+ tab_additions = [i for i in tab_selection if i.startswith("+")]
+
+ ret = 0
+
+ if len(whitespace_additions) > 0:
+ print("This diff appears to have added new lines with trailing whitespace.")
+ report_diff(whitespace_selection)
+ ret = 1
+
+ if len(tab_additions) > 0:
+ print("This diff appears to have added new lines with tab characters instead of spaces.")
+ report_diff(tab_selection)
+ ret = 1
+
+ sys.exit(ret)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/test/lint/lint-whitespace.sh b/test/lint/lint-whitespace.sh
deleted file mode 100755
index 9d55c71eb5..0000000000
--- a/test/lint/lint-whitespace.sh
+++ /dev/null
@@ -1,115 +0,0 @@
-#!/usr/bin/env bash
-#
-# Copyright (c) 2017-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.
-#
-# Check for new lines in diff that introduce trailing whitespace.
-
-# We can't run this check unless we know the commit range for the PR.
-
-export LC_ALL=C
-while getopts "?" opt; do
- case $opt in
- ?)
- echo "Usage: $0 [N]"
- echo " COMMIT_RANGE='<commit range>' $0"
- echo " $0 -?"
- echo "Checks unstaged changes, the previous N commits, or a commit range."
- echo "COMMIT_RANGE='47ba2c3...ee50c9e' $0"
- exit 0
- ;;
- esac
-done
-
-if [ -z "${COMMIT_RANGE}" ]; then
- if [ -n "$1" ]; then
- COMMIT_RANGE="HEAD~$1...HEAD"
- else
- # This assumes that the target branch of the pull request will be master.
- MERGE_BASE=$(git merge-base HEAD master)
- COMMIT_RANGE="$MERGE_BASE..HEAD"
- fi
-fi
-
-showdiff() {
- if ! git diff -U0 "${COMMIT_RANGE}" -- "." ":(exclude)depends/patches/" ":(exclude)contrib/guix/patches/" ":(exclude)src/leveldb/" ":(exclude)src/crc32c/" ":(exclude)src/secp256k1/" ":(exclude)src/minisketch/" ":(exclude)src/univalue/" ":(exclude)doc/release-notes/" ":(exclude)src/qt/locale/"; then
- echo "Failed to get a diff"
- exit 1
- fi
-}
-
-showcodediff() {
- if ! git diff -U0 "${COMMIT_RANGE}" -- *.cpp *.h *.md *.py *.sh ":(exclude)src/leveldb/" ":(exclude)src/crc32c/" ":(exclude)src/secp256k1/" ":(exclude)src/minisketch/" ":(exclude)src/univalue/" ":(exclude)doc/release-notes/" ":(exclude)src/qt/locale/"; then
- echo "Failed to get a diff"
- exit 1
- fi
-}
-
-RET=0
-
-# Check if trailing whitespace was found in the diff.
-if showdiff | grep -E -q '^\+.*\s+$'; then
- echo "This diff appears to have added new lines with trailing whitespace."
- echo "The following changes were suspected:"
- FILENAME=""
- SEEN=0
- SEENLN=0
- while read -r line; do
- if [[ "$line" =~ ^diff ]]; then
- FILENAME="$line"
- SEEN=0
- elif [[ "$line" =~ ^@@ ]]; then
- LINENUMBER="$line"
- SEENLN=0
- else
- if [ "$SEEN" -eq 0 ]; then
- # The first time a file is seen with trailing whitespace, we print the
- # filename (preceded by a newline).
- echo
- echo "$FILENAME"
- SEEN=1
- fi
- if [ "$SEENLN" -eq 0 ]; then
- echo "$LINENUMBER"
- SEENLN=1
- fi
- echo "$line"
- fi
- done < <(showdiff | grep -E '^(diff --git |@@|\+.*\s+$)')
- RET=1
-fi
-
-# Check if tab characters were found in the diff.
-if showcodediff | perl -nle '$MATCH++ if m{^\+.*\t}; END{exit 1 unless $MATCH>0}' > /dev/null; then
- echo "This diff appears to have added new lines with tab characters instead of spaces."
- echo "The following changes were suspected:"
- FILENAME=""
- SEEN=0
- SEENLN=0
- while read -r line; do
- if [[ "$line" =~ ^diff ]]; then
- FILENAME="$line"
- SEEN=0
- elif [[ "$line" =~ ^@@ ]]; then
- LINENUMBER="$line"
- SEENLN=0
- else
- if [ "$SEEN" -eq 0 ]; then
- # The first time a file is seen with a tab character, we print the
- # filename (preceded by a newline).
- echo
- echo "$FILENAME"
- SEEN=1
- fi
- if [ "$SEENLN" -eq 0 ]; then
- echo "$LINENUMBER"
- SEENLN=1
- fi
- echo "$line"
- fi
- done < <(showcodediff | perl -nle 'print if m{^(diff --git |@@|\+.*\t)}')
- RET=1
-fi
-
-exit $RET