diff options
100 files changed, 601 insertions, 522 deletions
diff --git a/.travis.yml b/.travis.yml index 75df290187..9332160926 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,8 +35,8 @@ env: - HOST=i686-pc-linux-gnu PACKAGES="g++-multilib python3-zmq" DEP_OPTS="NO_QT=1" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-zmq --enable-glibc-back-compat --enable-reduce-exports LDFLAGS=-static-libstdc++" CONFIG_SHELL="/bin/dash" # x86_64 Linux (uses qt5 dev package instead of depends Qt to speed up build and avoid timeout) - HOST=x86_64-unknown-linux-gnu PACKAGES="python3-zmq qtbase5-dev qttools5-dev-tools protobuf-compiler libdbus-1-dev libharfbuzz-dev libprotobuf-dev" DEP_OPTS="NO_QT=1 NO_UPNP=1 DEBUG=1 ALLOW_HOST_PACKAGES=1" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-zmq --with-gui=qt5 --enable-glibc-back-compat --enable-reduce-exports CPPFLAGS=-DDEBUG_LOCKORDER" -# Qt4 & system libs - - HOST=x86_64-unknown-linux-gnu PACKAGES="python3-zmq qt4-dev-tools libssl1.0-dev libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-program-options-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libzmq3-dev libprotobuf-dev protobuf-compiler libqrencode-dev xvfb libqt4-dev" NO_DEPENDS=1 NEED_XVFB=1 RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-zmq --with-incompatible-bdb --enable-glibc-back-compat --enable-reduce-exports --with-gui=qt4 CPPFLAGS=-DDEBUG_LOCKORDER" DISPLAY=:99.0 +# x86_64 Linux (Qt5 & system libs) + - HOST=x86_64-unknown-linux-gnu PACKAGES="python3-zmq qtbase5-dev qttools5-dev-tools libssl1.0-dev libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-program-options-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libzmq3-dev libprotobuf-dev protobuf-compiler libqrencode-dev" NO_DEPENDS=1 RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-zmq --with-incompatible-bdb --enable-glibc-back-compat --enable-reduce-exports --with-gui=qt5 CPPFLAGS=-DDEBUG_LOCKORDER" # x86_64 Linux, No wallet - HOST=x86_64-unknown-linux-gnu PACKAGES="python3" DEP_OPTS="NO_WALLET=1" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports" # Cross-Mac @@ -45,7 +45,7 @@ env: before_install: - export PATH=$(echo $PATH | tr ':' "\n" | sed '/\/opt\/python/d' | tr "\n" ":" | sed "s|::|:|g") install: - - env | grep -E '^(CCACHE_|WINEDEBUG|DISPLAY|BOOST_TEST_RANDOM|CONFIG_SHELL)' | tee /tmp/env + - env | grep -E '^(CCACHE_|WINEDEBUG|BOOST_TEST_RANDOM|CONFIG_SHELL)' | tee /tmp/env - if [[ $HOST = *-mingw32 ]]; then DOCKER_ADMIN="--cap-add SYS_ADMIN"; fi - DOCKER_ID=$(docker run $DOCKER_ADMIN -idt --mount type=bind,src=$TRAVIS_BUILD_DIR,dst=$TRAVIS_BUILD_DIR --mount type=bind,src=$CCACHE_DIR,dst=$CCACHE_DIR -w $TRAVIS_BUILD_DIR --env-file /tmp/env ubuntu:18.04) - DOCKER_EXEC () { docker exec $DOCKER_ID bash -c "cd $PWD && $*"; } @@ -58,8 +58,6 @@ before_script: - if [ -n "$OSX_SDK" -a -f depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz ]; then tar -C depends/SDKs -xf depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz; fi - if [[ $HOST = *-mingw32 ]]; then DOCKER_EXEC update-alternatives --set $HOST-g++ \$\(which $HOST-g++-posix\); fi - if [ -z "$NO_DEPENDS" ]; then DOCKER_EXEC CONFIG_SHELL= make $MAKEJOBS -C depends HOST=$HOST $DEP_OPTS; fi - # Start xvfb if needed, as documented at https://docs.travis-ci.com/user/gui-and-headless-browsers/#Using-xvfb-to-Run-Tests-That-Require-a-GUI - - if [ "$NEED_XVFB" = 1 ]; then DOCKER_EXEC /sbin/start-stop-daemon --start --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac; fi script: - export TRAVIS_COMMIT_LOG=`git log --format=fuller -1` - OUTDIR=$BASE_OUTDIR/$TRAVIS_PULL_REQUEST/$TRAVIS_JOB_NUMBER-$HOST diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c390595abf..3ee5a04796 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -127,10 +127,10 @@ before it will be merged. The basic squashing workflow is shown below. git checkout your_branch_name git rebase -i HEAD~n - # n is normally the number of commits in the pull - # set commits from 'pick' to 'squash', save and quit - # on the next screen, edit/refine commit messages - # save and quit + # n is normally the number of commits in the pull request. + # Set commits (except the one in the first line) from 'pick' to 'squash', save and quit. + # On the next screen, edit/refine commit messages. + # Save and quit. git push -f # (force push to GitHub) If you have problems with squashing (or other workflows with `git`), you can diff --git a/Makefile.am b/Makefile.am index 0ed3dd289a..f3f3302fce 100644 --- a/Makefile.am +++ b/Makefile.am @@ -95,9 +95,9 @@ $(OSX_APP)/Contents/Resources/bitcoin.icns: $(OSX_INSTALLER_ICONS) $(MKDIR_P) $(@D) $(INSTALL_DATA) $< $@ -$(OSX_APP)/Contents/MacOS/Bitcoin-Qt: $(BITCOIN_QT_BIN) +$(OSX_APP)/Contents/MacOS/Bitcoin-Qt: all-recursive $(MKDIR_P) $(@D) - STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $< $@ + STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $(BITCOIN_QT_BIN) $@ $(OSX_APP)/Contents/Resources/Base.lproj/InfoPlist.strings: $(MKDIR_P) $(@D) diff --git a/autogen.sh b/autogen.sh index 27417daf76..0c05626ccc 100755 --- a/autogen.sh +++ b/autogen.sh @@ -3,6 +3,7 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +export LC_ALL=C set -e srcdir="$(dirname $0)" cd "$srcdir" diff --git a/build-aux/m4/bitcoin_qt.m4 b/build-aux/m4/bitcoin_qt.m4 index f41508336c..fe0cc6c36d 100644 --- a/build-aux/m4/bitcoin_qt.m4 +++ b/build-aux/m4/bitcoin_qt.m4 @@ -53,8 +53,8 @@ dnl CAUTION: Do not use this inside of a conditional. AC_DEFUN([BITCOIN_QT_INIT],[ dnl enable qt support AC_ARG_WITH([gui], - [AS_HELP_STRING([--with-gui@<:@=no|qt4|qt5|auto@:>@], - [build bitcoin-qt GUI (default=auto, qt5 tried first)])], + [AS_HELP_STRING([--with-gui@<:@=no|qt5|auto@:>@], + [build bitcoin-qt GUI (default=auto)])], [ bitcoin_qt_want_version=$withval if test "x$bitcoin_qt_want_version" = xyes; then @@ -94,18 +94,17 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[ fi if test "x$use_pkgconfig" = xyes; then - BITCOIN_QT_CHECK([_BITCOIN_QT_FIND_LIBS_WITH_PKGCONFIG([$2])]) + BITCOIN_QT_CHECK([_BITCOIN_QT_FIND_LIBS_WITH_PKGCONFIG]) else BITCOIN_QT_CHECK([_BITCOIN_QT_FIND_LIBS_WITHOUT_PKGCONFIG]) fi dnl This is ugly and complicated. Yuck. Works as follows: - dnl We can't discern whether Qt4 builds are static or not. For Qt5, we can - dnl check a header to find out. When Qt is built statically, some plugins must - dnl be linked into the final binary as well. These plugins have changed between - dnl Qt4 and Qt5. With Qt5, languages moved into core and the WindowsIntegration - dnl plugin was added. Since we can't tell if Qt4 is static or not, it is - dnl assumed for windows builds. + dnl For Qt5, we can check a header to find out whether Qt is build + dnl statically. When Qt is built statically, some plugins must be linked into + dnl the final binary as well. + dnl With Qt5, languages moved into core and the WindowsIntegration plugin was + dnl added. dnl _BITCOIN_QT_CHECK_STATIC_PLUGINS does a quick link-check and appends the dnl results to QT_LIBS. BITCOIN_QT_CHECK([ @@ -113,53 +112,40 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[ TEMP_CXXFLAGS=$CXXFLAGS CPPFLAGS="$QT_INCLUDES $CPPFLAGS" CXXFLAGS="$PIC_FLAGS $CXXFLAGS" - if test "x$bitcoin_qt_got_major_vers" = x5; then - _BITCOIN_QT_IS_STATIC - if test "x$bitcoin_cv_static_qt" = xyes; then - _BITCOIN_QT_FIND_STATIC_PLUGINS - AC_DEFINE(QT_STATICPLUGIN, 1, [Define this symbol if qt plugins are static]) - AC_CACHE_CHECK(for Qt < 5.4, bitcoin_cv_need_acc_widget,[ - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ - #include <QtCore/qconfig.h> - #ifndef QT_VERSION - # include <QtCore/qglobal.h> - #endif - ]], - [[ - #if QT_VERSION >= 0x050400 - choke - #endif - ]])], - [bitcoin_cv_need_acc_widget=yes], - [bitcoin_cv_need_acc_widget=no]) - ]) - if test "x$bitcoin_cv_need_acc_widget" = xyes; then - _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(AccessibleFactory)], [-lqtaccessiblewidgets]) - fi - _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QMinimalIntegrationPlugin)],[-lqminimal]) - AC_DEFINE(QT_QPA_PLATFORM_MINIMAL, 1, [Define this symbol if the minimal qt platform exists]) - if test "x$TARGET_OS" = xwindows; then - _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin)],[-lqwindows]) - AC_DEFINE(QT_QPA_PLATFORM_WINDOWS, 1, [Define this symbol if the qt platform is windows]) - elif test "x$TARGET_OS" = xlinux; then - _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QXcbIntegrationPlugin)],[-lqxcb -lxcb-static]) - AC_DEFINE(QT_QPA_PLATFORM_XCB, 1, [Define this symbol if the qt platform is xcb]) - elif test "x$TARGET_OS" = xdarwin; then - AX_CHECK_LINK_FLAG([[-framework IOKit]],[QT_LIBS="$QT_LIBS -framework IOKit"],[AC_MSG_ERROR(could not iokit framework)]) - _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin)],[-lqcocoa]) - AC_DEFINE(QT_QPA_PLATFORM_COCOA, 1, [Define this symbol if the qt platform is cocoa]) - fi + _BITCOIN_QT_IS_STATIC + if test "x$bitcoin_cv_static_qt" = xyes; then + _BITCOIN_QT_FIND_STATIC_PLUGINS + AC_DEFINE(QT_STATICPLUGIN, 1, [Define this symbol if qt plugins are static]) + AC_CACHE_CHECK(for Qt < 5.4, bitcoin_cv_need_acc_widget,[ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #include <QtCore/qconfig.h> + #ifndef QT_VERSION + # include <QtCore/qglobal.h> + #endif + ]], + [[ + #if QT_VERSION >= 0x050400 + choke + #endif + ]])], + [bitcoin_cv_need_acc_widget=yes], + [bitcoin_cv_need_acc_widget=no]) + ]) + if test "x$bitcoin_cv_need_acc_widget" = xyes; then + _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(AccessibleFactory)], [-lqtaccessiblewidgets]) fi - else + _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QMinimalIntegrationPlugin)],[-lqminimal]) + AC_DEFINE(QT_QPA_PLATFORM_MINIMAL, 1, [Define this symbol if the minimal qt platform exists]) if test "x$TARGET_OS" = xwindows; then - AC_DEFINE(QT_STATICPLUGIN, 1, [Define this symbol if qt plugins are static]) - _BITCOIN_QT_CHECK_STATIC_PLUGINS([ - Q_IMPORT_PLUGIN(qcncodecs) - Q_IMPORT_PLUGIN(qjpcodecs) - Q_IMPORT_PLUGIN(qtwcodecs) - Q_IMPORT_PLUGIN(qkrcodecs) - Q_IMPORT_PLUGIN(AccessibleFactory)], - [-lqcncodecs -lqjpcodecs -lqtwcodecs -lqkrcodecs -lqtaccessiblewidgets]) + _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin)],[-lqwindows]) + AC_DEFINE(QT_QPA_PLATFORM_WINDOWS, 1, [Define this symbol if the qt platform is windows]) + elif test "x$TARGET_OS" = xlinux; then + _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QXcbIntegrationPlugin)],[-lqxcb -lxcb-static]) + AC_DEFINE(QT_QPA_PLATFORM_XCB, 1, [Define this symbol if the qt platform is xcb]) + elif test "x$TARGET_OS" = xdarwin; then + AX_CHECK_LINK_FLAG([[-framework IOKit]],[QT_LIBS="$QT_LIBS -framework IOKit"],[AC_MSG_ERROR(could not iokit framework)]) + _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin)],[-lqcocoa]) + AC_DEFINE(QT_QPA_PLATFORM_COCOA, 1, [Define this symbol if the qt platform is cocoa]) fi fi CPPFLAGS=$TEMP_CPPFLAGS @@ -167,9 +153,7 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[ ]) if test "x$use_pkgconfig$qt_bin_path" = xyes; then - if test "x$bitcoin_qt_got_major_vers" = x5; then - qt_bin_path="`$PKG_CONFIG --variable=host_bins Qt5Core 2>/dev/null`" - fi + qt_bin_path="`$PKG_CONFIG --variable=host_bins Qt5Core 2>/dev/null`" fi if test "x$use_hardening" != xno; then @@ -219,11 +203,11 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[ ]) fi - BITCOIN_QT_PATH_PROGS([MOC], [moc-qt${bitcoin_qt_got_major_vers} moc${bitcoin_qt_got_major_vers} moc], $qt_bin_path) - BITCOIN_QT_PATH_PROGS([UIC], [uic-qt${bitcoin_qt_got_major_vers} uic${bitcoin_qt_got_major_vers} uic], $qt_bin_path) - BITCOIN_QT_PATH_PROGS([RCC], [rcc-qt${bitcoin_qt_got_major_vers} rcc${bitcoin_qt_got_major_vers} rcc], $qt_bin_path) - BITCOIN_QT_PATH_PROGS([LRELEASE], [lrelease-qt${bitcoin_qt_got_major_vers} lrelease${bitcoin_qt_got_major_vers} lrelease], $qt_bin_path) - BITCOIN_QT_PATH_PROGS([LUPDATE], [lupdate-qt${bitcoin_qt_got_major_vers} lupdate${bitcoin_qt_got_major_vers} lupdate],$qt_bin_path, yes) + BITCOIN_QT_PATH_PROGS([MOC], [moc-qt5 moc5 moc], $qt_bin_path) + BITCOIN_QT_PATH_PROGS([UIC], [uic-qt5 uic5 uic], $qt_bin_path) + BITCOIN_QT_PATH_PROGS([RCC], [rcc-qt5 rcc5 rcc], $qt_bin_path) + BITCOIN_QT_PATH_PROGS([LRELEASE], [lrelease-qt5 lrelease5 lrelease], $qt_bin_path) + BITCOIN_QT_PATH_PROGS([LUPDATE], [lupdate-qt5 lupdate5 lupdate],$qt_bin_path, yes) MOC_DEFS='-DHAVE_CONFIG_H -I$(srcdir)' case $host in @@ -262,7 +246,7 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[ ],[ bitcoin_enable_qt=no ]) - AC_MSG_RESULT([$bitcoin_enable_qt (Qt${bitcoin_qt_got_major_vers})]) + AC_MSG_RESULT([$bitcoin_enable_qt (Qt5)]) AC_SUBST(QT_PIE_FLAGS) AC_SUBST(QT_INCLUDES) @@ -272,7 +256,7 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[ AC_SUBST(QT_DBUS_LIBS) AC_SUBST(QT_TEST_INCLUDES) AC_SUBST(QT_TEST_LIBS) - AC_SUBST(QT_SELECT, qt${bitcoin_qt_got_major_vers}) + AC_SUBST(QT_SELECT, qt5) AC_SUBST(MOC_DEFS) ]) @@ -301,7 +285,7 @@ AC_DEFUN([_BITCOIN_QT_CHECK_QT5],[ ])]) dnl Internal. Check if the linked version of Qt was built as static libs. -dnl Requires: Qt5. This check cannot determine if Qt4 is static. +dnl Requires: Qt5. dnl Requires: INCLUDES and LIBS must be populated as necessary. dnl Output: bitcoin_cv_static_qt=yes|no dnl Output: Defines QT_STATICPLUGIN if plugins are static. @@ -346,58 +330,50 @@ AC_DEFUN([_BITCOIN_QT_CHECK_STATIC_PLUGINS],[ ]) dnl Internal. Find paths necessary for linking qt static plugins -dnl Inputs: bitcoin_qt_got_major_vers. 4 or 5. dnl Inputs: qt_plugin_path. optional. dnl Outputs: QT_LIBS is appended AC_DEFUN([_BITCOIN_QT_FIND_STATIC_PLUGINS],[ - if test "x$bitcoin_qt_got_major_vers" = x5; then - if test "x$qt_plugin_path" != x; then - QT_LIBS="$QT_LIBS -L$qt_plugin_path/platforms" - if test -d "$qt_plugin_path/accessible"; then - QT_LIBS="$QT_LIBS -L$qt_plugin_path/accessible" - fi + if test "x$qt_plugin_path" != x; then + QT_LIBS="$QT_LIBS -L$qt_plugin_path/platforms" + if test -d "$qt_plugin_path/accessible"; then + QT_LIBS="$QT_LIBS -L$qt_plugin_path/accessible" fi - if test "x$use_pkgconfig" = xyes; then - : dnl - m4_ifdef([PKG_CHECK_MODULES],[ - PKG_CHECK_MODULES([QTPLATFORM], [Qt5PlatformSupport], [QT_LIBS="$QTPLATFORM_LIBS $QT_LIBS"]) - if test "x$TARGET_OS" = xlinux; then - PKG_CHECK_MODULES([X11XCB], [x11-xcb], [QT_LIBS="$X11XCB_LIBS $QT_LIBS"]) - if ${PKG_CONFIG} --exists "Qt5Core >= 5.5" 2>/dev/null; then - PKG_CHECK_MODULES([QTXCBQPA], [Qt5XcbQpa], [QT_LIBS="$QTXCBQPA_LIBS $QT_LIBS"]) - fi - elif test "x$TARGET_OS" = xdarwin; then - PKG_CHECK_MODULES([QTPRINT], [Qt5PrintSupport], [QT_LIBS="$QTPRINT_LIBS $QT_LIBS"]) + fi + if test "x$use_pkgconfig" = xyes; then + : dnl + m4_ifdef([PKG_CHECK_MODULES],[ + PKG_CHECK_MODULES([QTPLATFORM], [Qt5PlatformSupport], [QT_LIBS="$QTPLATFORM_LIBS $QT_LIBS"]) + if test "x$TARGET_OS" = xlinux; then + PKG_CHECK_MODULES([X11XCB], [x11-xcb], [QT_LIBS="$X11XCB_LIBS $QT_LIBS"]) + if ${PKG_CONFIG} --exists "Qt5Core >= 5.5" 2>/dev/null; then + PKG_CHECK_MODULES([QTXCBQPA], [Qt5XcbQpa], [QT_LIBS="$QTXCBQPA_LIBS $QT_LIBS"]) fi - ]) - else - if test "x$TARGET_OS" = xwindows; then - AC_CACHE_CHECK(for Qt >= 5.6, bitcoin_cv_need_platformsupport,[ - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ - #include <QtCore/qconfig.h> - #ifndef QT_VERSION - # include <QtCore/qglobal.h> - #endif - ]], - [[ - #if QT_VERSION < 0x050600 - choke - #endif - ]])], - [bitcoin_cv_need_platformsupport=yes], - [bitcoin_cv_need_platformsupport=no]) - ]) - if test "x$bitcoin_cv_need_platformsupport" = xyes; then - BITCOIN_QT_CHECK(AC_CHECK_LIB([${QT_LIB_PREFIX}PlatformSupport],[main],,BITCOIN_QT_FAIL(lib${QT_LIB_PREFIX}PlatformSupport not found))) - fi + elif test "x$TARGET_OS" = xdarwin; then + PKG_CHECK_MODULES([QTPRINT], [Qt5PrintSupport], [QT_LIBS="$QTPRINT_LIBS $QT_LIBS"]) + fi + ]) + else + if test "x$TARGET_OS" = xwindows; then + AC_CACHE_CHECK(for Qt >= 5.6, bitcoin_cv_need_platformsupport,[ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #include <QtCore/qconfig.h> + #ifndef QT_VERSION + # include <QtCore/qglobal.h> + #endif + ]], + [[ + #if QT_VERSION < 0x050600 + choke + #endif + ]])], + [bitcoin_cv_need_platformsupport=yes], + [bitcoin_cv_need_platformsupport=no]) + ]) + if test "x$bitcoin_cv_need_platformsupport" = xyes; then + BITCOIN_QT_CHECK(AC_CHECK_LIB([${QT_LIB_PREFIX}PlatformSupport],[main],,BITCOIN_QT_FAIL(lib${QT_LIB_PREFIX}PlatformSupport not found))) fi fi - else - if test "x$qt_plugin_path" != x; then - QT_LIBS="$QT_LIBS -L$qt_plugin_path/accessible" - QT_LIBS="$QT_LIBS -L$qt_plugin_path/codecs" - fi - fi + fi ]) dnl Internal. Find Qt libraries using pkg-config. @@ -406,38 +382,14 @@ dnl first. dnl Inputs: $1: If bitcoin_qt_want_version is "auto", check for this version dnl first. dnl Outputs: All necessary QT_* variables are set. -dnl Outputs: bitcoin_qt_got_major_vers is set to "4" or "5". dnl Outputs: have_qt_test and have_qt_dbus are set (if applicable) to yes|no. AC_DEFUN([_BITCOIN_QT_FIND_LIBS_WITH_PKGCONFIG],[ m4_ifdef([PKG_CHECK_MODULES],[ - auto_priority_version=$1 - if test "x$auto_priority_version" = x; then - auto_priority_version=qt5 - fi - if test "x$bitcoin_qt_want_version" = xqt5 || ( test "x$bitcoin_qt_want_version" = xauto && test "x$auto_priority_version" = xqt5 ); then - QT_LIB_PREFIX=Qt5 - bitcoin_qt_got_major_vers=5 - else - QT_LIB_PREFIX=Qt - bitcoin_qt_got_major_vers=4 - fi + QT_LIB_PREFIX=Qt5 qt5_modules="Qt5Core Qt5Gui Qt5Network Qt5Widgets" - qt4_modules="QtCore QtGui QtNetwork" BITCOIN_QT_CHECK([ - if test "x$bitcoin_qt_want_version" = xqt5 || ( test "x$bitcoin_qt_want_version" = xauto && test "x$auto_priority_version" = xqt5 ); then - PKG_CHECK_MODULES([QT5], [$qt5_modules], [QT_INCLUDES="$QT5_CFLAGS"; QT_LIBS="$QT5_LIBS" have_qt=yes],[have_qt=no]) - elif test "x$bitcoin_qt_want_version" = xqt4 || ( test "x$bitcoin_qt_want_version" = xauto && test "x$auto_priority_version" = xqt4 ); then - PKG_CHECK_MODULES([QT4], [$qt4_modules], [QT_INCLUDES="$QT4_CFLAGS"; QT_LIBS="$QT4_LIBS" ; have_qt=yes], [have_qt=no]) - fi + PKG_CHECK_MODULES([QT5], [$qt5_modules], [QT_INCLUDES="$QT5_CFLAGS"; QT_LIBS="$QT5_LIBS" have_qt=yes],[have_qt=no]) - dnl qt version is set to 'auto' and the preferred version wasn't found. Now try the other. - if test "x$have_qt" = xno && test "x$bitcoin_qt_want_version" = xauto; then - if test "x$auto_priority_version" = xqt5; then - PKG_CHECK_MODULES([QT4], [$qt4_modules], [QT_INCLUDES="$QT4_CFLAGS"; QT_LIBS="$QT4_LIBS" ; have_qt=yes; QT_LIB_PREFIX=Qt; bitcoin_qt_got_major_vers=4], [have_qt=no]) - else - PKG_CHECK_MODULES([QT5], [$qt5_modules], [QT_INCLUDES="$QT5_CFLAGS"; QT_LIBS="$QT5_LIBS" ; have_qt=yes; QT_LIB_PREFIX=Qt5; bitcoin_qt_got_major_vers=5], [have_qt=no]) - fi - fi if test "x$have_qt" != xyes; then have_qt=no BITCOIN_QT_FAIL([Qt dependencies not found]) @@ -458,7 +410,6 @@ dnl from the discovered headers. dnl Inputs: bitcoin_qt_want_version (from --with-gui=). The version to use. dnl If "auto", the version will be discovered by _BITCOIN_QT_CHECK_QT5. dnl Outputs: All necessary QT_* variables are set. -dnl Outputs: bitcoin_qt_got_major_vers is set to "4" or "5". dnl Outputs: have_qt_test and have_qt_dbus are set (if applicable) to yes|no. AC_DEFUN([_BITCOIN_QT_FIND_LIBS_WITHOUT_PKGCONFIG],[ TEMP_CPPFLAGS="$CPPFLAGS" @@ -480,13 +431,7 @@ AC_DEFUN([_BITCOIN_QT_FIND_LIBS_WITHOUT_PKGCONFIG],[ if test "x$bitcoin_qt_want_version" = xauto; then _BITCOIN_QT_CHECK_QT5 fi - if test "x$bitcoin_cv_qt5" = xyes || test "x$bitcoin_qt_want_version" = xqt5; then - QT_LIB_PREFIX=Qt5 - bitcoin_qt_got_major_vers=5 - else - QT_LIB_PREFIX=Qt - bitcoin_qt_got_major_vers=4 - fi + QT_LIB_PREFIX=Qt5 ]) BITCOIN_QT_CHECK([ @@ -508,9 +453,7 @@ AC_DEFUN([_BITCOIN_QT_FIND_LIBS_WITHOUT_PKGCONFIG],[ BITCOIN_QT_CHECK(AC_CHECK_LIB([${QT_LIB_PREFIX}Core] ,[main],,BITCOIN_QT_FAIL(lib${QT_LIB_PREFIX}Core not found))) BITCOIN_QT_CHECK(AC_CHECK_LIB([${QT_LIB_PREFIX}Gui] ,[main],,BITCOIN_QT_FAIL(lib${QT_LIB_PREFIX}Gui not found))) BITCOIN_QT_CHECK(AC_CHECK_LIB([${QT_LIB_PREFIX}Network],[main],,BITCOIN_QT_FAIL(lib${QT_LIB_PREFIX}Network not found))) - if test "x$bitcoin_qt_got_major_vers" = x5; then - BITCOIN_QT_CHECK(AC_CHECK_LIB([${QT_LIB_PREFIX}Widgets],[main],,BITCOIN_QT_FAIL(lib${QT_LIB_PREFIX}Widgets not found))) - fi + BITCOIN_QT_CHECK(AC_CHECK_LIB([${QT_LIB_PREFIX}Widgets],[main],,BITCOIN_QT_FAIL(lib${QT_LIB_PREFIX}Widgets not found))) QT_LIBS="$LIBS" LIBS="$TEMP_LIBS" diff --git a/configure.ac b/configure.ac index 71e4c97aba..ff7f1b39c8 100644 --- a/configure.ac +++ b/configure.ac @@ -838,7 +838,7 @@ fi BITCOIN_QT_INIT dnl sets $bitcoin_enable_qt, $bitcoin_enable_qt_test, $bitcoin_enable_qt_dbus -BITCOIN_QT_CONFIGURE([$use_pkgconfig], [qt5]) +BITCOIN_QT_CONFIGURE([$use_pkgconfig]) if test x$build_bitcoin_utils$build_bitcoind$bitcoin_enable_qt$use_tests$use_bench = xnonononono; then use_boost=no @@ -1423,7 +1423,6 @@ echo "Options used to compile and link:" echo " with wallet = $enable_wallet" echo " with gui / qt = $bitcoin_enable_qt" if test x$bitcoin_enable_qt != xno; then - echo " qt version = $bitcoin_qt_got_major_vers" echo " with qr = $use_qr" fi echo " with zmq = $use_zmq" diff --git a/contrib/devtools/circular-dependencies.py b/contrib/devtools/circular-dependencies.py index d544d5c371..abfa5ed5ae 100755 --- a/contrib/devtools/circular-dependencies.py +++ b/contrib/devtools/circular-dependencies.py @@ -37,7 +37,7 @@ for arg in sys.argv[1:]: # TODO: implement support for multiple include directories for arg in sorted(files.keys()): module = files[arg] - with open(arg, 'r') as f: + with open(arg, 'r', encoding="utf8") as f: for line in f: match = RE.match(line) if match: diff --git a/contrib/devtools/clang-format-diff.py b/contrib/devtools/clang-format-diff.py index 5402870fba..77e845a9b4 100755 --- a/contrib/devtools/clang-format-diff.py +++ b/contrib/devtools/clang-format-diff.py @@ -152,7 +152,7 @@ def main(): sys.exit(p.returncode) if not args.i: - with open(filename) as f: + with open(filename, encoding="utf8") as f: code = f.readlines() formatted_code = io.StringIO(stdout).readlines() diff = difflib.unified_diff(code, formatted_code, diff --git a/contrib/devtools/copyright_header.py b/contrib/devtools/copyright_header.py index 82d3c19683..da7d74bdc4 100755 --- a/contrib/devtools/copyright_header.py +++ b/contrib/devtools/copyright_header.py @@ -146,7 +146,7 @@ def file_has_without_c_style_copyright_for_holder(contents, holder_name): ################################################################################ def read_file(filename): - return open(os.path.abspath(filename), 'r').read() + return open(os.path.abspath(filename), 'r', encoding="utf8").read() def gather_file_info(filename): info = {} @@ -325,13 +325,13 @@ def get_most_recent_git_change_year(filename): ################################################################################ def read_file_lines(filename): - f = open(os.path.abspath(filename), 'r') + f = open(os.path.abspath(filename), 'r', encoding="utf8") file_lines = f.readlines() f.close() return file_lines def write_file_lines(filename, file_lines): - f = open(os.path.abspath(filename), 'w') + f = open(os.path.abspath(filename), 'w', encoding="utf8") f.write(''.join(file_lines)) f.close() diff --git a/contrib/devtools/gen-manpages.sh b/contrib/devtools/gen-manpages.sh index 27c80548c1..76d4adc81c 100755 --- a/contrib/devtools/gen-manpages.sh +++ b/contrib/devtools/gen-manpages.sh @@ -1,5 +1,6 @@ #!/bin/bash +export LC_ALL=C TOPDIR=${TOPDIR:-$(git rev-parse --show-toplevel)} BUILDDIR=${BUILDDIR:-$TOPDIR} diff --git a/contrib/devtools/github-merge.py b/contrib/devtools/github-merge.py index 187ef75fb7..4e90f85f50 100755 --- a/contrib/devtools/github-merge.py +++ b/contrib/devtools/github-merge.py @@ -191,7 +191,7 @@ def main(): merge_branch = 'pull/'+pull+'/merge' local_merge_branch = 'pull/'+pull+'/local-merge' - devnull = open(os.devnull,'w') + devnull = open(os.devnull, 'w', encoding="utf8") try: subprocess.check_call([GIT,'checkout','-q',branch]) except subprocess.CalledProcessError: diff --git a/contrib/devtools/test-security-check.py b/contrib/devtools/test-security-check.py index 307e057736..9b6d6bf665 100755 --- a/contrib/devtools/test-security-check.py +++ b/contrib/devtools/test-security-check.py @@ -9,7 +9,7 @@ import subprocess import unittest def write_testcode(filename): - with open(filename, 'w') as f: + with open(filename, 'w', encoding="utf8") as f: f.write(''' #include <stdio.h> int main() diff --git a/contrib/filter-lcov.py b/contrib/filter-lcov.py index 299377d691..df1db76e92 100755 --- a/contrib/filter-lcov.py +++ b/contrib/filter-lcov.py @@ -13,8 +13,8 @@ pattern = args.pattern outfile = args.outfile in_remove = False -with open(tracefile, 'r') as f: - with open(outfile, 'w') as wf: +with open(tracefile, 'r', encoding="utf8") as f: + with open(outfile, 'w', encoding="utf8") as wf: for line in f: for p in pattern: if line.startswith("SF:") and p in line: diff --git a/contrib/gitian-build.sh b/contrib/gitian-build.sh index 5a925f2282..8bfbf6d1a0 100755 --- a/contrib/gitian-build.sh +++ b/contrib/gitian-build.sh @@ -3,6 +3,7 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +export LC_ALL=C # What to do sign=false verify=false diff --git a/contrib/install_db4.sh b/contrib/install_db4.sh index d315a7d3b7..4f74e67f2f 100755 --- a/contrib/install_db4.sh +++ b/contrib/install_db4.sh @@ -2,6 +2,7 @@ # Install libdb4.8 (Berkeley DB). +export LC_ALL=C set -e if [ -z "${1}" ]; then diff --git a/contrib/linearize/linearize-data.py b/contrib/linearize/linearize-data.py index f8aea27342..b501388fd2 100755 --- a/contrib/linearize/linearize-data.py +++ b/contrib/linearize/linearize-data.py @@ -75,7 +75,7 @@ def get_blk_dt(blk_hdr): # When getting the list of block hashes, undo any byte reversals. def get_block_hashes(settings): blkindex = [] - f = open(settings['hashlist'], "r") + f = open(settings['hashlist'], "r", encoding="utf8") for line in f: line = line.rstrip() if settings['rev_hash_bytes'] == 'true': @@ -261,7 +261,7 @@ if __name__ == '__main__': print("Usage: linearize-data.py CONFIG-FILE") sys.exit(1) - f = open(sys.argv[1]) + f = open(sys.argv[1], encoding="utf8") for line in f: # skip comment lines m = re.search('^\s*#', line) diff --git a/contrib/linearize/linearize-hashes.py b/contrib/linearize/linearize-hashes.py index 8e1266ae0b..bfd2171947 100755 --- a/contrib/linearize/linearize-hashes.py +++ b/contrib/linearize/linearize-hashes.py @@ -96,7 +96,7 @@ def get_block_hashes(settings, max_blocks_per_call=10000): def get_rpc_cookie(): # Open the cookie file - with open(os.path.join(os.path.expanduser(settings['datadir']), '.cookie'), 'r') as f: + with open(os.path.join(os.path.expanduser(settings['datadir']), '.cookie'), 'r', encoding="ascii") as f: combined = f.readline() combined_split = combined.split(":") settings['rpcuser'] = combined_split[0] @@ -107,7 +107,7 @@ if __name__ == '__main__': print("Usage: linearize-hashes.py CONFIG-FILE") sys.exit(1) - f = open(sys.argv[1]) + f = open(sys.argv[1], encoding="utf8") for line in f: # skip comment lines m = re.search('^\s*#', line) diff --git a/contrib/macdeploy/detached-sig-apply.sh b/contrib/macdeploy/detached-sig-apply.sh index 91674a92e6..f8503e4de8 100755 --- a/contrib/macdeploy/detached-sig-apply.sh +++ b/contrib/macdeploy/detached-sig-apply.sh @@ -3,6 +3,7 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +export LC_ALL=C set -e UNSIGNED="$1" diff --git a/contrib/macdeploy/detached-sig-create.sh b/contrib/macdeploy/detached-sig-create.sh index 3379a4599c..5281ebcc47 100755 --- a/contrib/macdeploy/detached-sig-create.sh +++ b/contrib/macdeploy/detached-sig-create.sh @@ -3,6 +3,7 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +export LC_ALL=C set -e ROOTDIR=dist diff --git a/contrib/macdeploy/extract-osx-sdk.sh b/contrib/macdeploy/extract-osx-sdk.sh index ff9fbd58df..94122b56fc 100755 --- a/contrib/macdeploy/extract-osx-sdk.sh +++ b/contrib/macdeploy/extract-osx-sdk.sh @@ -3,6 +3,7 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +export LC_ALL=C set -e INPUTFILE="Xcode_7.3.1.dmg" diff --git a/contrib/qos/tc.sh b/contrib/qos/tc.sh index 0d1dd65b4f..738ea70dbe 100644 --- a/contrib/qos/tc.sh +++ b/contrib/qos/tc.sh @@ -2,6 +2,7 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +export LC_ALL=C #network interface on which to limit traffic IF="eth0" #limit of the network interface in question diff --git a/contrib/seeds/generate-seeds.py b/contrib/seeds/generate-seeds.py index ace7d3534f..fe7cd1d597 100755 --- a/contrib/seeds/generate-seeds.py +++ b/contrib/seeds/generate-seeds.py @@ -127,10 +127,10 @@ def main(): g.write(' * Each line contains a 16-byte IPv6 address and a port.\n') g.write(' * IPv4 as well as onion addresses are wrapped inside an IPv6 address accordingly.\n') g.write(' */\n') - with open(os.path.join(indir,'nodes_main.txt'),'r') as f: + with open(os.path.join(indir,'nodes_main.txt'), 'r', encoding="utf8") as f: process_nodes(g, f, 'pnSeed6_main', 8333) g.write('\n') - with open(os.path.join(indir,'nodes_test.txt'),'r') as f: + with open(os.path.join(indir,'nodes_test.txt'), 'r', encoding="utf8") as f: process_nodes(g, f, 'pnSeed6_test', 18333) g.write('#endif // BITCOIN_CHAINPARAMSSEEDS_H\n') diff --git a/contrib/verify-commits/gpg.sh b/contrib/verify-commits/gpg.sh index 8f3e4b8063..7a10ba7d7d 100755 --- a/contrib/verify-commits/gpg.sh +++ b/contrib/verify-commits/gpg.sh @@ -3,6 +3,7 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +export LC_ALL=C INPUT=$(cat /dev/stdin) VALID=false REVSIG=false @@ -57,7 +58,7 @@ if ! $VALID; then exit 1 fi if $VALID && $REVSIG; then - printf '%s\n' "$INPUT" | gpg --trust-model always "$@" 2>/dev/null | grep "\[GNUPG:\] \(NEWSIG\|SIG_ID\|VALIDSIG\)" + printf '%s\n' "$INPUT" | gpg --trust-model always "$@" 2>/dev/null | grep "^\[GNUPG:\] \(NEWSIG\|SIG_ID\|VALIDSIG\)" echo "$GOODREVSIG" else printf '%s\n' "$INPUT" | gpg --trust-model always "$@" 2>/dev/null diff --git a/contrib/verify-commits/pre-push-hook.sh b/contrib/verify-commits/pre-push-hook.sh index 1f14635f61..152f4e32e4 100755 --- a/contrib/verify-commits/pre-push-hook.sh +++ b/contrib/verify-commits/pre-push-hook.sh @@ -3,6 +3,7 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +export LC_ALL=C if ! [[ "$2" =~ ^(git@)?(www.)?github.com(:|/)bitcoin/bitcoin(.git)?$ ]]; then exit 0 fi diff --git a/contrib/verify-commits/verify-commits.py b/contrib/verify-commits/verify-commits.py index 80f0aa0bf1..a9e4977715 100755 --- a/contrib/verify-commits/verify-commits.py +++ b/contrib/verify-commits/verify-commits.py @@ -76,11 +76,11 @@ def main(): # get directory of this program and read data files dirname = os.path.dirname(os.path.abspath(__file__)) print("Using verify-commits data from " + dirname) - verified_root = open(dirname + "/trusted-git-root", "r").read().splitlines()[0] - verified_sha512_root = open(dirname + "/trusted-sha512-root-commit", "r").read().splitlines()[0] - revsig_allowed = open(dirname + "/allow-revsig-commits", "r").read().splitlines() - unclean_merge_allowed = open(dirname + "/allow-unclean-merge-commits", "r").read().splitlines() - incorrect_sha512_allowed = open(dirname + "/allow-incorrect-sha512-commits", "r").read().splitlines() + verified_root = open(dirname + "/trusted-git-root", "r", encoding="utf8").read().splitlines()[0] + verified_sha512_root = open(dirname + "/trusted-sha512-root-commit", "r", encoding="utf8").read().splitlines()[0] + revsig_allowed = open(dirname + "/allow-revsig-commits", "r", encoding="utf-8").read().splitlines() + unclean_merge_allowed = open(dirname + "/allow-unclean-merge-commits", "r", encoding="utf-8").read().splitlines() + incorrect_sha512_allowed = open(dirname + "/allow-incorrect-sha512-commits", "r", encoding="utf-8").read().splitlines() # Set commit and branch and set variables current_commit = args.commit diff --git a/contrib/verifybinaries/verify.sh b/contrib/verifybinaries/verify.sh index e0266bf08a..42d9ffb752 100755 --- a/contrib/verifybinaries/verify.sh +++ b/contrib/verifybinaries/verify.sh @@ -11,6 +11,7 @@ ### The script returns 0 if everything passes the checks. It returns 1 if either the ### signature check or the hash check doesn't pass. If an error occurs the return value is 2 +export LC_ALL=C function clean_up { for file in $* do diff --git a/contrib/windeploy/detached-sig-create.sh b/contrib/windeploy/detached-sig-create.sh index bf4978d143..15f8108cf0 100755 --- a/contrib/windeploy/detached-sig-create.sh +++ b/contrib/windeploy/detached-sig-create.sh @@ -3,6 +3,7 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +export LC_ALL=C if [ -z "$OSSLSIGNCODE" ]; then OSSLSIGNCODE=osslsigncode fi diff --git a/doc/build-osx.md b/doc/build-osx.md index abd305cf9a..a07dbd1e1d 100644 --- a/doc/build-osx.md +++ b/doc/build-osx.md @@ -24,8 +24,6 @@ If you want to build the disk image with `make deploy` (.dmg / optional), you ne brew install librsvg -NOTE: Building with Qt4 is still supported, however, could result in a broken UI. Building with Qt5 is recommended. - Berkeley DB ----------- It is recommended to use Berkeley DB 4.8. If you have to build it yourself, diff --git a/doc/build-unix.md b/doc/build-unix.md index 1ab5659511..e884c0ab67 100644 --- a/doc/build-unix.md +++ b/doc/build-unix.md @@ -100,18 +100,13 @@ ZMQ dependencies (provides ZMQ API 4.x): #### Dependencies for the GUI If you want to build Bitcoin-Qt, make sure that the required packages for Qt development -are installed. Either Qt 5 or Qt 4 are necessary to build the GUI. -If both Qt 4 and Qt 5 are installed, Qt 5 will be used. Pass `--with-gui=qt4` to configure to choose Qt4. +are installed. Qt 5 is necessary to build the GUI. To build without GUI pass `--without-gui`. -To build with Qt 5 (recommended) you need the following: +To build with Qt 5 you need the following: sudo apt-get install libqt5gui5 libqt5core5a libqt5dbus5 qttools5-dev qttools5-dev-tools libprotobuf-dev protobuf-compiler -Alternatively, to build with Qt 4 you need the following: - - sudo apt-get install libqt4-dev libprotobuf-dev protobuf-compiler - libqrencode (optional) can be installed with: sudo apt-get install libqrencode-dev @@ -132,7 +127,7 @@ Optional: sudo dnf install miniupnpc-devel -To build with Qt 5 (recommended) you need the following: +To build with Qt 5 you need the following: sudo dnf install qt5-qttools-devel qt5-qtbase-devel protobuf-devel diff --git a/doc/dependencies.md b/doc/dependencies.md index 7aa96c4c9b..793c659419 100644 --- a/doc/dependencies.md +++ b/doc/dependencies.md @@ -23,7 +23,7 @@ These are the dependencies currently used by Bitcoin Core. You can find instruct | protobuf | [2.6.3](https://github.com/google/protobuf/releases) | | No | | | | Python (tests) | | [3.4](https://www.python.org/downloads) | | | | | qrencode | [3.4.4](https://fukuchi.org/works/qrencode) | | No | | | -| Qt | [5.7.1](https://download.qt.io/official_releases/qt/) | 4.7+ | No | | | +| Qt | [5.7.1](https://download.qt.io/official_releases/qt/) | 5.x | No | | | | XCB | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L94) (Linux only) | | xkbcommon | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L93) (Linux only) | | ZeroMQ | [4.2.3](https://github.com/zeromq/libzmq/releases) | | No | | | diff --git a/doc/release-notes-pr10740.md b/doc/release-notes-pr10740.md index c81ea6a4db..a2eb8cd837 100644 --- a/doc/release-notes-pr10740.md +++ b/doc/release-notes-pr10740.md @@ -1,9 +1,10 @@ Dynamic loading and creation of wallets --------------------------------------- -Previously, wallets could only be loaded or created at startup, by specifying `-wallet` parameters on the command line or in the bitcoin.conf file. It is now possible to load and create wallets dynamically at runtime: +Previously, wallets could only be loaded or created at startup, by specifying `-wallet` parameters on the command line or in the bitcoin.conf file. It is now possible to load, create and unload wallets dynamically at runtime: - Existing wallets can be loaded by calling the `loadwallet` RPC. The wallet can be specified as file/directory basename (which must be located in the `walletdir` directory), or as an absolute path to a file/directory. - New wallets can be created (and loaded) by calling the `createwallet` RPC. The provided name must not match a wallet file in the `walletdir` directory or the name of a wallet that is currently loaded. +- Loaded wallets can be unloaded by calling the `unloadwallet` RPC. This feature is currently only available through the RPC interface. diff --git a/share/genbuild.sh b/share/genbuild.sh index 419e0da0fd..38c9ba176c 100755 --- a/share/genbuild.sh +++ b/share/genbuild.sh @@ -3,6 +3,7 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +export LC_ALL=C if [ $# -gt 1 ]; then cd "$2" || exit 1 fi diff --git a/share/qt/extract_strings_qt.py b/share/qt/extract_strings_qt.py index e8f0820ca8..e908071321 100755 --- a/share/qt/extract_strings_qt.py +++ b/share/qt/extract_strings_qt.py @@ -63,7 +63,7 @@ child = Popen([XGETTEXT,'--output=-','-n','--keyword=_'] + files, stdout=PIPE) messages = parse_po(out.decode('utf-8')) -f = open(OUT_CPP, 'w') +f = open(OUT_CPP, 'w', encoding="utf8") f.write(""" #include <QtGlobal> diff --git a/src/crypto/sha256.cpp b/src/crypto/sha256.cpp index 51824fbe9f..e62ddc125e 100644 --- a/src/crypto/sha256.cpp +++ b/src/crypto/sha256.cpp @@ -446,44 +446,104 @@ void TransformD64Wrapper(unsigned char* out, const unsigned char* in) WriteBE32(out + 28, s[7]); } -bool SelfTest(TransformType tr) { - static const unsigned char in1[65] = {0, 0x80}; - static const unsigned char in2[129] = { - 0, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0 - }; - static const uint32_t init[8] = {0x6a09e667ul, 0xbb67ae85ul, 0x3c6ef372ul, 0xa54ff53aul, 0x510e527ful, 0x9b05688cul, 0x1f83d9abul, 0x5be0cd19ul}; - static const uint32_t out1[8] = {0xe3b0c442ul, 0x98fc1c14ul, 0x9afbf4c8ul, 0x996fb924ul, 0x27ae41e4ul, 0x649b934cul, 0xa495991bul, 0x7852b855ul}; - static const uint32_t out2[8] = {0xce4153b0ul, 0x147c2a86ul, 0x3ed4298eul, 0xe0676bc8ul, 0x79fc77a1ul, 0x2abe1f49ul, 0xb2b055dful, 0x1069523eul}; - uint32_t buf[8]; - memcpy(buf, init, sizeof(buf)); - // Process nothing, and check we remain in the initial state. - tr(buf, nullptr, 0); - if (memcmp(buf, init, sizeof(buf))) return false; - // Process the padded empty string (unaligned) - tr(buf, in1 + 1, 1); - if (memcmp(buf, out1, sizeof(buf))) return false; - // Process 64 spaces (unaligned) - memcpy(buf, init, sizeof(buf)); - tr(buf, in2 + 1, 2); - if (memcmp(buf, out2, sizeof(buf))) return false; - return true; -} - TransformType Transform = sha256::Transform; TransformD64Type TransformD64 = sha256::TransformD64; TransformD64Type TransformD64_4way = nullptr; TransformD64Type TransformD64_8way = nullptr; +bool SelfTest() { + // Input state (equal to the initial SHA256 state) + static const uint32_t init[8] = { + 0x6a09e667ul, 0xbb67ae85ul, 0x3c6ef372ul, 0xa54ff53aul, 0x510e527ful, 0x9b05688cul, 0x1f83d9abul, 0x5be0cd19ul + }; + // Some random input data to test with + static const unsigned char data[641] = "-" // Intentionally not aligned + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do " + "eiusmod tempor incididunt ut labore et dolore magna aliqua. Et m" + "olestie ac feugiat sed lectus vestibulum mattis ullamcorper. Mor" + "bi blandit cursus risus at ultrices mi tempus imperdiet nulla. N" + "unc congue nisi vita suscipit tellus mauris. Imperdiet proin fer" + "mentum leo vel orci. Massa tempor nec feugiat nisl pretium fusce" + " id velit. Telus in metus vulputate eu scelerisque felis. Mi tem" + "pus imperdiet nulla malesuada pellentesque. Tristique magna sit."; + // Expected output state for hashing the i*64 first input bytes above (excluding SHA256 padding). + static const uint32_t result[9][8] = { + {0x6a09e667ul, 0xbb67ae85ul, 0x3c6ef372ul, 0xa54ff53aul, 0x510e527ful, 0x9b05688cul, 0x1f83d9abul, 0x5be0cd19ul}, + {0x91f8ec6bul, 0x4da10fe3ul, 0x1c9c292cul, 0x45e18185ul, 0x435cc111ul, 0x3ca26f09ul, 0xeb954caeul, 0x402a7069ul}, + {0xcabea5acul, 0x374fb97cul, 0x182ad996ul, 0x7bd69cbful, 0x450ff900ul, 0xc1d2be8aul, 0x6a41d505ul, 0xe6212dc3ul}, + {0xbcff09d6ul, 0x3e76f36eul, 0x3ecb2501ul, 0x78866e97ul, 0xe1c1e2fdul, 0x32f4eafful, 0x8aa6c4e5ul, 0xdfc024bcul}, + {0xa08c5d94ul, 0x0a862f93ul, 0x6b7f2f40ul, 0x8f9fae76ul, 0x6d40439ful, 0x79dcee0cul, 0x3e39ff3aul, 0xdc3bdbb1ul}, + {0x216a0895ul, 0x9f1a3662ul, 0xe99946f9ul, 0x87ba4364ul, 0x0fb5db2cul, 0x12bed3d3ul, 0x6689c0c7ul, 0x292f1b04ul}, + {0xca3067f8ul, 0xbc8c2656ul, 0x37cb7e0dul, 0x9b6b8b0ful, 0x46dc380bul, 0xf1287f57ul, 0xc42e4b23ul, 0x3fefe94dul}, + {0x3e4c4039ul, 0xbb6fca8cul, 0x6f27d2f7ul, 0x301e44a4ul, 0x8352ba14ul, 0x5769ce37ul, 0x48a1155ful, 0xc0e1c4c6ul}, + {0xfe2fa9ddul, 0x69d0862bul, 0x1ae0db23ul, 0x471f9244ul, 0xf55c0145ul, 0xc30f9c3bul, 0x40a84ea0ul, 0x5b8a266cul}, + }; + // Expected output for each of the individual 8 64-byte messages under full double SHA256 (including padding). + static const unsigned char result_d64[256] = { + 0x09, 0x3a, 0xc4, 0xd0, 0x0f, 0xf7, 0x57, 0xe1, 0x72, 0x85, 0x79, 0x42, 0xfe, 0xe7, 0xe0, 0xa0, + 0xfc, 0x52, 0xd7, 0xdb, 0x07, 0x63, 0x45, 0xfb, 0x53, 0x14, 0x7d, 0x17, 0x22, 0x86, 0xf0, 0x52, + 0x48, 0xb6, 0x11, 0x9e, 0x6e, 0x48, 0x81, 0x6d, 0xcc, 0x57, 0x1f, 0xb2, 0x97, 0xa8, 0xd5, 0x25, + 0x9b, 0x82, 0xaa, 0x89, 0xe2, 0xfd, 0x2d, 0x56, 0xe8, 0x28, 0x83, 0x0b, 0xe2, 0xfa, 0x53, 0xb7, + 0xd6, 0x6b, 0x07, 0x85, 0x83, 0xb0, 0x10, 0xa2, 0xf5, 0x51, 0x3c, 0xf9, 0x60, 0x03, 0xab, 0x45, + 0x6c, 0x15, 0x6e, 0xef, 0xb5, 0xac, 0x3e, 0x6c, 0xdf, 0xb4, 0x92, 0x22, 0x2d, 0xce, 0xbf, 0x3e, + 0xe9, 0xe5, 0xf6, 0x29, 0x0e, 0x01, 0x4f, 0xd2, 0xd4, 0x45, 0x65, 0xb3, 0xbb, 0xf2, 0x4c, 0x16, + 0x37, 0x50, 0x3c, 0x6e, 0x49, 0x8c, 0x5a, 0x89, 0x2b, 0x1b, 0xab, 0xc4, 0x37, 0xd1, 0x46, 0xe9, + 0x3d, 0x0e, 0x85, 0xa2, 0x50, 0x73, 0xa1, 0x5e, 0x54, 0x37, 0xd7, 0x94, 0x17, 0x56, 0xc2, 0xd8, + 0xe5, 0x9f, 0xed, 0x4e, 0xae, 0x15, 0x42, 0x06, 0x0d, 0x74, 0x74, 0x5e, 0x24, 0x30, 0xce, 0xd1, + 0x9e, 0x50, 0xa3, 0x9a, 0xb8, 0xf0, 0x4a, 0x57, 0x69, 0x78, 0x67, 0x12, 0x84, 0x58, 0xbe, 0xc7, + 0x36, 0xaa, 0xee, 0x7c, 0x64, 0xa3, 0x76, 0xec, 0xff, 0x55, 0x41, 0x00, 0x2a, 0x44, 0x68, 0x4d, + 0xb6, 0x53, 0x9e, 0x1c, 0x95, 0xb7, 0xca, 0xdc, 0x7f, 0x7d, 0x74, 0x27, 0x5c, 0x8e, 0xa6, 0x84, + 0xb5, 0xac, 0x87, 0xa9, 0xf3, 0xff, 0x75, 0xf2, 0x34, 0xcd, 0x1a, 0x3b, 0x82, 0x2c, 0x2b, 0x4e, + 0x6a, 0x46, 0x30, 0xa6, 0x89, 0x86, 0x23, 0xac, 0xf8, 0xa5, 0x15, 0xe9, 0x0a, 0xaa, 0x1e, 0x9a, + 0xd7, 0x93, 0x6b, 0x28, 0xe4, 0x3b, 0xfd, 0x59, 0xc6, 0xed, 0x7c, 0x5f, 0xa5, 0x41, 0xcb, 0x51 + }; + + + // Test Transform() for 0 through 8 transformations. + for (size_t i = 0; i <= 8; ++i) { + uint32_t state[8]; + std::copy(init, init + 8, state); + Transform(state, data + 1, i); + if (!std::equal(state, state + 8, result[i])) return false; + } + + // Test TransformD64 + unsigned char out[32]; + TransformD64(out, data + 1); + if (!std::equal(out, out + 32, result_d64)) return false; + + // Test TransformD64_4way, if available. + if (TransformD64_4way) { + unsigned char out[128]; + TransformD64_4way(out, data + 1); + if (!std::equal(out, out + 128, result_d64)) return false; + } + + // Test TransformD64_8way, if available. + if (TransformD64_8way) { + unsigned char out[256]; + TransformD64_8way(out, data + 1); + if (!std::equal(out, out + 256, result_d64)) return false; + } + + return true; +} + + #if defined(USE_ASM) && (defined(__x86_64__) || defined(__amd64__) || defined(__i386__)) // We can't use cpuid.h's __get_cpuid as it does not support subleafs. void inline cpuid(uint32_t leaf, uint32_t subleaf, uint32_t& a, uint32_t& b, uint32_t& c, uint32_t& d) { __asm__ ("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "0"(leaf), "2"(subleaf)); } + +/** Check whether the OS has enabled AVX registers. */ +bool AVXEnabled() +{ + uint32_t a, d; + __asm__("xgetbv" : "=a"(a), "=d"(d) : "c"(0)); + return (a & 6) == 6; +} #endif } // namespace @@ -492,6 +552,7 @@ std::string SHA256AutoDetect() { std::string ret = "standard"; #if defined(USE_ASM) && (defined(__x86_64__) || defined(__amd64__) || defined(__i386__)) + (void)AVXEnabled; // Silence unused warning (in case ENABLE_AVX2 is not defined) uint32_t eax, ebx, ecx, edx; cpuid(1, 0, eax, ebx, ecx, edx); if ((ecx >> 19) & 1) { @@ -503,10 +564,14 @@ std::string SHA256AutoDetect() TransformD64_4way = sha256d64_sse41::Transform_4way; ret = "sse4(1way+4way)"; #if defined(ENABLE_AVX2) && !defined(BUILD_BITCOIN_INTERNAL) - cpuid(7, 0, eax, ebx, ecx, edx); - if ((ebx >> 5) & 1) { - TransformD64_8way = sha256d64_avx2::Transform_8way; - ret += ",avx2(8way)"; + if (((ecx >> 27) & 1) && ((ecx >> 28) & 1)) { // XSAVE and AVX + cpuid(7, 0, eax, ebx, ecx, edx); + if ((ebx >> 5) & 1) { // AVX2 flag + if (AVXEnabled()) { // OS has enabled AVX registers + TransformD64_8way = sha256d64_avx2::Transform_8way; + ret += ",avx2(8way)"; + } + } } #endif #else @@ -515,7 +580,7 @@ std::string SHA256AutoDetect() } #endif - assert(SelfTest(Transform)); + assert(SelfTest()); return ret; } diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp index 3029dbe8e3..e98acba0df 100644 --- a/src/interfaces/wallet.cpp +++ b/src/interfaces/wallet.cpp @@ -429,6 +429,10 @@ public: bool hdEnabled() override { return m_wallet.IsHDEnabled(); } OutputType getDefaultAddressType() override { return m_wallet.m_default_address_type; } OutputType getDefaultChangeType() override { return m_wallet.m_default_change_type; } + std::unique_ptr<Handler> handleUnload(UnloadFn fn) override + { + return MakeHandler(m_wallet.NotifyUnload.connect(fn)); + } std::unique_ptr<Handler> handleShowProgress(ShowProgressFn fn) override { return MakeHandler(m_wallet.ShowProgress.connect(fn)); diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h index 82ae0b14b5..ce42e14eea 100644 --- a/src/interfaces/wallet.h +++ b/src/interfaces/wallet.h @@ -242,6 +242,10 @@ public: // Get default change type. virtual OutputType getDefaultChangeType() = 0; + //! Register handler for unload message. + using UnloadFn = std::function<void()>; + virtual std::unique_ptr<Handler> handleUnload(UnloadFn fn) = 0; + //! Register handler for show progress messages. using ShowProgressFn = std::function<void(const std::string& title, int progress)>; virtual std::unique_ptr<Handler> handleShowProgress(ShowProgressFn fn) = 0; diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 1c846d38ec..360615ec56 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -388,11 +388,6 @@ struct CMutableTransaction */ uint256 GetHash() const; - friend bool operator==(const CMutableTransaction& a, const CMutableTransaction& b) - { - return a.GetHash() == b.GetHash(); - } - bool HasWitness() const { for (size_t i = 0; i < vin.size(); i++) { diff --git a/src/qt/README.md b/src/qt/README.md index bf8139666c..3ec538b4f4 100644 --- a/src/qt/README.md +++ b/src/qt/README.md @@ -1,6 +1,6 @@ This directory contains the BitcoinQT graphical user interface (GUI). It uses the cross-platform framework [Qt](https://www1.qt.io/developers/). -The current precise version for Qt 5 is specified in [qt.mk](/depends/packages/qt.mk). Qt 4 is also supported (see [#8263](https://github.com/bitcoin/bitcoin/issues/8263)). +The current precise version for Qt 5 is specified in [qt.mk](/depends/packages/qt.mk). ## Compile and run @@ -16,7 +16,7 @@ To run: ### forms -Contains [Designer UI](http://doc.qt.io/qt-5.9/designer-using-a-ui-file.html) files. They are created with [Qt Creator](#using-qt-creator-as-ide), but can be edited using any text editor. +Contains [Designer UI](https://doc.qt.io/qt-5.9/designer-using-a-ui-file.html) files. They are created with [Qt Creator](#using-qt-creator-as-ide), but can be edited using any text editor. ### locale @@ -36,7 +36,7 @@ Represents the main window of the Bitcoin UI. ### \*model.(h/cpp) -The model. When it has a corresponding controller, it generally inherits from [QAbstractTableModel](http://doc.qt.io/qt-5/qabstracttablemodel.html). Models that are used by controllers as helpers inherit from other Qt classes like [QValidator](http://doc.qt.io/qt-5/qvalidator.html). +The model. When it has a corresponding controller, it generally inherits from [QAbstractTableModel](https://doc.qt.io/qt-5/qabstracttablemodel.html). Models that are used by controllers as helpers inherit from other Qt classes like [QValidator](https://doc.qt.io/qt-5/qvalidator.html). ClientModel is used by the main application `bitcoingui` and several models like `peertablemodel`. @@ -46,7 +46,7 @@ A controller. `:NAMEpage.cpp` generally includes `:NAMEmodel.h` and `forms/:NAME ### \*dialog.(h/cpp) -Various dialogs, e.g. to open a URL. Inherit from [QDialog](http://doc.qt.io/qt-4.8/qdialog.html). +Various dialogs, e.g. to open a URL. Inherit from [QDialog](https://doc.qt.io/qt-5/qdialog.html). ### paymentserver.(h/cpp) diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp index f2ddbf259b..d51069d922 100644 --- a/src/qt/addressbookpage.cpp +++ b/src/qt/addressbookpage.cpp @@ -160,13 +160,8 @@ void AddressBookPage::setModel(AddressTableModel *_model) ui->tableView->sortByColumn(0, Qt::AscendingOrder); // Set column widths -#if QT_VERSION < 0x050000 - ui->tableView->horizontalHeader()->setResizeMode(AddressTableModel::Label, QHeaderView::Stretch); - ui->tableView->horizontalHeader()->setResizeMode(AddressTableModel::Address, QHeaderView::ResizeToContents); -#else ui->tableView->horizontalHeader()->setSectionResizeMode(AddressTableModel::Label, QHeaderView::Stretch); ui->tableView->horizontalHeader()->setSectionResizeMode(AddressTableModel::Address, QHeaderView::ResizeToContents); -#endif connect(ui->tableView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(selectionChanged())); diff --git a/src/qt/bantablemodel.cpp b/src/qt/bantablemodel.cpp index 26cb03c2c7..aa0d4a31d3 100644 --- a/src/qt/bantablemodel.cpp +++ b/src/qt/bantablemodel.cpp @@ -52,9 +52,7 @@ public: node.getBanned(banMap); cachedBanlist.clear(); -#if QT_VERSION >= 0x040700 cachedBanlist.reserve(banMap.size()); -#endif for (const auto& entry : banMap) { CCombinedBan banEntry; diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 31d9f936e7..e3d1c746b1 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -55,13 +55,6 @@ #if defined(QT_STATICPLUGIN) #include <QtPlugin> -#if QT_VERSION < 0x050000 -Q_IMPORT_PLUGIN(qcncodecs) -Q_IMPORT_PLUGIN(qjpcodecs) -Q_IMPORT_PLUGIN(qtwcodecs) -Q_IMPORT_PLUGIN(qkrcodecs) -Q_IMPORT_PLUGIN(qtaccessiblewidgets) -#else #if QT_VERSION < 0x050400 Q_IMPORT_PLUGIN(AccessibleFactory) #endif @@ -73,11 +66,6 @@ Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin); Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin); #endif #endif -#endif - -#if QT_VERSION < 0x050000 -#include <QTextCodec> -#endif // Declare meta types used for QMetaObject::invokeMethod Q_DECLARE_METATYPE(bool*) @@ -151,16 +139,6 @@ static void initTranslations(QTranslator &qtTranslatorBase, QTranslator &qtTrans } /* qDebug() message handler --> debug.log */ -#if QT_VERSION < 0x050000 -void DebugMessageHandler(QtMsgType type, const char *msg) -{ - if (type == QtDebugMsg) { - LogPrint(BCLog::QT, "GUI: %s\n", msg); - } else { - LogPrintf("GUI: %s\n", msg); - } -} -#else void DebugMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString &msg) { Q_UNUSED(context); @@ -170,7 +148,6 @@ void DebugMessageHandler(QtMsgType type, const QMessageLogContext& context, cons LogPrintf("GUI: %s\n", msg.toStdString()); } } -#endif /** Class encapsulating Bitcoin Core startup and shutdown. * Allows running startup and shutdown in a different thread from the UI thread. @@ -238,6 +215,7 @@ public Q_SLOTS: /// Handle runaway exceptions. Shows a message box with the problem and quits the program. void handleRunawayException(const QString &message); void addWallet(WalletModel* walletModel); + void removeWallet(); Q_SIGNALS: void requestedInitialize(); @@ -467,11 +445,22 @@ void BitcoinApplication::addWallet(WalletModel* walletModel) connect(walletModel, SIGNAL(coinsSent(WalletModel*, SendCoinsRecipient, QByteArray)), paymentServer, SLOT(fetchPaymentACK(WalletModel*, const SendCoinsRecipient&, QByteArray))); + connect(walletModel, SIGNAL(unload()), this, SLOT(removeWallet())); m_wallet_models.push_back(walletModel); #endif } +void BitcoinApplication::removeWallet() +{ +#ifdef ENABLE_WALLET + WalletModel* walletModel = static_cast<WalletModel*>(sender()); + m_wallet_models.erase(std::find(m_wallet_models.begin(), m_wallet_models.end(), walletModel)); + window->removeWallet(walletModel); + walletModel->deleteLater(); +#endif +} + void BitcoinApplication::initializeResult(bool success) { qDebug() << __func__ << ": Initialization result: " << success; @@ -491,8 +480,10 @@ void BitcoinApplication::initializeResult(bool success) #ifdef ENABLE_WALLET m_handler_load_wallet = m_node.handleLoadWallet([this](std::unique_ptr<interfaces::Wallet> wallet) { - QMetaObject::invokeMethod(this, "addWallet", Qt::QueuedConnection, - Q_ARG(WalletModel*, new WalletModel(std::move(wallet), m_node, platformStyle, optionsModel))); + WalletModel* wallet_model = new WalletModel(std::move(wallet), m_node, platformStyle, optionsModel, nullptr); + // Fix wallet model thread affinity. + wallet_model->moveToThread(thread()); + QMetaObject::invokeMethod(this, "addWallet", Qt::QueuedConnection, Q_ARG(WalletModel*, wallet_model)); }); for (auto& wallet : m_node.getWallets()) { @@ -572,12 +563,6 @@ int main(int argc, char *argv[]) // Do not refer to data directory yet, this can be overridden by Intro::pickDataDirectory /// 1. Basic Qt initialization (not dependent on parameters or configuration) -#if QT_VERSION < 0x050000 - // Internal string conversion is all UTF-8 - QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8")); - QTextCodec::setCodecForCStrings(QTextCodec::codecForTr()); -#endif - Q_INIT_RESOURCE(bitcoin); Q_INIT_RESOURCE(bitcoin_locale); @@ -708,17 +693,12 @@ int main(int argc, char *argv[]) /// 9. Main GUI initialization // Install global event filter that makes sure that long tooltips can be word-wrapped app.installEventFilter(new GUIUtil::ToolTipToRichTextFilter(TOOLTIP_WRAP_THRESHOLD, &app)); -#if QT_VERSION < 0x050000 - // Install qDebug() message handler to route to debug.log - qInstallMsgHandler(DebugMessageHandler); -#else #if defined(Q_OS_WIN) // Install global event filter for processing Windows session related Windows messages (WM_QUERYENDSESSION and WM_ENDSESSION) qApp->installNativeEventFilter(new WinShutdownMonitor()); #endif // Install qDebug() message handler to route to debug.log qInstallMessageHandler(DebugMessageHandler); -#endif // Allow parameter interaction before we create the options model app.parameterSetup(); // Load GUI settings from QSettings @@ -739,7 +719,7 @@ int main(int argc, char *argv[]) // so the GUI thread won't be held up. if (node->baseInitialize()) { app.requestInitialize(); -#if defined(Q_OS_WIN) && QT_VERSION >= 0x050000 +#if defined(Q_OS_WIN) WinShutdownMonitor::registerShutdownBlockReason(QObject::tr("%1 didn't yet exit safely...").arg(QObject::tr(PACKAGE_NAME)), (HWND)app.getMainWinId()); #endif app.exec(); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 9f5ea02e14..b3c8f57deb 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -55,14 +55,8 @@ #include <QStyle> #include <QTimer> #include <QToolBar> -#include <QVBoxLayout> - -#if QT_VERSION < 0x050000 -#include <QTextDocument> -#include <QUrl> -#else #include <QUrlQuery> -#endif +#include <QVBoxLayout> const std::string BitcoinGUI::DEFAULT_UIPLATFORM = #if defined(Q_OS_MAC) @@ -120,6 +114,7 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty modalOverlay(0), prevBlocks(0), spinnerFrame(0), + m_wallet_selector_label(nullptr), platformStyle(_platformStyle) { QSettings settings; @@ -147,12 +142,6 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty #endif setWindowTitle(windowTitle); -#if defined(Q_OS_MAC) && QT_VERSION < 0x050000 - // This property is not implemented in Qt 5. Setting it has no effect. - // A replacement API (QtMacUnifiedToolBar) is available in QtMacExtras. - setUnifiedTitleAndToolBarOnMac(true); -#endif - rpcConsole = new RPCConsole(node, _platformStyle, 0); helpMessageDialog = new HelpMessageDialog(node, this, false); #ifdef ENABLE_WALLET @@ -229,7 +218,7 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty // Override style sheet for progress bar for styles that have a segmented progress bar, // as they make the text unreadable (workaround for issue #1071) - // See https://qt-project.org/doc/qt-4.8/gallery.html + // See https://doc.qt.io/qt-5/gallery.html QString curStyle = QApplication::style()->metaObject()->className(); if(curStyle == "QWindowsStyle" || curStyle == "QWindowsXPStyle") { @@ -477,6 +466,16 @@ void BitcoinGUI::createToolBars() m_wallet_selector = new QComboBox(); connect(m_wallet_selector, SIGNAL(currentIndexChanged(int)), this, SLOT(setCurrentWalletBySelectorIndex(int))); + + m_wallet_selector_label = new QLabel(); + m_wallet_selector_label->setText(tr("Wallet:") + " "); + m_wallet_selector_label->setBuddy(m_wallet_selector); + + m_wallet_selector_label_action = appToolBar->addWidget(m_wallet_selector_label); + m_wallet_selector_action = appToolBar->addWidget(m_wallet_selector); + + m_wallet_selector_label_action->setVisible(false); + m_wallet_selector_action->setVisible(false); #endif } } @@ -556,16 +555,29 @@ bool BitcoinGUI::addWallet(WalletModel *walletModel) setWalletActionsEnabled(true); m_wallet_selector->addItem(display_name, name); if (m_wallet_selector->count() == 2) { - m_wallet_selector_label = new QLabel(); - m_wallet_selector_label->setText(tr("Wallet:") + " "); - m_wallet_selector_label->setBuddy(m_wallet_selector); - appToolBar->addWidget(m_wallet_selector_label); - appToolBar->addWidget(m_wallet_selector); + m_wallet_selector_label_action->setVisible(true); + m_wallet_selector_action->setVisible(true); } rpcConsole->addWallet(walletModel); return walletFrame->addWallet(walletModel); } +bool BitcoinGUI::removeWallet(WalletModel* walletModel) +{ + if (!walletFrame) return false; + QString name = walletModel->getWalletName(); + int index = m_wallet_selector->findData(name); + m_wallet_selector->removeItem(index); + if (m_wallet_selector->count() == 0) { + setWalletActionsEnabled(false); + } else if (m_wallet_selector->count() == 1) { + m_wallet_selector_label_action->setVisible(false); + m_wallet_selector_action->setVisible(false); + } + rpcConsole->removeWallet(walletModel); + return walletFrame->removeWallet(name); +} + bool BitcoinGUI::setCurrentWallet(const QString& name) { if(!walletFrame) diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 89c1c73a79..68c35557cc 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -70,6 +70,7 @@ public: functionality. */ bool addWallet(WalletModel *walletModel); + bool removeWallet(WalletModel* walletModel); void removeAllWallets(); #endif // ENABLE_WALLET bool enableWallet; @@ -122,8 +123,10 @@ private: QAction *openRPCConsoleAction; QAction *openAction; QAction *showHelpMessageAction; + QAction *m_wallet_selector_label_action = nullptr; + QAction *m_wallet_selector_action = nullptr; - QLabel *m_wallet_selector_label; + QLabel *m_wallet_selector_label = nullptr; QComboBox *m_wallet_selector; QSystemTrayIcon *trayIcon; diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index b08de27041..222ed6f65f 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -109,11 +109,7 @@ CoinControlDialog::CoinControlDialog(const PlatformStyle *_platformStyle, QWidge connect(ui->treeWidget, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(viewItemChanged(QTreeWidgetItem*, int))); // click on header -#if QT_VERSION < 0x050000 - ui->treeWidget->header()->setClickable(true); -#else ui->treeWidget->header()->setSectionsClickable(true); -#endif connect(ui->treeWidget->header(), SIGNAL(sectionClicked(int)), this, SLOT(headerSectionClicked(int))); // ok button @@ -392,13 +388,11 @@ void CoinControlDialog::viewItemChanged(QTreeWidgetItem* item, int column) // TODO: Remove this temporary qt5 fix after Qt5.3 and Qt5.4 are no longer used. // Fixed in Qt5.5 and above: https://bugreports.qt.io/browse/QTBUG-43473 -#if QT_VERSION >= 0x050000 else if (column == COLUMN_CHECKBOX && item->childCount() > 0) { if (item->checkState(COLUMN_CHECKBOX) == Qt::PartiallyChecked && item->child(0)->checkState(COLUMN_CHECKBOX) == Qt::PartiallyChecked) item->setCheckState(COLUMN_CHECKBOX, Qt::Checked); } -#endif } // shows count of locked unspent outputs diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 563f930dec..acd9f7b35c 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -53,13 +53,9 @@ #include <QSettings> #include <QTextDocument> // for Qt::mightBeRichText #include <QThread> +#include <QUrlQuery> #include <QMouseEvent> -#if QT_VERSION < 0x050000 -#include <QUrl> -#else -#include <QUrlQuery> -#endif #if QT_VERSION >= 0x50200 #include <QFontDatabase> @@ -95,11 +91,7 @@ QFont fixedPitchFont() return QFontDatabase::systemFont(QFontDatabase::FixedFont); #else QFont font("Monospace"); -#if QT_VERSION >= 0x040800 font.setStyleHint(QFont::Monospace); -#else - font.setStyleHint(QFont::TypeWriter); -#endif return font; #endif } @@ -127,12 +119,10 @@ void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent) parent->setFocusProxy(widget); widget->setFont(fixedPitchFont()); -#if QT_VERSION >= 0x040700 // We don't want translators to use own addresses in translations // and this is the only place, where this address is supplied. widget->setPlaceholderText(QObject::tr("Enter a Bitcoin address (e.g. %1)").arg( QString::fromStdString(DummyAddress(Params())))); -#endif widget->setValidator(new BitcoinAddressEntryValidator(parent)); widget->setCheckValidator(new BitcoinAddressCheckValidator(parent)); } @@ -151,12 +141,8 @@ bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out) } rv.amount = 0; -#if QT_VERSION < 0x050000 - QList<QPair<QString, QString> > items = uri.queryItems(); -#else QUrlQuery uriQuery(uri); QList<QPair<QString, QString> > items = uriQuery.queryItems(); -#endif for (QList<QPair<QString, QString> >::iterator i = items.begin(); i != items.end(); i++) { bool fShouldReturnFalse = false; @@ -242,11 +228,7 @@ bool isDust(interfaces::Node& node, const QString& address, const CAmount& amoun QString HtmlEscape(const QString& str, bool fMultiLine) { -#if QT_VERSION < 0x050000 - QString escaped = Qt::escape(str); -#else QString escaped = str.toHtmlEscaped(); -#endif if(fMultiLine) { escaped = escaped.replace("\n", "<br>\n"); @@ -287,11 +269,7 @@ QString getSaveFileName(QWidget *parent, const QString &caption, const QString & QString myDir; if(dir.isEmpty()) // Default to user documents location { -#if QT_VERSION < 0x050000 - myDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation); -#else myDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); -#endif } else { @@ -337,11 +315,7 @@ QString getOpenFileName(QWidget *parent, const QString &caption, const QString & QString myDir; if(dir.isEmpty()) // Default to user documents location { -#if QT_VERSION < 0x050000 - myDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation); -#else myDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); -#endif } else { @@ -495,11 +469,7 @@ void TableViewLastColumnResizingFixer::disconnectViewHeadersSignals() // Refactored here for readability. void TableViewLastColumnResizingFixer::setViewHeaderResizeMode(int logicalIndex, QHeaderView::ResizeMode resizeMode) { -#if QT_VERSION < 0x050000 - tableView->horizontalHeader()->setResizeMode(logicalIndex, resizeMode); -#else tableView->horizontalHeader()->setSectionResizeMode(logicalIndex, resizeMode); -#endif } void TableViewLastColumnResizingFixer::resizeColumn(int nColumnIndex, int width) diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index 4a26964098..e965a91d18 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -233,7 +233,7 @@ namespace GUIUtil void mouseReleaseEvent(QMouseEvent *event); }; -#if defined(Q_OS_MAC) && QT_VERSION >= 0x050000 +#if defined(Q_OS_MAC) // workaround for Qt OSX Bug: // https://bugreports.qt-project.org/browse/QTBUG-15631 // QProgressBar uses around 10% CPU even when app is in background diff --git a/src/qt/macdockiconhandler.mm b/src/qt/macdockiconhandler.mm index 9e7de0f98f..a0b62ae000 100644 --- a/src/qt/macdockiconhandler.mm +++ b/src/qt/macdockiconhandler.mm @@ -14,10 +14,6 @@ #include <objc/objc.h> #include <objc/message.h> -#if QT_VERSION < 0x050000 -extern void qt_mac_set_dock_menu(QMenu *); -#endif - static MacDockIconHandler *s_instance = nullptr; bool dockClickHandler(id self,SEL _cmd,...) { @@ -54,9 +50,7 @@ MacDockIconHandler::MacDockIconHandler() : QObject() this->m_dummyWidget = new QWidget(); this->m_dockMenu = new QMenu(this->m_dummyWidget); this->setMainWindow(nullptr); -#if QT_VERSION < 0x050000 - qt_mac_set_dock_menu(this->m_dockMenu); -#elif QT_VERSION >= 0x050200 +#if QT_VERSION >= 0x050200 this->m_dockMenu->setAsDockMenu(); #endif [pool release]; diff --git a/src/qt/networkstyle.cpp b/src/qt/networkstyle.cpp index 2816633b0f..c4b732e3e0 100644 --- a/src/qt/networkstyle.cpp +++ b/src/qt/networkstyle.cpp @@ -68,11 +68,7 @@ NetworkStyle::NetworkStyle(const QString &_appName, const int iconColorHueShift, } //convert back to QPixmap -#if QT_VERSION >= 0x040700 pixmap.convertFromImage(img); -#else - pixmap = QPixmap::fromImage(img); -#endif } appIcon = QIcon(pixmap); diff --git a/src/qt/openuridialog.cpp b/src/qt/openuridialog.cpp index 751cfa8b43..d211f908c8 100644 --- a/src/qt/openuridialog.cpp +++ b/src/qt/openuridialog.cpp @@ -15,9 +15,7 @@ OpenURIDialog::OpenURIDialog(QWidget *parent) : ui(new Ui::OpenURIDialog) { ui->setupUi(this); -#if QT_VERSION >= 0x040700 ui->uriEdit->setPlaceholderText("bitcoin:"); -#endif } OpenURIDialog::~OpenURIDialog() diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 108aa4f99c..3ff6f59c25 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -97,28 +97,16 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) : /** check if the locale name consists of 2 parts (language_country) */ if(langStr.contains("_")) { -#if QT_VERSION >= 0x040800 /** display language strings as "native language - native country (locale name)", e.g. "Deutsch - Deutschland (de)" */ ui->lang->addItem(locale.nativeLanguageName() + QString(" - ") + locale.nativeCountryName() + QString(" (") + langStr + QString(")"), QVariant(langStr)); -#else - /** display language strings as "language - country (locale name)", e.g. "German - Germany (de)" */ - ui->lang->addItem(QLocale::languageToString(locale.language()) + QString(" - ") + QLocale::countryToString(locale.country()) + QString(" (") + langStr + QString(")"), QVariant(langStr)); -#endif } else { -#if QT_VERSION >= 0x040800 /** display language strings as "native language (locale name)", e.g. "Deutsch (de)" */ ui->lang->addItem(locale.nativeLanguageName() + QString(" (") + langStr + QString(")"), QVariant(langStr)); -#else - /** display language strings as "language (locale name)", e.g. "German (de)" */ - ui->lang->addItem(QLocale::languageToString(locale.language()) + QString(" (") + langStr + QString(")"), QVariant(langStr)); -#endif } } -#if QT_VERSION >= 0x040700 ui->thirdPartyTxUrls->setPlaceholderText("https://example.com/tx/%s"); -#endif ui->unit->setModel(new BitcoinUnits(this)); diff --git a/src/qt/paymentrequestplus.cpp b/src/qt/paymentrequestplus.cpp index b0ef475b35..dfeb70d669 100644 --- a/src/qt/paymentrequestplus.cpp +++ b/src/qt/paymentrequestplus.cpp @@ -97,12 +97,10 @@ bool PaymentRequestPlus::getMerchant(X509_STORE* certStore, QString& merchant) c qWarning() << "PaymentRequestPlus::getMerchant: Payment request: certificate expired or not yet active: " << qCert; return false; } -#if QT_VERSION >= 0x050000 if (qCert.isBlacklisted()) { qWarning() << "PaymentRequestPlus::getMerchant: Payment request: certificate blacklisted: " << qCert; return false; } -#endif const unsigned char *data = (const unsigned char *)certChain.certificate(i).data(); X509 *cert = d2i_X509(nullptr, &data, certChain.certificate(i).size()); if (cert) diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp index 59bb5d5bb6..e5e6430959 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -41,12 +41,7 @@ #include <QSslSocket> #include <QStringList> #include <QTextDocument> - -#if QT_VERSION < 0x050000 -#include <QUrl> -#else #include <QUrlQuery> -#endif const int BITCOIN_IPC_CONNECT_TIMEOUT = 1000; // milliseconds const QString BITCOIN_IPC_PREFIX("bitcoin:"); @@ -100,11 +95,7 @@ static QList<QString> savedPaymentRequests; static void ReportInvalidCertificate(const QSslCertificate& cert) { -#if QT_VERSION < 0x050000 - qDebug() << QString("%1: Payment server found an invalid certificate: ").arg(__func__) << cert.serialNumber() << cert.subjectInfo(QSslCertificate::CommonName) << cert.subjectInfo(QSslCertificate::OrganizationalUnitName); -#else qDebug() << QString("%1: Payment server found an invalid certificate: ").arg(__func__) << cert.serialNumber() << cert.subjectInfo(QSslCertificate::CommonName) << cert.subjectInfo(QSslCertificate::DistinguishedNameQualifier) << cert.subjectInfo(QSslCertificate::OrganizationalUnitName); -#endif } // @@ -157,13 +148,11 @@ void PaymentServer::LoadRootCAs(X509_STORE* _store) continue; } -#if QT_VERSION >= 0x050000 // Blacklisted certificate if (cert.isBlacklisted()) { ReportInvalidCertificate(cert); continue; } -#endif QByteArray certData = cert.toDer(); const unsigned char *data = (const unsigned char *)certData.data(); @@ -413,11 +402,7 @@ void PaymentServer::handleURIOrFile(const QString& s) } else if (s.startsWith(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) // bitcoin: URI { -#if QT_VERSION < 0x050000 - QUrl uri(s); -#else QUrlQuery uri((QUrl(s))); -#endif if (uri.hasQueryItem("r")) // payment request URI { QByteArray temp; diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp index 7e318e3035..0ca9d48bf6 100644 --- a/src/qt/peertablemodel.cpp +++ b/src/qt/peertablemodel.cpp @@ -64,9 +64,7 @@ public: interfaces::Node::NodesStats nodes_stats; node.getNodesStats(nodes_stats); -#if QT_VERSION >= 0x040700 cachedNodeStats.reserve(nodes_stats.size()); -#endif for (auto& node_stats : nodes_stats) { CNodeCombinedStats stats; diff --git a/src/qt/receiverequestdialog.cpp b/src/qt/receiverequestdialog.cpp index 75146e2214..f9610f2d3b 100644 --- a/src/qt/receiverequestdialog.cpp +++ b/src/qt/receiverequestdialog.cpp @@ -16,9 +16,6 @@ #include <QMimeData> #include <QMouseEvent> #include <QPixmap> -#if QT_VERSION < 0x050000 -#include <QUrl> -#endif #if defined(HAVE_CONFIG_H) #include <config/bitcoin-config.h> /* for USE_QRCODE */ diff --git a/src/qt/res/movies/makespinner.sh b/src/qt/res/movies/makespinner.sh index d0deb1238c..76e36e4f31 100755 --- a/src/qt/res/movies/makespinner.sh +++ b/src/qt/res/movies/makespinner.sh @@ -2,6 +2,7 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +export LC_ALL=C FRAMEDIR=$(dirname $0) for i in {0..35} do diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 4550ae9396..f222357f27 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -40,10 +40,6 @@ #include <QTimer> #include <QStringList> -#if QT_VERSION < 0x050000 -#include <QUrl> -#endif - // TODO: add a scrollback limit, as there is currently none // TODO: make it possible to filter out categories (esp debug messages when implemented) // TODO: receive errors and debug messages through ClientModel @@ -713,6 +709,16 @@ void RPCConsole::addWallet(WalletModel * const walletModel) ui->WalletSelectorLabel->setVisible(true); } } + +void RPCConsole::removeWallet(WalletModel * const walletModel) +{ + const QString name = walletModel->getWalletName(); + ui->WalletSelector->removeItem(ui->WalletSelector->findData(name)); + if (ui->WalletSelector->count() == 2) { + ui->WalletSelector->setVisible(false); + ui->WalletSelectorLabel->setVisible(false); + } +} #endif static QString categoryClass(int category) diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h index a53c4c24f9..0a1a469934 100644 --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -48,6 +48,7 @@ public: void setClientModel(ClientModel *model); void addWallet(WalletModel * const walletModel); + void removeWallet(WalletModel* const walletModel); enum MessageClass { MC_ERROR, diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp index 977425f7e3..e8c85bc2a1 100644 --- a/src/qt/sendcoinsentry.cpp +++ b/src/qt/sendcoinsentry.cpp @@ -32,9 +32,7 @@ SendCoinsEntry::SendCoinsEntry(const PlatformStyle *_platformStyle, QWidget *par if (platformStyle->getUseExtraSpacing()) ui->payToLayout->setSpacing(4); -#if QT_VERSION >= 0x040700 ui->addAsLabel->setPlaceholderText(tr("Enter a label for this address to add it to your address book")); -#endif // normal bitcoin address field GUIUtil::setupAddressWidget(ui->payTo, this); diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp index 94a3ad7987..c8e694e658 100644 --- a/src/qt/signverifymessagedialog.cpp +++ b/src/qt/signverifymessagedialog.cpp @@ -37,9 +37,7 @@ SignVerifyMessageDialog::SignVerifyMessageDialog(const PlatformStyle *_platformS ui->verifyMessageButton_VM->setIcon(platformStyle->SingleColorIcon(":/icons/transaction_0")); ui->clearButton_VM->setIcon(platformStyle->SingleColorIcon(":/icons/remove")); -#if QT_VERSION >= 0x040700 ui->signatureOut_SM->setPlaceholderText(tr("Click \"Sign Message\" to generate signature")); -#endif GUIUtil::setupAddressWidget(ui->addressIn_SM, this); GUIUtil::setupAddressWidget(ui->addressIn_VM, this); diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp index 56d4d3e457..f0ac43a58c 100644 --- a/src/qt/test/test_main.cpp +++ b/src/qt/test/test_main.cpp @@ -26,12 +26,6 @@ #if defined(QT_STATICPLUGIN) #include <QtPlugin> -#if QT_VERSION < 0x050000 -Q_IMPORT_PLUGIN(qcncodecs) -Q_IMPORT_PLUGIN(qjpcodecs) -Q_IMPORT_PLUGIN(qtwcodecs) -Q_IMPORT_PLUGIN(qkrcodecs) -#else #if defined(QT_QPA_PLATFORM_MINIMAL) Q_IMPORT_PLUGIN(QMinimalIntegrationPlugin); #endif @@ -43,7 +37,6 @@ Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin); Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin); #endif #endif -#endif extern void noui_connect(); diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp index 33c49dc7cb..c314dadde4 100644 --- a/src/qt/test/wallettests.cpp +++ b/src/qt/test/wallettests.cpp @@ -87,17 +87,6 @@ QModelIndex FindTx(const QAbstractItemModel& model, const uint256& txid) return {}; } -//! Request context menu (call method that is public in qt5, but protected in qt4). -void RequestContextMenu(QWidget* widget) -{ - class Qt4Hack : public QWidget - { - public: - using QWidget::customContextMenuRequested; - }; - static_cast<Qt4Hack*>(widget)->customContextMenuRequested({}); -} - //! Invoke bumpfee on txid and check results. void BumpFee(TransactionView& view, const uint256& txid, bool expectDisabled, std::string expectError, bool cancel) { @@ -110,7 +99,7 @@ void BumpFee(TransactionView& view, const uint256& txid, bool expectDisabled, st QAction* action = view.findChild<QAction*>("bumpFeeAction"); table->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); action->setEnabled(expectDisabled); - RequestContextMenu(table); + table->customContextMenuRequested({}); QCOMPARE(action->isEnabled(), !expectDisabled); action->setEnabled(true); diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index aa6444245a..e60a387934 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -95,15 +95,11 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa hlayout->addWidget(typeWidget); search_widget = new QLineEdit(this); -#if QT_VERSION >= 0x040700 search_widget->setPlaceholderText(tr("Enter address, transaction id, or label to search")); -#endif hlayout->addWidget(search_widget); amountWidget = new QLineEdit(this); -#if QT_VERSION >= 0x040700 amountWidget->setPlaceholderText(tr("Min amount")); -#endif if (platformStyle->getUseExtraSpacing()) { amountWidget->setFixedWidth(97); } else { diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp index eb0eba21ef..c5a13f61f4 100644 --- a/src/qt/walletframe.cpp +++ b/src/qt/walletframe.cpp @@ -94,6 +94,7 @@ bool WalletFrame::removeWallet(const QString &name) WalletView *walletView = mapWalletViews.take(name); walletStack->removeWidget(walletView); + delete walletView; return true; } diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 3418b1f1a9..389acf0a95 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -364,6 +364,12 @@ bool WalletModel::changePassphrase(const SecureString &oldPass, const SecureStri } // Handlers for core signals +static void NotifyUnload(WalletModel* walletModel) +{ + qDebug() << "NotifyUnload"; + QMetaObject::invokeMethod(walletModel, "unload", Qt::QueuedConnection); +} + static void NotifyKeyStoreStatusChanged(WalletModel *walletmodel) { qDebug() << "NotifyKeyStoreStatusChanged"; @@ -411,6 +417,7 @@ static void NotifyWatchonlyChanged(WalletModel *walletmodel, bool fHaveWatchonly void WalletModel::subscribeToCoreSignals() { // Connect signals to wallet + m_handler_unload = m_wallet->handleUnload(boost::bind(&NotifyUnload, this)); m_handler_status_changed = m_wallet->handleStatusChanged(boost::bind(&NotifyKeyStoreStatusChanged, this)); m_handler_address_book_changed = m_wallet->handleAddressBookChanged(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5)); m_handler_transaction_changed = m_wallet->handleTransactionChanged(boost::bind(NotifyTransactionChanged, this, _1, _2)); @@ -421,6 +428,7 @@ void WalletModel::subscribeToCoreSignals() void WalletModel::unsubscribeFromCoreSignals() { // Disconnect signals from wallet + m_handler_unload->disconnect(); m_handler_status_changed->disconnect(); m_handler_address_book_changed->disconnect(); m_handler_transaction_changed->disconnect(); diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 9173fcae52..35ededb121 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -208,6 +208,7 @@ public: AddressTableModel* getAddressTableModel() const { return addressTableModel; } private: std::unique_ptr<interfaces::Wallet> m_wallet; + std::unique_ptr<interfaces::Handler> m_handler_unload; std::unique_ptr<interfaces::Handler> m_handler_status_changed; std::unique_ptr<interfaces::Handler> m_handler_address_book_changed; std::unique_ptr<interfaces::Handler> m_handler_transaction_changed; @@ -261,6 +262,9 @@ Q_SIGNALS: // Watch-only address added void notifyWatchonlyChanged(bool fHaveWatchonly); + // Signal that wallet is about to be removed + void unload(); + public Q_SLOTS: /* Wallet status might have changed */ void updateStatus(); diff --git a/src/qt/winshutdownmonitor.cpp b/src/qt/winshutdownmonitor.cpp index 1e7a76efc0..6190a74598 100644 --- a/src/qt/winshutdownmonitor.cpp +++ b/src/qt/winshutdownmonitor.cpp @@ -4,7 +4,7 @@ #include <qt/winshutdownmonitor.h> -#if defined(Q_OS_WIN) && QT_VERSION >= 0x050000 +#if defined(Q_OS_WIN) #include <init.h> #include <util.h> diff --git a/src/qt/winshutdownmonitor.h b/src/qt/winshutdownmonitor.h index 0bed55a2c6..c8a523a538 100644 --- a/src/qt/winshutdownmonitor.h +++ b/src/qt/winshutdownmonitor.h @@ -9,7 +9,6 @@ #include <QByteArray> #include <QString> -#if QT_VERSION >= 0x050000 #include <windef.h> // for HWND #include <QAbstractNativeEventFilter> @@ -24,6 +23,5 @@ public: static void registerShutdownBlockReason(const QString& strReason, const HWND& mainWinId); }; #endif -#endif #endif // BITCOIN_QT_WINSHUTDOWNMONITOR_H diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 85b864e6b9..81c4fb040f 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -725,7 +725,6 @@ static UniValue submitblock(const JSONRPCRequest& request) } uint256 hash = block.GetHash(); - bool fBlockPresent = false; { LOCK(cs_main); const CBlockIndex* pindex = LookupBlockIndex(hash); @@ -736,8 +735,6 @@ static UniValue submitblock(const JSONRPCRequest& request) if (pindex->nStatus & BLOCK_FAILED_MASK) { return "duplicate-invalid"; } - // Otherwise, we might only have the header - process the block before returning - fBlockPresent = true; } } @@ -749,13 +746,15 @@ static UniValue submitblock(const JSONRPCRequest& request) } } + bool new_block; submitblock_StateCatcher sc(block.GetHash()); RegisterValidationInterface(&sc); - bool fAccepted = ProcessNewBlock(Params(), blockptr, true, nullptr); + bool accepted = ProcessNewBlock(Params(), blockptr, /* fForceProcessing */ true, /* fNewBlock */ &new_block); UnregisterValidationInterface(&sc); - if (fBlockPresent) { - if (fAccepted && !sc.found) { - return "duplicate-inconclusive"; + if (!new_block) { + if (!accepted) { + // TODO Maybe pass down fNewBlock to AcceptBlockHeader, so it is properly set to true in this case? + return "invalid"; } return "duplicate"; } diff --git a/src/validation.cpp b/src/validation.cpp index 598b3dadd7..3b8118b036 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -205,7 +205,7 @@ private: void InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state); CBlockIndex* FindMostWorkChain(); - bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBlockIndex *pindexNew, const CDiskBlockPos& pos, const Consensus::Params& consensusParams); + void ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pindexNew, const CDiskBlockPos& pos, const Consensus::Params& consensusParams); bool RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& inputs, const CChainParams& params); @@ -2953,7 +2953,7 @@ CBlockIndex* CChainState::AddToBlockIndex(const CBlockHeader& block) } /** Mark a block as having its data received and checked (up to BLOCK_VALID_TRANSACTIONS). */ -bool CChainState::ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBlockIndex *pindexNew, const CDiskBlockPos& pos, const Consensus::Params& consensusParams) +void CChainState::ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pindexNew, const CDiskBlockPos& pos, const Consensus::Params& consensusParams) { pindexNew->nTx = block.vtx.size(); pindexNew->nChainTx = 0; @@ -2997,8 +2997,6 @@ bool CChainState::ReceivedBlockTransactions(const CBlock &block, CValidationStat mapBlocksUnlinked.insert(std::make_pair(pindexNew->pprev, pindexNew)); } } - - return true; } static bool FindBlockPos(CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64_t nTime, bool fKnown = false) @@ -3510,7 +3508,6 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CVali // request; don't process these. if (pindex->nChainWork < nMinimumChainWork) return true; } - if (fNewBlock) *fNewBlock = true; if (!CheckBlock(block, state, chainparams.GetConsensus()) || !ContextualCheckBlock(block, state, chainparams.GetConsensus(), pindex->pprev)) { @@ -3527,14 +3524,14 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CVali GetMainSignals().NewPoWValidBlock(pindex, pblock); // Write block to history file + if (fNewBlock) *fNewBlock = true; try { CDiskBlockPos blockPos = SaveBlockToDisk(block, pindex->nHeight, chainparams, dbp); if (blockPos.IsNull()) { state.Error(strprintf("%s: Failed to find position to write new block to disk", __func__)); return false; } - if (!ReceivedBlockTransactions(block, state, pindex, blockPos, chainparams.GetConsensus())) - return error("AcceptBlock(): ReceivedBlockTransactions failed"); + ReceivedBlockTransactions(block, pindex, blockPos, chainparams.GetConsensus()); } catch (const std::runtime_error& e) { return AbortNode(state, std::string("System error: ") + e.what()); } @@ -4343,9 +4340,7 @@ bool CChainState::LoadGenesisBlock(const CChainParams& chainparams) if (blockPos.IsNull()) return error("%s: writing genesis block to disk failed", __func__); CBlockIndex *pindex = AddToBlockIndex(block); - CValidationState state; - if (!ReceivedBlockTransactions(block, state, pindex, blockPos, chainparams.GetConsensus())) - return error("%s: genesis block not accepted (%s)", __func__, FormatStateMessage(state)); + ReceivedBlockTransactions(block, pindex, blockPos, chainparams.GetConsensus()); } catch (const std::runtime_error& e) { return error("%s: failed to write genesis block: %s", __func__, e.what()); } diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index 410dd5009f..01b8eacccb 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -694,8 +694,10 @@ void BerkeleyEnvironment::Flush(bool fShutdown) if (mapFileUseCount.empty()) { dbenv->log_archive(&listp, DB_ARCH_REMOVE); Close(); - if (!fMockDb) + if (!fMockDb) { fs::remove_all(fs::path(strPath) / "database"); + } + g_dbenvs.erase(strPath); } } } @@ -794,5 +796,6 @@ void BerkeleyDatabase::Flush(bool shutdown) { if (!IsDummy()) { env->Flush(shutdown); + if (shutdown) env = nullptr; } } diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index fd9e23cb64..3f2255d042 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -40,12 +40,21 @@ static const std::string WALLET_ENDPOINT_BASE = "/wallet/"; -std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& request) +bool GetWalletNameFromJSONRPCRequest(const JSONRPCRequest& request, std::string& wallet_name) { if (request.URI.substr(0, WALLET_ENDPOINT_BASE.size()) == WALLET_ENDPOINT_BASE) { // wallet endpoint was used - std::string requestedWallet = urlDecode(request.URI.substr(WALLET_ENDPOINT_BASE.size())); - std::shared_ptr<CWallet> pwallet = GetWallet(requestedWallet); + wallet_name = urlDecode(request.URI.substr(WALLET_ENDPOINT_BASE.size())); + return true; + } + return false; +} + +std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& request) +{ + std::string wallet_name; + if (GetWalletNameFromJSONRPCRequest(request, wallet_name)) { + std::shared_ptr<CWallet> pwallet = GetWallet(wallet_name); if (!pwallet) throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded"); return pwallet; } @@ -66,11 +75,6 @@ bool EnsureWalletIsAvailable(CWallet * const pwallet, bool avoidException) if (pwallet) return true; if (avoidException) return false; if (!HasWallets()) { - // Note: It isn't currently possible to trigger this error because - // wallet RPC methods aren't registered unless a wallet is loaded. But - // this error is being kept as a precaution, because it's possible in - // the future that wallet RPC methods might get or remain registered - // when no wallets are loaded. throw JSONRPCError( RPC_METHOD_NOT_FOUND, "Method not found (wallet method is disabled because no wallet is loaded)"); } @@ -3053,7 +3057,7 @@ static UniValue listwallets(const JSONRPCRequest& request) return obj; } -UniValue loadwallet(const JSONRPCRequest& request) +static UniValue loadwallet(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 1) throw std::runtime_error( @@ -3100,7 +3104,7 @@ UniValue loadwallet(const JSONRPCRequest& request) return obj; } -UniValue createwallet(const JSONRPCRequest& request) +static UniValue createwallet(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( @@ -3147,6 +3151,55 @@ UniValue createwallet(const JSONRPCRequest& request) return obj; } +static UniValue unloadwallet(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() > 1) { + throw std::runtime_error( + "unloadwallet ( \"wallet_name\" )\n" + "Unloads the wallet referenced by the request endpoint otherwise unloads the wallet specified in the argument.\n" + "Specifying the wallet name on a wallet endpoint is invalid." + "\nArguments:\n" + "1. \"wallet_name\" (string, optional) The name of the wallet to unload.\n" + "\nExamples:\n" + + HelpExampleCli("unloadwallet", "wallet_name") + + HelpExampleRpc("unloadwallet", "wallet_name") + ); + } + + std::string wallet_name; + if (GetWalletNameFromJSONRPCRequest(request, wallet_name)) { + if (!request.params[0].isNull()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot unload the requested wallet"); + } + } else { + wallet_name = request.params[0].get_str(); + } + + std::shared_ptr<CWallet> wallet = GetWallet(wallet_name); + if (!wallet) { + throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded"); + } + + // Release the "main" shared pointer and prevent further notifications. + // Note that any attempt to load the same wallet would fail until the wallet + // is destroyed (see CheckUniqueFileid). + if (!RemoveWallet(wallet)) { + throw JSONRPCError(RPC_MISC_ERROR, "Requested wallet already unloaded"); + } + UnregisterValidationInterface(wallet.get()); + + // The wallet can be in use so it's not possible to explicitly unload here. + // Just notify the unload intent so that all shared pointers are released. + // The wallet will be destroyed once the last shared pointer is released. + wallet->NotifyUnload(); + + // There's no point in waiting for the wallet to unload. + // At this point this method should never fail. The unloading could only + // fail due to an unexpected error which would cause a process termination. + + return NullUniValue; +} + static UniValue resendwallettransactions(const JSONRPCRequest& request) { std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); @@ -4382,6 +4435,7 @@ static const CRPCCommand commands[] = { "wallet", "settxfee", &settxfee, {"amount"} }, { "wallet", "signmessage", &signmessage, {"address","message"} }, { "wallet", "signrawtransactionwithwallet", &signrawtransactionwithwallet, {"hexstring","prevtxs","sighashtype"} }, + { "wallet", "unloadwallet", &unloadwallet, {"wallet_name"} }, { "wallet", "walletlock", &walletlock, {} }, { "wallet", "walletpassphrasechange", &walletpassphrasechange, {"oldpassphrase","newpassphrase"} }, { "wallet", "walletpassphrase", &walletpassphrase, {"passphrase","timeout"} }, diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index f55c2262e6..6255f28b17 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -79,6 +79,15 @@ std::shared_ptr<CWallet> GetWallet(const std::string& name) return nullptr; } +// Custom deleter for shared_ptr<CWallet>. +static void ReleaseWallet(CWallet* wallet) +{ + LogPrintf("Releasing wallet %s\n", wallet->GetName()); + wallet->BlockUntilSyncedToCurrentChain(); + wallet->Flush(); + delete wallet; +} + const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000; const uint256 CMerkleTx::ABANDON_HASH(uint256S("0000000000000000000000000000000000000000000000000000000000000001")); @@ -923,11 +932,10 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose) CWalletTx& wtx = (*ret.first).second; wtx.BindWallet(this); bool fInsertedNew = ret.second; - if (fInsertedNew) - { + if (fInsertedNew) { wtx.nTimeReceived = GetAdjustedTime(); wtx.nOrderPos = IncOrderPosNext(&batch); - wtxOrdered.insert(std::make_pair(wtx.nOrderPos, TxPair(&wtx, nullptr))); + wtx.m_it_wtxOrdered = wtxOrdered.insert(std::make_pair(wtx.nOrderPos, TxPair(&wtx, nullptr))); wtx.nTimeSmart = ComputeTimeSmart(wtx); AddToSpends(hash); } @@ -998,9 +1006,12 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose) bool CWallet::LoadToWallet(const CWalletTx& wtxIn) { uint256 hash = wtxIn.GetHash(); - CWalletTx& wtx = mapWallet.emplace(hash, wtxIn).first->second; + const auto& ins = mapWallet.emplace(hash, wtxIn); + CWalletTx& wtx = ins.first->second; wtx.BindWallet(this); - wtxOrdered.insert(std::make_pair(wtx.nOrderPos, TxPair(&wtx, nullptr))); + if (/* insertion took place */ ins.second) { + wtx.m_it_wtxOrdered = wtxOrdered.insert(std::make_pair(wtx.nOrderPos, TxPair(&wtx, nullptr))); + } AddToSpends(hash); for (const CTxIn& txin : wtx.tx->vin) { auto it = mapWallet.find(txin.prevout.hash); @@ -1294,7 +1305,7 @@ void CWallet::BlockUntilSyncedToCurrentChain() { LOCK(cs_main); const CBlockIndex* initialChainTip = chainActive.Tip(); - if (m_last_block_processed->GetAncestor(initialChainTip->nHeight) == initialChainTip) { + if (m_last_block_processed && m_last_block_processed->GetAncestor(initialChainTip->nHeight) == initialChainTip) { return; } } @@ -3210,8 +3221,11 @@ DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256 { AssertLockHeld(cs_wallet); // mapWallet DBErrors nZapSelectTxRet = WalletBatch(*database,"cr+").ZapSelectTx(vHashIn, vHashOut); - for (uint256 hash : vHashOut) - mapWallet.erase(hash); + for (uint256 hash : vHashOut) { + const auto& it = mapWallet.find(hash); + wtxOrdered.erase(it->second.m_it_wtxOrdered); + mapWallet.erase(it); + } if (nZapSelectTxRet == DBErrors::NEED_REWRITE) { @@ -4061,7 +4075,9 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name, int64_t nStart = GetTimeMillis(); bool fFirstRun = true; - std::shared_ptr<CWallet> walletInstance = std::make_shared<CWallet>(name, WalletDatabase::Create(path)); + // TODO: Can't use std::make_shared because we need a custom deleter but + // should be possible to use std::allocate_shared. + std::shared_ptr<CWallet> walletInstance(new CWallet(name, WalletDatabase::Create(path)), ReleaseWallet); DBErrors nLoadWalletRet = walletInstance->LoadWallet(fFirstRun); if (nLoadWalletRet != DBErrors::LOAD_OK) { @@ -4090,8 +4106,6 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name, } } - uiInterface.LoadWallet(walletInstance); - int prev_version = walletInstance->nWalletVersion; if (gArgs.GetBoolArg("-upgradewallet", fFirstRun)) { @@ -4341,6 +4355,8 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name, } } + uiInterface.LoadWallet(walletInstance); + // Register with the validation interface. It's ok to do this after rescan since we're still holding cs_main. RegisterValidationInterface(walletInstance.get()); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 1ec2a9e771..b829394847 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -339,6 +339,7 @@ public: char fFromMe; std::string strFromAccount; int64_t nOrderPos; //!< position in ordered transaction list + std::multimap<int64_t, std::pair<CWalletTx*, CAccountingEntry*>>::const_iterator m_it_wtxOrdered; // memory only mutable bool fDebitCached; @@ -1099,6 +1100,9 @@ public: //! Flush wallet (bitdb flush) void Flush(bool shutdown=false); + /** Wallet is about to be unloaded */ + boost::signals2::signal<void ()> NotifyUnload; + /** * Address book entry changed. * @note called with lock cs_wallet held. diff --git a/test/functional/feature_assumevalid.py b/test/functional/feature_assumevalid.py index 5a09142412..a925c70783 100755 --- a/test/functional/feature_assumevalid.py +++ b/test/functional/feature_assumevalid.py @@ -68,12 +68,12 @@ class AssumeValidTest(BitcoinTestFramework): def send_blocks_until_disconnected(self, p2p_conn): """Keep sending blocks to the node until we're disconnected.""" for i in range(len(self.blocks)): - if p2p_conn.state != "connected": + if not p2p_conn.is_connected: break try: p2p_conn.send_message(msg_block(self.blocks[i])) except IOError as e: - assert str(e) == 'Not connected, no pushbuf' + assert not p2p_conn.is_connected break def assert_blockchain_height(self, node, height): diff --git a/test/functional/feature_notifications.py b/test/functional/feature_notifications.py index 8964c8d64b..6d51f31e35 100755 --- a/test/functional/feature_notifications.py +++ b/test/functional/feature_notifications.py @@ -36,7 +36,7 @@ class NotificationsTest(BitcoinTestFramework): wait_until(lambda: os.path.isfile(self.block_filename) and os.stat(self.block_filename).st_size >= (block_count * 65), timeout=10) # file content should equal the generated blocks hashes - with open(self.block_filename, 'r') as f: + with open(self.block_filename, 'r', encoding="utf-8") as f: assert_equal(sorted(blocks), sorted(l.strip() for l in f.read().splitlines())) self.log.info("test -walletnotify") @@ -45,7 +45,7 @@ class NotificationsTest(BitcoinTestFramework): # file content should equal the generated transaction hashes txids_rpc = list(map(lambda t: t['txid'], self.nodes[1].listtransactions("*", block_count))) - with open(self.tx_filename, 'r') as f: + with open(self.tx_filename, 'r', encoding="ascii") as f: assert_equal(sorted(txids_rpc), sorted(l.strip() for l in f.read().splitlines())) os.remove(self.tx_filename) @@ -58,7 +58,7 @@ class NotificationsTest(BitcoinTestFramework): # file content should equal the generated transaction hashes txids_rpc = list(map(lambda t: t['txid'], self.nodes[1].listtransactions("*", block_count))) - with open(self.tx_filename, 'r') as f: + with open(self.tx_filename, 'r', encoding="ascii") as f: assert_equal(sorted(txids_rpc), sorted(l.strip() for l in f.read().splitlines())) # Mine another 41 up-version blocks. -alertnotify should trigger on the 51st. diff --git a/test/functional/p2p_compactblocks.py b/test/functional/p2p_compactblocks.py index cb4c9867a3..37849e9213 100755 --- a/test/functional/p2p_compactblocks.py +++ b/test/functional/p2p_compactblocks.py @@ -87,7 +87,7 @@ class TestP2PConn(P2PInterface): This is used when we want to send a message into the node that we expect will get us disconnected, eg an invalid block.""" self.send_message(message) - wait_until(lambda: self.state != "connected", timeout=timeout, lock=mininode_lock) + wait_until(lambda: not self.is_connected, timeout=timeout, lock=mininode_lock) class CompactBlocksTest(BitcoinTestFramework): def set_test_params(self): diff --git a/test/functional/p2p_leak.py b/test/functional/p2p_leak.py index 198dcc1490..dca5ea39de 100755 --- a/test/functional/p2p_leak.py +++ b/test/functional/p2p_leak.py @@ -118,11 +118,11 @@ class P2PLeakTest(BitcoinTestFramework): time.sleep(5) #This node should have been banned - assert no_version_bannode.state != "connected" + assert not no_version_bannode.is_connected # These nodes should have been disconnected - assert unsupported_service_bit5_node.state != "connected" - assert unsupported_service_bit7_node.state != "connected" + assert not unsupported_service_bit5_node.is_connected + assert not unsupported_service_bit7_node.is_connected self.nodes[0].disconnect_p2ps() diff --git a/test/functional/p2p_timeouts.py b/test/functional/p2p_timeouts.py index 6a21b693b4..e958536cf9 100755 --- a/test/functional/p2p_timeouts.py +++ b/test/functional/p2p_timeouts.py @@ -47,9 +47,9 @@ class TimeoutsTest(BitcoinTestFramework): sleep(1) - assert no_verack_node.connected - assert no_version_node.connected - assert no_send_node.connected + assert no_verack_node.is_connected + assert no_version_node.is_connected + assert no_send_node.is_connected no_verack_node.send_message(msg_ping()) no_version_node.send_message(msg_ping()) @@ -58,18 +58,18 @@ class TimeoutsTest(BitcoinTestFramework): assert "version" in no_verack_node.last_message - assert no_verack_node.connected - assert no_version_node.connected - assert no_send_node.connected + assert no_verack_node.is_connected + assert no_version_node.is_connected + assert no_send_node.is_connected no_verack_node.send_message(msg_ping()) no_version_node.send_message(msg_ping()) sleep(31) - assert not no_verack_node.connected - assert not no_version_node.connected - assert not no_send_node.connected + assert not no_verack_node.is_connected + assert not no_version_node.is_connected + assert not no_send_node.is_connected if __name__ == '__main__': TimeoutsTest().main() diff --git a/test/functional/rpc_getblockstats.py b/test/functional/rpc_getblockstats.py index 060a2373b1..f573faaf17 100755 --- a/test/functional/rpc_getblockstats.py +++ b/test/functional/rpc_getblockstats.py @@ -81,11 +81,11 @@ class GetblockstatsTest(BitcoinTestFramework): 'mocktime': int(mocktime), 'stats': self.expected_stats, } - with open(filename, 'w') as f: + with open(filename, 'w', encoding="utf8") as f: json.dump(to_dump, f, sort_keys=True, indent=2) def load_test_data(self, filename): - with open(filename, 'r') as f: + with open(filename, 'r', encoding="utf8") as f: d = json.load(f) blocks = d['blocks'] mocktime = d['mocktime'] diff --git a/test/functional/test_framework/mininode.py b/test/functional/test_framework/mininode.py index 7c2125a177..5859f108a4 100755 --- a/test/functional/test_framework/mininode.py +++ b/test/functional/test_framework/mininode.py @@ -77,6 +77,12 @@ class P2PConnection(asyncore.dispatcher): super().__init__(map=mininode_socket_map) + self._conn_open = False + + @property + def is_connected(self): + return self._conn_open + def peer_connect(self, dstaddr, dstport, net="regtest"): self.dstaddr = dstaddr self.dstport = dstport @@ -84,7 +90,7 @@ class P2PConnection(asyncore.dispatcher): self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) self.sendbuf = b"" self.recvbuf = b"" - self.state = "connecting" + self._asyncore_pre_connection = True self.network = net self.disconnect = False @@ -97,22 +103,23 @@ class P2PConnection(asyncore.dispatcher): def peer_disconnect(self): # Connection could have already been closed by other end. - if self.state == "connected": - self.disconnect_node() + if self.is_connected: + self.disconnect = True # Signal asyncore to disconnect # Connection and disconnection methods def handle_connect(self): """asyncore callback when a connection is opened.""" - if self.state != "connected": + if not self.is_connected: logger.debug("Connected & Listening: %s:%d" % (self.dstaddr, self.dstport)) - self.state = "connected" + self._conn_open = True + self._asyncore_pre_connection = False self.on_open() def handle_close(self): """asyncore callback when a connection is closed.""" logger.debug("Closing connection to: %s:%d" % (self.dstaddr, self.dstport)) - self.state = "closed" + self._conn_open = False self.recvbuf = b"" self.sendbuf = b"" try: @@ -121,13 +128,6 @@ class P2PConnection(asyncore.dispatcher): pass self.on_close() - def disconnect_node(self): - """Disconnect the p2p connection. - - Called by the test logic thread. Causes the p2p connection - to be disconnected on the next iteration of the asyncore loop.""" - self.disconnect = True - # Socket read methods def handle_read(self): @@ -182,9 +182,8 @@ class P2PConnection(asyncore.dispatcher): def writable(self): """asyncore method to determine whether the handle_write() callback should be called on the next loop.""" with mininode_lock: - pre_connection = self.state == "connecting" length = len(self.sendbuf) - return (length > 0 or pre_connection) + return length > 0 or self._asyncore_pre_connection def handle_write(self): """asyncore callback when data should be written to the socket.""" @@ -192,7 +191,7 @@ class P2PConnection(asyncore.dispatcher): # asyncore does not expose socket connection, only the first read/write # event, thus we must check connection manually here to know when we # actually connect - if self.state == "connecting": + if self._asyncore_pre_connection: self.handle_connect() if not self.writable(): return @@ -204,26 +203,17 @@ class P2PConnection(asyncore.dispatcher): return self.sendbuf = self.sendbuf[sent:] - def send_message(self, message, pushbuf=False): + def send_message(self, message): """Send a P2P message over the socket. This method takes a P2P payload, builds the P2P header and adds the message to the send buffer to be sent over the socket.""" - if self.state != "connected" and not pushbuf: - raise IOError('Not connected, no pushbuf') + if not self.is_connected: + raise IOError('Not connected') self._log_message("send", message) - command = message.command - data = message.serialize() - tmsg = MAGIC_BYTES[self.network] - tmsg += command - tmsg += b"\x00" * (12 - len(command)) - tmsg += struct.pack("<I", len(data)) - th = sha256(data) - h = sha256(th) - tmsg += h[:4] - tmsg += data + tmsg = self._build_message(message) with mininode_lock: - if (len(self.sendbuf) == 0 and not pushbuf): + if len(self.sendbuf) == 0: try: sent = self.send(tmsg) self.sendbuf = tmsg[sent:] @@ -234,6 +224,20 @@ class P2PConnection(asyncore.dispatcher): # Class utility methods + def _build_message(self, message): + """Build a serialized P2P message""" + command = message.command + data = message.serialize() + tmsg = MAGIC_BYTES[self.network] + tmsg += command + tmsg += b"\x00" * (12 - len(command)) + tmsg += struct.pack("<I", len(data)) + th = sha256(data) + h = sha256(th) + tmsg += h[:4] + tmsg += data + return tmsg + def _log_message(self, direction, msg): """Logs a message being sent or received over the connection.""" if direction == "send": @@ -280,7 +284,7 @@ class P2PInterface(P2PConnection): vt.addrTo.port = self.dstport vt.addrFrom.ip = "0.0.0.0" vt.addrFrom.port = 0 - self.send_message(vt, True) + self.sendbuf = self._build_message(vt) # Will be sent right after handle_connect # Message receiving methods @@ -348,7 +352,7 @@ class P2PInterface(P2PConnection): # Connection helper methods def wait_for_disconnect(self, timeout=60): - test_function = lambda: self.state != "connected" + test_function = lambda: not self.is_connected wait_until(test_function, timeout=timeout, lock=mininode_lock) # Message receiving helper methods diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py index e016148f70..5e0b61b5e7 100644 --- a/test/functional/test_framework/util.py +++ b/test/functional/test_framework/util.py @@ -327,7 +327,7 @@ def get_auth_cookie(datadir): assert password is None # Ensure that there is only one rpcpassword line password = line.split("=")[1].strip("\n") if os.path.isfile(os.path.join(datadir, "regtest", ".cookie")): - with open(os.path.join(datadir, "regtest", ".cookie"), 'r') as f: + with open(os.path.join(datadir, "regtest", ".cookie"), 'r', encoding="ascii") as f: userpass = f.read() split_userpass = userpass.split(':') user = split_userpass[0] diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 5b3a4df0f9..36101d9f57 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -213,7 +213,7 @@ def main(): # Read config generated by configure. config = configparser.ConfigParser() configfile = os.path.abspath(os.path.dirname(__file__)) + "/../config.ini" - config.read_file(open(configfile)) + config.read_file(open(configfile, encoding="utf8")) passon_args.append("--configfile=%s" % configfile) @@ -590,7 +590,7 @@ class RPCCoverage(): if not os.path.isfile(coverage_ref_filename): raise RuntimeError("No coverage reference found") - with open(coverage_ref_filename, 'r') as coverage_ref_file: + with open(coverage_ref_filename, 'r', encoding="utf8") as coverage_ref_file: all_cmds.update([line.strip() for line in coverage_ref_file.readlines()]) for root, dirs, files in os.walk(self.dir): @@ -599,7 +599,7 @@ class RPCCoverage(): coverage_filenames.add(os.path.join(root, filename)) for filename in coverage_filenames: - with open(filename, 'r') as coverage_file: + with open(filename, 'r', encoding="utf8") as coverage_file: covered_cmds.update([line.strip() for line in coverage_file.readlines()]) return all_cmds - covered_cmds diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py index 53638615f6..3cefd83459 100755 --- a/test/functional/wallet_multiwallet.py +++ b/test/functional/wallet_multiwallet.py @@ -88,7 +88,7 @@ class MultiWalletTest(BitcoinTestFramework): self.nodes[0].assert_start_raises_init_error(['-walletdir=bad'], 'Error: Specified -walletdir "bad" does not exist') # should not initialize if the specified walletdir is not a directory not_a_dir = wallet_dir('notadir') - open(not_a_dir, 'a').close() + open(not_a_dir, 'a', encoding="utf8").close() self.nodes[0].assert_start_raises_init_error(['-walletdir=' + not_a_dir], 'Error: Specified -walletdir "' + not_a_dir + '" is not a directory') self.log.info("Do not allow -zapwallettxes with multiwallet") @@ -234,5 +234,32 @@ class MultiWalletTest(BitcoinTestFramework): assert new_wallet_name in self.nodes[0].listwallets() + self.log.info("Test dynamic wallet unloading") + + # Test `unloadwallet` errors + assert_raises_rpc_error(-1, "JSON value is not a string as expected", self.nodes[0].unloadwallet) + assert_raises_rpc_error(-18, "Requested wallet does not exist or is not loaded", self.nodes[0].unloadwallet, "dummy") + assert_raises_rpc_error(-18, "Requested wallet does not exist or is not loaded", node.get_wallet_rpc("dummy").unloadwallet) + assert_raises_rpc_error(-8, "Cannot unload the requested wallet", w1.unloadwallet, "w2"), + + # Successfully unload the specified wallet name + self.nodes[0].unloadwallet("w1") + assert 'w1' not in self.nodes[0].listwallets() + + # Successfully unload the wallet referenced by the request endpoint + w2.unloadwallet() + assert 'w2' not in self.nodes[0].listwallets() + + # Successfully unload all wallets + for wallet_name in self.nodes[0].listwallets(): + self.nodes[0].unloadwallet(wallet_name) + assert_equal(self.nodes[0].listwallets(), []) + assert_raises_rpc_error(-32601, "Method not found (wallet method is disabled because no wallet is loaded)", self.nodes[0].getwalletinfo) + + # Successfully load a previously unloaded wallet + self.nodes[0].loadwallet('w1') + assert_equal(self.nodes[0].listwallets(), ['w1']) + assert_equal(w1.getwalletinfo()['walletname'], 'w1') + if __name__ == '__main__': MultiWalletTest().main() diff --git a/test/lint/check-rpc-mappings.py b/test/lint/check-rpc-mappings.py index 7e96852c5c..c3cdeef580 100755 --- a/test/lint/check-rpc-mappings.py +++ b/test/lint/check-rpc-mappings.py @@ -44,7 +44,7 @@ def process_commands(fname): """Find and parse dispatch table in implementation file `fname`.""" cmds = [] in_rpcs = False - with open(fname, "r") as f: + with open(fname, "r", encoding="utf8") as f: for line in f: line = line.rstrip() if not in_rpcs: @@ -70,7 +70,7 @@ def process_mapping(fname): """Find and parse conversion table in implementation file `fname`.""" cmds = [] in_rpcs = False - with open(fname, "r") as f: + with open(fname, "r", encoding="utf8") as f: for line in f: line = line.rstrip() if not in_rpcs: diff --git a/test/lint/commit-script-check.sh b/test/lint/commit-script-check.sh index 1c9dbc7f68..f1327469f3 100755 --- a/test/lint/commit-script-check.sh +++ b/test/lint/commit-script-check.sh @@ -11,6 +11,7 @@ # The resulting script should exactly transform the previous commit into the current # one. Any remaining diff signals an error. +export LC_ALL=C if test "x$1" = "x"; then echo "Usage: $0 <commit>..." exit 1 diff --git a/test/lint/git-subtree-check.sh b/test/lint/git-subtree-check.sh index 184951715e..85e8b841b6 100755 --- a/test/lint/git-subtree-check.sh +++ b/test/lint/git-subtree-check.sh @@ -3,6 +3,7 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +export LC_ALL=C DIR="$1" COMMIT="$2" if [ -z "$COMMIT" ]; then diff --git a/test/lint/lint-all.sh b/test/lint/lint-all.sh index b6d86959c6..c9d4ec7199 100755 --- a/test/lint/lint-all.sh +++ b/test/lint/lint-all.sh @@ -7,6 +7,10 @@ # This script runs all contrib/devtools/lint-*.sh files, and fails if any exit # with a non-zero status code. +# This script is intentionally locale dependent by not setting "export LC_ALL=C" +# in order to allow for the executed lint scripts to opt in or opt out of locale +# dependence themselves. + set -u SCRIPTDIR=$(dirname "${BASH_SOURCE[0]}") diff --git a/test/lint/lint-include-guards.sh b/test/lint/lint-include-guards.sh index 6a0dd556bb..ea791b0176 100755 --- a/test/lint/lint-include-guards.sh +++ b/test/lint/lint-include-guards.sh @@ -6,6 +6,7 @@ # # Check include guards. +export LC_ALL=C HEADER_ID_PREFIX="BITCOIN_" HEADER_ID_SUFFIX="_H" diff --git a/test/lint/lint-includes.sh b/test/lint/lint-includes.sh index f5daf4f9ac..03350576a7 100755 --- a/test/lint/lint-includes.sh +++ b/test/lint/lint-includes.sh @@ -8,6 +8,7 @@ # 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|univalue)/" filter_suffix() { diff --git a/test/lint/lint-locale-dependence.sh b/test/lint/lint-locale-dependence.sh index 3144f2c841..98814ce4b9 100755 --- a/test/lint/lint-locale-dependence.sh +++ b/test/lint/lint-locale-dependence.sh @@ -1,5 +1,6 @@ #!/bin/bash +export LC_ALL=C KNOWN_VIOLATIONS=( "src/base58.cpp:.*isspace" "src/bitcoin-tx.cpp.*stoul" diff --git a/test/lint/lint-logs.sh b/test/lint/lint-logs.sh index 35be13ec19..89e7fb8764 100755 --- a/test/lint/lint-logs.sh +++ b/test/lint/lint-logs.sh @@ -12,7 +12,7 @@ # 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 "/\* Continued \*/" | \ diff --git a/test/lint/lint-python-shebang.sh b/test/lint/lint-python-shebang.sh index f5c5971c03..031487a91a 100755 --- a/test/lint/lint-python-shebang.sh +++ b/test/lint/lint-python-shebang.sh @@ -1,5 +1,7 @@ #!/bin/bash # Shebang must use python3 (not python or python2) + +export LC_ALL=C EXIT_CODE=0 for PYTHON_FILE in $(git ls-files -- "*.py"); do if [[ $(head -c 2 "${PYTHON_FILE}") == "#!" && diff --git a/test/lint/lint-python-utf8-encoding.sh b/test/lint/lint-python-utf8-encoding.sh new file mode 100755 index 0000000000..d0ff38ea0a --- /dev/null +++ b/test/lint/lint-python-utf8-encoding.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# +# Copyright (c) 2018 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +# +# Make sure we explicitly open all text files using UTF-8 (or ASCII) encoding to +# avoid potential issues on the BSDs where the locale is not always set. + +export LC_ALL=C +EXIT_CODE=0 +OUTPUT=$(git grep " open(" -- "*.py" | grep -vE "encoding=.(ascii|utf8|utf-8)." | grep -vE "open\([^,]*, ['\"][^'\"]*b[^'\"]*['\"]") +if [[ ${OUTPUT} != "" ]]; then + echo "Python's open(...) seems to be used to open text files without explicitly" + echo "specifying encoding=\"utf8\":" + echo + echo "${OUTPUT}" + EXIT_CODE=1 +fi +exit ${EXIT_CODE} diff --git a/test/lint/lint-python.sh b/test/lint/lint-python.sh index 7d3555b6d4..d9d46d86d5 100755 --- a/test/lint/lint-python.sh +++ b/test/lint/lint-python.sh @@ -6,6 +6,8 @@ # # Check for specified flake8 warnings in python files. +export LC_ALL=C + # E101 indentation contains mixed spaces and tabs # E112 expected an indented block # E113 unexpected indentation diff --git a/test/lint/lint-shell-locale.sh b/test/lint/lint-shell-locale.sh new file mode 100755 index 0000000000..d78bac2d47 --- /dev/null +++ b/test/lint/lint-shell-locale.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# +# Copyright (c) 2018 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +# +# Make sure all shell scripts: +# a.) explicitly opt out of locale dependence using "export LC_ALL=C", or +# b.) explicitly opt in to locale dependence using the annotation below. + +export LC_ALL=C + +EXIT_CODE=0 +for SHELL_SCRIPT in $(git ls-files -- "*.sh" | grep -vE "src/(secp256k1|univalue)/"); do + if grep -q "# This script is intentionally locale dependent by not setting \"export LC_ALL=C\"" "${SHELL_SCRIPT}"; then + continue + fi + FIRST_NON_COMMENT_LINE=$(grep -vE '^(#.*|)$' "${SHELL_SCRIPT}" | head -1) + if [[ ${FIRST_NON_COMMENT_LINE} != "export LC_ALL=C" ]]; then + echo "Missing \"export LC_ALL=C\" (to avoid locale dependence) as first non-comment non-empty line in ${SHELL_SCRIPT}" + EXIT_CODE=1 + fi +done +exit ${EXIT_CODE} diff --git a/test/lint/lint-shell.sh b/test/lint/lint-shell.sh index 5f5fa9a925..bf56db90c6 100755 --- a/test/lint/lint-shell.sh +++ b/test/lint/lint-shell.sh @@ -6,6 +6,10 @@ # # Check for shellcheck warnings in shell scripts. +# This script is intentionally locale dependent by not setting "export LC_ALL=C" +# to allow running certain versions of shellcheck that core dump when LC_ALL=C +# is set. + # Disabled warnings: # SC2001: See if you can use ${variable//search/replace} instead. # SC2004: $/${} is unnecessary on arithmetic variables. diff --git a/test/lint/lint-tests.sh b/test/lint/lint-tests.sh index ffc0660551..9d83547df4 100755 --- a/test/lint/lint-tests.sh +++ b/test/lint/lint-tests.sh @@ -6,6 +6,7 @@ # # Check the test suite naming conventions +export LC_ALL=C EXIT_CODE=0 NAMING_INCONSISTENCIES=$(git grep -E '^BOOST_FIXTURE_TEST_SUITE\(' -- \ diff --git a/test/lint/lint-whitespace.sh b/test/lint/lint-whitespace.sh index c5d43043d5..128aa10da5 100755 --- a/test/lint/lint-whitespace.sh +++ b/test/lint/lint-whitespace.sh @@ -8,6 +8,7 @@ # 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 ?) diff --git a/test/util/bitcoin-util-test.py b/test/util/bitcoin-util-test.py index 30bd13d0dc..16371a6234 100755 --- a/test/util/bitcoin-util-test.py +++ b/test/util/bitcoin-util-test.py @@ -28,7 +28,7 @@ import sys def main(): config = configparser.ConfigParser() config.optionxform = str - config.readfp(open(os.path.join(os.path.dirname(__file__), "../config.ini"))) + config.readfp(open(os.path.join(os.path.dirname(__file__), "../config.ini"), encoding="utf8")) env_conf = dict(config.items('environment')) parser = argparse.ArgumentParser(description=__doc__) @@ -49,7 +49,7 @@ def main(): def bctester(testDir, input_basename, buildenv): """ Loads and parses the input file, runs all tests and reports results""" input_filename = os.path.join(testDir, input_basename) - raw_data = open(input_filename).read() + raw_data = open(input_filename, encoding="utf8").read() input_data = json.loads(raw_data) failed_testcases = [] @@ -86,7 +86,7 @@ def bctest(testDir, testObj, buildenv): inputData = None if "input" in testObj: filename = os.path.join(testDir, testObj["input"]) - inputData = open(filename).read() + inputData = open(filename, encoding="utf8").read() stdinCfg = subprocess.PIPE # Read the expected output data (if there is any) @@ -97,7 +97,7 @@ def bctest(testDir, testObj, buildenv): outputFn = testObj['output_cmp'] outputType = os.path.splitext(outputFn)[1][1:] # output type from file extension (determines how to compare) try: - outputData = open(os.path.join(testDir, outputFn)).read() + outputData = open(os.path.join(testDir, outputFn), encoding="utf8").read() except: logging.error("Output file " + outputFn + " can not be opened") raise diff --git a/test/util/rpcauth-test.py b/test/util/rpcauth-test.py index 2456feb102..46e9fbc739 100755 --- a/test/util/rpcauth-test.py +++ b/test/util/rpcauth-test.py @@ -18,7 +18,7 @@ class TestRPCAuth(unittest.TestCase): config_path = os.path.abspath( os.path.join(os.sep, os.path.abspath(os.path.dirname(__file__)), "../config.ini")) - with open(config_path) as config_file: + with open(config_path, encoding="utf8") as config_file: config.read_file(config_file) sys.path.insert(0, os.path.dirname(config['environment']['RPCAUTH'])) self.rpcauth = importlib.import_module('rpcauth') |