diff options
174 files changed, 3145 insertions, 2553 deletions
diff --git a/.travis.yml b/.travis.yml index 75df290187..ec78896f44 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,9 +34,9 @@ env: # 32-bit + dash - 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 + - 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 --enable-debug" +# 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/build-aux/m4/bitcoin_qt.m4 b/build-aux/m4/bitcoin_qt.m4 index f41508336c..05df8621d2 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) ]) @@ -292,7 +276,7 @@ AC_DEFUN([_BITCOIN_QT_CHECK_QT5],[ #endif ]], [[ - #if QT_VERSION < 0x050000 + #if QT_VERSION < 0x050000 || QT_VERSION_MAJOR < 5 choke #endif ]])], @@ -300,13 +284,11 @@ AC_DEFUN([_BITCOIN_QT_CHECK_QT5],[ [bitcoin_cv_qt5=no]) ])]) -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: 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. -AC_DEFUN([_BITCOIN_QT_IS_STATIC],[ - AC_CACHE_CHECK(for static Qt, bitcoin_cv_static_qt,[ +dnl Internal. Check if the included version of Qt is greater than Qt58. +dnl Requires: INCLUDES must be populated as necessary. +dnl Output: bitcoin_cv_qt5=yes|no +AC_DEFUN([_BITCOIN_QT_CHECK_QT58],[ + AC_CACHE_CHECK(for > Qt 5.7, bitcoin_cv_qt58,[ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include <QtCore/qconfig.h> #ifndef QT_VERSION @@ -314,13 +296,36 @@ AC_DEFUN([_BITCOIN_QT_IS_STATIC],[ #endif ]], [[ - #if !defined(QT_STATIC) + #if QT_VERSION_MINOR < 8 choke #endif ]])], - [bitcoin_cv_static_qt=yes], - [bitcoin_cv_static_qt=no]) - ]) + [bitcoin_cv_qt58=yes], + [bitcoin_cv_qt58=no]) +])]) + + +dnl Internal. Check if the linked version of Qt was built as static libs. +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. +AC_DEFUN([_BITCOIN_QT_IS_STATIC],[ + AC_CACHE_CHECK(for static Qt, bitcoin_cv_static_qt,[ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #include <QtCore/qconfig.h> + #ifndef QT_VERSION OR QT_VERSION_STR + # include <QtCore/qglobal.h> + #endif + ]], + [[ + #if !defined(QT_STATIC) + choke + #endif + ]])], + [bitcoin_cv_static_qt=yes], + [bitcoin_cv_static_qt=no]) + ]) if test "x$bitcoin_cv_static_qt" = xyes; then AC_DEFINE(QT_STATICPLUGIN, 1, [Define this symbol for static Qt plugins]) fi @@ -346,28 +351,36 @@ 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$bitcoin_cv_qt58 = xno; then + PKG_CHECK_MODULES([QTPLATFORM], [Qt5PlatformSupport], [QT_LIBS="$QTPLATFORM_LIBS $QT_LIBS"]) + else + PKG_CHECK_MODULES([QTFONTDATABASE], [Qt5FontDatabaseSupport], [QT_LIBS="-lQt5FontDatabaseSupport $QT_LIBS"]) + PKG_CHECK_MODULES([QTEVENTDISPATCHER], [Qt5EventDispatcherSupport], [QT_LIBS="-lQt5EventDispatcherSupport $QT_LIBS"]) + PKG_CHECK_MODULES([QTTHEME], [Qt5ThemeSupport], [QT_LIBS="-lQt5ThemeSupport $QT_LIBS"]) + PKG_CHECK_MODULES([QTDEVICEDISCOVERY], [Qt5DeviceDiscoverySupport], [QT_LIBS="-lQt5DeviceDiscoverySupport $QT_LIBS"]) + PKG_CHECK_MODULES([QTACCESSIBILITY], [Qt5AccessibilitySupport], [QT_LIBS="-lQt5AccessibilitySupport $QT_LIBS"]) + PKG_CHECK_MODULES([QTFB], [Qt5FbSupport], [QT_LIBS="-lQt5FbSupport $QT_LIBS"]) + fi 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"]) + PKG_CHECK_MODULES([QTCLIPBOARD], [Qt5ClipboardSupport], [QT_LIBS="-lQt5ClipboardSupport $QT_LIBS"]) + PKG_CHECK_MODULES([QTGRAPHICS], [Qt5GraphicsSupport], [QT_LIBS="-lQt5GraphicsSupport $QT_LIBS"]) + PKG_CHECK_MODULES([QTCGL], [Qt5CglSupport], [QT_LIBS="-lQt5CglSupport $QT_LIBS"]) fi ]) else @@ -380,7 +393,7 @@ AC_DEFUN([_BITCOIN_QT_FIND_STATIC_PLUGINS],[ #endif ]], [[ - #if QT_VERSION < 0x050600 + #if QT_VERSION < 0x050600 || QT_VERSION_MINOR < 6 choke #endif ]])], @@ -388,16 +401,21 @@ AC_DEFUN([_BITCOIN_QT_FIND_STATIC_PLUGINS],[ [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))) + if test x$bitcoin_cv_qt58 = xno; then + BITCOIN_QT_CHECK(AC_CHECK_LIB([${QT_LIB_PREFIX}PlatformSupport],[main],,BITCOIN_QT_FAIL(lib$QT_LIB_PREFIXPlatformSupport not found))) + else + BITCOIN_QT_CHECK(AC_CHECK_LIB([${QT_LIB_PREFIX}FontDatabaseSupport],[main],,BITCOIN_QT_FAIL(lib$QT_LIB_PREFIXFontDatabaseSupport not found))) + BITCOIN_QT_CHECK(AC_CHECK_LIB([${QT_LIB_PREFIX}EventDispatcherSupport],[main],,BITCOIN_QT_FAIL(lib$QT_LIB_PREFIXEventDispatcherSupport not found))) + BITCOIN_QT_CHECK(AC_CHECK_LIB([${QT_LIB_PREFIX}ThemeSupport],[main],,BITCOIN_QT_FAIL(lib$QT_LIB_PREFIXThemeSupport not found))) + BITCOIN_QT_CHECK(AC_CHECK_LIB([${QT_LIB_PREFIX}FbSupport],[main],,BITCOIN_QT_FAIL(lib$QT_LIB_PREFIXFbSupport not found))) + BITCOIN_QT_CHECK(AC_CHECK_LIB([${QT_LIB_PREFIX}DeviceDiscoverySupport],[main],,BITCOIN_QT_FAIL(lib$QT_LIB_PREFIXDeviceDiscoverySupport not found))) + BITCOIN_QT_CHECK(AC_CHECK_LIB([${QT_LIB_PREFIX}AccessibilitySupport],[main],,BITCOIN_QT_FAIL(lib$QT_LIB_PREFIXAccessibilitySupport not found))) + QT_LIBS="$QT_LIBS -lversion -ldwmapi -luxtheme" + fi fi 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 +424,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 +452,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" @@ -479,14 +472,9 @@ AC_DEFUN([_BITCOIN_QT_FIND_LIBS_WITHOUT_PKGCONFIG],[ BITCOIN_QT_CHECK([ if test "x$bitcoin_qt_want_version" = xauto; then _BITCOIN_QT_CHECK_QT5 + _BITCOIN_QT_CHECK_QT58 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([ @@ -501,16 +489,19 @@ AC_DEFUN([_BITCOIN_QT_FIND_LIBS_WITHOUT_PKGCONFIG],[ ]) BITCOIN_QT_CHECK(AC_CHECK_LIB([z] ,[main],,AC_MSG_WARN([zlib not found. Assuming qt has it built-in]))) - BITCOIN_QT_CHECK(AC_SEARCH_LIBS([png_error] ,[qtpng png],,AC_MSG_WARN([libpng not found. Assuming qt has it built-in]))) BITCOIN_QT_CHECK(AC_SEARCH_LIBS([jpeg_create_decompress] ,[qtjpeg jpeg],,AC_MSG_WARN([libjpeg not found. Assuming qt has it built-in]))) - BITCOIN_QT_CHECK(AC_SEARCH_LIBS([pcre16_exec], [qtpcre pcre16],,AC_MSG_WARN([libpcre16 not found. Assuming qt has it built-in]))) - BITCOIN_QT_CHECK(AC_SEARCH_LIBS([hb_ot_tags_from_script] ,[qtharfbuzzng harfbuzz],,AC_MSG_WARN([libharfbuzz not found. Assuming qt has it built-in or support is disabled]))) + if test x$bitcoin_cv_qt58 = xno; then + BITCOIN_QT_CHECK(AC_SEARCH_LIBS([png_error] ,[qtpng png],,AC_MSG_WARN([libpng not found. Assuming qt has it built-in]))) + BITCOIN_QT_CHECK(AC_SEARCH_LIBS([pcre16_exec], [qtpcre pcre16],,AC_MSG_WARN([libpcre16 not found. Assuming qt has it built-in]))) + else + BITCOIN_QT_CHECK(AC_SEARCH_LIBS([png_error] ,[qtlibpng png],,AC_MSG_WARN([libpng not found. Assuming qt has it built-in]))) + BITCOIN_QT_CHECK(AC_SEARCH_LIBS([pcre2_match_16], [qtpcre2 libqtpcre2],,AC_MSG_WARN([libqtpcre2 not found. Assuming qt has it built-in]))) + fi + BITCOIN_QT_CHECK(AC_SEARCH_LIBS([hb_ot_tags_from_script] ,[qtharfbuzzng qtharfbuzz harfbuzz],,AC_MSG_WARN([libharfbuzz not found. Assuming qt has it built-in or support is disabled]))) 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 926d0b8d00..490b638ee1 100644 --- a/configure.ac +++ b/configure.ac @@ -263,6 +263,7 @@ if test "x$enable_debug" = xyes; then AX_CHECK_PREPROC_FLAG([-DDEBUG],[[DEBUG_CPPFLAGS="$DEBUG_CPPFLAGS -DDEBUG"]],,[[$CXXFLAG_WERROR]]) AX_CHECK_PREPROC_FLAG([-DDEBUG_LOCKORDER],[[DEBUG_CPPFLAGS="$DEBUG_CPPFLAGS -DDEBUG_LOCKORDER"]],,[[$CXXFLAG_WERROR]]) + AX_CHECK_COMPILE_FLAG([-ftrapv],[DEBUG_CXXFLAGS="$DEBUG_CXXFLAGS -ftrapv"],,[[$CXXFLAG_WERROR]]) fi if test x$use_sanitizers != x; then @@ -301,6 +302,7 @@ if test "x$CXXFLAGS_overridden" = "xno"; then AX_CHECK_COMPILE_FLAG([-Wvla],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wvla"],,[[$CXXFLAG_WERROR]]) AX_CHECK_COMPILE_FLAG([-Wformat-security],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wformat-security"],,[[$CXXFLAG_WERROR]]) AX_CHECK_COMPILE_FLAG([-Wthread-safety-analysis],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wthread-safety-analysis"],,[[$CXXFLAG_WERROR]]) + AX_CHECK_COMPILE_FLAG([-Wrange-loop-analysis],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wrange-loop-analysis"],,[[$CXXFLAG_WERROR]]) ## Some compilers (gcc) ignore unknown -Wno-* options, but warn about all ## unknown options if any other warning is produced. Test the -Wfoo case, and @@ -318,6 +320,7 @@ fi AX_CHECK_COMPILE_FLAG([-msse4.2],[[SSE42_CXXFLAGS="-msse4.2"]],,[[$CXXFLAG_WERROR]]) AX_CHECK_COMPILE_FLAG([-msse4.1],[[SSE41_CXXFLAGS="-msse4.1"]],,[[$CXXFLAG_WERROR]]) AX_CHECK_COMPILE_FLAG([-mavx -mavx2],[[AVX2_CXXFLAGS="-mavx -mavx2"]],,[[$CXXFLAG_WERROR]]) +AX_CHECK_COMPILE_FLAG([-msse4 -msha],[[SHANI_CXXFLAGS="-msse4 -msha"]],,[[$CXXFLAG_WERROR]]) TEMP_CXXFLAGS="$CXXFLAGS" CXXFLAGS="$CXXFLAGS $SSE42_CXXFLAGS" @@ -346,11 +349,7 @@ CXXFLAGS="$CXXFLAGS $SSE41_CXXFLAGS" AC_MSG_CHECKING(for SSE4.1 intrinsics) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include <stdint.h> - #if defined(_MSC_VER) #include <immintrin.h> - #elif defined(__GNUC__) - #include <x86intrin.h> - #endif ]],[[ __m128i l = _mm_set1_epi32(0); return _mm_extract_epi32(l, 3); @@ -365,11 +364,7 @@ CXXFLAGS="$CXXFLAGS $AVX2_CXXFLAGS" AC_MSG_CHECKING(for AVX2 intrinsics) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include <stdint.h> - #if defined(_MSC_VER) #include <immintrin.h> - #elif defined(__GNUC__) && defined(__AVX2__) - #include <x86intrin.h> - #endif ]],[[ __m256i l = _mm256_set1_epi32(0); return _mm256_extract_epi32(l, 7); @@ -379,6 +374,23 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ ) CXXFLAGS="$TEMP_CXXFLAGS" +TEMP_CXXFLAGS="$CXXFLAGS" +CXXFLAGS="$CXXFLAGS $SHANI_CXXFLAGS" +AC_MSG_CHECKING(for SHA-NI intrinsics) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #include <stdint.h> + #include <immintrin.h> + ]],[[ + __m128i i = _mm_set1_epi32(0); + __m128i j = _mm_set1_epi32(1); + __m128i k = _mm_set1_epi32(2); + return _mm_extract_epi32(_mm_sha256rnds2_epu32(i, i, k), 0); + ]])], + [ AC_MSG_RESULT(yes); enable_shani=yes; AC_DEFINE(ENABLE_SHANI, 1, [Define this symbol to build code that uses SHA-NI intrinsics]) ], + [ AC_MSG_RESULT(no)] +) +CXXFLAGS="$TEMP_CXXFLAGS" + CPPFLAGS="$CPPFLAGS -DHAVE_BUILD_INFO -D__STDC_FORMAT_MACROS" AC_ARG_WITH([utils], @@ -806,6 +818,14 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <sys/types.h> [ AC_MSG_RESULT(no)] ) +AC_MSG_CHECKING(for if type char equals int8_t) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <stdint.h> + #include <type_traits>]], + [[ static_assert(std::is_same<int8_t, char>::value, ""); ]])], + [ AC_MSG_RESULT(yes); AC_DEFINE(CHAR_EQUALS_INT8, 1,[Define this symbol if type char equals int8_t]) ], + [ AC_MSG_RESULT(no)] +) + # Check for reduced exports if test x$use_reduce_exports = xyes; then AX_CHECK_COMPILE_FLAG([-fvisibility=hidden],[RE_CXXFLAGS="-fvisibility=hidden"], @@ -837,7 +857,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 @@ -1299,6 +1319,7 @@ AM_CONDITIONAL([HARDEN],[test x$use_hardening = xyes]) AM_CONDITIONAL([ENABLE_HWCRC32],[test x$enable_hwcrc32 = xyes]) AM_CONDITIONAL([ENABLE_SSE41],[test x$enable_sse41 = xyes]) AM_CONDITIONAL([ENABLE_AVX2],[test x$enable_avx2 = xyes]) +AM_CONDITIONAL([ENABLE_SHANI],[test x$enable_shani = xyes]) AM_CONDITIONAL([USE_ASM],[test x$use_asm = xyes]) AC_DEFINE(CLIENT_VERSION_MAJOR, _CLIENT_VERSION_MAJOR, [Major version]) @@ -1343,6 +1364,7 @@ AC_SUBST(SANITIZER_LDFLAGS) AC_SUBST(SSE42_CXXFLAGS) AC_SUBST(SSE41_CXXFLAGS) AC_SUBST(AVX2_CXXFLAGS) +AC_SUBST(SHANI_CXXFLAGS) AC_SUBST(LIBTOOL_APP_LDFLAGS) AC_SUBST(USE_UPNP) AC_SUBST(USE_QRCODE) @@ -1422,7 +1444,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/gen-manpages.sh b/contrib/devtools/gen-manpages.sh index 76d4adc81c..b5de5a395f 100755 --- a/contrib/devtools/gen-manpages.sh +++ b/contrib/devtools/gen-manpages.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash export LC_ALL=C TOPDIR=${TOPDIR:-$(git rev-parse --show-toplevel)} diff --git a/contrib/gitian-build.sh b/contrib/gitian-build.sh index 8bfbf6d1a0..05e7d6f4c6 100755 --- a/contrib/gitian-build.sh +++ b/contrib/gitian-build.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright (c) 2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/contrib/gitian-descriptors/gitian-linux.yml b/contrib/gitian-descriptors/gitian-linux.yml index 3e9ee0495a..f78eea73a3 100644 --- a/contrib/gitian-descriptors/gitian-linux.yml +++ b/contrib/gitian-descriptors/gitian-linux.yml @@ -56,7 +56,7 @@ script: | function create_global_faketime_wrappers { for prog in ${FAKETIME_PROGS}; do - echo '#!/bin/bash' > ${WRAP_DIR}/${prog} + echo '#!/usr/bin/env bash' > ${WRAP_DIR}/${prog} echo "REAL=\`which -a ${prog} | grep -v ${WRAP_DIR}/${prog} | head -1\`" >> ${WRAP_DIR}/${prog} echo 'export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${prog} echo "export FAKETIME=\"$1\"" >> ${WRAP_DIR}/${prog} @@ -68,7 +68,7 @@ script: | function create_per-host_faketime_wrappers { for i in $HOSTS; do for prog in ${FAKETIME_HOST_PROGS}; do - echo '#!/bin/bash' > ${WRAP_DIR}/${i}-${prog} + echo '#!/usr/bin/env bash' > ${WRAP_DIR}/${i}-${prog} echo "REAL=\`which -a ${i}-${prog} | grep -v ${WRAP_DIR}/${i}-${prog} | head -1\`" >> ${WRAP_DIR}/${i}-${prog} echo 'export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${i}-${prog} echo "export FAKETIME=\"$1\"" >> ${WRAP_DIR}/${i}-${prog} @@ -98,7 +98,7 @@ script: | for prog in gcc g++; do rm -f ${WRAP_DIR}/${prog} cat << EOF > ${WRAP_DIR}/${prog} - #!/bin/bash + #!/usr/bin/env bash REAL="`which -a ${prog} | grep -v ${WRAP_DIR}/${prog} | head -1`" for var in "\$@" do diff --git a/contrib/gitian-descriptors/gitian-osx-signer.yml b/contrib/gitian-descriptors/gitian-osx-signer.yml index f6e9414ab1..b5c863bb14 100644 --- a/contrib/gitian-descriptors/gitian-osx-signer.yml +++ b/contrib/gitian-descriptors/gitian-osx-signer.yml @@ -19,7 +19,7 @@ script: | # Create global faketime wrappers for prog in ${FAKETIME_PROGS}; do - echo '#!/bin/bash' > ${WRAP_DIR}/${prog} + echo '#!/usr/bin/env bash' > ${WRAP_DIR}/${prog} echo "REAL=\`which -a ${prog} | grep -v ${WRAP_DIR}/${prog} | head -1\`" >> ${WRAP_DIR}/${prog} echo 'export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${prog} echo "export FAKETIME=\"${REFERENCE_DATETIME}\"" >> ${WRAP_DIR}/${prog} diff --git a/contrib/gitian-descriptors/gitian-osx.yml b/contrib/gitian-descriptors/gitian-osx.yml index a84dce3e3a..2cc62d4ce7 100644 --- a/contrib/gitian-descriptors/gitian-osx.yml +++ b/contrib/gitian-descriptors/gitian-osx.yml @@ -55,7 +55,7 @@ script: | function create_global_faketime_wrappers { for prog in ${FAKETIME_PROGS}; do - echo '#!/bin/bash' > ${WRAP_DIR}/${prog} + echo '#!/usr/bin/env bash' > ${WRAP_DIR}/${prog} echo "REAL=\`which -a ${prog} | grep -v ${WRAP_DIR}/${prog} | head -1\`" >> ${WRAP_DIR}/${prog} echo 'export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${prog} echo "export FAKETIME=\"$1\"" >> ${WRAP_DIR}/${prog} @@ -67,7 +67,7 @@ script: | function create_per-host_faketime_wrappers { for i in $HOSTS; do for prog in ${FAKETIME_HOST_PROGS}; do - echo '#!/bin/bash' > ${WRAP_DIR}/${i}-${prog} + echo '#!/usr/bin/env bash' > ${WRAP_DIR}/${i}-${prog} echo "REAL=\`which -a ${i}-${prog} | grep -v ${WRAP_DIR}/${i}-${prog} | head -1\`" >> ${WRAP_DIR}/${i}-${prog} echo 'export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${i}-${prog} echo "export FAKETIME=\"$1\"" >> ${WRAP_DIR}/${i}-${prog} diff --git a/contrib/gitian-descriptors/gitian-win.yml b/contrib/gitian-descriptors/gitian-win.yml index 8a87d91754..f6eb1815f6 100644 --- a/contrib/gitian-descriptors/gitian-win.yml +++ b/contrib/gitian-descriptors/gitian-win.yml @@ -48,7 +48,7 @@ script: | function create_global_faketime_wrappers { for prog in ${FAKETIME_PROGS}; do - echo '#!/bin/bash' > ${WRAP_DIR}/${prog} + echo '#!/usr/bin/env bash' > ${WRAP_DIR}/${prog} echo "REAL=\`which -a ${prog} | grep -v ${WRAP_DIR}/${prog} | head -1\`" >> ${WRAP_DIR}/${prog} echo 'export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${prog} echo "export FAKETIME=\"$1\"" >> ${WRAP_DIR}/${prog} @@ -60,7 +60,7 @@ script: | function create_per-host_faketime_wrappers { for i in $HOSTS; do for prog in ${FAKETIME_HOST_PROGS}; do - echo '#!/bin/bash' > ${WRAP_DIR}/${i}-${prog} + echo '#!/usr/bin/env bash' > ${WRAP_DIR}/${i}-${prog} echo "REAL=\`which -a ${i}-${prog} | grep -v ${WRAP_DIR}/${i}-${prog} | head -1\`" >> ${WRAP_DIR}/${i}-${prog} echo 'export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${i}-${prog} echo "export FAKETIME=\"$1\"" >> ${WRAP_DIR}/${i}-${prog} @@ -76,14 +76,14 @@ script: | for i in $HOSTS; do mkdir -p ${WRAP_DIR}/${i} for prog in collect2; do - echo '#!/bin/bash' > ${WRAP_DIR}/${i}/${prog} + echo '#!/usr/bin/env bash' > ${WRAP_DIR}/${i}/${prog} REAL=$(${i}-gcc -print-prog-name=${prog}) echo "export MALLOC_PERTURB_=255" >> ${WRAP_DIR}/${i}/${prog} echo "${REAL} \$@" >> $WRAP_DIR/${i}/${prog} chmod +x ${WRAP_DIR}/${i}/${prog} done for prog in gcc g++; do - echo '#!/bin/bash' > ${WRAP_DIR}/${i}-${prog} + echo '#!/usr/bin/env bash' > ${WRAP_DIR}/${i}-${prog} echo "REAL=\`which -a ${i}-${prog} | grep -v ${WRAP_DIR}/${i}-${prog} | head -1\`" >> ${WRAP_DIR}/${i}-${prog} echo 'export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${i}-${prog} echo "export FAKETIME=\"$1\"" >> ${WRAP_DIR}/${i}-${prog} diff --git a/contrib/gitian-keys/README.md b/contrib/gitian-keys/README.md index a9339c8bda..ffe4fb144b 100644 --- a/contrib/gitian-keys/README.md +++ b/contrib/gitian-keys/README.md @@ -1,9 +1,10 @@ ## PGP keys of Gitian builders and Developers -The keys.txt contains the public keys of Gitian builders and active developers. +The file `keys.txt` contains fingerprints of the public keys of Gitian builders +and active developers. -The keys are mainly used to sign git commits or the build results of Gitian -builds. +The associated keys are mainly used to sign git commits or the build results +of Gitian builds. The most recent version of each pgp key can be found on most pgp key servers. diff --git a/contrib/init/bitcoind.init b/contrib/init/bitcoind.init index db5061874b..0c95baf3a1 100644 --- a/contrib/init/bitcoind.init +++ b/contrib/init/bitcoind.init @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # bitcoind The bitcoin core server. # diff --git a/contrib/macdeploy/extract-osx-sdk.sh b/contrib/macdeploy/extract-osx-sdk.sh index 94122b56fc..4c175156f4 100755 --- a/contrib/macdeploy/extract-osx-sdk.sh +++ b/contrib/macdeploy/extract-osx-sdk.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright (c) 2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/contrib/verify-commits/pre-push-hook.sh b/contrib/verify-commits/pre-push-hook.sh index 152f4e32e4..4db4a90853 100755 --- a/contrib/verify-commits/pre-push-hook.sh +++ b/contrib/verify-commits/pre-push-hook.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright (c) 2014-2015 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/contrib/verifybinaries/verify.sh b/contrib/verifybinaries/verify.sh index 42d9ffb752..fc7492ad3b 100755 --- a/contrib/verifybinaries/verify.sh +++ b/contrib/verifybinaries/verify.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright (c) 2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk index 745c9e1157..b4d7756b56 100644 --- a/depends/packages/qt.mk +++ b/depends/packages/qt.mk @@ -1,22 +1,20 @@ PACKAGE=qt -$(package)_version=5.7.1 -$(package)_download_path=http://download.qt.io/official_releases/qt/5.7/$($(package)_version)/submodules -$(package)_suffix=opensource-src-$($(package)_version).tar.gz +$(package)_version=5.9.6 +$(package)_download_path=https://download.qt.io/official_releases/qt/5.9/$($(package)_version)/submodules +$(package)_suffix=opensource-src-$($(package)_version).tar.xz $(package)_file_name=qtbase-$($(package)_suffix) -$(package)_sha256_hash=95f83e532d23b3ddbde7973f380ecae1bac13230340557276f75f2e37984e410 +$(package)_sha256_hash=eed620cb268b199bd83b3fc6a471c51d51e1dc2dbb5374fc97a0cc75facbe36f $(package)_dependencies=openssl zlib $(package)_linux_dependencies=freetype fontconfig libxcb libX11 xproto libXext $(package)_build_subdir=qtbase $(package)_qt_libs=corelib network widgets gui plugins testlib -$(package)_patches=mac-qmake.conf mingw-uuidof.patch pidlist_absolute.patch fix-xcb-include-order.patch -$(package)_patches+=fix_qt_pkgconfig.patch fix-cocoahelpers-macos.patch qfixed-coretext.patch +$(package)_patches=fix_qt_pkgconfig.patch mac-qmake.conf fix_configure_mac.patch fix_no_printer.patch $(package)_qttranslations_file_name=qttranslations-$($(package)_suffix) -$(package)_qttranslations_sha256_hash=3a15aebd523c6d89fb97b2d3df866c94149653a26d27a00aac9b6d3020bc5a1d - +$(package)_qttranslations_sha256_hash=9822084f8e2d2939ba39f4af4c0c2320e45d5996762a9423f833055607604ed8 $(package)_qttools_file_name=qttools-$($(package)_suffix) -$(package)_qttools_sha256_hash=22d67de915cb8cd93e16fdd38fa006224ad9170bd217c2be1e53045a8dd02f0f +$(package)_qttools_sha256_hash=50e75417ec0c74bb8b1989d1d8e981ee83690dce7dfc0c2169f7c00f397e5117 $(package)_extra_sources = $($(package)_qttranslations_file_name) $(package)_extra_sources += $($(package)_qttools_file_name) @@ -29,25 +27,18 @@ $(package)_config_opts += -c++std c++11 $(package)_config_opts += -confirm-license $(package)_config_opts += -dbus-runtime $(package)_config_opts += -hostprefix $(build_prefix) -$(package)_config_opts += -no-alsa -$(package)_config_opts += -no-audio-backend $(package)_config_opts += -no-cups $(package)_config_opts += -no-egl $(package)_config_opts += -no-eglfs -$(package)_config_opts += -no-feature-style-windowsmobile -$(package)_config_opts += -no-feature-style-windowsce $(package)_config_opts += -no-freetype $(package)_config_opts += -no-gif $(package)_config_opts += -no-glib -$(package)_config_opts += -no-gstreamer $(package)_config_opts += -no-icu $(package)_config_opts += -no-iconv $(package)_config_opts += -no-kms $(package)_config_opts += -no-linuxfb $(package)_config_opts += -no-libudev -$(package)_config_opts += -no-mitshm $(package)_config_opts += -no-mtdev -$(package)_config_opts += -no-pulseaudio $(package)_config_opts += -no-openvg $(package)_config_opts += -no-reduce-relocations $(package)_config_opts += -no-qml-debug @@ -62,7 +53,6 @@ $(package)_config_opts += -no-sql-sqlite $(package)_config_opts += -no-sql-sqlite2 $(package)_config_opts += -no-use-gold-linker $(package)_config_opts += -no-xinput2 -$(package)_config_opts += -no-xrender $(package)_config_opts += -nomake examples $(package)_config_opts += -nomake tests $(package)_config_opts += -opensource @@ -75,12 +65,13 @@ $(package)_config_opts += -qt-libpng $(package)_config_opts += -qt-libjpeg $(package)_config_opts += -qt-pcre $(package)_config_opts += -system-zlib -$(package)_config_opts += -reduce-exports $(package)_config_opts += -static $(package)_config_opts += -silent $(package)_config_opts += -v $(package)_config_opts += -no-feature-printer $(package)_config_opts += -no-feature-printdialog +$(package)_config_opts += -no-feature-concurrent +$(package)_config_opts += -no-feature-xml ifneq ($(build_os),darwin) $(package)_config_opts_darwin = -xplatform macx-clang-linux @@ -95,11 +86,12 @@ endif $(package)_config_opts_linux = -qt-xkbcommon $(package)_config_opts_linux += -qt-xcb $(package)_config_opts_linux += -system-freetype -$(package)_config_opts_linux += -no-sm +$(package)_config_opts_linux += -no-feature-sessionmanager $(package)_config_opts_linux += -fontconfig $(package)_config_opts_linux += -no-opengl $(package)_config_opts_arm_linux = -platform linux-g++ -xplatform $(host) $(package)_config_opts_i686_linux = -xplatform linux-g++-32 +$(package)_config_opts_x86_64_linux = -xplatform linux-g++-64 $(package)_config_opts_mingw32 = -no-opengl -xplatform win32-g++ -device-option CROSS_COMPILE="$(host)-" $(package)_build_env = QT_RCC_TEST=1 endef @@ -124,11 +116,10 @@ define $(package)_extract_cmds tar --strip-components=1 -xf $($(package)_source_dir)/$($(package)_qttools_file_name) -C qttools endef - define $(package)_preprocess_cmds sed -i.old "s|updateqm.commands = \$$$$\$$$$LRELEASE|updateqm.commands = $($(package)_extract_dir)/qttools/bin/lrelease|" qttranslations/translations/translations.pro && \ sed -i.old "/updateqm.depends =/d" qttranslations/translations/translations.pro && \ - sed -i.old "s/src_plugins.depends = src_sql src_xml src_network/src_plugins.depends = src_xml src_network/" qtbase/src/src.pro && \ + sed -i.old "s/src_plugins.depends = src_sql src_network/src_plugins.depends = src_network/" qtbase/src/src.pro && \ sed -i.old "s|X11/extensions/XIproto.h|X11/X.h|" qtbase/src/plugins/platforms/xcb/qxcbxsettings.cpp && \ sed -i.old 's/if \[ "$$$$XPLATFORM_MAC" = "yes" \]; then xspecvals=$$$$(macSDKify/if \[ "$$$$BUILD_ON_MAC" = "yes" \]; then xspecvals=$$$$(macSDKify/' qtbase/configure && \ sed -i.old 's/CGEventCreateMouseEvent(0, kCGEventMouseMoved, pos, 0)/CGEventCreateMouseEvent(0, kCGEventMouseMoved, pos, kCGMouseButtonLeft)/' qtbase/src/plugins/platforms/cocoa/qcocoacursor.mm && \ @@ -137,19 +128,17 @@ define $(package)_preprocess_cmds cp -f qtbase/mkspecs/macx-clang/Info.plist.app qtbase/mkspecs/macx-clang-linux/ &&\ cp -f qtbase/mkspecs/macx-clang/qplatformdefs.h qtbase/mkspecs/macx-clang-linux/ &&\ cp -f $($(package)_patch_dir)/mac-qmake.conf qtbase/mkspecs/macx-clang-linux/qmake.conf && \ - patch -p1 < $($(package)_patch_dir)/mingw-uuidof.patch && \ - patch -p1 < $($(package)_patch_dir)/pidlist_absolute.patch && \ - patch -p1 < $($(package)_patch_dir)/fix-xcb-include-order.patch && \ - patch -p1 < $($(package)_patch_dir)/fix_qt_pkgconfig.patch && \ - patch -p1 < $($(package)_patch_dir)/fix-cocoahelpers-macos.patch && \ - patch -p1 < $($(package)_patch_dir)/qfixed-coretext.patch && \ + patch -p1 -i $($(package)_patch_dir)/fix_qt_pkgconfig.patch &&\ + patch -p1 -i $($(package)_patch_dir)/fix_configure_mac.patch &&\ + patch -p1 -i $($(package)_patch_dir)/fix_no_printer.patch &&\ echo "!host_build: QMAKE_CFLAGS += $($(package)_cflags) $($(package)_cppflags)" >> qtbase/mkspecs/common/gcc-base.conf && \ echo "!host_build: QMAKE_CXXFLAGS += $($(package)_cxxflags) $($(package)_cppflags)" >> qtbase/mkspecs/common/gcc-base.conf && \ echo "!host_build: QMAKE_LFLAGS += $($(package)_ldflags)" >> qtbase/mkspecs/common/gcc-base.conf && \ + echo "QMAKE_LINK_OBJECT_MAX = 10" >> qtbase/mkspecs/win32-g++/qmake.conf &&\ + echo "QMAKE_LINK_OBJECT_SCRIPT = object_script" >> qtbase/mkspecs/win32-g++/qmake.conf &&\ sed -i.old "s|QMAKE_CFLAGS = |!host_build: QMAKE_CFLAGS = $($(package)_cflags) $($(package)_cppflags) |" qtbase/mkspecs/win32-g++/qmake.conf && \ sed -i.old "s|QMAKE_LFLAGS = |!host_build: QMAKE_LFLAGS = $($(package)_ldflags) |" qtbase/mkspecs/win32-g++/qmake.conf && \ sed -i.old "s|QMAKE_CXXFLAGS = |!host_build: QMAKE_CXXFLAGS = $($(package)_cxxflags) $($(package)_cppflags) |" qtbase/mkspecs/win32-g++/qmake.conf - endef define $(package)_config_cmds @@ -161,19 +150,22 @@ define $(package)_config_cmds echo "CONFIG += force_bootstrap" >> mkspecs/qconfig.pri && \ $(MAKE) sub-src-clean && \ cd ../qttranslations && ../qtbase/bin/qmake qttranslations.pro -o Makefile && \ - cd translations && ../../qtbase/bin/qmake translations.pro -o Makefile && cd ../.. &&\ - cd qttools/src/linguist/lrelease/ && ../../../../qtbase/bin/qmake lrelease.pro -o Makefile + cd translations && ../../qtbase/bin/qmake translations.pro -o Makefile && cd ../.. && \ + cd qttools/src/linguist/lrelease/ && ../../../../qtbase/bin/qmake lrelease.pro -o Makefile && \ + cd ../lupdate/ && ../../../../qtbase/bin/qmake lupdate.pro -o Makefile && cd ../../../.. endef define $(package)_build_cmds $(MAKE) -C src $(addprefix sub-,$($(package)_qt_libs)) && \ $(MAKE) -C ../qttools/src/linguist/lrelease && \ + $(MAKE) -C ../qttools/src/linguist/lupdate && \ $(MAKE) -C ../qttranslations endef define $(package)_stage_cmds - $(MAKE) -C src INSTALL_ROOT=$($(package)_staging_dir) $(addsuffix -install_subtargets,$(addprefix sub-,$($(package)_qt_libs))) && cd .. &&\ + $(MAKE) -C src INSTALL_ROOT=$($(package)_staging_dir) $(addsuffix -install_subtargets,$(addprefix sub-,$($(package)_qt_libs))) && cd .. && \ $(MAKE) -C qttools/src/linguist/lrelease INSTALL_ROOT=$($(package)_staging_dir) install_target && \ + $(MAKE) -C qttools/src/linguist/lupdate INSTALL_ROOT=$($(package)_staging_dir) install_target && \ $(MAKE) -C qttranslations INSTALL_ROOT=$($(package)_staging_dir) install_subtargets && \ if `test -f qtbase/src/plugins/platforms/xcb/xcb-static/libxcb-static.a`; then \ cp qtbase/src/plugins/platforms/xcb/xcb-static/libxcb-static.a $($(package)_staging_prefix_dir)/lib; \ diff --git a/depends/patches/qt/fix-cocoahelpers-macos.patch b/depends/patches/qt/fix-cocoahelpers-macos.patch deleted file mode 100644 index 1b43a9eff8..0000000000 --- a/depends/patches/qt/fix-cocoahelpers-macos.patch +++ /dev/null @@ -1,70 +0,0 @@ -From 0707260a4f8e64dfadf1df5f935e74cabb7c7d27 Mon Sep 17 00:00:00 2001 -From: Jake Petroules <jake.petroules@qt.io> -Date: Sun, 1 Oct 2017 21:48:17 -0700 -Subject: [PATCH] Fix build error with macOS 10.13 SDK -MIME-Version: 1.0 -Content-Type: text/plain; charset=utf8 -Content-Transfer-Encoding: 8bit - -Several of these variables/macros are no longer defined. We didn't -validate the preconditions on iOS, tvOS, or watchOS, so no -need to bother validating them on macOS either. Nor did we check the -OSStatus result on any platform anyways. - -Task-number: QTBUG-63401 -Change-Id: Ife64dff767cf6d3f4b839fc53ec486181c176bf3 -(cherry-picked from 861544583511d4e6f7745d2339b26ff1cd44132b) -Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io> -Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io> ---- - src/plugins/platforms/cocoa/qcocoahelpers.h | 2 +- - src/plugins/platforms/cocoa/qcocoahelpers.mm | 13 +------------ - 2 files changed, 2 insertions(+), 13 deletions(-) - -diff --git old/qtbase/src/plugins/platforms/cocoa/qcocoahelpers.h new/qtbase/src/plugins/platforms/cocoa/qcocoahelpers.h -index bbb3793..74371d5 100644 ---- old/qtbase/src/plugins/platforms/cocoa/qcocoahelpers.h -+++ new/qtbase/src/plugins/platforms/cocoa/qcocoahelpers.h -@@ -80,7 +80,7 @@ QColor qt_mac_toQColor(CGColorRef color); - // Creates a mutable shape, it's the caller's responsibility to release. - HIMutableShapeRef qt_mac_QRegionToHIMutableShape(const QRegion ®ion); - --OSStatus qt_mac_drawCGImage(CGContextRef inContext, const CGRect *inBounds, CGImageRef inImage); -+void qt_mac_drawCGImage(CGContextRef inContext, const CGRect *inBounds, CGImageRef inImage); - - NSDragOperation qt_mac_mapDropAction(Qt::DropAction action); - NSDragOperation qt_mac_mapDropActions(Qt::DropActions actions); -diff --git old/qtbase/src/plugins/platforms/cocoa/qcocoahelpers.mm new/qtbase/src/plugins/platforms/cocoa/qcocoahelpers.mm -index cd73148..3f8429e 100644 ---- old/qtbase/src/plugins/platforms/cocoa/qcocoahelpers.mm -+++ new/qtbase/src/plugins/platforms/cocoa/qcocoahelpers.mm -@@ -544,15 +544,8 @@ NSRect qt_mac_flipRect(const QRect &rect) - return NSMakeRect(rect.x(), flippedY, rect.width(), rect.height()); - } - --OSStatus qt_mac_drawCGImage(CGContextRef inContext, const CGRect *inBounds, CGImageRef inImage) -+void qt_mac_drawCGImage(CGContextRef inContext, const CGRect *inBounds, CGImageRef inImage) - { -- // Verbatim copy if HIViewDrawCGImage (as shown on Carbon-Dev) -- OSStatus err = noErr; -- -- require_action(inContext != NULL, InvalidContext, err = paramErr); -- require_action(inBounds != NULL, InvalidBounds, err = paramErr); -- require_action(inImage != NULL, InvalidImage, err = paramErr); -- - CGContextSaveGState( inContext ); - CGContextTranslateCTM (inContext, 0, inBounds->origin.y + CGRectGetMaxY(*inBounds)); - CGContextScaleCTM(inContext, 1, -1); -@@ -560,10 +553,6 @@ OSStatus qt_mac_drawCGImage(CGContextRef inContext, const CGRect *inBounds, CGIm - CGContextDrawImage(inContext, *inBounds, inImage); - - CGContextRestoreGState(inContext); --InvalidImage: --InvalidBounds: --InvalidContext: -- return err; - } - - Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum) --- -2.7.4 diff --git a/depends/patches/qt/fix-xcb-include-order.patch b/depends/patches/qt/fix-xcb-include-order.patch deleted file mode 100644 index ec2bc17d9b..0000000000 --- a/depends/patches/qt/fix-xcb-include-order.patch +++ /dev/null @@ -1,49 +0,0 @@ ---- old/qtbase/src/plugins/platforms/xcb/xcb_qpa_lib.pro 2015-03-17 -+++ new/qtbase/src/plugins/platforms/xcb/xcb_qpa_lib.pro 2015-03-17 -@@ -76,8 +76,6 @@ - - DEFINES += $$QMAKE_DEFINES_XCB - LIBS += $$QMAKE_LIBS_XCB --QMAKE_CXXFLAGS += $$QMAKE_CFLAGS_XCB --QMAKE_CFLAGS += $$QMAKE_CFLAGS_XCB - - CONFIG += qpa/genericunixfontdatabase - -@@ -89,7 +87,8 @@ - contains(QT_CONFIG, xcb-qt) { - DEFINES += XCB_USE_RENDER - XCB_DIR = ../../../3rdparty/xcb -- INCLUDEPATH += $$XCB_DIR/include $$XCB_DIR/sysinclude -+ QMAKE_CFLAGS += -I$$XCB_DIR/include -I$$XCB_DIR/sysinclude $$QMAKE_CFLAGS_XCB -+ QMAKE_CXXFLAGS += -I$$XCB_DIR/include -I$$XCB_DIR/sysinclude $$QMAKE_CFLAGS_XCB - LIBS += -lxcb -L$$MODULE_BASE_OUTDIR/lib -lxcb-static$$qtPlatformTargetSuffix() - } else { - LIBS += -lxcb -lxcb-image -lxcb-icccm -lxcb-sync -lxcb-xfixes -lxcb-shm -lxcb-randr -lxcb-shape -lxcb-keysyms -lxcb-xinerama ---- old/qtbase/src/plugins/platforms/xcb/xcb-static/xcb-static.pro -+++ new/qtbase/src/plugins/platforms/xcb/xcb-static/xcb-static.pro -@@ -9,7 +9,8 @@ - - XCB_DIR = ../../../../3rdparty/xcb - --INCLUDEPATH += $$XCB_DIR/include $$XCB_DIR/include/xcb $$XCB_DIR/sysinclude -+QMAKE_CFLAGS += -I$$XCB_DIR/include -I$$XCB_DIR/include/xcb -I$$XCB_DIR/sysinclude -+QMAKE_CXXFLAGS += -I$$XCB_DIR/include -I$$XCB_DIR/include/xcb -I$$XCB_DIR/sysinclude - - QMAKE_CXXFLAGS += $$QMAKE_CFLAGS_XCB - QMAKE_CFLAGS += $$QMAKE_CFLAGS_XCB ---- old/qtbase/src/plugins/platforms/xcb/xcb-plugin.pro -+++ new/qtbase/src/plugins/platforms/xcb/xcb-plugin.pro -@@ -6,6 +6,13 @@ - qxcbmain.cpp - OTHER_FILES += xcb.json README - -+contains(QT_CONFIG, xcb-qt) { -+ DEFINES += XCB_USE_RENDER -+ XCB_DIR = ../../../3rdparty/xcb -+ QMAKE_CFLAGS += -I$$XCB_DIR/include -I$$XCB_DIR/sysinclude $$QMAKE_CFLAGS_XCB -+ QMAKE_CXXFLAGS += -I$$XCB_DIR/include -I$$XCB_DIR/sysinclude $$QMAKE_CFLAGS_XCB -+} -+ - PLUGIN_TYPE = platforms - PLUGIN_CLASS_NAME = QXcbIntegrationPlugin - !equals(TARGET, $$QT_DEFAULT_QPA_PLUGIN): PLUGIN_EXTENDS = - diff --git a/depends/patches/qt/fix_configure_mac.patch b/depends/patches/qt/fix_configure_mac.patch new file mode 100644 index 0000000000..0d7dd647de --- /dev/null +++ b/depends/patches/qt/fix_configure_mac.patch @@ -0,0 +1,50 @@ +--- old/qtbase/mkspecs/features/mac/sdk.prf 2018-02-08 10:24:48.000000000 -0800 ++++ new/qtbase/mkspecs/features/mac/sdk.prf 2018-03-23 10:38:56.000000000 -0700 +@@ -8,21 +8,21 @@ + defineReplace(xcodeSDKInfo) { + info = $$1 + equals(info, "Path"): \ +- info = --show-sdk-path ++ infoarg = --show-sdk-path + equals(info, "PlatformPath"): \ +- info = --show-sdk-platform-path ++ infoarg = --show-sdk-platform-path + equals(info, "SDKVersion"): \ +- info = --show-sdk-version ++ infoarg = --show-sdk-version + sdk = $$2 + isEmpty(sdk): \ + sdk = $$QMAKE_MAC_SDK + + isEmpty(QMAKE_MAC_SDK.$${sdk}.$${info}) { +- QMAKE_MAC_SDK.$${sdk}.$${info} = $$system("/usr/bin/xcrun --sdk $$sdk $$info 2>/dev/null") ++ QMAKE_MAC_SDK.$${sdk}.$${info} = $$system("/usr/bin/xcrun --sdk $$sdk $$infoarg 2>/dev/null") + # --show-sdk-platform-path won't work for Command Line Tools; this is fine + # only used by the XCTest backend to testlib +- isEmpty(QMAKE_MAC_SDK.$${sdk}.$${info}):if(!isEmpty(QMAKE_XCODEBUILD_PATH)|!equals(info, "--show-sdk-platform-path")): \ +- error("Could not resolve SDK $$info for \'$$sdk\'") ++ isEmpty(QMAKE_MAC_SDK.$${sdk}.$${info}):if(!isEmpty(QMAKE_XCODEBUILD_PATH)|!equals(infoarg, "--show-sdk-platform-path")): \ ++ error("Could not resolve SDK $$info for \'$$sdk\' using $$infoarg") + cache(QMAKE_MAC_SDK.$${sdk}.$${info}, set stash, QMAKE_MAC_SDK.$${sdk}.$${info}) + } + +--- old/qtbase/configure 2018-02-08 10:24:48.000000000 -0800 ++++ new/qtbase/configure 2018-03-23 05:42:29.000000000 -0700 +@@ -232,8 +232,13 @@ + + sdk=$(getSingleQMakeVariable "QMAKE_MAC_SDK" "$1") + if [ -z "$sdk" ]; then echo "QMAKE_MAC_SDK must be set when building on Mac" >&2; exit 1; fi +- sysroot=$(/usr/bin/xcrun --sdk $sdk --show-sdk-path 2>/dev/null) +- if [ -z "$sysroot" ]; then echo "Failed to resolve SDK path for '$sdk'" >&2; exit 1; fi ++ sysroot=$(getSingleQMakeVariable "QMAKE_MAC_SDK_PATH" "$1") ++ ++ echo "sysroot pre-configured as $sysroot"; ++ if [ -z "$sysroot" ]; then ++ sysroot=$(/usr/bin/xcrun --sdk $sdk --show-sdk-path 2>/dev/null) ++ if [ -z "$sysroot" ]; then echo "Failed to resolve SDK path for '$sdk'" >&2; exit 1; fi ++ fi + + case "$sdk" in + macosx*) + + diff --git a/depends/patches/qt/fix_no_printer.patch b/depends/patches/qt/fix_no_printer.patch new file mode 100644 index 0000000000..f868ca2577 --- /dev/null +++ b/depends/patches/qt/fix_no_printer.patch @@ -0,0 +1,19 @@ +--- x/qtbase/src/plugins/platforms/cocoa/qprintengine_mac_p.h ++++ y/qtbase/src/plugins/platforms/cocoa/qprintengine_mac_p.h +@@ -52,6 +52,7 @@ + // + + #include <QtCore/qglobal.h> ++#include <qpa/qplatformprintdevice.h> + + #ifndef QT_NO_PRINTER + +--- x/qtbase/src/plugins/plugins.pro ++++ y/qtbase/src/plugins/plugins.pro +@@ -8,6 +8,3 @@ qtHaveModule(gui) { + qtConfig(imageformatplugin): SUBDIRS *= imageformats + !android:qtConfig(library): SUBDIRS *= generic + } +- +-!winrt:qtHaveModule(printsupport): \ +- SUBDIRS += printsupport diff --git a/depends/patches/qt/mac-qmake.conf b/depends/patches/qt/mac-qmake.conf index ca70d30b15..337d0eb9ca 100644 --- a/depends/patches/qt/mac-qmake.conf +++ b/depends/patches/qt/mac-qmake.conf @@ -14,6 +14,7 @@ QMAKE_MAC_SDK.macosx.Path = $${MAC_SDK_PATH} QMAKE_MAC_SDK.macosx.platform_name = macosx QMAKE_MAC_SDK.macosx.SDKVersion = $${MAC_SDK_VERSION} QMAKE_MAC_SDK.macosx.PlatformPath = /phony +QMAKE_APPLE_DEVICE_ARCHS=x86_64 !host_build: QMAKE_CFLAGS += -target $${MAC_TARGET} !host_build: QMAKE_OBJECTIVE_CFLAGS += $$QMAKE_CFLAGS !host_build: QMAKE_CXXFLAGS += $$QMAKE_CFLAGS diff --git a/depends/patches/qt/mingw-uuidof.patch b/depends/patches/qt/mingw-uuidof.patch deleted file mode 100644 index fb21923c8c..0000000000 --- a/depends/patches/qt/mingw-uuidof.patch +++ /dev/null @@ -1,44 +0,0 @@ ---- old/qtbase/src/plugins/platforms/windows/qwindowscontext.cpp -+++ new/qtbase/src/plugins/platforms/windows/qwindowscontext.cpp -@@ -77,7 +77,7 @@ - #include <stdlib.h> - #include <stdio.h> - #include <windowsx.h> --#ifndef Q_OS_WINCE -+#if !defined(Q_OS_WINCE) && (!defined(USE___UUIDOF) || (defined(USE___UUIDOF) && USE___UUIDOF == 1)) - # include <comdef.h> - #endif - -@@ -814,7 +814,7 @@ - HWND_MESSAGE, NULL, static_cast<HINSTANCE>(GetModuleHandle(0)), NULL); - } - --#ifndef Q_OS_WINCE -+#if !defined(Q_OS_WINCE) && (!defined(USE___UUIDOF) || (defined(USE___UUIDOF) && USE___UUIDOF == 1)) - // Re-engineered from the inline function _com_error::ErrorMessage(). - // We cannot use it directly since it uses swprintf_s(), which is not - // present in the MSVCRT.DLL found on Windows XP (QTBUG-35617). -@@ -833,7 +833,7 @@ - return QString::asprintf("IDispatch error #%u", uint(wCode)); - return QString::asprintf("Unknown error 0x0%x", uint(comError.Error())); - } --#endif // !Q_OS_WINCE -+#endif // !defined(Q_OS_WINCE) && (!defined(USE___UUIDOF) || (defined(USE___UUIDOF) && USE___UUIDOF == 1)) - - /*! - \brief Common COM error strings. -@@ -901,12 +901,12 @@ - default: - break; - } --#ifndef Q_OS_WINCE -+#if !defined(Q_OS_WINCE) && (!defined(USE___UUIDOF) || (defined(USE___UUIDOF) && USE___UUIDOF == 1)) - _com_error error(hr); - result += QByteArrayLiteral(" ("); - result += errorMessageFromComError(error); - result += ')'; --#endif // !Q_OS_WINCE -+#endif // !defined(Q_OS_WINCE) && (!defined(USE___UUIDOF) || (defined(USE___UUIDOF) && USE___UUIDOF == 1)) - return result; - } - diff --git a/depends/patches/qt/pidlist_absolute.patch b/depends/patches/qt/pidlist_absolute.patch deleted file mode 100644 index c792824179..0000000000 --- a/depends/patches/qt/pidlist_absolute.patch +++ /dev/null @@ -1,37 +0,0 @@ -diff -dur old/qtbase/src/plugins/platforms/windows/qwindowscontext.h new/qtbase/src/plugins/platforms/windows/qwindowscontext.h ---- old/qtbase/src/plugins/platforms/windows/qwindowscontext.h -+++ new/qtbase/src/plugins/platforms/windows/qwindowscontext.h -@@ -136,10 +136,18 @@ - inline void init(); - - typedef HRESULT (WINAPI *SHCreateItemFromParsingName)(PCWSTR, IBindCtx *, const GUID&, void **); -+#if defined(Q_CC_MINGW) && (!defined(__MINGW64_VERSION_MAJOR) || __MINGW64_VERSION_MAJOR < 3) -+ typedef HRESULT (WINAPI *SHGetKnownFolderIDList)(const GUID &, DWORD, HANDLE, ITEMIDLIST **); -+#else - typedef HRESULT (WINAPI *SHGetKnownFolderIDList)(const GUID &, DWORD, HANDLE, PIDLIST_ABSOLUTE *); -+#endif - typedef HRESULT (WINAPI *SHGetStockIconInfo)(int , int , _SHSTOCKICONINFO *); - typedef HRESULT (WINAPI *SHGetImageList)(int, REFIID , void **); -+#if defined(Q_CC_MINGW) && (!defined(__MINGW64_VERSION_MAJOR) || __MINGW64_VERSION_MAJOR < 3) -+ typedef HRESULT (WINAPI *SHCreateItemFromIDList)(const ITEMIDLIST *, REFIID, void **); -+#else - typedef HRESULT (WINAPI *SHCreateItemFromIDList)(PCIDLIST_ABSOLUTE, REFIID, void **); -+#endif - - SHCreateItemFromParsingName sHCreateItemFromParsingName; - SHGetKnownFolderIDList sHGetKnownFolderIDList; -diff -dur old/qtbase/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp new/qtbase/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp ---- old/qtbase/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp -+++ new/qtbase/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp -@@ -1016,7 +1016,11 @@ - qWarning() << __FUNCTION__ << ": Invalid CLSID: " << url.path(); - return Q_NULLPTR; - } -+#if defined(Q_CC_MINGW) && (!defined(__MINGW64_VERSION_MAJOR) || __MINGW64_VERSION_MAJOR < 3) -+ ITEMIDLIST *idList; -+#else - PIDLIST_ABSOLUTE idList; -+#endif - HRESULT hr = QWindowsContext::shell32dll.sHGetKnownFolderIDList(uuid, 0, 0, &idList); - if (FAILED(hr)) { - qErrnoWarning("%s: SHGetKnownFolderIDList(%s)) failed", __FUNCTION__, qPrintable(url.toString())); diff --git a/depends/patches/qt/qfixed-coretext.patch b/depends/patches/qt/qfixed-coretext.patch deleted file mode 100644 index aa56f1e1de..0000000000 --- a/depends/patches/qt/qfixed-coretext.patch +++ /dev/null @@ -1,34 +0,0 @@ -From dbdd5f0ffbce52c8b789ed09f1aa3f1da6c02e23 Mon Sep 17 00:00:00 2001 -From: Gabriel de Dietrich <gabriel.dedietrich@qt.io> -Date: Fri, 30 Mar 2018 11:58:16 -0700 -Subject: [PATCH] QCoreTextFontEngine: Fix build with Xcode 9.3 - -Apple LLVM version 9.1.0 (clang-902.0.39.1) - -Error message: - -.../qfontengine_coretext.mm:827:20: error: qualified reference to - 'QFixed' is a constructor name rather than a type in this context - return QFixed::QFixed(int(CTFontGetUnitsPerEm(ctfont))); - -Change-Id: Iebe26b3b087a16b10664208fc8851cbddb47f043 -Reviewed-by: Konstantin Ritt <ritt.ks@gmail.com> ---- - src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git old/qtbase/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm new/qtbase/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm -index 25ff69d877d..98b753eff96 100644 ---- old/qtbase/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm -+++ new/qtbase/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm -@@ -824,7 +824,7 @@ void QCoreTextFontEngine::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, gl - - QFixed QCoreTextFontEngine::emSquareSize() const - { -- return QFixed::QFixed(int(CTFontGetUnitsPerEm(ctfont))); -+ return QFixed(int(CTFontGetUnitsPerEm(ctfont))); - } - - QFontEngine *QCoreTextFontEngine::cloneWithSize(qreal pixelSize) const --- -2.16.3
\ No newline at end of file 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/doc/release-notes.md b/doc/release-notes.md index e1bb84cca9..57f93abe03 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -79,6 +79,8 @@ RPC changes `getmempoolentry` when verbosity is set to `true` with sub-fields `ancestor`, `base`, `modified` and `descendant` denominated in BTC. This new field deprecates previous fee fields, such as `fee`, `modifiedfee`, `ancestorfee` and `descendantfee`. +- The new RPC `getzmqnotifications` returns information about active ZMQ + notifications. External wallet files --------------------- diff --git a/doc/translation_strings_policy.md b/doc/translation_strings_policy.md index b95259cdc9..737d11f045 100644 --- a/doc/translation_strings_policy.md +++ b/doc/translation_strings_policy.md @@ -21,21 +21,11 @@ On a high level, these strings are to be translated: - GUI strings, anything that appears in a dialog or window -- Command-line option documentation - ### GUI strings Anything that appears to the user in the GUI is to be translated. This includes labels, menu items, button texts, tooltips and window titles. This includes messages passed to the GUI through the UI interface through `InitMessage`, `ThreadSafeMessageBox` or `ShowProgress`. -### Command-line options - -Documentation for the command line options in the output of `--help` should be translated as well. - -Make sure that default values do not end up in the string, but use string formatting like `strprintf(_("Threshold for disconnecting misbehaving peers (default: %u)"), 100)`. Putting default values in strings has led to accidental translations in the past, and forces the string to be retranslated every time the value changes. - -Do not translate messages that are only shown to developers, such as those that only appear when `--help-debug` is used. - General recommendations ------------------------ diff --git a/src/Makefile.am b/src/Makefile.am index e03c21f16e..458721293e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -52,8 +52,12 @@ if ENABLE_AVX2 LIBBITCOIN_CRYPTO_AVX2 = crypto/libbitcoin_crypto_avx2.a LIBBITCOIN_CRYPTO += $(LIBBITCOIN_CRYPTO_AVX2) endif +if ENABLE_SHANI +LIBBITCOIN_CRYPTO_SHANI = crypto/libbitcoin_crypto_shani.a +LIBBITCOIN_CRYPTO += $(LIBBITCOIN_CRYPTO_SHANI) +endif -$(LIBSECP256K1): $(wildcard secp256k1/src/*) $(wildcard secp256k1/include/*) +$(LIBSECP256K1): $(wildcard secp256k1/src/*.h) $(wildcard secp256k1/src/*.c) $(wildcard secp256k1/include/*) $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F) # Make is not made aware of per-object dependencies to avoid limiting building parallelization @@ -157,6 +161,7 @@ BITCOIN_CORE_H = \ script/sigcache.h \ script/sign.h \ script/standard.h \ + shutdown.h \ streams.h \ support/allocators/secure.h \ support/allocators/zeroafterfree.h \ @@ -193,7 +198,8 @@ BITCOIN_CORE_H = \ zmq/zmqabstractnotifier.h \ zmq/zmqconfig.h\ zmq/zmqnotificationinterface.h \ - zmq/zmqpublishnotifier.h + zmq/zmqpublishnotifier.h \ + zmq/zmqrpc.h obj/build.h: FORCE @@ -235,7 +241,9 @@ libbitcoin_server_a_SOURCES = \ rpc/net.cpp \ rpc/rawtransaction.cpp \ rpc/server.cpp \ + rpc/util.cpp \ script/sigcache.cpp \ + shutdown.cpp \ timedata.cpp \ torcontrol.cpp \ txdb.cpp \ @@ -252,7 +260,8 @@ libbitcoin_zmq_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_zmq_a_SOURCES = \ zmq/zmqabstractnotifier.cpp \ zmq/zmqnotificationinterface.cpp \ - zmq/zmqpublishnotifier.cpp + zmq/zmqpublishnotifier.cpp \ + zmq/zmqrpc.cpp endif @@ -313,6 +322,12 @@ crypto_libbitcoin_crypto_avx2_a_CXXFLAGS += $(AVX2_CXXFLAGS) crypto_libbitcoin_crypto_avx2_a_CPPFLAGS += -DENABLE_AVX2 crypto_libbitcoin_crypto_avx2_a_SOURCES = crypto/sha256_avx2.cpp +crypto_libbitcoin_crypto_shani_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +crypto_libbitcoin_crypto_shani_a_CPPFLAGS = $(AM_CPPFLAGS) +crypto_libbitcoin_crypto_shani_a_CXXFLAGS += $(SHANI_CXXFLAGS) +crypto_libbitcoin_crypto_shani_a_CPPFLAGS += -DENABLE_SHANI +crypto_libbitcoin_crypto_shani_a_SOURCES = crypto/sha256_shani.cpp + # consensus: shared between all executables that validate any consensus rules. libbitcoin_consensus_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) libbitcoin_consensus_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) @@ -392,7 +407,6 @@ libbitcoin_util_a_SOURCES = \ logging.cpp \ random.cpp \ rpc/protocol.cpp \ - rpc/util.cpp \ support/cleanse.cpp \ sync.cpp \ threadinterrupt.cpp \ diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include index bc2c46f228..7b32b72bd1 100644 --- a/src/Makefile.bench.include +++ b/src/Makefile.bench.include @@ -15,6 +15,7 @@ bench_bench_bitcoin_SOURCES = \ bench/bench_bitcoin.cpp \ bench/bench.cpp \ bench/bench.h \ + bench/block_assemble.cpp \ bench/checkblock.cpp \ bench/checkqueue.cpp \ bench/examples.cpp \ @@ -33,8 +34,8 @@ nodist_bench_bench_bitcoin_SOURCES = $(GENERATED_BENCH_FILES) bench_bench_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(EVENT_CLFAGS) $(EVENT_PTHREADS_CFLAGS) -I$(builddir)/bench/ bench_bench_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) bench_bench_bitcoin_LDADD = \ - $(LIBBITCOIN_SERVER) \ $(LIBBITCOIN_WALLET) \ + $(LIBBITCOIN_SERVER) \ $(LIBBITCOIN_COMMON) \ $(LIBBITCOIN_UTIL) \ $(LIBBITCOIN_CONSENSUS) \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include index a4d31795ec..0c1516f4d5 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -137,6 +137,7 @@ test_test_bitcoin_fuzzy_LDADD = \ $(LIBBITCOIN_CRYPTO) \ $(LIBBITCOIN_CRYPTO_SSE41) \ $(LIBBITCOIN_CRYPTO_AVX2) \ + $(LIBBITCOIN_CRYPTO_SHANI) \ $(LIBSECP256K1) test_test_bitcoin_fuzzy_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) diff --git a/src/bench/block_assemble.cpp b/src/bench/block_assemble.cpp new file mode 100644 index 0000000000..36fa175a76 --- /dev/null +++ b/src/bench/block_assemble.cpp @@ -0,0 +1,116 @@ +// Copyright (c) 2011-2017 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <bench/bench.h> +#include <chainparams.h> +#include <coins.h> +#include <consensus/merkle.h> +#include <consensus/validation.h> +#include <miner.h> +#include <policy/policy.h> +#include <pow.h> +#include <scheduler.h> +#include <txdb.h> +#include <txmempool.h> +#include <utiltime.h> +#include <validation.h> +#include <validationinterface.h> + +#include <boost/thread.hpp> + +#include <list> +#include <vector> + +static std::shared_ptr<CBlock> PrepareBlock(const CScript& coinbase_scriptPubKey) +{ + auto block = std::make_shared<CBlock>( + BlockAssembler{Params()} + .CreateNewBlock(coinbase_scriptPubKey, /* fMineWitnessTx */ true) + ->block); + + block->nTime = ::chainActive.Tip()->GetMedianTimePast() + 1; + block->hashMerkleRoot = BlockMerkleRoot(*block); + + return block; +} + + +static CTxIn MineBlock(const CScript& coinbase_scriptPubKey) +{ + auto block = PrepareBlock(coinbase_scriptPubKey); + + while (!CheckProofOfWork(block->GetHash(), block->nBits, Params().GetConsensus())) { + assert(++block->nNonce); + } + + bool processed{ProcessNewBlock(Params(), block, true, nullptr)}; + assert(processed); + + return CTxIn{block->vtx[0]->GetHash(), 0}; +} + + +static void AssembleBlock(benchmark::State& state) +{ + const std::vector<unsigned char> op_true{OP_TRUE}; + CScriptWitness witness; + witness.stack.push_back(op_true); + + uint256 witness_program; + CSHA256().Write(&op_true[0], op_true.size()).Finalize(witness_program.begin()); + + const CScript SCRIPT_PUB{CScript(OP_0) << std::vector<unsigned char>{witness_program.begin(), witness_program.end()}}; + + // Switch to regtest so we can mine faster + // Also segwit is active, so we can include witness transactions + SelectParams(CBaseChainParams::REGTEST); + + InitScriptExecutionCache(); + + boost::thread_group thread_group; + CScheduler scheduler; + { + ::pblocktree.reset(new CBlockTreeDB(1 << 20, true)); + ::pcoinsdbview.reset(new CCoinsViewDB(1 << 23, true)); + ::pcoinsTip.reset(new CCoinsViewCache(pcoinsdbview.get())); + + const CChainParams& chainparams = Params(); + thread_group.create_thread(boost::bind(&CScheduler::serviceQueue, &scheduler)); + GetMainSignals().RegisterBackgroundSignalScheduler(scheduler); + LoadGenesisBlock(chainparams); + CValidationState state; + ActivateBestChain(state, chainparams); + assert(::chainActive.Tip() != nullptr); + const bool witness_enabled{IsWitnessEnabled(::chainActive.Tip(), chainparams.GetConsensus())}; + assert(witness_enabled); + } + + // Collect some loose transactions that spend the coinbases of our mined blocks + constexpr size_t NUM_BLOCKS{200}; + std::array<CTransactionRef, NUM_BLOCKS - COINBASE_MATURITY + 1> txs; + for (size_t b{0}; b < NUM_BLOCKS; ++b) { + CMutableTransaction tx; + tx.vin.push_back(MineBlock(SCRIPT_PUB)); + tx.vin.back().scriptWitness = witness; + tx.vout.emplace_back(1337, SCRIPT_PUB); + if (NUM_BLOCKS - b >= COINBASE_MATURITY) + txs.at(b) = MakeTransactionRef(tx); + } + for (const auto& txr : txs) { + CValidationState state; + bool ret{::AcceptToMemoryPool(::mempool, state, txr, nullptr /* pfMissingInputs */, nullptr /* plTxnReplaced */, false /* bypass_limits */, /* nAbsurdFee */ 0)}; + assert(ret); + } + + while (state.KeepRunning()) { + PrepareBlock(SCRIPT_PUB); + } + + thread_group.interrupt_all(); + thread_group.join_all(); + GetMainSignals().FlushBackgroundCallbacks(); + GetMainSignals().UnregisterBackgroundSignalScheduler(); +} + +BENCHMARK(AssembleBlock, 700); diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp index 64ec056c4d..f3180809b5 100644 --- a/src/bench/coin_selection.cpp +++ b/src/bench/coin_selection.cpp @@ -34,31 +34,25 @@ static void addCoin(const CAmount& nValue, const CWallet& wallet, std::vector<CO static void CoinSelection(benchmark::State& state) { const CWallet wallet("dummy", WalletDatabase::CreateDummy()); - std::vector<COutput> vCoins; LOCK(wallet.cs_wallet); - while (state.KeepRunning()) { - // Add coins. - for (int i = 0; i < 1000; i++) - addCoin(1000 * COIN, wallet, vCoins); - addCoin(3 * COIN, wallet, vCoins); + // Add coins. + std::vector<COutput> vCoins; + for (int i = 0; i < 1000; ++i) { + addCoin(1000 * COIN, wallet, vCoins); + } + addCoin(3 * COIN, wallet, vCoins); + const CoinEligibilityFilter filter_standard(1, 6, 0); + const CoinSelectionParams coin_selection_params(true, 34, 148, CFeeRate(0), 0); + while (state.KeepRunning()) { std::set<CInputCoin> setCoinsRet; CAmount nValueRet; bool bnb_used; - CoinEligibilityFilter filter_standard(1, 6, 0); - CoinSelectionParams coin_selection_params(false, 34, 148, CFeeRate(0), 0); - bool success = wallet.SelectCoinsMinConf(1003 * COIN, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used) - || wallet.SelectCoinsMinConf(1003 * COIN, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used); + bool success = wallet.SelectCoinsMinConf(1003 * COIN, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used); assert(success); assert(nValueRet == 1003 * COIN); assert(setCoinsRet.size() == 2); - - // Empty wallet. - for (COutput& output : vCoins) { - delete output.tx; - } - vCoins.clear(); } } diff --git a/src/bench/prevector.cpp b/src/bench/prevector.cpp index 3cfad1b2c4..09c7020848 100644 --- a/src/bench/prevector.cpp +++ b/src/bench/prevector.cpp @@ -42,7 +42,7 @@ static void PrevectorClear(benchmark::State& state) t0.resize(28); t0.clear(); t1.resize(29); - t0.clear(); + t1.clear(); } } } @@ -64,11 +64,11 @@ static void PrevectorResize(benchmark::State& state) #define PREVECTOR_TEST(name, nontrivops, trivops) \ static void Prevector ## name ## Nontrivial(benchmark::State& state) { \ - PrevectorResize<nontrivial_t>(state); \ + Prevector ## name<nontrivial_t>(state); \ } \ BENCHMARK(Prevector ## name ## Nontrivial, nontrivops); \ static void Prevector ## name ## Trivial(benchmark::State& state) { \ - PrevectorResize<trivial_t>(state); \ + Prevector ## name<trivial_t>(state); \ } \ BENCHMARK(Prevector ## name ## Trivial, trivops); diff --git a/src/bench/verify_script.cpp b/src/bench/verify_script.cpp index 4100519d48..ae60588c2d 100644 --- a/src/bench/verify_script.cpp +++ b/src/bench/verify_script.cpp @@ -9,6 +9,7 @@ #endif #include <script/script.h> #include <script/sign.h> +#include <script/standard.h> #include <streams.h> #include <array> diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index e6eb723cf4..181e2bb1bc 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2009-2017 The Bitcoin Core developers +// Copyright (c) 2009-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. @@ -193,18 +193,18 @@ static CAmount ExtractAndValidateValue(const std::string& strValue) static void MutateTxVersion(CMutableTransaction& tx, const std::string& cmdVal) { - int64_t newVersion = atoi64(cmdVal); - if (newVersion < 1 || newVersion > CTransaction::MAX_STANDARD_VERSION) - throw std::runtime_error("Invalid TX version requested"); + int64_t newVersion; + if (!ParseInt64(cmdVal, &newVersion) || newVersion < 1 || newVersion > CTransaction::MAX_STANDARD_VERSION) + throw std::runtime_error("Invalid TX version requested: '" + cmdVal + "'"); tx.nVersion = (int) newVersion; } static void MutateTxLocktime(CMutableTransaction& tx, const std::string& cmdVal) { - int64_t newLocktime = atoi64(cmdVal); - if (newLocktime < 0LL || newLocktime > 0xffffffffLL) - throw std::runtime_error("Invalid TX locktime requested"); + int64_t newLocktime; + if (!ParseInt64(cmdVal, &newLocktime) || newLocktime < 0LL || newLocktime > 0xffffffffLL) + throw std::runtime_error("Invalid TX locktime requested: '" + cmdVal + "'"); tx.nLockTime = (unsigned int) newLocktime; } @@ -212,8 +212,8 @@ static void MutateTxLocktime(CMutableTransaction& tx, const std::string& cmdVal) static void MutateTxRBFOptIn(CMutableTransaction& tx, const std::string& strInIdx) { // parse requested index - int inIdx = atoi(strInIdx); - if (inIdx < 0 || inIdx >= (int)tx.vin.size()) { + int64_t inIdx; + if (!ParseInt64(strInIdx, &inIdx) || inIdx < 0 || inIdx >= static_cast<int64_t>(tx.vin.size())) { throw std::runtime_error("Invalid TX input index '" + strInIdx + "'"); } @@ -248,10 +248,10 @@ static void MutateTxAddInput(CMutableTransaction& tx, const std::string& strInpu static const unsigned int maxVout = MAX_BLOCK_WEIGHT / (WITNESS_SCALE_FACTOR * minTxOutSz); // extract and validate vout - std::string strVout = vStrInputParts[1]; - int vout = atoi(strVout); - if ((vout < 0) || (vout > (int)maxVout)) - throw std::runtime_error("invalid TX input vout"); + const std::string& strVout = vStrInputParts[1]; + int64_t vout; + if (!ParseInt64(strVout, &vout) || vout < 0 || vout > static_cast<int64_t>(maxVout)) + throw std::runtime_error("invalid TX input vout '" + strVout + "'"); // extract the optional sequence number uint32_t nSequenceIn=std::numeric_limits<unsigned int>::max(); @@ -481,10 +481,9 @@ static void MutateTxAddOutScript(CMutableTransaction& tx, const std::string& str static void MutateTxDelInput(CMutableTransaction& tx, const std::string& strInIdx) { // parse requested deletion index - int inIdx = atoi(strInIdx); - if (inIdx < 0 || inIdx >= (int)tx.vin.size()) { - std::string strErr = "Invalid TX input index '" + strInIdx + "'"; - throw std::runtime_error(strErr.c_str()); + int64_t inIdx; + if (!ParseInt64(strInIdx, &inIdx) || inIdx < 0 || inIdx >= static_cast<int64_t>(tx.vin.size())) { + throw std::runtime_error("Invalid TX input index '" + strInIdx + "'"); } // delete input from transaction @@ -494,10 +493,9 @@ static void MutateTxDelInput(CMutableTransaction& tx, const std::string& strInId static void MutateTxDelOutput(CMutableTransaction& tx, const std::string& strOutIdx) { // parse requested deletion index - int outIdx = atoi(strOutIdx); - if (outIdx < 0 || outIdx >= (int)tx.vout.size()) { - std::string strErr = "Invalid TX output index '" + strOutIdx + "'"; - throw std::runtime_error(strErr.c_str()); + int64_t outIdx; + if (!ParseInt64(strOutIdx, &outIdx) || outIdx < 0 || outIdx >= static_cast<int64_t>(tx.vout.size())) { + throw std::runtime_error("Invalid TX output index '" + strOutIdx + "'"); } // delete output from transaction @@ -593,7 +591,7 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr) uint256 txid = ParseHashStr(prevOut["txid"].get_str(), "txid"); - int nOut = atoi(prevOut["vout"].getValStr()); + const int nOut = prevOut["vout"].get_int(); if (nOut < 0) throw std::runtime_error("vout must be positive"); @@ -645,13 +643,11 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr) const CScript& prevPubKey = coin.out.scriptPubKey; const CAmount& amount = coin.out.nValue; - SignatureData sigdata; + SignatureData sigdata = DataFromTransaction(mergedTx, i, coin.out); // Only sign SIGHASH_SINGLE if there's a corresponding output: if (!fHashSingle || (i < mergedTx.vout.size())) ProduceSignature(keystore, MutableTransactionSignatureCreator(&mergedTx, i, amount, nHashType), prevPubKey, sigdata); - // ... and merge in other signatures: - sigdata = CombineSignatures(prevPubKey, MutableTransactionSignatureChecker(&mergedTx, i, amount), sigdata, DataFromTransaction(txv, i)); UpdateInput(txin, sigdata); } diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index 4b9abb2a1b..494a925a79 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2017 The Bitcoin Core developers +// Copyright (c) 2009-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. @@ -14,6 +14,7 @@ #include <rpc/server.h> #include <init.h> #include <noui.h> +#include <shutdown.h> #include <util.h> #include <httpserver.h> #include <httprpc.h> diff --git a/src/crypto/sha256.cpp b/src/crypto/sha256.cpp index 100d0d1fac..fbdbef0bc6 100644 --- a/src/crypto/sha256.cpp +++ b/src/crypto/sha256.cpp @@ -9,7 +9,7 @@ #include <string.h> #include <atomic> -#if defined(__x86_64__) || defined(__amd64__) +#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__) #if defined(USE_ASM) #include <cpuid.h> namespace sha256_sse4 @@ -29,6 +29,16 @@ namespace sha256d64_avx2 void Transform_8way(unsigned char* out, const unsigned char* in); } +namespace sha256d64_shani +{ +void Transform_2way(unsigned char* out, const unsigned char* in); +} + +namespace sha256_shani +{ +void Transform(uint32_t* s, const unsigned char* chunk, size_t blocks); +} + // Internal implementation code. namespace { @@ -448,6 +458,7 @@ void TransformD64Wrapper(unsigned char* out, const unsigned char* in) TransformType Transform = sha256::Transform; TransformD64Type TransformD64 = sha256::TransformD64; +TransformD64Type TransformD64_2way = nullptr; TransformD64Type TransformD64_4way = nullptr; TransformD64Type TransformD64_8way = nullptr; @@ -512,6 +523,13 @@ bool SelfTest() { TransformD64(out, data + 1); if (!std::equal(out, out + 32, result_d64)) return false; + // Test TransformD64_2way, if available. + if (TransformD64_2way) { + unsigned char out[64]; + TransformD64_2way(out, data + 1); + if (!std::equal(out, out + 64, result_d64)) return false; + } + // Test TransformD64_4way, if available. if (TransformD64_4way) { unsigned char out[128]; @@ -534,7 +552,19 @@ bool SelfTest() { // 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) { +#ifdef __GNUC__ + __cpuid_count(leaf, subleaf, a, b, c, d); +#else __asm__ ("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "0"(leaf), "2"(subleaf)); +#endif +} + +/** 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 @@ -544,27 +574,64 @@ std::string SHA256AutoDetect() { std::string ret = "standard"; #if defined(USE_ASM) && (defined(__x86_64__) || defined(__amd64__) || defined(__i386__)) + bool have_sse4 = false; + bool have_xsave = false; + bool have_avx = false; + bool have_avx2 = false; + bool have_shani = false; + bool enabled_avx = false; + + (void)AVXEnabled; + (void)have_sse4; + (void)have_avx; + (void)have_xsave; + (void)have_avx2; + (void)have_shani; + (void)enabled_avx; + uint32_t eax, ebx, ecx, edx; cpuid(1, 0, eax, ebx, ecx, edx); - if ((ecx >> 19) & 1) { + have_sse4 = (ecx >> 19) & 1; + have_xsave = (ecx >> 27) & 1; + have_avx = (ecx >> 28) & 1; + if (have_xsave && have_avx) { + enabled_avx = AVXEnabled(); + } + if (have_sse4) { + cpuid(7, 0, eax, ebx, ecx, edx); + have_avx2 = (ebx >> 5) & 1; + have_shani = (ebx >> 29) & 1; + } + +#if defined(ENABLE_SHANI) && !defined(BUILD_BITCOIN_INTERNAL) + if (have_shani) { + Transform = sha256_shani::Transform; + TransformD64 = TransformD64Wrapper<sha256_shani::Transform>; + TransformD64_2way = sha256d64_shani::Transform_2way; + ret = "shani(1way,2way)"; + have_sse4 = false; // Disable SSE4/AVX2; + have_avx2 = false; + } +#endif + + if (have_sse4) { #if defined(__x86_64__) || defined(__amd64__) Transform = sha256_sse4::Transform; TransformD64 = TransformD64Wrapper<sha256_sse4::Transform>; + ret = "sse4(1way)"; #endif #if defined(ENABLE_SSE41) && !defined(BUILD_BITCOIN_INTERNAL) 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)"; - } -#endif -#else - ret = "sse4"; + ret += ",sse41(4way)"; #endif } + +#if defined(ENABLE_AVX2) && !defined(BUILD_BITCOIN_INTERNAL) + if (have_avx2 && have_avx && enabled_avx) { + TransformD64_8way = sha256d64_avx2::Transform_8way; + ret += ",avx2(8way)"; + } +#endif #endif assert(SelfTest()); @@ -646,6 +713,14 @@ void SHA256D64(unsigned char* out, const unsigned char* in, size_t blocks) blocks -= 4; } } + if (TransformD64_2way) { + while (blocks >= 2) { + TransformD64_2way(out, in); + out += 64; + in += 128; + blocks -= 2; + } + } while (blocks) { TransformD64(out, in); out += 32; diff --git a/src/crypto/sha256_avx2.cpp b/src/crypto/sha256_avx2.cpp index b338b06927..068e0e5ff6 100644 --- a/src/crypto/sha256_avx2.cpp +++ b/src/crypto/sha256_avx2.cpp @@ -1,11 +1,7 @@ #ifdef ENABLE_AVX2 #include <stdint.h> -#if defined(_MSC_VER) #include <immintrin.h> -#elif defined(__GNUC__) -#include <x86intrin.h> -#endif #include <crypto/sha256.h> #include <crypto/common.h> diff --git a/src/crypto/sha256_shani.cpp b/src/crypto/sha256_shani.cpp new file mode 100644 index 0000000000..e561da42c5 --- /dev/null +++ b/src/crypto/sha256_shani.cpp @@ -0,0 +1,359 @@ +// 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. +// +// Based on https://github.com/noloader/SHA-Intrinsics/blob/master/sha256-x86.c, +// Written and placed in public domain by Jeffrey Walton. +// Based on code from Intel, and by Sean Gulley for the miTLS project. + +#ifdef ENABLE_SHANI + +#include <stdint.h> +#include <immintrin.h> + +#include <crypto/common.h> + + +namespace { + +const __m128i MASK = _mm_set_epi64x(0x0c0d0e0f08090a0bULL, 0x0405060700010203ULL); +const __m128i INIT0 = _mm_set_epi64x(0x6a09e667bb67ae85ull, 0x510e527f9b05688cull); +const __m128i INIT1 = _mm_set_epi64x(0x3c6ef372a54ff53aull, 0x1f83d9ab5be0cd19ull); + +void inline __attribute__((always_inline)) QuadRound(__m128i& state0, __m128i& state1, uint64_t k1, uint64_t k0) +{ + const __m128i msg = _mm_set_epi64x(k1, k0); + state1 = _mm_sha256rnds2_epu32(state1, state0, msg); + state0 = _mm_sha256rnds2_epu32(state0, state1, _mm_shuffle_epi32(msg, 0x0e)); +} + +void inline __attribute__((always_inline)) QuadRound(__m128i& state0, __m128i& state1, __m128i m, uint64_t k1, uint64_t k0) +{ + const __m128i msg = _mm_add_epi32(m, _mm_set_epi64x(k1, k0)); + state1 = _mm_sha256rnds2_epu32(state1, state0, msg); + state0 = _mm_sha256rnds2_epu32(state0, state1, _mm_shuffle_epi32(msg, 0x0e)); +} + +void inline __attribute__((always_inline)) ShiftMessageA(__m128i& m0, __m128i m1) +{ + m0 = _mm_sha256msg1_epu32(m0, m1); +} + +void inline __attribute__((always_inline)) ShiftMessageC(__m128i& m0, __m128i m1, __m128i& m2) +{ + m2 = _mm_sha256msg2_epu32(_mm_add_epi32(m2, _mm_alignr_epi8(m1, m0, 4)), m1); +} + +void inline __attribute__((always_inline)) ShiftMessageB(__m128i& m0, __m128i m1, __m128i& m2) +{ + ShiftMessageC(m0, m1, m2); + ShiftMessageA(m0, m1); +} + +void inline __attribute__((always_inline)) Shuffle(__m128i& s0, __m128i& s1) +{ + const __m128i t1 = _mm_shuffle_epi32(s0, 0xB1); + const __m128i t2 = _mm_shuffle_epi32(s1, 0x1B); + s0 = _mm_alignr_epi8(t1, t2, 0x08); + s1 = _mm_blend_epi16(t2, t1, 0xF0); +} + +void inline __attribute__((always_inline)) Unshuffle(__m128i& s0, __m128i& s1) +{ + const __m128i t1 = _mm_shuffle_epi32(s0, 0x1B); + const __m128i t2 = _mm_shuffle_epi32(s1, 0xB1); + s0 = _mm_blend_epi16(t1, t2, 0xF0); + s1 = _mm_alignr_epi8(t2, t1, 0x08); +} + +__m128i inline __attribute__((always_inline)) Load(const unsigned char* in) +{ + return _mm_shuffle_epi8(_mm_loadu_si128((const __m128i*)in), MASK); +} + +void inline __attribute__((always_inline)) Save(unsigned char* out, __m128i s) +{ + _mm_storeu_si128((__m128i*)out, _mm_shuffle_epi8(s, MASK)); +} +} + +namespace sha256_shani { +void Transform(uint32_t* s, const unsigned char* chunk, size_t blocks) +{ + __m128i m0, m1, m2, m3, s0, s1, so0, so1; + + /* Load state */ + s0 = _mm_loadu_si128((const __m128i*)s); + s1 = _mm_loadu_si128((const __m128i*)(s + 4)); + Shuffle(s0, s1); + + while (blocks--) { + /* Remember old state */ + so0 = s0; + so1 = s1; + + /* Load data and transform */ + m0 = Load(chunk); + QuadRound(s0, s1, m0, 0xe9b5dba5b5c0fbcfull, 0x71374491428a2f98ull); + m1 = Load(chunk + 16); + QuadRound(s0, s1, m1, 0xab1c5ed5923f82a4ull, 0x59f111f13956c25bull); + ShiftMessageA(m0, m1); + m2 = Load(chunk + 32); + QuadRound(s0, s1, m2, 0x550c7dc3243185beull, 0x12835b01d807aa98ull); + ShiftMessageA(m1, m2); + m3 = Load(chunk + 48); + QuadRound(s0, s1, m3, 0xc19bf1749bdc06a7ull, 0x80deb1fe72be5d74ull); + ShiftMessageB(m2, m3, m0); + QuadRound(s0, s1, m0, 0x240ca1cc0fc19dc6ull, 0xefbe4786E49b69c1ull); + ShiftMessageB(m3, m0, m1); + QuadRound(s0, s1, m1, 0x76f988da5cb0a9dcull, 0x4a7484aa2de92c6full); + ShiftMessageB(m0, m1, m2); + QuadRound(s0, s1, m2, 0xbf597fc7b00327c8ull, 0xa831c66d983e5152ull); + ShiftMessageB(m1, m2, m3); + QuadRound(s0, s1, m3, 0x1429296706ca6351ull, 0xd5a79147c6e00bf3ull); + ShiftMessageB(m2, m3, m0); + QuadRound(s0, s1, m0, 0x53380d134d2c6dfcull, 0x2e1b213827b70a85ull); + ShiftMessageB(m3, m0, m1); + QuadRound(s0, s1, m1, 0x92722c8581c2c92eull, 0x766a0abb650a7354ull); + ShiftMessageB(m0, m1, m2); + QuadRound(s0, s1, m2, 0xc76c51A3c24b8b70ull, 0xa81a664ba2bfe8a1ull); + ShiftMessageB(m1, m2, m3); + QuadRound(s0, s1, m3, 0x106aa070f40e3585ull, 0xd6990624d192e819ull); + ShiftMessageB(m2, m3, m0); + QuadRound(s0, s1, m0, 0x34b0bcb52748774cull, 0x1e376c0819a4c116ull); + ShiftMessageB(m3, m0, m1); + QuadRound(s0, s1, m1, 0x682e6ff35b9cca4full, 0x4ed8aa4a391c0cb3ull); + ShiftMessageC(m0, m1, m2); + QuadRound(s0, s1, m2, 0x8cc7020884c87814ull, 0x78a5636f748f82eeull); + ShiftMessageC(m1, m2, m3); + QuadRound(s0, s1, m3, 0xc67178f2bef9A3f7ull, 0xa4506ceb90befffaull); + + /* Combine with old state */ + s0 = _mm_add_epi32(s0, so0); + s1 = _mm_add_epi32(s1, so1); + + /* Advance */ + chunk += 64; + } + + Unshuffle(s0, s1); + _mm_storeu_si128((__m128i*)s, s0); + _mm_storeu_si128((__m128i*)(s + 4), s1); +} +} + +namespace sha256d64_shani { + +void Transform_2way(unsigned char* out, const unsigned char* in) +{ + __m128i am0, am1, am2, am3, as0, as1, aso0, aso1; + __m128i bm0, bm1, bm2, bm3, bs0, bs1, bso0, bso1; + + /* Transform 1 */ + bs0 = as0 = INIT0; + bs1 = as1 = INIT1; + am0 = Load(in); + bm0 = Load(in + 64); + QuadRound(as0, as1, am0, 0xe9b5dba5b5c0fbcfull, 0x71374491428a2f98ull); + QuadRound(bs0, bs1, bm0, 0xe9b5dba5b5c0fbcfull, 0x71374491428a2f98ull); + am1 = Load(in + 16); + bm1 = Load(in + 80); + QuadRound(as0, as1, am1, 0xab1c5ed5923f82a4ull, 0x59f111f13956c25bull); + QuadRound(bs0, bs1, bm1, 0xab1c5ed5923f82a4ull, 0x59f111f13956c25bull); + ShiftMessageA(am0, am1); + ShiftMessageA(bm0, bm1); + am2 = Load(in + 32); + bm2 = Load(in + 96); + QuadRound(as0, as1, am2, 0x550c7dc3243185beull, 0x12835b01d807aa98ull); + QuadRound(bs0, bs1, bm2, 0x550c7dc3243185beull, 0x12835b01d807aa98ull); + ShiftMessageA(am1, am2); + ShiftMessageA(bm1, bm2); + am3 = Load(in + 48); + bm3 = Load(in + 112); + QuadRound(as0, as1, am3, 0xc19bf1749bdc06a7ull, 0x80deb1fe72be5d74ull); + QuadRound(bs0, bs1, bm3, 0xc19bf1749bdc06a7ull, 0x80deb1fe72be5d74ull); + ShiftMessageB(am2, am3, am0); + ShiftMessageB(bm2, bm3, bm0); + QuadRound(as0, as1, am0, 0x240ca1cc0fc19dc6ull, 0xefbe4786E49b69c1ull); + QuadRound(bs0, bs1, bm0, 0x240ca1cc0fc19dc6ull, 0xefbe4786E49b69c1ull); + ShiftMessageB(am3, am0, am1); + ShiftMessageB(bm3, bm0, bm1); + QuadRound(as0, as1, am1, 0x76f988da5cb0a9dcull, 0x4a7484aa2de92c6full); + QuadRound(bs0, bs1, bm1, 0x76f988da5cb0a9dcull, 0x4a7484aa2de92c6full); + ShiftMessageB(am0, am1, am2); + ShiftMessageB(bm0, bm1, bm2); + QuadRound(as0, as1, am2, 0xbf597fc7b00327c8ull, 0xa831c66d983e5152ull); + QuadRound(bs0, bs1, bm2, 0xbf597fc7b00327c8ull, 0xa831c66d983e5152ull); + ShiftMessageB(am1, am2, am3); + ShiftMessageB(bm1, bm2, bm3); + QuadRound(as0, as1, am3, 0x1429296706ca6351ull, 0xd5a79147c6e00bf3ull); + QuadRound(bs0, bs1, bm3, 0x1429296706ca6351ull, 0xd5a79147c6e00bf3ull); + ShiftMessageB(am2, am3, am0); + ShiftMessageB(bm2, bm3, bm0); + QuadRound(as0, as1, am0, 0x53380d134d2c6dfcull, 0x2e1b213827b70a85ull); + QuadRound(bs0, bs1, bm0, 0x53380d134d2c6dfcull, 0x2e1b213827b70a85ull); + ShiftMessageB(am3, am0, am1); + ShiftMessageB(bm3, bm0, bm1); + QuadRound(as0, as1, am1, 0x92722c8581c2c92eull, 0x766a0abb650a7354ull); + QuadRound(bs0, bs1, bm1, 0x92722c8581c2c92eull, 0x766a0abb650a7354ull); + ShiftMessageB(am0, am1, am2); + ShiftMessageB(bm0, bm1, bm2); + QuadRound(as0, as1, am2, 0xc76c51A3c24b8b70ull, 0xa81a664ba2bfe8a1ull); + QuadRound(bs0, bs1, bm2, 0xc76c51A3c24b8b70ull, 0xa81a664ba2bfe8a1ull); + ShiftMessageB(am1, am2, am3); + ShiftMessageB(bm1, bm2, bm3); + QuadRound(as0, as1, am3, 0x106aa070f40e3585ull, 0xd6990624d192e819ull); + QuadRound(bs0, bs1, bm3, 0x106aa070f40e3585ull, 0xd6990624d192e819ull); + ShiftMessageB(am2, am3, am0); + ShiftMessageB(bm2, bm3, bm0); + QuadRound(as0, as1, am0, 0x34b0bcb52748774cull, 0x1e376c0819a4c116ull); + QuadRound(bs0, bs1, bm0, 0x34b0bcb52748774cull, 0x1e376c0819a4c116ull); + ShiftMessageB(am3, am0, am1); + ShiftMessageB(bm3, bm0, bm1); + QuadRound(as0, as1, am1, 0x682e6ff35b9cca4full, 0x4ed8aa4a391c0cb3ull); + QuadRound(bs0, bs1, bm1, 0x682e6ff35b9cca4full, 0x4ed8aa4a391c0cb3ull); + ShiftMessageC(am0, am1, am2); + ShiftMessageC(bm0, bm1, bm2); + QuadRound(as0, as1, am2, 0x8cc7020884c87814ull, 0x78a5636f748f82eeull); + QuadRound(bs0, bs1, bm2, 0x8cc7020884c87814ull, 0x78a5636f748f82eeull); + ShiftMessageC(am1, am2, am3); + ShiftMessageC(bm1, bm2, bm3); + QuadRound(as0, as1, am3, 0xc67178f2bef9A3f7ull, 0xa4506ceb90befffaull); + QuadRound(bs0, bs1, bm3, 0xc67178f2bef9A3f7ull, 0xa4506ceb90befffaull); + as0 = _mm_add_epi32(as0, INIT0); + bs0 = _mm_add_epi32(bs0, INIT0); + as1 = _mm_add_epi32(as1, INIT1); + bs1 = _mm_add_epi32(bs1, INIT1); + + /* Transform 2 */ + aso0 = as0; + bso0 = bs0; + aso1 = as1; + bso1 = bs1; + QuadRound(as0, as1, 0xe9b5dba5b5c0fbcfull, 0x71374491c28a2f98ull); + QuadRound(bs0, bs1, 0xe9b5dba5b5c0fbcfull, 0x71374491c28a2f98ull); + QuadRound(as0, as1, 0xab1c5ed5923f82a4ull, 0x59f111f13956c25bull); + QuadRound(bs0, bs1, 0xab1c5ed5923f82a4ull, 0x59f111f13956c25bull); + QuadRound(as0, as1, 0x550c7dc3243185beull, 0x12835b01d807aa98ull); + QuadRound(bs0, bs1, 0x550c7dc3243185beull, 0x12835b01d807aa98ull); + QuadRound(as0, as1, 0xc19bf3749bdc06a7ull, 0x80deb1fe72be5d74ull); + QuadRound(bs0, bs1, 0xc19bf3749bdc06a7ull, 0x80deb1fe72be5d74ull); + QuadRound(as0, as1, 0x240cf2540fe1edc6ull, 0xf0fe4786649b69c1ull); + QuadRound(bs0, bs1, 0x240cf2540fe1edc6ull, 0xf0fe4786649b69c1ull); + QuadRound(as0, as1, 0x16f988fa61b9411eull, 0x6cc984be4fe9346full); + QuadRound(bs0, bs1, 0x16f988fa61b9411eull, 0x6cc984be4fe9346full); + QuadRound(as0, as1, 0xb9d99ec7b019fc65ull, 0xa88e5a6df2c65152ull); + QuadRound(bs0, bs1, 0xb9d99ec7b019fc65ull, 0xa88e5a6df2c65152ull); + QuadRound(as0, as1, 0xc7353eb0fdb1232bull, 0xe70eeaa09a1231c3ull); + QuadRound(bs0, bs1, 0xc7353eb0fdb1232bull, 0xe70eeaa09a1231c3ull); + QuadRound(as0, as1, 0xdc1eeefd5a0f118full, 0xcb976d5f3069bad5ull); + QuadRound(bs0, bs1, 0xdc1eeefd5a0f118full, 0xcb976d5f3069bad5ull); + QuadRound(as0, as1, 0xe15d5b1658f4ca9dull, 0xde0b7a040a35b689ull); + QuadRound(bs0, bs1, 0xe15d5b1658f4ca9dull, 0xde0b7a040a35b689ull); + QuadRound(as0, as1, 0x6fab9537a507ea32ull, 0x37088980007f3e86ull); + QuadRound(bs0, bs1, 0x6fab9537a507ea32ull, 0x37088980007f3e86ull); + QuadRound(as0, as1, 0xc0bbbe37cdaa3b6dull, 0x0d8cd6f117406110ull); + QuadRound(bs0, bs1, 0xc0bbbe37cdaa3b6dull, 0x0d8cd6f117406110ull); + QuadRound(as0, as1, 0x6fd15ca70b02e931ull, 0xdb48a36383613bdaull); + QuadRound(bs0, bs1, 0x6fd15ca70b02e931ull, 0xdb48a36383613bdaull); + QuadRound(as0, as1, 0x6d4378906ed41a95ull, 0x31338431521afacaull); + QuadRound(bs0, bs1, 0x6d4378906ed41a95ull, 0x31338431521afacaull); + QuadRound(as0, as1, 0x532fb63cb5c9a0e6ull, 0x9eccabbdc39c91f2ull); + QuadRound(bs0, bs1, 0x532fb63cb5c9a0e6ull, 0x9eccabbdc39c91f2ull); + QuadRound(as0, as1, 0x4c191d76a4954b68ull, 0x07237ea3d2c741c6ull); + QuadRound(bs0, bs1, 0x4c191d76a4954b68ull, 0x07237ea3d2c741c6ull); + as0 = _mm_add_epi32(as0, aso0); + bs0 = _mm_add_epi32(bs0, bso0); + as1 = _mm_add_epi32(as1, aso1); + bs1 = _mm_add_epi32(bs1, bso1); + + /* Extract hash */ + Unshuffle(as0, as1); + Unshuffle(bs0, bs1); + am0 = as0; + bm0 = bs0; + am1 = as1; + bm1 = bs1; + + /* Transform 3 */ + bs0 = as0 = INIT0; + bs1 = as1 = INIT1; + QuadRound(as0, as1, am0, 0xe9b5dba5B5c0fbcfull, 0x71374491428a2f98ull); + QuadRound(bs0, bs1, bm0, 0xe9b5dba5B5c0fbcfull, 0x71374491428a2f98ull); + QuadRound(as0, as1, am1, 0xab1c5ed5923f82a4ull, 0x59f111f13956c25bull); + QuadRound(bs0, bs1, bm1, 0xab1c5ed5923f82a4ull, 0x59f111f13956c25bull); + ShiftMessageA(am0, am1); + ShiftMessageA(bm0, bm1); + bm2 = am2 = _mm_set_epi64x(0x0ull, 0x80000000ull); + QuadRound(as0, as1, 0x550c7dc3243185beull, 0x12835b015807aa98ull); + QuadRound(bs0, bs1, 0x550c7dc3243185beull, 0x12835b015807aa98ull); + ShiftMessageA(am1, am2); + ShiftMessageA(bm1, bm2); + bm3 = am3 = _mm_set_epi64x(0x10000000000ull, 0x0ull); + QuadRound(as0, as1, 0xc19bf2749bdc06a7ull, 0x80deb1fe72be5d74ull); + QuadRound(bs0, bs1, 0xc19bf2749bdc06a7ull, 0x80deb1fe72be5d74ull); + ShiftMessageB(am2, am3, am0); + ShiftMessageB(bm2, bm3, bm0); + QuadRound(as0, as1, am0, 0x240ca1cc0fc19dc6ull, 0xefbe4786e49b69c1ull); + QuadRound(bs0, bs1, bm0, 0x240ca1cc0fc19dc6ull, 0xefbe4786e49b69c1ull); + ShiftMessageB(am3, am0, am1); + ShiftMessageB(bm3, bm0, bm1); + QuadRound(as0, as1, am1, 0x76f988da5cb0a9dcull, 0x4a7484aa2de92c6full); + QuadRound(bs0, bs1, bm1, 0x76f988da5cb0a9dcull, 0x4a7484aa2de92c6full); + ShiftMessageB(am0, am1, am2); + ShiftMessageB(bm0, bm1, bm2); + QuadRound(as0, as1, am2, 0xbf597fc7b00327c8ull, 0xa831c66d983e5152ull); + QuadRound(bs0, bs1, bm2, 0xbf597fc7b00327c8ull, 0xa831c66d983e5152ull); + ShiftMessageB(am1, am2, am3); + ShiftMessageB(bm1, bm2, bm3); + QuadRound(as0, as1, am3, 0x1429296706ca6351ull, 0xd5a79147c6e00bf3ull); + QuadRound(bs0, bs1, bm3, 0x1429296706ca6351ull, 0xd5a79147c6e00bf3ull); + ShiftMessageB(am2, am3, am0); + ShiftMessageB(bm2, bm3, bm0); + QuadRound(as0, as1, am0, 0x53380d134d2c6dfcull, 0x2e1b213827b70a85ull); + QuadRound(bs0, bs1, bm0, 0x53380d134d2c6dfcull, 0x2e1b213827b70a85ull); + ShiftMessageB(am3, am0, am1); + ShiftMessageB(bm3, bm0, bm1); + QuadRound(as0, as1, am1, 0x92722c8581c2c92eull, 0x766a0abb650a7354ull); + QuadRound(bs0, bs1, bm1, 0x92722c8581c2c92eull, 0x766a0abb650a7354ull); + ShiftMessageB(am0, am1, am2); + ShiftMessageB(bm0, bm1, bm2); + QuadRound(as0, as1, am2, 0xc76c51a3c24b8b70ull, 0xa81a664ba2bfe8A1ull); + QuadRound(bs0, bs1, bm2, 0xc76c51a3c24b8b70ull, 0xa81a664ba2bfe8A1ull); + ShiftMessageB(am1, am2, am3); + ShiftMessageB(bm1, bm2, bm3); + QuadRound(as0, as1, am3, 0x106aa070f40e3585ull, 0xd6990624d192e819ull); + QuadRound(bs0, bs1, bm3, 0x106aa070f40e3585ull, 0xd6990624d192e819ull); + ShiftMessageB(am2, am3, am0); + ShiftMessageB(bm2, bm3, bm0); + QuadRound(as0, as1, am0, 0x34b0bcb52748774cull, 0x1e376c0819a4c116ull); + QuadRound(bs0, bs1, bm0, 0x34b0bcb52748774cull, 0x1e376c0819a4c116ull); + ShiftMessageB(am3, am0, am1); + ShiftMessageB(bm3, bm0, bm1); + QuadRound(as0, as1, am1, 0x682e6ff35b9cca4full, 0x4ed8aa4a391c0cb3ull); + QuadRound(bs0, bs1, bm1, 0x682e6ff35b9cca4full, 0x4ed8aa4a391c0cb3ull); + ShiftMessageC(am0, am1, am2); + ShiftMessageC(bm0, bm1, bm2); + QuadRound(as0, as1, am2, 0x8cc7020884c87814ull, 0x78a5636f748f82eeull); + QuadRound(bs0, bs1, bm2, 0x8cc7020884c87814ull, 0x78a5636f748f82eeull); + ShiftMessageC(am1, am2, am3); + ShiftMessageC(bm1, bm2, bm3); + QuadRound(as0, as1, am3, 0xc67178f2bef9a3f7ull, 0xa4506ceb90befffaull); + QuadRound(bs0, bs1, bm3, 0xc67178f2bef9a3f7ull, 0xa4506ceb90befffaull); + as0 = _mm_add_epi32(as0, INIT0); + bs0 = _mm_add_epi32(bs0, INIT0); + as1 = _mm_add_epi32(as1, INIT1); + bs1 = _mm_add_epi32(bs1, INIT1); + + /* Extract hash into out */ + Unshuffle(as0, as1); + Unshuffle(bs0, bs1); + Save(out, as0); + Save(out + 16, as1); + Save(out + 32, bs0); + Save(out + 48, bs1); +} + +} + +#endif diff --git a/src/crypto/sha256_sse41.cpp b/src/crypto/sha256_sse41.cpp index be71dd8fb8..adca870e2d 100644 --- a/src/crypto/sha256_sse41.cpp +++ b/src/crypto/sha256_sse41.cpp @@ -1,11 +1,7 @@ #ifdef ENABLE_SSE41 #include <stdint.h> -#if defined(_MSC_VER) #include <immintrin.h> -#elif defined(__GNUC__) -#include <x86intrin.h> -#endif #include <crypto/sha256.h> #include <crypto/common.h> diff --git a/src/index/base.cpp b/src/index/base.cpp index 738166dc94..788f7adccd 100644 --- a/src/index/base.cpp +++ b/src/index/base.cpp @@ -4,7 +4,7 @@ #include <chainparams.h> #include <index/base.h> -#include <init.h> +#include <shutdown.h> #include <tinyformat.h> #include <ui_interface.h> #include <util.h> diff --git a/src/index/txindex.cpp b/src/index/txindex.cpp index e106b9b420..c85030e18e 100644 --- a/src/index/txindex.cpp +++ b/src/index/txindex.cpp @@ -3,7 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <index/txindex.h> -#include <init.h> +#include <shutdown.h> #include <ui_interface.h> #include <util.h> #include <validation.h> diff --git a/src/init.cpp b/src/init.cpp index 1b5507703c..66b0b65eb4 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2017 The Bitcoin Core developers +// Copyright (c) 2009-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. @@ -35,6 +35,7 @@ #include <script/standard.h> #include <script/sigcache.h> #include <scheduler.h> +#include <shutdown.h> #include <timedata.h> #include <txdb.h> #include <txmempool.h> @@ -62,6 +63,7 @@ #if ENABLE_ZMQ #include <zmq/zmqnotificationinterface.h> +#include <zmq/zmqrpc.h> #endif bool fFeeEstimatesInitialized = false; @@ -99,10 +101,6 @@ void DummyWalletInit::AddWalletOptions() const const WalletInitInterface& g_wallet_init_interface = DummyWalletInit(); #endif -#if ENABLE_ZMQ -static CZMQNotificationInterface* pzmqNotificationInterface = nullptr; -#endif - #ifdef WIN32 // Win32 LevelDB doesn't use filedescriptors, and the ones used for // accessing block files don't count towards the fd_set size limit @@ -126,7 +124,7 @@ static const char* FEE_ESTIMATES_FILENAME="fee_estimates.dat"; // created by AppInit() or the Qt main() function. // // A clean exit happens when StartShutdown() or the SIGTERM -// signal handler sets fRequestShutdown, which makes main thread's +// signal handler sets ShutdownRequested(), which makes main thread's // WaitForShutdown() interrupts the thread group. // And then, WaitForShutdown() makes all other on-going threads // in the thread group join the main thread. @@ -135,21 +133,10 @@ static const char* FEE_ESTIMATES_FILENAME="fee_estimates.dat"; // threads have exited. // // Shutdown for Qt is very similar, only it uses a QTimer to detect -// fRequestShutdown getting set, and then does the normal Qt +// ShutdownRequested() getting set, and then does the normal Qt // shutdown thing. // -std::atomic<bool> fRequestShutdown(false); - -void StartShutdown() -{ - fRequestShutdown = true; -} -bool ShutdownRequested() -{ - return fRequestShutdown; -} - /** * This is a minimally invasive approach to shutdown on LevelDB read errors from the * chainstate, while keeping user interface out of the common library, which is shared @@ -279,10 +266,10 @@ void Shutdown() g_wallet_init_interface.Stop(); #if ENABLE_ZMQ - if (pzmqNotificationInterface) { - UnregisterValidationInterface(pzmqNotificationInterface); - delete pzmqNotificationInterface; - pzmqNotificationInterface = nullptr; + if (g_zmq_notification_interface) { + UnregisterValidationInterface(g_zmq_notification_interface); + delete g_zmq_notification_interface; + g_zmq_notification_interface = nullptr; } #endif @@ -310,7 +297,7 @@ void Shutdown() #ifndef WIN32 static void HandleSIGTERM(int) { - fRequestShutdown = true; + StartShutdown(); } static void HandleSIGHUP(int) @@ -320,7 +307,7 @@ static void HandleSIGHUP(int) #else static BOOL WINAPI consoleCtrlHandler(DWORD dwCtrlType) { - fRequestShutdown = true; + StartShutdown(); Sleep(INFINITE); return true; } @@ -377,7 +364,7 @@ void SetupServerArgs() gArgs.AddArg("-datadir=<dir>", "Specify data directory", false, OptionsCategory::OPTIONS); gArgs.AddArg("-dbbatchsize", strprintf("Maximum database write batch size in bytes (default: %u)", nDefaultDbBatchSize), true, OptionsCategory::OPTIONS); gArgs.AddArg("-dbcache=<n>", strprintf("Set database cache size in megabytes (%d to %d, default: %d)", nMinDbCache, nMaxDbCache, nDefaultDbCache), false, OptionsCategory::OPTIONS); - gArgs.AddArg("-debuglogfile=<file>", strprintf("Specify location of debug log file. Relative paths will be prefixed by a net-specific datadir location. (default: %s)", DEFAULT_DEBUGLOGFILE), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-debuglogfile=<file>", strprintf("Specify location of debug log file. Relative paths will be prefixed by a net-specific datadir location. (0 to disable; default: %s)", DEFAULT_DEBUGLOGFILE), false, OptionsCategory::OPTIONS); gArgs.AddArg("-feefilter", strprintf("Tell other nodes to filter invs to us by our mempool min fee (default: %u)", DEFAULT_FEEFILTER), true, OptionsCategory::OPTIONS); gArgs.AddArg("-includeconf=<file>", "Specify additional configuration file, relative to the -datadir path (only useable from configuration file, not command line)", false, OptionsCategory::OPTIONS); gArgs.AddArg("-loadblock=<file>", "Imports blocks from external blk000??.dat file on startup", false, OptionsCategory::OPTIONS); @@ -405,11 +392,11 @@ void SetupServerArgs() #endif gArgs.AddArg("-txindex", strprintf("Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u)", DEFAULT_TXINDEX), false, OptionsCategory::OPTIONS); - gArgs.AddArg("-addnode=<ip>", "Add a node to connect to and attempt to keep the connection open (see the `addnode` RPC command help for more info)", false, OptionsCategory::CONNECTION); + gArgs.AddArg("-addnode=<ip>", "Add a node to connect to and attempt to keep the connection open (see the `addnode` RPC command help for more info). This option can be specified multiple times to add multiple nodes.", false, OptionsCategory::CONNECTION); gArgs.AddArg("-banscore=<n>", strprintf("Threshold for disconnecting misbehaving peers (default: %u)", DEFAULT_BANSCORE_THRESHOLD), false, OptionsCategory::CONNECTION); gArgs.AddArg("-bantime=<n>", strprintf("Number of seconds to keep misbehaving peers from reconnecting (default: %u)", DEFAULT_MISBEHAVING_BANTIME), false, OptionsCategory::CONNECTION); gArgs.AddArg("-bind=<addr>", "Bind to given address and always listen on it. Use [host]:port notation for IPv6", false, OptionsCategory::CONNECTION); - gArgs.AddArg("-connect=<ip>", "Connect only to the specified node(s); -connect=0 disables automatic connections (the rules for this peer are the same as for -addnode)", false, OptionsCategory::CONNECTION); + gArgs.AddArg("-connect=<ip>", "Connect only to the specified node; -connect=0 disables automatic connections (the rules for this peer are the same as for -addnode). This option can be specified multiple times to connect to multiple nodes.", false, OptionsCategory::CONNECTION); gArgs.AddArg("-discover", "Discover own IP addresses (default: 1 when listening and no -externalip or -proxy)", false, OptionsCategory::CONNECTION); gArgs.AddArg("-dns", strprintf("Allow DNS lookups for -addnode, -seednode and -connect (default: %u)", DEFAULT_NAME_LOOKUP), false, OptionsCategory::CONNECTION); gArgs.AddArg("-dnsseed", "Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect used)", false, OptionsCategory::CONNECTION); @@ -424,13 +411,13 @@ void SetupServerArgs() gArgs.AddArg("-maxtimeadjustment", strprintf("Maximum allowed median peer time offset adjustment. Local perspective of time may be influenced by peers forward or backward by this amount. (default: %u seconds)", DEFAULT_MAX_TIME_ADJUSTMENT), false, OptionsCategory::CONNECTION); gArgs.AddArg("-maxuploadtarget=<n>", strprintf("Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = no limit (default: %d)", DEFAULT_MAX_UPLOAD_TARGET), false, OptionsCategory::CONNECTION); gArgs.AddArg("-onion=<ip:port>", "Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: -proxy)", false, OptionsCategory::CONNECTION); - gArgs.AddArg("-onlynet=<net>", "Only connect to nodes in network <net> (ipv4, ipv6 or onion)", false, OptionsCategory::CONNECTION); + gArgs.AddArg("-onlynet=<net>", "Make outgoing connections only through network <net> (ipv4, ipv6 or onion). Incoming connections are not affected by this option. This option can be specified multiple times to allow multiple networks.", false, OptionsCategory::CONNECTION); gArgs.AddArg("-peerbloomfilters", strprintf("Support filtering of blocks and transaction with bloom filters (default: %u)", DEFAULT_PEERBLOOMFILTERS), false, OptionsCategory::CONNECTION); gArgs.AddArg("-permitbaremultisig", strprintf("Relay non-P2SH multisig (default: %u)", DEFAULT_PERMIT_BAREMULTISIG), false, OptionsCategory::CONNECTION); gArgs.AddArg("-port=<port>", strprintf("Listen for connections on <port> (default: %u or testnet: %u)", defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort()), false, OptionsCategory::CONNECTION); gArgs.AddArg("-proxy=<ip:port>", "Connect through SOCKS5 proxy", false, OptionsCategory::CONNECTION); gArgs.AddArg("-proxyrandomize", strprintf("Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)", DEFAULT_PROXYRANDOMIZE), false, OptionsCategory::CONNECTION); - gArgs.AddArg("-seednode=<ip>", "Connect to a node to retrieve peer addresses, and disconnect", false, OptionsCategory::CONNECTION); + gArgs.AddArg("-seednode=<ip>", "Connect to a node to retrieve peer addresses, and disconnect. This option can be specified multiple times to connect to multiple nodes.", false, OptionsCategory::CONNECTION); gArgs.AddArg("-timeout=<n>", strprintf("Specify connection timeout in milliseconds (minimum: 1, default: %d)", DEFAULT_CONNECT_TIMEOUT), false, OptionsCategory::CONNECTION); gArgs.AddArg("-torcontrol=<ip>:<port>", strprintf("Tor control port to use if onion listening enabled (default: %s)", DEFAULT_TOR_CONTROL), false, OptionsCategory::CONNECTION); gArgs.AddArg("-torpassword=<pass>", "Tor control port password (default: empty)", false, OptionsCategory::CONNECTION); @@ -489,7 +476,7 @@ void SetupServerArgs() gArgs.AddArg("-maxtxfee=<amt>", strprintf("Maximum total fees (in %s) to use in a single wallet transaction or raw transaction; setting this too low may abort large transactions (default: %s)", CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MAXFEE)), false, OptionsCategory::DEBUG_TEST); gArgs.AddArg("-printpriority", strprintf("Log transaction fee per kB when mining blocks (default: %u)", DEFAULT_PRINTPRIORITY), true, OptionsCategory::DEBUG_TEST); - gArgs.AddArg("-printtoconsole", "Send trace/debug info to console instead of debug.log file", false, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-printtoconsole", "Send trace/debug info to console (default: 1 when no -daemon. To disable logging to file, set debuglogfile=0)", false, OptionsCategory::DEBUG_TEST); gArgs.AddArg("-shrinkdebugfile", "Shrink debug.log file on client startup (default: 1 when no -debug)", false, OptionsCategory::DEBUG_TEST); gArgs.AddArg("-uacomment=<cmt>", "Append comment to the user agent string", false, OptionsCategory::DEBUG_TEST); @@ -713,7 +700,7 @@ static void ThreadImport(std::vector<fs::path> vImportFiles) if (gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) { LoadMempool(); } - g_is_mempool_loaded = !fRequestShutdown; + g_is_mempool_loaded = !ShutdownRequested(); } /** Sanity checks @@ -961,7 +948,8 @@ bool AppInitParameterInteraction() nMaxConnections = std::max(nUserMaxConnections, 0); // Trim requested connection counts, to fit into system limitations - nMaxConnections = std::max(std::min(nMaxConnections, FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS), 0); + // <int> in std::min<int>(...) to work around FreeBSD compilation issue described in #2695 + nMaxConnections = std::max(std::min<int>(nMaxConnections, FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS), 0); nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS + MAX_ADDNODE_CONNECTIONS); if (nFD < MIN_CORE_FILEDESCRIPTORS) return InitError(_("Not enough file descriptors available.")); @@ -1291,6 +1279,9 @@ bool AppInitMain() */ RegisterAllCoreRPCCommands(tableRPC); g_wallet_init_interface.RegisterRPC(tableRPC); +#if ENABLE_ZMQ + RegisterZMQRPCCommands(tableRPC); +#endif /* Start the RPC server already. It will be started in "warmup" mode * and not really process calls already (but it will signify connections @@ -1304,8 +1295,6 @@ bool AppInitMain() return InitError(_("Unable to start HTTP server. See debug log for details.")); } - int64_t nStart; - // ********************************************************* Step 5: verify wallet database integrity if (!g_wallet_init_interface.Verify()) return false; @@ -1357,7 +1346,7 @@ bool AppInitMain() // -proxy sets a proxy for all outgoing network traffic // -noproxy (or -proxy=0) as well as the empty string can be used to not set a proxy, this is the default std::string proxyArg = gArgs.GetArg("-proxy", ""); - SetLimited(NET_TOR); + SetLimited(NET_ONION); if (proxyArg != "" && proxyArg != "0") { CService proxyAddr; if (!Lookup(proxyArg.c_str(), proxyAddr, 9050, fNameLookup)) { @@ -1370,9 +1359,9 @@ bool AppInitMain() SetProxy(NET_IPV4, addrProxy); SetProxy(NET_IPV6, addrProxy); - SetProxy(NET_TOR, addrProxy); + SetProxy(NET_ONION, addrProxy); SetNameProxy(addrProxy); - SetLimited(NET_TOR, false); // by default, -proxy sets onion as reachable, unless -noonion later + SetLimited(NET_ONION, false); // by default, -proxy sets onion as reachable, unless -noonion later } // -onion can be used to set only a proxy for .onion, or override normal proxy for .onion addresses @@ -1381,7 +1370,7 @@ bool AppInitMain() std::string onionArg = gArgs.GetArg("-onion", ""); if (onionArg != "") { if (onionArg == "0") { // Handle -noonion/-onion=0 - SetLimited(NET_TOR); // set onions as unreachable + SetLimited(NET_ONION); // set onions as unreachable } else { CService onionProxy; if (!Lookup(onionArg.c_str(), onionProxy, 9050, fNameLookup)) { @@ -1390,8 +1379,8 @@ bool AppInitMain() proxyType addrOnion = proxyType(onionProxy, proxyRandomize); if (!addrOnion.IsValid()) return InitError(strprintf(_("Invalid -onion address or hostname: '%s'"), onionArg)); - SetProxy(NET_TOR, addrOnion); - SetLimited(NET_TOR, false); + SetProxy(NET_ONION, addrOnion); + SetLimited(NET_ONION, false); } } @@ -1409,10 +1398,10 @@ bool AppInitMain() } #if ENABLE_ZMQ - pzmqNotificationInterface = CZMQNotificationInterface::Create(); + g_zmq_notification_interface = CZMQNotificationInterface::Create(); - if (pzmqNotificationInterface) { - RegisterValidationInterface(pzmqNotificationInterface); + if (g_zmq_notification_interface) { + RegisterValidationInterface(g_zmq_notification_interface); } #endif uint64_t nMaxOutboundLimit = 0; //unlimited unless -maxuploadtarget is set @@ -1449,7 +1438,7 @@ bool AppInitMain() LogPrintf("* Using %.1fMiB for in-memory UTXO set (plus up to %.1fMiB of unused mempool space)\n", nCoinCacheUsage * (1.0 / 1024 / 1024), nMempoolSizeMax * (1.0 / 1024 / 1024)); bool fLoaded = false; - while (!fLoaded && !fRequestShutdown) { + while (!fLoaded && !ShutdownRequested()) { bool fReset = fReindex; std::string strLoadError; @@ -1457,8 +1446,8 @@ bool AppInitMain() LOCK(cs_main); - nStart = GetTimeMillis(); do { + const int64_t load_block_index_start_time = GetTimeMillis(); try { UnloadBlockIndex(); pcoinsTip.reset(); @@ -1476,7 +1465,7 @@ bool AppInitMain() CleanupBlockRevFiles(); } - if (fRequestShutdown) break; + if (ShutdownRequested()) break; // LoadBlockIndex will load fHavePruned if we've ever removed a // block file from disk. @@ -1581,9 +1570,10 @@ bool AppInitMain() } fLoaded = true; + LogPrintf(" block index %15dms\n", GetTimeMillis() - load_block_index_start_time); } while(false); - if (!fLoaded && !fRequestShutdown) { + if (!fLoaded && !ShutdownRequested()) { // first suggest a reindex if (!fReset) { bool fRet = uiInterface.ThreadSafeQuestion( @@ -1592,7 +1582,7 @@ bool AppInitMain() "", CClientUIInterface::MSG_ERROR | CClientUIInterface::BTN_ABORT); if (fRet) { fReindex = true; - fRequestShutdown = false; + AbortShutdown(); } else { LogPrintf("Aborted block database rebuild. Exiting.\n"); return false; @@ -1606,14 +1596,10 @@ bool AppInitMain() // As LoadBlockIndex can take several minutes, it's possible the user // requested to kill the GUI during the last operation. If so, exit. // As the program has not fully started yet, Shutdown() is possibly overkill. - if (fRequestShutdown) - { + if (ShutdownRequested()) { LogPrintf("Shutdown requested. Exiting.\n"); return false; } - if (fLoaded) { - LogPrintf(" block index %15dms\n", GetTimeMillis() - nStart); - } fs::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME; CAutoFile est_filein(fsbridge::fopen(est_path, "rb"), SER_DISK, CLIENT_VERSION); diff --git a/src/init.h b/src/init.h index 5423a042a6..0c85d3c9dc 100644 --- a/src/init.h +++ b/src/init.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2017 The Bitcoin Core developers +// Copyright (c) 2009-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. @@ -21,8 +21,6 @@ namespace boost class thread_group; } // namespace boost -void StartShutdown(); -bool ShutdownRequested(); /** Interrupt threads */ void Interrupt(); void Shutdown(); diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp index 4189ff7497..db371d104e 100644 --- a/src/interfaces/node.cpp +++ b/src/interfaces/node.cpp @@ -21,6 +21,7 @@ #include <primitives/block.h> #include <rpc/server.h> #include <scheduler.h> +#include <shutdown.h> #include <sync.h> #include <txmempool.h> #include <ui_interface.h> 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/merkleblock.h b/src/merkleblock.h index 0976e21c3a..984e33a961 100644 --- a/src/merkleblock.h +++ b/src/merkleblock.h @@ -115,6 +115,12 @@ public: * returns the merkle root, or 0 in case of failure */ uint256 ExtractMatches(std::vector<uint256> &vMatch, std::vector<unsigned int> &vnIndex); + + /** Get number of transactions the merkle proof is indicating for cross-reference with + * local blockchain knowledge. + */ + unsigned int GetNumTransactions() const { return nTransactions; }; + }; diff --git a/src/miner.cpp b/src/miner.cpp index d4527a1d67..738ccad1b9 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -209,7 +209,7 @@ bool BlockAssembler::TestPackage(uint64_t packageSize, int64_t packageSigOpsCost // segwit activation) bool BlockAssembler::TestPackageTransactions(const CTxMemPool::setEntries& package) { - for (const CTxMemPool::txiter it : package) { + for (CTxMemPool::txiter it : package) { if (!IsFinalTx(it->GetTx(), nHeight, nLockTimeCutoff)) return false; if (!fIncludeWitness && it->GetTx().HasWitness()) @@ -241,7 +241,7 @@ int BlockAssembler::UpdatePackagesForAdded(const CTxMemPool::setEntries& already indexed_modified_transaction_set &mapModifiedTx) { int nDescendantsUpdated = 0; - for (const CTxMemPool::txiter it : alreadyAdded) { + for (CTxMemPool::txiter it : alreadyAdded) { CTxMemPool::setEntries descendants; mempool.CalculateDescendants(it, descendants); // Insert all descendants (not yet in block) into the modified set diff --git a/src/net.cpp b/src/net.cpp index 55043ffe30..b24ceb67d6 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -2042,7 +2042,7 @@ void CConnman::ThreadMessageHandler() // Send messages { LOCK(pnode->cs_sendProcessing); - m_msgproc->SendMessages(pnode, flagInterruptMsgProc); + m_msgproc->SendMessages(pnode); } if (flagInterruptMsgProc) @@ -466,7 +466,7 @@ class NetEventsInterface { public: virtual bool ProcessMessages(CNode* pnode, std::atomic<bool>& interrupt) = 0; - virtual bool SendMessages(CNode* pnode, std::atomic<bool>& interrupt) = 0; + virtual bool SendMessages(CNode* pnode) = 0; virtual void InitializeNode(CNode* pnode) = 0; virtual void FinalizeNode(NodeId id, bool& update_connection_time) = 0; diff --git a/src/net_processing.cpp b/src/net_processing.cpp index de456e87f4..458e9c2e9f 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -11,7 +11,6 @@ #include <chainparams.h> #include <consensus/validation.h> #include <hash.h> -#include <init.h> #include <validation.h> #include <merkleblock.h> #include <netmessagemaker.h> @@ -665,10 +664,10 @@ bool AddOrphanTx(const CTransactionRef& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRE // large transaction with a missing parent then we assume // it will rebroadcast it later, after the parent transaction(s) // have been mined or received. - // 100 orphans, each of which is at most 99,999 bytes big is + // 100 orphans, each of which is at most 100,000 bytes big is // at most 10 megabytes of orphans and somewhat more byprev index (in the worst case): unsigned int sz = GetTransactionWeight(*tx); - if (sz >= MAX_STANDARD_TX_WEIGHT) + if (sz > MAX_STANDARD_TX_WEIGHT) { LogPrint(BCLog::MEMPOOL, "ignoring large orphan tx (size: %u, hash: %s)\n", sz, hash.ToString()); return false; @@ -1071,7 +1070,7 @@ static void RelayAddress(const CAddress& addr, bool fReachable, CConnman* connma connman->ForEachNodeThen(std::move(sortfunc), std::move(pushfunc)); } -void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, const CInv& inv, CConnman* connman, const std::atomic<bool>& interruptMsgProc) +void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, const CInv& inv, CConnman* connman) { bool send = false; std::shared_ptr<const CBlock> a_recent_block; @@ -1275,7 +1274,7 @@ void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnm const CInv &inv = *it; if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_CMPCT_BLOCK || inv.type == MSG_WITNESS_BLOCK) { it++; - ProcessGetBlockData(pfrom, chainparams, inv, connman, interruptMsgProc); + ProcessGetBlockData(pfrom, chainparams, inv, connman); } } @@ -3219,7 +3218,7 @@ public: } }; -bool PeerLogicValidation::SendMessages(CNode* pto, std::atomic<bool>& interruptMsgProc) +bool PeerLogicValidation::SendMessages(CNode* pto) { const Consensus::Params& consensusParams = Params().GetConsensus(); { diff --git a/src/net_processing.h b/src/net_processing.h index b0b905d922..0d97b316eb 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -68,16 +68,20 @@ public: void InitializeNode(CNode* pnode) override; /** Handle removal of a peer by updating various state and removing it from mapNodeState */ void FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTime) override; - /** Process protocol messages received from a given node */ + /** + * Process protocol messages received from a given node + * + * @param[in] pfrom The node which we have received messages from. + * @param[in] interrupt Interrupt condition for processing threads + */ bool ProcessMessages(CNode* pfrom, std::atomic<bool>& interrupt) override; /** * Send queued protocol messages to be sent to a give node. * * @param[in] pto The node which we are sending messages to. - * @param[in] interrupt Interrupt condition for processing threads * @return True if there is more work to be done */ - bool SendMessages(CNode* pto, std::atomic<bool>& interrupt) override; + bool SendMessages(CNode* pto) override EXCLUSIVE_LOCKS_REQUIRED(pto->cs_sendProcessing); /** Consider evicting an outbound peer based on the amount of time they've been behind our tip */ void ConsiderEviction(CNode *pto, int64_t time_in_seconds); diff --git a/src/netaddress.cpp b/src/netaddress.cpp index 18d5948f85..5ccbabd03d 100644 --- a/src/netaddress.cpp +++ b/src/netaddress.cpp @@ -246,7 +246,7 @@ enum Network CNetAddr::GetNetwork() const return NET_IPV4; if (IsTor()) - return NET_TOR; + return NET_ONION; return NET_IPV6; } @@ -355,7 +355,7 @@ std::vector<unsigned char> CNetAddr::GetGroup() const } else if (IsTor()) { - nClass = NET_TOR; + nClass = NET_ONION; nStartByte = 6; nBits = 4; } @@ -433,11 +433,11 @@ int CNetAddr::GetReachabilityFrom(const CNetAddr *paddrPartner) const case NET_IPV4: return REACH_IPV4; case NET_IPV6: return fTunnel ? REACH_IPV6_WEAK : REACH_IPV6_STRONG; // only prefer giving our IPv6 address if it's not tunnelled } - case NET_TOR: + case NET_ONION: switch(ourNet) { default: return REACH_DEFAULT; case NET_IPV4: return REACH_IPV4; // Tor users can connect to IPv4 as well - case NET_TOR: return REACH_PRIVATE; + case NET_ONION: return REACH_PRIVATE; } case NET_TEREDO: switch(ourNet) { @@ -454,7 +454,7 @@ int CNetAddr::GetReachabilityFrom(const CNetAddr *paddrPartner) const case NET_TEREDO: return REACH_TEREDO; case NET_IPV6: return REACH_IPV6_WEAK; case NET_IPV4: return REACH_IPV4; - case NET_TOR: return REACH_PRIVATE; // either from Tor, or don't care about our address + case NET_ONION: return REACH_PRIVATE; // either from Tor, or don't care about our address } } } diff --git a/src/netaddress.h b/src/netaddress.h index f8f2ab99ff..966bef8cdf 100644 --- a/src/netaddress.h +++ b/src/netaddress.h @@ -22,7 +22,7 @@ enum Network NET_UNROUTABLE = 0, NET_IPV4, NET_IPV6, - NET_TOR, + NET_ONION, NET_INTERNAL, NET_MAX, diff --git a/src/netbase.cpp b/src/netbase.cpp index 15f9016be8..4ce63cb0ec 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -41,7 +41,11 @@ enum Network ParseNetwork(std::string net) { boost::to_lower(net); if (net == "ipv4") return NET_IPV4; if (net == "ipv6") return NET_IPV6; - if (net == "tor" || net == "onion") return NET_TOR; + if (net == "onion") return NET_ONION; + if (net == "tor") { + LogPrintf("Warning: net name 'tor' is deprecated and will be removed in the future. You should use 'onion' instead.\n"); + return NET_ONION; + } return NET_UNROUTABLE; } @@ -50,7 +54,7 @@ std::string GetNetworkName(enum Network net) { { case NET_IPV4: return "ipv4"; case NET_IPV6: return "ipv6"; - case NET_TOR: return "onion"; + case NET_ONION: return "onion"; default: return ""; } } diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index aac3fe5c14..3a592e40d3 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -88,7 +88,7 @@ bool IsStandardTx(const CTransaction& tx, std::string& reason) // computing signature hashes is O(ninputs*txsize). Limiting transactions // to MAX_STANDARD_TX_WEIGHT mitigates CPU exhaustion attacks. unsigned int sz = GetTransactionWeight(tx); - if (sz >= MAX_STANDARD_TX_WEIGHT) { + if (sz > MAX_STANDARD_TX_WEIGHT) { reason = "tx-size"; return false; } 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..3454d3421e 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -26,7 +26,6 @@ #include <qt/walletmodel.h> #endif -#include <init.h> #include <interfaces/handler.h> #include <interfaces/node.h> #include <rpc/server.h> @@ -55,13 +54,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 +65,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 +138,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 +147,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 +214,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 +444,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 +479,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 +562,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 +692,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 +718,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..2438361a58 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -29,7 +29,6 @@ #endif #include <chainparams.h> -#include <init.h> #include <interfaces/handler.h> #include <interfaces/node.h> #include <ui_interface.h> @@ -55,14 +54,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) @@ -76,50 +69,7 @@ const std::string BitcoinGUI::DEFAULT_UIPLATFORM = BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformStyle, const NetworkStyle *networkStyle, QWidget *parent) : QMainWindow(parent), - enableWallet(false), m_node(node), - clientModel(0), - walletFrame(0), - unitDisplayControl(0), - labelWalletEncryptionIcon(0), - labelWalletHDStatusIcon(0), - labelProxyIcon(0), - connectionsControl(0), - labelBlocksIcon(0), - progressBarLabel(0), - progressBar(0), - progressDialog(0), - appMenuBar(0), - appToolBar(0), - overviewAction(0), - historyAction(0), - quitAction(0), - sendCoinsAction(0), - sendCoinsMenuAction(0), - usedSendingAddressesAction(0), - usedReceivingAddressesAction(0), - signMessageAction(0), - verifyMessageAction(0), - aboutAction(0), - receiveCoinsAction(0), - receiveCoinsMenuAction(0), - optionsAction(0), - toggleHideAction(0), - encryptWalletAction(0), - backupWalletAction(0), - changePassphraseAction(0), - aboutQtAction(0), - openRPCConsoleAction(0), - openAction(0), - showHelpMessageAction(0), - trayIcon(0), - trayIconMenu(0), - notificator(0), - rpcConsole(0), - helpMessageDialog(0), - modalOverlay(0), - prevBlocks(0), - spinnerFrame(0), platformStyle(_platformStyle) { QSettings settings; @@ -147,12 +97,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 +173,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 +421,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 +510,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..4deeb325b3 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -70,9 +70,10 @@ public: functionality. */ bool addWallet(WalletModel *walletModel); + bool removeWallet(WalletModel* walletModel); void removeAllWallets(); #endif // ENABLE_WALLET - bool enableWallet; + bool enableWallet = false; protected: void changeEvent(QEvent *e); @@ -86,56 +87,58 @@ private: interfaces::Node& m_node; std::unique_ptr<interfaces::Handler> m_handler_message_box; std::unique_ptr<interfaces::Handler> m_handler_question; - ClientModel *clientModel; - WalletFrame *walletFrame; - - UnitDisplayStatusBarControl *unitDisplayControl; - QLabel *labelWalletEncryptionIcon; - QLabel *labelWalletHDStatusIcon; - QLabel *labelProxyIcon; - QLabel *connectionsControl; - QLabel *labelBlocksIcon; - QLabel *progressBarLabel; - QProgressBar *progressBar; - QProgressDialog *progressDialog; - - QMenuBar *appMenuBar; - QToolBar *appToolBar; - QAction *overviewAction; - QAction *historyAction; - QAction *quitAction; - QAction *sendCoinsAction; - QAction *sendCoinsMenuAction; - QAction *usedSendingAddressesAction; - QAction *usedReceivingAddressesAction; - QAction *signMessageAction; - QAction *verifyMessageAction; - QAction *aboutAction; - QAction *receiveCoinsAction; - QAction *receiveCoinsMenuAction; - QAction *optionsAction; - QAction *toggleHideAction; - QAction *encryptWalletAction; - QAction *backupWalletAction; - QAction *changePassphraseAction; - QAction *aboutQtAction; - QAction *openRPCConsoleAction; - QAction *openAction; - QAction *showHelpMessageAction; - - QLabel *m_wallet_selector_label; - QComboBox *m_wallet_selector; - - QSystemTrayIcon *trayIcon; - QMenu *trayIconMenu; - Notificator *notificator; - RPCConsole *rpcConsole; - HelpMessageDialog *helpMessageDialog; - ModalOverlay *modalOverlay; + ClientModel* clientModel = nullptr; + WalletFrame* walletFrame = nullptr; + + UnitDisplayStatusBarControl* unitDisplayControl = nullptr; + QLabel* labelWalletEncryptionIcon = nullptr; + QLabel* labelWalletHDStatusIcon = nullptr; + QLabel* labelProxyIcon = nullptr; + QLabel* connectionsControl = nullptr; + QLabel* labelBlocksIcon = nullptr; + QLabel* progressBarLabel = nullptr; + QProgressBar* progressBar = nullptr; + QProgressDialog* progressDialog = nullptr; + + QMenuBar* appMenuBar = nullptr; + QToolBar* appToolBar = nullptr; + QAction* overviewAction = nullptr; + QAction* historyAction = nullptr; + QAction* quitAction = nullptr; + QAction* sendCoinsAction = nullptr; + QAction* sendCoinsMenuAction = nullptr; + QAction* usedSendingAddressesAction = nullptr; + QAction* usedReceivingAddressesAction = nullptr; + QAction* signMessageAction = nullptr; + QAction* verifyMessageAction = nullptr; + QAction* aboutAction = nullptr; + QAction* receiveCoinsAction = nullptr; + QAction* receiveCoinsMenuAction = nullptr; + QAction* optionsAction = nullptr; + QAction* toggleHideAction = nullptr; + QAction* encryptWalletAction = nullptr; + QAction* backupWalletAction = nullptr; + QAction* changePassphraseAction = nullptr; + QAction* aboutQtAction = nullptr; + QAction* openRPCConsoleAction = nullptr; + QAction* openAction = nullptr; + QAction* showHelpMessageAction = nullptr; + QAction* m_wallet_selector_label_action = nullptr; + QAction* m_wallet_selector_action = nullptr; + + QLabel *m_wallet_selector_label = nullptr; + QComboBox* m_wallet_selector = nullptr; + + QSystemTrayIcon* trayIcon = nullptr; + QMenu* trayIconMenu = nullptr; + Notificator* notificator = nullptr; + RPCConsole* rpcConsole = nullptr; + HelpMessageDialog* helpMessageDialog = nullptr; + ModalOverlay* modalOverlay = nullptr; /** Keep track of previous number of blocks, to detect progress */ - int prevBlocks; - int spinnerFrame; + int prevBlocks = 0; + int spinnerFrame = 0; const PlatformStyle *platformStyle; @@ -254,7 +257,7 @@ private Q_SLOTS: /** Simply calls showNormalIfMinimized(true) for use in SLOT() macro */ void toggleHidden(); - /** called by a timer to check if fRequestShutdown has been set **/ + /** called by a timer to check if ShutdownRequested() has been set **/ void detectShutdown(); /** Show progress dialog e.g. for verifychain */ diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index b08de27041..9b6480a915 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 @@ -122,10 +118,6 @@ CoinControlDialog::CoinControlDialog(const PlatformStyle *_platformStyle, QWidge // (un)select all connect(ui->pushButtonSelectAll, SIGNAL(clicked()), this, SLOT(buttonSelectAllClicked())); - // change coin control first column label due Qt4 bug. - // see https://github.com/bitcoin/bitcoin/issues/5716 - ui->treeWidget->headerItem()->setText(COLUMN_CHECKBOX, QString()); - ui->treeWidget->setColumnWidth(COLUMN_CHECKBOX, 84); ui->treeWidget->setColumnWidth(COLUMN_AMOUNT, 110); ui->treeWidget->setColumnWidth(COLUMN_LABEL, 190); @@ -392,13 +384,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..a57343f036 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)); @@ -343,7 +331,7 @@ void OptionsDialog::updateDefaultProxyNets() strDefaultProxyGUI = ui->proxyIp->text() + ":" + ui->proxyPort->text(); (strProxy == strDefaultProxyGUI.toStdString()) ? ui->proxyReachIPv6->setChecked(true) : ui->proxyReachIPv6->setChecked(false); - model->node().getProxy(NET_TOR, proxy); + model->node().getProxy(NET_ONION, proxy); strProxy = proxy.proxy.ToStringIP() + ":" + proxy.proxy.ToStringPort(); strDefaultProxyGUI = ui->proxyIp->text() + ":" + ui->proxyPort->text(); (strProxy == strDefaultProxyGUI.toStdString()) ? ui->proxyReachTor->setChecked(true) : ui->proxyReachTor->setChecked(false); 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..1c90504e9e 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; @@ -162,7 +160,8 @@ QVariant PeerTableModel::data(const QModelIndex &index, int role) const case NetNodeId: return (qint64)rec->nodeStats.nodeid; case Address: - return QString::fromStdString(rec->nodeStats.addrName); + // prepend to peer address down-arrow symbol for inbound connection and up-arrow for outbound connection + return QString(rec->nodeStats.fInbound ? "↓ " : "↑ ") + QString::fromStdString(rec->nodeStats.addrName); case Subversion: return QString::fromStdString(rec->nodeStats.cleanSubVer); case Ping: diff --git a/src/qt/platformstyle.cpp b/src/qt/platformstyle.cpp index fce71f661a..a3a10aac18 100644 --- a/src/qt/platformstyle.cpp +++ b/src/qt/platformstyle.cpp @@ -46,7 +46,7 @@ void MakeSingleColorImage(QImage& img, const QColor& colorbase) QIcon ColorizeIcon(const QIcon& ico, const QColor& colorbase) { QIcon new_ico; - for (const QSize sz : ico.availableSizes()) + for (const QSize& sz : ico.availableSizes()) { QImage img(ico.pixmap(sz).toImage()); MakeSingleColorImage(img, colorbase); 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/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..223b39dc86 100644 --- a/src/qt/signverifymessagedialog.cpp +++ b/src/qt/signverifymessagedialog.cpp @@ -10,7 +10,6 @@ #include <qt/platformstyle.h> #include <qt/walletmodel.h> -#include <init.h> #include <key_io.h> #include <validation.h> // For strMessageMagic #include <wallet/wallet.h> @@ -37,9 +36,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/splashscreen.cpp b/src/qt/splashscreen.cpp index 4d972b431c..831ef68cab 100644 --- a/src/qt/splashscreen.cpp +++ b/src/qt/splashscreen.cpp @@ -11,7 +11,6 @@ #include <qt/networkstyle.h> #include <clientversion.h> -#include <init.h> #include <interfaces/handler.h> #include <interfaces/node.h> #include <interfaces/wallet.h> 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..122d6f0b12 100644 --- a/src/qt/winshutdownmonitor.cpp +++ b/src/qt/winshutdownmonitor.cpp @@ -4,8 +4,8 @@ #include <qt/winshutdownmonitor.h> -#if defined(Q_OS_WIN) && QT_VERSION >= 0x050000 -#include <init.h> +#if defined(Q_OS_WIN) +#include <shutdown.h> #include <util.h> #include <windows.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/blockchain.cpp b/src/rpc/blockchain.cpp index 68279d7ffd..d9d803ac7d 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -853,7 +853,7 @@ static void ApplyStats(CCoinsStats &stats, CHashWriter& ss, const uint256& hash, ss << hash; ss << VARINT(outputs.begin()->second.nHeight * 2 + outputs.begin()->second.fCoinBase ? 1u : 0u); stats.nTransactions++; - for (const auto output : outputs) { + for (const auto& output : outputs) { ss << VARINT(output.first + 1); ss << output.second.out.scriptPubKey; ss << VARINT(output.second.out.nValue, VarIntMode::NONNEGATIVE_SIGNED); diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 85b864e6b9..1f1044d80b 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2017 The Bitcoin Core developers +// Copyright (c) 2009-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. @@ -10,7 +10,6 @@ #include <consensus/params.h> #include <consensus/validation.h> #include <core_io.h> -#include <init.h> #include <validation.h> #include <key_io.h> #include <miner.h> @@ -20,6 +19,7 @@ #include <rpc/blockchain.h> #include <rpc/mining.h> #include <rpc/server.h> +#include <shutdown.h> #include <txmempool.h> #include <util.h> #include <utilstrencodings.h> @@ -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/rpc/misc.cpp b/src/rpc/misc.cpp index 6772784d3d..4eeb7f29d2 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -7,7 +7,6 @@ #include <clientversion.h> #include <core_io.h> #include <crypto/ripemd160.h> -#include <init.h> #include <key_io.h> #include <validation.h> #include <httpserver.h> @@ -78,7 +77,7 @@ static UniValue validateaddress(const JSONRPCRequest& request) ret.pushKV("address", currentAddress); CScript scriptPubKey = GetScriptForDestination(dest); - ret.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()));; + ret.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end())); UniValue detail = DescribeAddress(dest); ret.pushKVs(detail); diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 3b3f43edea..3e06b05aca 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -8,7 +8,6 @@ #include <consensus/validation.h> #include <core_io.h> #include <index/txindex.h> -#include <init.h> #include <keystore.h> #include <validation.h> #include <validationinterface.h> @@ -307,7 +306,7 @@ static UniValue verifytxoutproof(const JSONRPCRequest& request) "\nArguments:\n" "1. \"proof\" (string, required) The hex-encoded proof generated by gettxoutproof\n" "\nResult:\n" - "[\"txid\"] (array, strings) The txid(s) which the proof commits to, or empty array if the proof is invalid\n" + "[\"txid\"] (array, strings) The txid(s) which the proof commits to, or empty array if the proof can not be validated.\n" ); CDataStream ssMB(ParseHexV(request.params[0], "proof"), SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS); @@ -324,12 +323,17 @@ static UniValue verifytxoutproof(const JSONRPCRequest& request) LOCK(cs_main); const CBlockIndex* pindex = LookupBlockIndex(merkleBlock.header.GetHash()); - if (!pindex || !chainActive.Contains(pindex)) { + if (!pindex || !chainActive.Contains(pindex) || pindex->nTx == 0) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain"); } - for (const uint256& hash : vMatch) - res.push_back(hash.GetHex()); + // Check if proof is valid, only add results if so + if (pindex->nTx == merkleBlock.txn.GetNumTransactions()) { + for (const uint256& hash : vMatch) { + res.push_back(hash.GetHex()); + } + } + return res; } @@ -637,9 +641,7 @@ static UniValue decodescript(const JSONRPCRequest& request) } else { // Scripts that are not fit for P2WPKH are encoded as P2WSH. // Newer segwit program versions should be considered when then become available. - uint256 scriptHash; - CSHA256().Write(script.data(), script.size()).Finalize(scriptHash.begin()); - segwitScr = GetScriptForDestination(WitnessV0ScriptHash(scriptHash)); + segwitScr = GetScriptForDestination(WitnessV0ScriptHash(script)); } ScriptPubKeyToUniv(segwitScr, sr, true); sr.pushKV("p2sh-segwit", EncodeDestination(CScriptID(segwitScr))); @@ -736,17 +738,15 @@ static UniValue combinerawtransaction(const JSONRPCRequest& request) if (coin.IsSpent()) { throw JSONRPCError(RPC_VERIFY_ERROR, "Input not found or already spent"); } - const CScript& prevPubKey = coin.out.scriptPubKey; - const CAmount& amount = coin.out.nValue; - SignatureData sigdata; // ... and merge in other signatures: for (const CMutableTransaction& txv : txVariants) { if (txv.vin.size() > i) { - sigdata = CombineSignatures(prevPubKey, TransactionSignatureChecker(&txConst, i, amount), sigdata, DataFromTransaction(txv, i)); + sigdata.MergeSignatureData(DataFromTransaction(txv, i, coin.out)); } } + ProduceSignature(DUMMY_SIGNING_PROVIDER, MutableTransactionSignatureCreator(&mergedTx, i, coin.out.nValue, 1), coin.out.scriptPubKey, sigdata); UpdateInput(txin, sigdata); } @@ -875,12 +875,11 @@ UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival const CScript& prevPubKey = coin.out.scriptPubKey; const CAmount& amount = coin.out.nValue; - SignatureData sigdata; + SignatureData sigdata = DataFromTransaction(mtx, i, coin.out); // Only sign SIGHASH_SINGLE if there's a corresponding output: if (!fHashSingle || (i < mtx.vout.size())) { ProduceSignature(*keystore, MutableTransactionSignatureCreator(&mtx, i, amount, nHashType), prevPubKey, sigdata); } - sigdata = CombineSignatures(prevPubKey, TransactionSignatureChecker(&txConst, i, amount), sigdata, DataFromTransaction(mtx, i)); UpdateInput(txin, sigdata); diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 10040b1255..b420e9d8b3 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -1,14 +1,14 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2017 The Bitcoin Core developers +// Copyright (c) 2009-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. #include <rpc/server.h> #include <fs.h> -#include <init.h> #include <key_io.h> #include <random.h> +#include <shutdown.h> #include <sync.h> #include <ui_interface.h> #include <util.h> diff --git a/src/script/ismine.cpp b/src/script/ismine.cpp index 43dd9e582e..8c26866483 100644 --- a/src/script/ismine.cpp +++ b/src/script/ismine.cpp @@ -38,7 +38,7 @@ enum class IsMineResult NO = 0, //! Not ours WATCH_ONLY = 1, //! Included in watch-only balance SPENDABLE = 2, //! Included in all balances - INVALID = 3, //! Not spendable by anyone + INVALID = 3, //! Not spendable by anyone (uncompressed pubkey in segwit, P2SH inside P2SH or witness, witness inside witness) }; bool PermitsUncompressed(IsMineSigVersion sigversion) @@ -173,12 +173,10 @@ IsMineResult IsMineInner(const CKeyStore& keystore, const CScript& scriptPubKey, } // namespace -isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey, bool& isInvalid) +isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey) { - isInvalid = false; switch (IsMineInner(keystore, scriptPubKey, IsMineSigVersion::TOP)) { case IsMineResult::INVALID: - isInvalid = true; case IsMineResult::NO: return ISMINE_NO; case IsMineResult::WATCH_ONLY: @@ -189,12 +187,6 @@ isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey, bool& assert(false); } -isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey) -{ - bool isInvalid = false; - return IsMine(keystore, scriptPubKey, isInvalid); -} - isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest) { CScript script = GetScriptForDestination(dest); diff --git a/src/script/ismine.h b/src/script/ismine.h index a15768aecb..4246da49fe 100644 --- a/src/script/ismine.h +++ b/src/script/ismine.h @@ -24,12 +24,6 @@ enum isminetype /** used for bitflags of isminetype */ typedef uint8_t isminefilter; -/* isInvalid becomes true when the script is found invalid by consensus or policy. This will terminate the recursion - * and return ISMINE_NO immediately, as an invalid script should never be considered as "mine". This is needed as - * different SIGVERSION may have different network rules. Currently the only use of isInvalid is indicate uncompressed - * keys in SigVersion::WITNESS_V0 script, but could also be used in similar cases in the future - */ -isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey, bool& isInvalid); isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey); isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest); diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 6dbfbda029..60a8a2655d 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -33,27 +33,51 @@ bool MutableTransactionSignatureCreator::CreateSig(const SigningProvider& provid return true; } -static bool Sign1(const SigningProvider& provider, const CKeyID& address, const BaseSignatureCreator& creator, const CScript& scriptCode, std::vector<valtype>& ret, SigVersion sigversion) +static bool GetCScript(const SigningProvider& provider, const SignatureData& sigdata, const CScriptID& scriptid, CScript& script) { - std::vector<unsigned char> vchSig; - if (!creator.CreateSig(provider, vchSig, address, scriptCode, sigversion)) - return false; - ret.push_back(vchSig); - return true; + if (provider.GetCScript(scriptid, script)) { + return true; + } + // Look for scripts in SignatureData + if (CScriptID(sigdata.redeem_script) == scriptid) { + script = sigdata.redeem_script; + return true; + } else if (CScriptID(sigdata.witness_script) == scriptid) { + script = sigdata.witness_script; + return true; + } + return false; } -static bool SignN(const SigningProvider& provider, const std::vector<valtype>& multisigdata, const BaseSignatureCreator& creator, const CScript& scriptCode, std::vector<valtype>& ret, SigVersion sigversion) +static bool GetPubKey(const SigningProvider& provider, const SignatureData& sigdata, const CKeyID& address, CPubKey& pubkey) { - int nSigned = 0; - int nRequired = multisigdata.front()[0]; - for (unsigned int i = 1; i < multisigdata.size()-1 && nSigned < nRequired; i++) - { - const valtype& pubkey = multisigdata[i]; - CKeyID keyID = CPubKey(pubkey).GetID(); - if (Sign1(provider, keyID, creator, scriptCode, ret, sigversion)) - ++nSigned; + if (provider.GetPubKey(address, pubkey)) { + return true; + } + // Look for pubkey in all partial sigs + const auto it = sigdata.signatures.find(address); + if (it != sigdata.signatures.end()) { + pubkey = it->second.first; + return true; } - return nSigned==nRequired; + return false; +} + +static bool CreateSig(const BaseSignatureCreator& creator, SignatureData& sigdata, const SigningProvider& provider, std::vector<unsigned char>& sig_out, const CKeyID& keyid, const CScript& scriptcode, SigVersion sigversion) +{ + const auto it = sigdata.signatures.find(keyid); + if (it != sigdata.signatures.end()) { + sig_out = it->second.second; + return true; + } + if (creator.CreateSig(provider, sig_out, keyid, scriptcode, sigversion)) { + CPubKey pubkey; + GetPubKey(provider, sigdata, keyid, pubkey); + auto i = sigdata.signatures.emplace(keyid, SigPair(pubkey, sig_out)); + assert(i.second); + return true; + } + return false; } /** @@ -63,17 +87,17 @@ static bool SignN(const SigningProvider& provider, const std::vector<valtype>& m * Returns false if scriptPubKey could not be completely satisfied. */ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& scriptPubKey, - std::vector<valtype>& ret, txnouttype& whichTypeRet, SigVersion sigversion) + std::vector<valtype>& ret, txnouttype& whichTypeRet, SigVersion sigversion, SignatureData& sigdata) { CScript scriptRet; uint160 h160; ret.clear(); + std::vector<unsigned char> sig; std::vector<valtype> vSolutions; if (!Solver(scriptPubKey, whichTypeRet, vSolutions)) return false; - CKeyID keyID; switch (whichTypeRet) { case TX_NONSTANDARD: @@ -81,37 +105,47 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator case TX_WITNESS_UNKNOWN: return false; case TX_PUBKEY: - keyID = CPubKey(vSolutions[0]).GetID(); - return Sign1(provider, keyID, creator, scriptPubKey, ret, sigversion); - case TX_PUBKEYHASH: - keyID = CKeyID(uint160(vSolutions[0])); - if (!Sign1(provider, keyID, creator, scriptPubKey, ret, sigversion)) - return false; - else - { - CPubKey vch; - provider.GetPubKey(keyID, vch); - ret.push_back(ToByteVector(vch)); - } + if (!CreateSig(creator, sigdata, provider, sig, CPubKey(vSolutions[0]).GetID(), scriptPubKey, sigversion)) return false; + ret.push_back(std::move(sig)); + return true; + case TX_PUBKEYHASH: { + CKeyID keyID = CKeyID(uint160(vSolutions[0])); + if (!CreateSig(creator, sigdata, provider, sig, keyID, scriptPubKey, sigversion)) return false; + ret.push_back(std::move(sig)); + CPubKey pubkey; + GetPubKey(provider, sigdata, keyID, pubkey); + ret.push_back(ToByteVector(pubkey)); return true; + } case TX_SCRIPTHASH: - if (provider.GetCScript(uint160(vSolutions[0]), scriptRet)) { + if (GetCScript(provider, sigdata, uint160(vSolutions[0]), scriptRet)) { ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end())); return true; } return false; - case TX_MULTISIG: + case TX_MULTISIG: { + size_t required = vSolutions.front()[0]; ret.push_back(valtype()); // workaround CHECKMULTISIG bug - return (SignN(provider, vSolutions, creator, scriptPubKey, ret, sigversion)); - + for (size_t i = 1; i < vSolutions.size() - 1; ++i) { + CPubKey pubkey = CPubKey(vSolutions[i]); + if (ret.size() < required + 1 && CreateSig(creator, sigdata, provider, sig, pubkey.GetID(), scriptPubKey, sigversion)) { + ret.push_back(std::move(sig)); + } + } + bool ok = ret.size() == required + 1; + for (size_t i = 0; i + ret.size() < required + 1; ++i) { + ret.push_back(valtype()); + } + return ok; + } case TX_WITNESS_V0_KEYHASH: ret.push_back(vSolutions[0]); return true; case TX_WITNESS_V0_SCRIPTHASH: CRIPEMD160().Write(&vSolutions[0][0], vSolutions[0].size()).Finalize(h160.begin()); - if (provider.GetCScript(h160, scriptRet)) { + if (GetCScript(provider, sigdata, h160, scriptRet)) { ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end())); return true; } @@ -139,9 +173,11 @@ static CScript PushAll(const std::vector<valtype>& values) bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& fromPubKey, SignatureData& sigdata) { + if (sigdata.complete) return true; + std::vector<valtype> result; txnouttype whichType; - bool solved = SignStep(provider, creator, fromPubKey, result, whichType, SigVersion::BASE); + bool solved = SignStep(provider, creator, fromPubKey, result, whichType, SigVersion::BASE, sigdata); bool P2SH = false; CScript subscript; sigdata.scriptWitness.stack.clear(); @@ -152,7 +188,8 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato // the final scriptSig is the signatures from that // and then the serialized subscript: subscript = CScript(result[0].begin(), result[0].end()); - solved = solved && SignStep(provider, creator, subscript, result, whichType, SigVersion::BASE) && whichType != TX_SCRIPTHASH; + sigdata.redeem_script = subscript; + solved = solved && SignStep(provider, creator, subscript, result, whichType, SigVersion::BASE, sigdata) && whichType != TX_SCRIPTHASH; P2SH = true; } @@ -161,15 +198,16 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato CScript witnessscript; witnessscript << OP_DUP << OP_HASH160 << ToByteVector(result[0]) << OP_EQUALVERIFY << OP_CHECKSIG; txnouttype subType; - solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0); + solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0, sigdata); sigdata.scriptWitness.stack = result; result.clear(); } else if (solved && whichType == TX_WITNESS_V0_SCRIPTHASH) { CScript witnessscript(result[0].begin(), result[0].end()); + sigdata.witness_script = witnessscript; txnouttype subType; - solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0) && subType != TX_SCRIPTHASH && subType != TX_WITNESS_V0_SCRIPTHASH && subType != TX_WITNESS_V0_KEYHASH; + solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0, sigdata) && subType != TX_SCRIPTHASH && subType != TX_WITNESS_V0_SCRIPTHASH && subType != TX_WITNESS_V0_KEYHASH; result.push_back(std::vector<unsigned char>(witnessscript.begin(), witnessscript.end())); sigdata.scriptWitness.stack = result; result.clear(); @@ -181,99 +219,29 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato sigdata.scriptSig = PushAll(result); // Test solution - return solved && VerifyScript(sigdata.scriptSig, fromPubKey, &sigdata.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker()); -} - -SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn) -{ - SignatureData data; - assert(tx.vin.size() > nIn); - data.scriptSig = tx.vin[nIn].scriptSig; - data.scriptWitness = tx.vin[nIn].scriptWitness; - return data; + sigdata.complete = solved && VerifyScript(sigdata.scriptSig, fromPubKey, &sigdata.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker()); + return sigdata.complete; } -void UpdateInput(CTxIn& input, const SignatureData& data) +class SignatureExtractorChecker final : public BaseSignatureChecker { - input.scriptSig = data.scriptSig; - input.scriptWitness = data.scriptWitness; -} +private: + SignatureData& sigdata; + BaseSignatureChecker& checker; -bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType) -{ - assert(nIn < txTo.vin.size()); - - MutableTransactionSignatureCreator creator(&txTo, nIn, amount, nHashType); - - SignatureData sigdata; - bool ret = ProduceSignature(provider, creator, fromPubKey, sigdata); - UpdateInput(txTo.vin.at(nIn), sigdata); - return ret; -} - -bool SignSignature(const SigningProvider &provider, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType) -{ - assert(nIn < txTo.vin.size()); - CTxIn& txin = txTo.vin[nIn]; - assert(txin.prevout.n < txFrom.vout.size()); - const CTxOut& txout = txFrom.vout[txin.prevout.n]; - - return SignSignature(provider, txout.scriptPubKey, txTo, nIn, txout.nValue, nHashType); -} +public: + SignatureExtractorChecker(SignatureData& sigdata, BaseSignatureChecker& checker) : sigdata(sigdata), checker(checker) {} + bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override; +}; -static std::vector<valtype> CombineMultisig(const CScript& scriptPubKey, const BaseSignatureChecker& checker, - const std::vector<valtype>& vSolutions, - const std::vector<valtype>& sigs1, const std::vector<valtype>& sigs2, SigVersion sigversion) +bool SignatureExtractorChecker::CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const { - // Combine all the signatures we've got: - std::set<valtype> allsigs; - for (const valtype& v : sigs1) - { - if (!v.empty()) - allsigs.insert(v); - } - for (const valtype& v : sigs2) - { - if (!v.empty()) - allsigs.insert(v); - } - - // Build a map of pubkey -> signature by matching sigs to pubkeys: - assert(vSolutions.size() > 1); - unsigned int nSigsRequired = vSolutions.front()[0]; - unsigned int nPubKeys = vSolutions.size()-2; - std::map<valtype, valtype> sigs; - for (const valtype& sig : allsigs) - { - for (unsigned int i = 0; i < nPubKeys; i++) - { - const valtype& pubkey = vSolutions[i+1]; - if (sigs.count(pubkey)) - continue; // Already got a sig for this pubkey - - if (checker.CheckSig(sig, pubkey, scriptPubKey, sigversion)) - { - sigs[pubkey] = sig; - break; - } - } - } - // Now build a merged CScript: - unsigned int nSigsHave = 0; - std::vector<valtype> result; result.push_back(valtype()); // pop-one-too-many workaround - for (unsigned int i = 0; i < nPubKeys && nSigsHave < nSigsRequired; i++) - { - if (sigs.count(vSolutions[i+1])) - { - result.push_back(sigs[vSolutions[i+1]]); - ++nSigsHave; - } + if (checker.CheckSig(scriptSig, vchPubKey, scriptCode, sigversion)) { + CPubKey pubkey(vchPubKey); + sigdata.signatures.emplace(pubkey.GetID(), SigPair(pubkey, scriptSig)); + return true; } - // Fill any missing with OP_0: - for (unsigned int i = nSigsHave; i < nSigsRequired; i++) - result.push_back(valtype()); - - return result; + return false; } namespace @@ -298,89 +266,115 @@ struct Stacks }; } -static Stacks CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, - const txnouttype txType, const std::vector<valtype>& vSolutions, - Stacks sigs1, Stacks sigs2, SigVersion sigversion) +// Extracts signatures and scripts from incomplete scriptSigs. Please do not extend this, use PSBT instead +SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn, const CTxOut& txout) { - switch (txType) - { - case TX_NONSTANDARD: - case TX_NULL_DATA: - case TX_WITNESS_UNKNOWN: - // Don't know anything about this, assume bigger one is correct: - if (sigs1.script.size() >= sigs2.script.size()) - return sigs1; - return sigs2; - case TX_PUBKEY: - case TX_PUBKEYHASH: - // Signatures are bigger than placeholders or empty scripts: - if (sigs1.script.empty() || sigs1.script[0].empty()) - return sigs2; - return sigs1; - case TX_WITNESS_V0_KEYHASH: - // Signatures are bigger than placeholders or empty scripts: - if (sigs1.witness.empty() || sigs1.witness[0].empty()) - return sigs2; - return sigs1; - case TX_SCRIPTHASH: - if (sigs1.script.empty() || sigs1.script.back().empty()) - return sigs2; - else if (sigs2.script.empty() || sigs2.script.back().empty()) - return sigs1; - else - { - // Recur to combine: - valtype spk = sigs1.script.back(); - CScript pubKey2(spk.begin(), spk.end()); - - txnouttype txType2; - std::vector<std::vector<unsigned char> > vSolutions2; - Solver(pubKey2, txType2, vSolutions2); - sigs1.script.pop_back(); - sigs2.script.pop_back(); - Stacks result = CombineSignatures(pubKey2, checker, txType2, vSolutions2, sigs1, sigs2, sigversion); - result.script.push_back(spk); - return result; - } - case TX_MULTISIG: - return Stacks(CombineMultisig(scriptPubKey, checker, vSolutions, sigs1.script, sigs2.script, sigversion)); - case TX_WITNESS_V0_SCRIPTHASH: - if (sigs1.witness.empty() || sigs1.witness.back().empty()) - return sigs2; - else if (sigs2.witness.empty() || sigs2.witness.back().empty()) - return sigs1; - else - { - // Recur to combine: - CScript pubKey2(sigs1.witness.back().begin(), sigs1.witness.back().end()); - txnouttype txType2; - std::vector<valtype> vSolutions2; - Solver(pubKey2, txType2, vSolutions2); - sigs1.witness.pop_back(); - sigs1.script = sigs1.witness; - sigs1.witness.clear(); - sigs2.witness.pop_back(); - sigs2.script = sigs2.witness; - sigs2.witness.clear(); - Stacks result = CombineSignatures(pubKey2, checker, txType2, vSolutions2, sigs1, sigs2, SigVersion::WITNESS_V0); - result.witness = result.script; - result.script.clear(); - result.witness.push_back(valtype(pubKey2.begin(), pubKey2.end())); - return result; + SignatureData data; + assert(tx.vin.size() > nIn); + data.scriptSig = tx.vin[nIn].scriptSig; + data.scriptWitness = tx.vin[nIn].scriptWitness; + Stacks stack(data); + + // Get signatures + MutableTransactionSignatureChecker tx_checker(&tx, nIn, txout.nValue); + SignatureExtractorChecker extractor_checker(data, tx_checker); + if (VerifyScript(data.scriptSig, txout.scriptPubKey, &data.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, extractor_checker)) { + data.complete = true; + return data; + } + + // Get scripts + txnouttype script_type; + std::vector<std::vector<unsigned char>> solutions; + Solver(txout.scriptPubKey, script_type, solutions); + SigVersion sigversion = SigVersion::BASE; + CScript next_script = txout.scriptPubKey; + + if (script_type == TX_SCRIPTHASH && !stack.script.empty() && !stack.script.back().empty()) { + // Get the redeemScript + CScript redeem_script(stack.script.back().begin(), stack.script.back().end()); + data.redeem_script = redeem_script; + next_script = std::move(redeem_script); + + // Get redeemScript type + Solver(next_script, script_type, solutions); + stack.script.pop_back(); + } + if (script_type == TX_WITNESS_V0_SCRIPTHASH && !stack.witness.empty() && !stack.witness.back().empty()) { + // Get the witnessScript + CScript witness_script(stack.witness.back().begin(), stack.witness.back().end()); + data.witness_script = witness_script; + next_script = std::move(witness_script); + + // Get witnessScript type + Solver(next_script, script_type, solutions); + stack.witness.pop_back(); + stack.script = std::move(stack.witness); + stack.witness.clear(); + sigversion = SigVersion::WITNESS_V0; + } + if (script_type == TX_MULTISIG && !stack.script.empty()) { + // Build a map of pubkey -> signature by matching sigs to pubkeys: + assert(solutions.size() > 1); + unsigned int num_pubkeys = solutions.size()-2; + unsigned int last_success_key = 0; + for (const valtype& sig : stack.script) { + for (unsigned int i = last_success_key; i < num_pubkeys; ++i) { + const valtype& pubkey = solutions[i+1]; + // We either have a signature for this pubkey, or we have found a signature and it is valid + if (data.signatures.count(CPubKey(pubkey).GetID()) || extractor_checker.CheckSig(sig, pubkey, next_script, sigversion)) { + last_success_key = i + 1; + break; + } + } } - default: - return Stacks(); } + + return data; +} + +void UpdateInput(CTxIn& input, const SignatureData& data) +{ + input.scriptSig = data.scriptSig; + input.scriptWitness = data.scriptWitness; +} + +void SignatureData::MergeSignatureData(SignatureData sigdata) +{ + if (complete) return; + if (sigdata.complete) { + *this = std::move(sigdata); + return; + } + if (redeem_script.empty() && !sigdata.redeem_script.empty()) { + redeem_script = sigdata.redeem_script; + } + if (witness_script.empty() && !sigdata.witness_script.empty()) { + witness_script = sigdata.witness_script; + } + signatures.insert(std::make_move_iterator(sigdata.signatures.begin()), std::make_move_iterator(sigdata.signatures.end())); +} + +bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType) +{ + assert(nIn < txTo.vin.size()); + + MutableTransactionSignatureCreator creator(&txTo, nIn, amount, nHashType); + + SignatureData sigdata; + bool ret = ProduceSignature(provider, creator, fromPubKey, sigdata); + UpdateInput(txTo.vin.at(nIn), sigdata); + return ret; } -SignatureData CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, - const SignatureData& scriptSig1, const SignatureData& scriptSig2) +bool SignSignature(const SigningProvider &provider, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType) { - txnouttype txType; - std::vector<std::vector<unsigned char> > vSolutions; - Solver(scriptPubKey, txType, vSolutions); + assert(nIn < txTo.vin.size()); + CTxIn& txin = txTo.vin[nIn]; + assert(txin.prevout.n < txFrom.vout.size()); + const CTxOut& txout = txFrom.vout[txin.prevout.n]; - return CombineSignatures(scriptPubKey, checker, txType, vSolutions, Stacks(scriptSig1), Stacks(scriptSig2), SigVersion::BASE).Output(); + return SignSignature(provider, txout.scriptPubKey, txTo, nIn, txout.nValue, nHashType); } namespace { @@ -416,6 +410,7 @@ public: } const BaseSignatureCreator& DUMMY_SIGNATURE_CREATOR = DummySignatureCreator(); +const SigningProvider& DUMMY_SIGNING_PROVIDER = SigningProvider(); bool IsSolvable(const SigningProvider& provider, const CScript& script) { diff --git a/src/script/sign.h b/src/script/sign.h index 8ef0306bfe..3666859641 100644 --- a/src/script/sign.h +++ b/src/script/sign.h @@ -21,11 +21,13 @@ class SigningProvider { public: virtual ~SigningProvider() {} - virtual bool GetCScript(const CScriptID &scriptid, CScript& script) const =0; - virtual bool GetPubKey(const CKeyID &address, CPubKey& pubkey) const =0; - virtual bool GetKey(const CKeyID &address, CKey& key) const =0; + virtual bool GetCScript(const CScriptID &scriptid, CScript& script) const { return false; } + virtual bool GetPubKey(const CKeyID &address, CPubKey& pubkey) const { return false; } + virtual bool GetKey(const CKeyID &address, CKey& key) const { return false; } }; +extern const SigningProvider& DUMMY_SIGNING_PROVIDER; + /** Interface for signature creators. */ class BaseSignatureCreator { public: @@ -53,12 +55,22 @@ public: /** A signature creator that just produces 72-byte empty signatures. */ extern const BaseSignatureCreator& DUMMY_SIGNATURE_CREATOR; +typedef std::pair<CPubKey, std::vector<unsigned char>> SigPair; + +// This struct contains information from a transaction input and also contains signatures for that input. +// The information contained here can be used to create a signature and is also filled by ProduceSignature +// in order to construct final scriptSigs and scriptWitnesses. struct SignatureData { - CScript scriptSig; - CScriptWitness scriptWitness; + bool complete = false; ///< Stores whether the scriptSig and scriptWitness are complete + CScript scriptSig; ///< The scriptSig of an input. Contains complete signatures or the traditional partial signatures format + CScript redeem_script; ///< The redeemScript (if any) for the input + CScript witness_script; ///< The witnessScript (if any) for the input. witnessScripts are used in P2WSH outputs. + CScriptWitness scriptWitness; ///< The scriptWitness of an input. Contains complete signatures or the traditional partial signatures format. scriptWitness is part of a transaction input per BIP 144. + std::map<CKeyID, SigPair> signatures; ///< BIP 174 style partial signatures for the input. May contain all signatures necessary for producing a final scriptSig or scriptWitness. SignatureData() {} explicit SignatureData(const CScript& script) : scriptSig(script) {} + void MergeSignatureData(SignatureData sigdata); }; /** Produce a script signature using a generic signature creator. */ @@ -68,11 +80,8 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType); bool SignSignature(const SigningProvider &provider, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType); -/** Combine two script signatures using a generic signature checker, intelligently, possibly with OP_0 placeholders. */ -SignatureData CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, const SignatureData& scriptSig1, const SignatureData& scriptSig2); - -/** Extract signature data from a transaction, and insert it. */ -SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn); +/** Extract signature data from a transaction input, and insert it. */ +SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn, const CTxOut& txout); void UpdateInput(CTxIn& input, const SignatureData& data); /* Check whether we know how to sign for an output like this, assuming we diff --git a/src/script/standard.cpp b/src/script/standard.cpp index d9269d6147..f0b2c62a91 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -5,6 +5,7 @@ #include <script/standard.h> +#include <crypto/sha256.h> #include <pubkey.h> #include <script/script.h> #include <util.h> @@ -18,6 +19,11 @@ unsigned nMaxDatacarrierBytes = MAX_OP_RETURN_RELAY; CScriptID::CScriptID(const CScript& in) : uint160(Hash160(in.begin(), in.end())) {} +WitnessV0ScriptHash::WitnessV0ScriptHash(const CScript& in) +{ + CSHA256().Write(in.data(), in.size()).Finalize(begin()); +} + const char* GetTxnOutputType(txnouttype t) { switch (t) @@ -329,9 +335,7 @@ CScript GetScriptForWitness(const CScript& redeemscript) return GetScriptForDestination(WitnessV0KeyHash(vSolutions[0])); } } - uint256 hash; - CSHA256().Write(&redeemscript[0], redeemscript.size()).Finalize(hash.begin()); - return GetScriptForDestination(WitnessV0ScriptHash(hash)); + return GetScriptForDestination(WitnessV0ScriptHash(redeemscript)); } bool IsValidDestination(const CTxDestination& dest) { diff --git a/src/script/standard.h b/src/script/standard.h index 4922b7236b..1380030871 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -77,6 +77,7 @@ struct WitnessV0ScriptHash : public uint256 { WitnessV0ScriptHash() : uint256() {} explicit WitnessV0ScriptHash(const uint256& hash) : uint256(hash) {} + explicit WitnessV0ScriptHash(const CScript& script); using uint256::uint256; }; diff --git a/src/serialize.h b/src/serialize.h index e54c7483d2..df3b47ba87 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -189,7 +189,9 @@ template<typename X> const X& ReadWriteAsHelper(const X& x) { return x; } SerializationOp(s, CSerActionUnserialize()); \ } +#ifndef CHAR_EQUALS_INT8 template<typename Stream> inline void Serialize(Stream& s, char a ) { ser_writedata8(s, a); } // TODO Get rid of bare char +#endif template<typename Stream> inline void Serialize(Stream& s, int8_t a ) { ser_writedata8(s, a); } template<typename Stream> inline void Serialize(Stream& s, uint8_t a ) { ser_writedata8(s, a); } template<typename Stream> inline void Serialize(Stream& s, int16_t a ) { ser_writedata16(s, a); } @@ -205,7 +207,9 @@ template<typename Stream, int N> inline void Serialize(Stream& s, const unsigned template<typename Stream> inline void Serialize(Stream& s, const Span<const unsigned char>& span) { s.write(CharCast(span.data()), span.size()); } template<typename Stream> inline void Serialize(Stream& s, const Span<unsigned char>& span) { s.write(CharCast(span.data()), span.size()); } +#ifndef CHAR_EQUALS_INT8 template<typename Stream> inline void Unserialize(Stream& s, char& a ) { a = ser_readdata8(s); } // TODO Get rid of bare char +#endif template<typename Stream> inline void Unserialize(Stream& s, int8_t& a ) { a = ser_readdata8(s); } template<typename Stream> inline void Unserialize(Stream& s, uint8_t& a ) { a = ser_readdata8(s); } template<typename Stream> inline void Unserialize(Stream& s, int16_t& a ) { a = ser_readdata16(s); } diff --git a/src/shutdown.cpp b/src/shutdown.cpp new file mode 100644 index 0000000000..dec497d8ec --- /dev/null +++ b/src/shutdown.cpp @@ -0,0 +1,23 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-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. + +#include <shutdown.h> + +#include <atomic> + +static std::atomic<bool> fRequestShutdown(false); + +void StartShutdown() +{ + fRequestShutdown = true; +} +void AbortShutdown() +{ + fRequestShutdown = false; +} +bool ShutdownRequested() +{ + return fRequestShutdown; +} diff --git a/src/shutdown.h b/src/shutdown.h new file mode 100644 index 0000000000..3ed851c789 --- /dev/null +++ b/src/shutdown.h @@ -0,0 +1,13 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-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. + +#ifndef BITCOIN_SHUTDOWN_H +#define BITCOIN_SHUTDOWN_H + +void StartShutdown(); +void AbortShutdown(); +bool ShutdownRequested(); + +#endif diff --git a/src/test/cuckoocache_tests.cpp b/src/test/cuckoocache_tests.cpp index 857ab8a1b7..2edc7c16d4 100644 --- a/src/test/cuckoocache_tests.cpp +++ b/src/test/cuckoocache_tests.cpp @@ -114,7 +114,7 @@ static double normalize_hit_rate(double hits, double load) return hits * std::max(load, 1.0); } -/** Check the hit rate on loads ranging from 0.1 to 2.0 */ +/** Check the hit rate on loads ranging from 0.1 to 1.6 */ BOOST_AUTO_TEST_CASE(cuckoocache_hit_rate_ok) { /** Arbitrarily selected Hit Rate threshold that happens to work for this test diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp index e5f914ba8a..cc871726fd 100644 --- a/src/test/denialofservice_tests.cpp +++ b/src/test/denialofservice_tests.cpp @@ -54,7 +54,6 @@ BOOST_FIXTURE_TEST_SUITE(denialofservice_tests, TestingSetup) // work. BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction) { - std::atomic<bool> interruptDummy(false); // Mock an outbound peer CAddress addr1(ip(0xa0b0c001), NODE_NONE); @@ -66,25 +65,40 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction) dummyNode1.fSuccessfullyConnected = true; // This test requires that we have a chain with non-zero work. - LOCK(cs_main); - BOOST_CHECK(chainActive.Tip() != nullptr); - BOOST_CHECK(chainActive.Tip()->nChainWork > 0); + { + LOCK(cs_main); + BOOST_CHECK(chainActive.Tip() != nullptr); + BOOST_CHECK(chainActive.Tip()->nChainWork > 0); + } // Test starts here - LOCK(dummyNode1.cs_sendProcessing); - peerLogic->SendMessages(&dummyNode1, interruptDummy); // should result in getheaders - LOCK(dummyNode1.cs_vSend); - BOOST_CHECK(dummyNode1.vSendMsg.size() > 0); - dummyNode1.vSendMsg.clear(); + { + LOCK2(cs_main, dummyNode1.cs_sendProcessing); + peerLogic->SendMessages(&dummyNode1); // should result in getheaders + } + { + LOCK2(cs_main, dummyNode1.cs_vSend); + BOOST_CHECK(dummyNode1.vSendMsg.size() > 0); + dummyNode1.vSendMsg.clear(); + } int64_t nStartTime = GetTime(); // Wait 21 minutes SetMockTime(nStartTime+21*60); - peerLogic->SendMessages(&dummyNode1, interruptDummy); // should result in getheaders - BOOST_CHECK(dummyNode1.vSendMsg.size() > 0); + { + LOCK2(cs_main, dummyNode1.cs_sendProcessing); + peerLogic->SendMessages(&dummyNode1); // should result in getheaders + } + { + LOCK2(cs_main, dummyNode1.cs_vSend); + BOOST_CHECK(dummyNode1.vSendMsg.size() > 0); + } // Wait 3 more minutes SetMockTime(nStartTime+24*60); - peerLogic->SendMessages(&dummyNode1, interruptDummy); // should result in disconnect + { + LOCK2(cs_main, dummyNode1.cs_sendProcessing); + peerLogic->SendMessages(&dummyNode1); // should result in disconnect + } BOOST_CHECK(dummyNode1.fDisconnect == true); SetMockTime(0); @@ -177,7 +191,6 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management) BOOST_AUTO_TEST_CASE(DoS_banning) { - std::atomic<bool> interruptDummy(false); connman->ClearBanned(); CAddress addr1(ip(0xa0b0c001), NODE_NONE); @@ -190,8 +203,10 @@ BOOST_AUTO_TEST_CASE(DoS_banning) LOCK(cs_main); Misbehaving(dummyNode1.GetId(), 100); // Should get banned } - LOCK(dummyNode1.cs_sendProcessing); - peerLogic->SendMessages(&dummyNode1, interruptDummy); + { + LOCK2(cs_main, dummyNode1.cs_sendProcessing); + peerLogic->SendMessages(&dummyNode1); + } BOOST_CHECK(connman->IsBanned(addr1)); BOOST_CHECK(!connman->IsBanned(ip(0xa0b0c001|0x0000ff00))); // Different IP, not banned @@ -205,15 +220,20 @@ BOOST_AUTO_TEST_CASE(DoS_banning) LOCK(cs_main); Misbehaving(dummyNode2.GetId(), 50); } - LOCK(dummyNode2.cs_sendProcessing); - peerLogic->SendMessages(&dummyNode2, interruptDummy); + { + LOCK2(cs_main, dummyNode2.cs_sendProcessing); + peerLogic->SendMessages(&dummyNode2); + } BOOST_CHECK(!connman->IsBanned(addr2)); // 2 not banned yet... BOOST_CHECK(connman->IsBanned(addr1)); // ... but 1 still should be { LOCK(cs_main); Misbehaving(dummyNode2.GetId(), 50); } - peerLogic->SendMessages(&dummyNode2, interruptDummy); + { + LOCK2(cs_main, dummyNode2.cs_sendProcessing); + peerLogic->SendMessages(&dummyNode2); + } BOOST_CHECK(connman->IsBanned(addr2)); bool dummy; @@ -223,7 +243,6 @@ BOOST_AUTO_TEST_CASE(DoS_banning) BOOST_AUTO_TEST_CASE(DoS_banscore) { - std::atomic<bool> interruptDummy(false); connman->ClearBanned(); gArgs.ForceSetArg("-banscore", "111"); // because 11 is my favorite number @@ -237,20 +256,28 @@ BOOST_AUTO_TEST_CASE(DoS_banscore) LOCK(cs_main); Misbehaving(dummyNode1.GetId(), 100); } - LOCK(dummyNode1.cs_sendProcessing); - peerLogic->SendMessages(&dummyNode1, interruptDummy); + { + LOCK2(cs_main, dummyNode1.cs_sendProcessing); + peerLogic->SendMessages(&dummyNode1); + } BOOST_CHECK(!connman->IsBanned(addr1)); { LOCK(cs_main); Misbehaving(dummyNode1.GetId(), 10); } - peerLogic->SendMessages(&dummyNode1, interruptDummy); + { + LOCK2(cs_main, dummyNode1.cs_sendProcessing); + peerLogic->SendMessages(&dummyNode1); + } BOOST_CHECK(!connman->IsBanned(addr1)); { LOCK(cs_main); Misbehaving(dummyNode1.GetId(), 1); } - peerLogic->SendMessages(&dummyNode1, interruptDummy); + { + LOCK2(cs_main, dummyNode1.cs_sendProcessing); + peerLogic->SendMessages(&dummyNode1); + } BOOST_CHECK(connman->IsBanned(addr1)); gArgs.ForceSetArg("-banscore", std::to_string(DEFAULT_BANSCORE_THRESHOLD)); @@ -260,7 +287,6 @@ BOOST_AUTO_TEST_CASE(DoS_banscore) BOOST_AUTO_TEST_CASE(DoS_bantime) { - std::atomic<bool> interruptDummy(false); connman->ClearBanned(); int64_t nStartTime = GetTime(); @@ -277,8 +303,10 @@ BOOST_AUTO_TEST_CASE(DoS_bantime) LOCK(cs_main); Misbehaving(dummyNode.GetId(), 100); } - LOCK(dummyNode.cs_sendProcessing); - peerLogic->SendMessages(&dummyNode, interruptDummy); + { + LOCK2(cs_main, dummyNode.cs_sendProcessing); + peerLogic->SendMessages(&dummyNode); + } BOOST_CHECK(connman->IsBanned(addr)); SetMockTime(nStartTime+60*60); diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp index 027214e512..bc90e5ae09 100644 --- a/src/test/netbase_tests.cpp +++ b/src/test/netbase_tests.cpp @@ -39,7 +39,7 @@ BOOST_AUTO_TEST_CASE(netbase_networks) BOOST_CHECK(ResolveIP("::1").GetNetwork() == NET_UNROUTABLE); BOOST_CHECK(ResolveIP("8.8.8.8").GetNetwork() == NET_IPV4); BOOST_CHECK(ResolveIP("2001::8888").GetNetwork() == NET_IPV6); - BOOST_CHECK(ResolveIP("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").GetNetwork() == NET_TOR); + BOOST_CHECK(ResolveIP("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").GetNetwork() == NET_ONION); BOOST_CHECK(CreateInternal("foo.com").GetNetwork() == NET_INTERNAL); } @@ -293,7 +293,7 @@ BOOST_AUTO_TEST_CASE(netbase_getgroup) BOOST_CHECK(ResolveIP("64:FF9B::102:304").GetGroup() == std::vector<unsigned char>({(unsigned char)NET_IPV4, 1, 2})); // RFC6052 BOOST_CHECK(ResolveIP("2002:102:304:9999:9999:9999:9999:9999").GetGroup() == std::vector<unsigned char>({(unsigned char)NET_IPV4, 1, 2})); // RFC3964 BOOST_CHECK(ResolveIP("2001:0:9999:9999:9999:9999:FEFD:FCFB").GetGroup() == std::vector<unsigned char>({(unsigned char)NET_IPV4, 1, 2})); // RFC4380 - BOOST_CHECK(ResolveIP("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").GetGroup() == std::vector<unsigned char>({(unsigned char)NET_TOR, 239})); // Tor + BOOST_CHECK(ResolveIP("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").GetGroup() == std::vector<unsigned char>({(unsigned char)NET_ONION, 239})); // Tor BOOST_CHECK(ResolveIP("2001:470:abcd:9999:9999:9999:9999:9999").GetGroup() == std::vector<unsigned char>({(unsigned char)NET_IPV6, 32, 1, 4, 112, 175})); //he.net BOOST_CHECK(ResolveIP("2001:2001:9999:9999:9999:9999:9999:9999").GetGroup() == std::vector<unsigned char>({(unsigned char)NET_IPV6, 32, 1, 32, 1})); //IPv6 diff --git a/src/test/script_standard_tests.cpp b/src/test/script_standard_tests.cpp index 7ab0978228..7d4734986a 100644 --- a/src/test/script_standard_tests.cpp +++ b/src/test/script_standard_tests.cpp @@ -398,106 +398,149 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine) CScript scriptPubKey; isminetype result; - bool isInvalid; // P2PK compressed { CBasicKeyStore keystore; - scriptPubKey.clear(); - scriptPubKey << ToByteVector(pubkeys[0]) << OP_CHECKSIG; + scriptPubKey = GetScriptForRawPubKey(pubkeys[0]); // Keystore does not have key - result = IsMine(keystore, scriptPubKey, isInvalid); + result = IsMine(keystore, scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(!isInvalid); // Keystore has key keystore.AddKey(keys[0]); - result = IsMine(keystore, scriptPubKey, isInvalid); + result = IsMine(keystore, scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); - BOOST_CHECK(!isInvalid); } // P2PK uncompressed { CBasicKeyStore keystore; - scriptPubKey.clear(); - scriptPubKey << ToByteVector(uncompressedPubkey) << OP_CHECKSIG; + scriptPubKey = GetScriptForRawPubKey(uncompressedPubkey); // Keystore does not have key - result = IsMine(keystore, scriptPubKey, isInvalid); + result = IsMine(keystore, scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(!isInvalid); // Keystore has key keystore.AddKey(uncompressedKey); - result = IsMine(keystore, scriptPubKey, isInvalid); + result = IsMine(keystore, scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); - BOOST_CHECK(!isInvalid); } // P2PKH compressed { CBasicKeyStore keystore; - scriptPubKey.clear(); - scriptPubKey << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; + scriptPubKey = GetScriptForDestination(pubkeys[0].GetID()); // Keystore does not have key - result = IsMine(keystore, scriptPubKey, isInvalid); + result = IsMine(keystore, scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(!isInvalid); // Keystore has key keystore.AddKey(keys[0]); - result = IsMine(keystore, scriptPubKey, isInvalid); + result = IsMine(keystore, scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); - BOOST_CHECK(!isInvalid); } // P2PKH uncompressed { CBasicKeyStore keystore; - scriptPubKey.clear(); - scriptPubKey << OP_DUP << OP_HASH160 << ToByteVector(uncompressedPubkey.GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; + scriptPubKey = GetScriptForDestination(uncompressedPubkey.GetID()); // Keystore does not have key - result = IsMine(keystore, scriptPubKey, isInvalid); + result = IsMine(keystore, scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(!isInvalid); // Keystore has key keystore.AddKey(uncompressedKey); - result = IsMine(keystore, scriptPubKey, isInvalid); + result = IsMine(keystore, scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); - BOOST_CHECK(!isInvalid); } // P2SH { CBasicKeyStore keystore; - CScript redeemScript; - redeemScript << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; - - scriptPubKey.clear(); - scriptPubKey << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL; + CScript redeemScript = GetScriptForDestination(pubkeys[0].GetID()); + scriptPubKey = GetScriptForDestination(CScriptID(redeemScript)); // Keystore does not have redeemScript or key - result = IsMine(keystore, scriptPubKey, isInvalid); + result = IsMine(keystore, scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(!isInvalid); // Keystore has redeemScript but no key keystore.AddCScript(redeemScript); - result = IsMine(keystore, scriptPubKey, isInvalid); + result = IsMine(keystore, scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(!isInvalid); // Keystore has redeemScript and key keystore.AddKey(keys[0]); - result = IsMine(keystore, scriptPubKey, isInvalid); + result = IsMine(keystore, scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); - BOOST_CHECK(!isInvalid); + } + + // (P2PKH inside) P2SH inside P2SH (invalid) + { + CBasicKeyStore keystore; + + CScript redeemscript_inner = GetScriptForDestination(pubkeys[0].GetID()); + CScript redeemscript = GetScriptForDestination(CScriptID(redeemscript_inner)); + scriptPubKey = GetScriptForDestination(CScriptID(redeemscript)); + + keystore.AddCScript(redeemscript); + keystore.AddCScript(redeemscript_inner); + keystore.AddCScript(scriptPubKey); + keystore.AddKey(keys[0]); + result = IsMine(keystore, scriptPubKey); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + } + + // (P2PKH inside) P2SH inside P2WSH (invalid) + { + CBasicKeyStore keystore; + + CScript redeemscript = GetScriptForDestination(pubkeys[0].GetID()); + CScript witnessscript = GetScriptForDestination(CScriptID(redeemscript)); + scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessscript)); + + keystore.AddCScript(witnessscript); + keystore.AddCScript(redeemscript); + keystore.AddCScript(scriptPubKey); + keystore.AddKey(keys[0]); + result = IsMine(keystore, scriptPubKey); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + } + + // P2WPKH inside P2WSH (invalid) + { + CBasicKeyStore keystore; + + CScript witnessscript = GetScriptForDestination(WitnessV0KeyHash(pubkeys[0].GetID())); + scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessscript)); + + keystore.AddCScript(witnessscript); + keystore.AddCScript(scriptPubKey); + keystore.AddKey(keys[0]); + result = IsMine(keystore, scriptPubKey); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + } + + // (P2PKH inside) P2WSH inside P2WSH (invalid) + { + CBasicKeyStore keystore; + + CScript witnessscript_inner = GetScriptForDestination(pubkeys[0].GetID()); + CScript witnessscript = GetScriptForDestination(WitnessV0ScriptHash(witnessscript_inner)); + scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessscript)); + + keystore.AddCScript(witnessscript_inner); + keystore.AddCScript(witnessscript); + keystore.AddCScript(scriptPubKey); + keystore.AddKey(keys[0]); + result = IsMine(keystore, scriptPubKey); + BOOST_CHECK_EQUAL(result, ISMINE_NO); } // P2WPKH compressed @@ -505,14 +548,12 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine) CBasicKeyStore keystore; keystore.AddKey(keys[0]); - scriptPubKey.clear(); - scriptPubKey << OP_0 << ToByteVector(pubkeys[0].GetID()); + scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(pubkeys[0].GetID())); // Keystore implicitly has key and P2SH redeemScript keystore.AddCScript(scriptPubKey); - result = IsMine(keystore, scriptPubKey, isInvalid); + result = IsMine(keystore, scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); - BOOST_CHECK(!isInvalid); } // P2WPKH uncompressed @@ -520,56 +561,45 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine) CBasicKeyStore keystore; keystore.AddKey(uncompressedKey); - scriptPubKey.clear(); - scriptPubKey << OP_0 << ToByteVector(uncompressedPubkey.GetID()); + scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(uncompressedPubkey.GetID())); // Keystore has key, but no P2SH redeemScript - result = IsMine(keystore, scriptPubKey, isInvalid); + result = IsMine(keystore, scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(!isInvalid); // Keystore has key and P2SH redeemScript keystore.AddCScript(scriptPubKey); - result = IsMine(keystore, scriptPubKey, isInvalid); + result = IsMine(keystore, scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(isInvalid); } // scriptPubKey multisig { CBasicKeyStore keystore; - scriptPubKey.clear(); - scriptPubKey << OP_2 << - ToByteVector(uncompressedPubkey) << - ToByteVector(pubkeys[1]) << - OP_2 << OP_CHECKMULTISIG; + scriptPubKey = GetScriptForMultisig(2, {uncompressedPubkey, pubkeys[1]}); // Keystore does not have any keys - result = IsMine(keystore, scriptPubKey, isInvalid); + result = IsMine(keystore, scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(!isInvalid); // Keystore has 1/2 keys keystore.AddKey(uncompressedKey); - result = IsMine(keystore, scriptPubKey, isInvalid); + result = IsMine(keystore, scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(!isInvalid); // Keystore has 2/2 keys keystore.AddKey(keys[1]); - result = IsMine(keystore, scriptPubKey, isInvalid); + result = IsMine(keystore, scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(!isInvalid); // Keystore has 2/2 keys and the script keystore.AddCScript(scriptPubKey); - result = IsMine(keystore, scriptPubKey, isInvalid); + result = IsMine(keystore, scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(!isInvalid); } // P2SH multisig @@ -578,25 +608,17 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine) keystore.AddKey(uncompressedKey); keystore.AddKey(keys[1]); - CScript redeemScript; - redeemScript << OP_2 << - ToByteVector(uncompressedPubkey) << - ToByteVector(pubkeys[1]) << - OP_2 << OP_CHECKMULTISIG; - - scriptPubKey.clear(); - scriptPubKey << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL; + CScript redeemScript = GetScriptForMultisig(2, {uncompressedPubkey, pubkeys[1]}); + scriptPubKey = GetScriptForDestination(CScriptID(redeemScript)); // Keystore has no redeemScript - result = IsMine(keystore, scriptPubKey, isInvalid); + result = IsMine(keystore, scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(!isInvalid); // Keystore has redeemScript keystore.AddCScript(redeemScript); - result = IsMine(keystore, scriptPubKey, isInvalid); + result = IsMine(keystore, scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); - BOOST_CHECK(!isInvalid); } // P2WSH multisig with compressed keys @@ -605,35 +627,22 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine) keystore.AddKey(keys[0]); keystore.AddKey(keys[1]); - CScript witnessScript; - witnessScript << OP_2 << - ToByteVector(pubkeys[0]) << - ToByteVector(pubkeys[1]) << - OP_2 << OP_CHECKMULTISIG; - - uint256 scriptHash; - CSHA256().Write(&witnessScript[0], witnessScript.size()) - .Finalize(scriptHash.begin()); - - scriptPubKey.clear(); - scriptPubKey << OP_0 << ToByteVector(scriptHash); + CScript witnessScript = GetScriptForMultisig(2, {pubkeys[0], pubkeys[1]}); + scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessScript)); // Keystore has keys, but no witnessScript or P2SH redeemScript - result = IsMine(keystore, scriptPubKey, isInvalid); + result = IsMine(keystore, scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(!isInvalid); // Keystore has keys and witnessScript, but no P2SH redeemScript keystore.AddCScript(witnessScript); - result = IsMine(keystore, scriptPubKey, isInvalid); + result = IsMine(keystore, scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(!isInvalid); // Keystore has keys, witnessScript, P2SH redeemScript keystore.AddCScript(scriptPubKey); - result = IsMine(keystore, scriptPubKey, isInvalid); + result = IsMine(keystore, scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); - BOOST_CHECK(!isInvalid); } // P2WSH multisig with uncompressed key @@ -642,75 +651,47 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine) keystore.AddKey(uncompressedKey); keystore.AddKey(keys[1]); - CScript witnessScript; - witnessScript << OP_2 << - ToByteVector(uncompressedPubkey) << - ToByteVector(pubkeys[1]) << - OP_2 << OP_CHECKMULTISIG; - - uint256 scriptHash; - CSHA256().Write(&witnessScript[0], witnessScript.size()) - .Finalize(scriptHash.begin()); - - scriptPubKey.clear(); - scriptPubKey << OP_0 << ToByteVector(scriptHash); + CScript witnessScript = GetScriptForMultisig(2, {uncompressedPubkey, pubkeys[1]}); + scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessScript)); // Keystore has keys, but no witnessScript or P2SH redeemScript - result = IsMine(keystore, scriptPubKey, isInvalid); + result = IsMine(keystore, scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(!isInvalid); // Keystore has keys and witnessScript, but no P2SH redeemScript keystore.AddCScript(witnessScript); - result = IsMine(keystore, scriptPubKey, isInvalid); + result = IsMine(keystore, scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(!isInvalid); // Keystore has keys, witnessScript, P2SH redeemScript keystore.AddCScript(scriptPubKey); - result = IsMine(keystore, scriptPubKey, isInvalid); + result = IsMine(keystore, scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(isInvalid); } // P2WSH multisig wrapped in P2SH { CBasicKeyStore keystore; - CScript witnessScript; - witnessScript << OP_2 << - ToByteVector(pubkeys[0]) << - ToByteVector(pubkeys[1]) << - OP_2 << OP_CHECKMULTISIG; - - uint256 scriptHash; - CSHA256().Write(&witnessScript[0], witnessScript.size()) - .Finalize(scriptHash.begin()); - - CScript redeemScript; - redeemScript << OP_0 << ToByteVector(scriptHash); - - scriptPubKey.clear(); - scriptPubKey << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL; + CScript witnessScript = GetScriptForMultisig(2, {pubkeys[0], pubkeys[1]}); + CScript redeemScript = GetScriptForDestination(WitnessV0ScriptHash(witnessScript)); + scriptPubKey = GetScriptForDestination(CScriptID(redeemScript)); // Keystore has no witnessScript, P2SH redeemScript, or keys - result = IsMine(keystore, scriptPubKey, isInvalid); + result = IsMine(keystore, scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(!isInvalid); // Keystore has witnessScript and P2SH redeemScript, but no keys keystore.AddCScript(redeemScript); keystore.AddCScript(witnessScript); - result = IsMine(keystore, scriptPubKey, isInvalid); + result = IsMine(keystore, scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(!isInvalid); // Keystore has keys, witnessScript, P2SH redeemScript keystore.AddKey(keys[0]); keystore.AddKey(keys[1]); - result = IsMine(keystore, scriptPubKey, isInvalid); + result = IsMine(keystore, scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); - BOOST_CHECK(!isInvalid); } // OP_RETURN @@ -721,9 +702,8 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine) scriptPubKey.clear(); scriptPubKey << OP_RETURN << ToByteVector(pubkeys[0]); - result = IsMine(keystore, scriptPubKey, isInvalid); + result = IsMine(keystore, scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(!isInvalid); } // witness unspendable @@ -734,9 +714,8 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine) scriptPubKey.clear(); scriptPubKey << OP_0 << ToByteVector(ParseHex("aabb")); - result = IsMine(keystore, scriptPubKey, isInvalid); + result = IsMine(keystore, scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(!isInvalid); } // witness unknown @@ -747,9 +726,8 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine) scriptPubKey.clear(); scriptPubKey << OP_16 << ToByteVector(ParseHex("aabb")); - result = IsMine(keystore, scriptPubKey, isInvalid); + result = IsMine(keystore, scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(!isInvalid); } // Nonstandard @@ -760,9 +738,8 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine) scriptPubKey.clear(); scriptPubKey << OP_9 << OP_ADD << OP_11 << OP_EQUAL; - result = IsMine(keystore, scriptPubKey, isInvalid); + result = IsMine(keystore, scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(!isInvalid); } } diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index c05e60996d..c7cdd7ca82 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -1161,10 +1161,19 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23) BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_INVALID_STACK_OPERATION, ScriptErrorString(err)); } +/* Wrapper around ProduceSignature to combine two scriptsigs */ +SignatureData CombineSignatures(const CTxOut& txout, const CMutableTransaction& tx, const SignatureData& scriptSig1, const SignatureData& scriptSig2) +{ + SignatureData data; + data.MergeSignatureData(scriptSig1); + data.MergeSignatureData(scriptSig2); + ProduceSignature(DUMMY_SIGNING_PROVIDER, MutableTransactionSignatureCreator(&tx, 0, txout.nValue), txout.scriptPubKey, data); + return data; +} + BOOST_AUTO_TEST_CASE(script_combineSigs) { - // Test the CombineSignatures function - CAmount amount = 0; + // Test the ProduceSignature's ability to combine signatures function CBasicKeyStore keystore; std::vector<CKey> keys; std::vector<CPubKey> pubkeys; @@ -1180,52 +1189,51 @@ BOOST_AUTO_TEST_CASE(script_combineSigs) CMutableTransaction txFrom = BuildCreditingTransaction(GetScriptForDestination(keys[0].GetPubKey().GetID())); CMutableTransaction txTo = BuildSpendingTransaction(CScript(), CScriptWitness(), txFrom); CScript& scriptPubKey = txFrom.vout[0].scriptPubKey; - CScript& scriptSig = txTo.vin[0].scriptSig; + SignatureData scriptSig; SignatureData empty; - SignatureData combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), empty, empty); + SignatureData combined = CombineSignatures(txFrom.vout[0], txTo, empty, empty); BOOST_CHECK(combined.scriptSig.empty()); // Single signature case: SignSignature(keystore, txFrom, txTo, 0, SIGHASH_ALL); // changes scriptSig - combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(scriptSig), empty); - BOOST_CHECK(combined.scriptSig == scriptSig); - combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), empty, SignatureData(scriptSig)); - BOOST_CHECK(combined.scriptSig == scriptSig); - CScript scriptSigCopy = scriptSig; + scriptSig = DataFromTransaction(txTo, 0, txFrom.vout[0]); + combined = CombineSignatures(txFrom.vout[0], txTo, scriptSig, empty); + BOOST_CHECK(combined.scriptSig == scriptSig.scriptSig); + combined = CombineSignatures(txFrom.vout[0], txTo, empty, scriptSig); + BOOST_CHECK(combined.scriptSig == scriptSig.scriptSig); + SignatureData scriptSigCopy = scriptSig; // Signing again will give a different, valid signature: SignSignature(keystore, txFrom, txTo, 0, SIGHASH_ALL); - combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(scriptSigCopy), SignatureData(scriptSig)); - BOOST_CHECK(combined.scriptSig == scriptSigCopy || combined.scriptSig == scriptSig); + scriptSig = DataFromTransaction(txTo, 0, txFrom.vout[0]); + combined = CombineSignatures(txFrom.vout[0], txTo, scriptSigCopy, scriptSig); + BOOST_CHECK(combined.scriptSig == scriptSigCopy.scriptSig || combined.scriptSig == scriptSig.scriptSig); // P2SH, single-signature case: CScript pkSingle; pkSingle << ToByteVector(keys[0].GetPubKey()) << OP_CHECKSIG; keystore.AddCScript(pkSingle); scriptPubKey = GetScriptForDestination(CScriptID(pkSingle)); SignSignature(keystore, txFrom, txTo, 0, SIGHASH_ALL); - combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(scriptSig), empty); - BOOST_CHECK(combined.scriptSig == scriptSig); - combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), empty, SignatureData(scriptSig)); - BOOST_CHECK(combined.scriptSig == scriptSig); + scriptSig = DataFromTransaction(txTo, 0, txFrom.vout[0]); + combined = CombineSignatures(txFrom.vout[0], txTo, scriptSig, empty); + BOOST_CHECK(combined.scriptSig == scriptSig.scriptSig); + combined = CombineSignatures(txFrom.vout[0], txTo, empty, scriptSig); + BOOST_CHECK(combined.scriptSig == scriptSig.scriptSig); scriptSigCopy = scriptSig; SignSignature(keystore, txFrom, txTo, 0, SIGHASH_ALL); - combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(scriptSigCopy), SignatureData(scriptSig)); - BOOST_CHECK(combined.scriptSig == scriptSigCopy || combined.scriptSig == scriptSig); - // dummy scriptSigCopy with placeholder, should always choose non-placeholder: - scriptSigCopy = CScript() << OP_0 << std::vector<unsigned char>(pkSingle.begin(), pkSingle.end()); - combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(scriptSigCopy), SignatureData(scriptSig)); - BOOST_CHECK(combined.scriptSig == scriptSig); - combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(scriptSig), SignatureData(scriptSigCopy)); - BOOST_CHECK(combined.scriptSig == scriptSig); + scriptSig = DataFromTransaction(txTo, 0, txFrom.vout[0]); + combined = CombineSignatures(txFrom.vout[0], txTo, scriptSigCopy, scriptSig); + BOOST_CHECK(combined.scriptSig == scriptSigCopy.scriptSig || combined.scriptSig == scriptSig.scriptSig); // Hardest case: Multisig 2-of-3 scriptPubKey = GetScriptForMultisig(2, pubkeys); keystore.AddCScript(scriptPubKey); SignSignature(keystore, txFrom, txTo, 0, SIGHASH_ALL); - combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(scriptSig), empty); - BOOST_CHECK(combined.scriptSig == scriptSig); - combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), empty, SignatureData(scriptSig)); - BOOST_CHECK(combined.scriptSig == scriptSig); + scriptSig = DataFromTransaction(txTo, 0, txFrom.vout[0]); + combined = CombineSignatures(txFrom.vout[0], txTo, scriptSig, empty); + BOOST_CHECK(combined.scriptSig == scriptSig.scriptSig); + combined = CombineSignatures(txFrom.vout[0], txTo, empty, scriptSig); + BOOST_CHECK(combined.scriptSig == scriptSig.scriptSig); // A couple of partially-signed versions: std::vector<unsigned char> sig1; @@ -1252,22 +1260,28 @@ BOOST_AUTO_TEST_CASE(script_combineSigs) CScript complete12 = CScript() << OP_0 << sig1 << sig2; CScript complete13 = CScript() << OP_0 << sig1 << sig3; CScript complete23 = CScript() << OP_0 << sig2 << sig3; - - combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial1a), SignatureData(partial1b)); + SignatureData partial1_sigs; + partial1_sigs.signatures.emplace(keys[0].GetPubKey().GetID(), SigPair(keys[0].GetPubKey(), sig1)); + SignatureData partial2_sigs; + partial2_sigs.signatures.emplace(keys[1].GetPubKey().GetID(), SigPair(keys[1].GetPubKey(), sig2)); + SignatureData partial3_sigs; + partial3_sigs.signatures.emplace(keys[2].GetPubKey().GetID(), SigPair(keys[2].GetPubKey(), sig3)); + + combined = CombineSignatures(txFrom.vout[0], txTo, partial1_sigs, partial1_sigs); BOOST_CHECK(combined.scriptSig == partial1a); - combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial1a), SignatureData(partial2a)); + combined = CombineSignatures(txFrom.vout[0], txTo, partial1_sigs, partial2_sigs); BOOST_CHECK(combined.scriptSig == complete12); - combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial2a), SignatureData(partial1a)); + combined = CombineSignatures(txFrom.vout[0], txTo, partial2_sigs, partial1_sigs); BOOST_CHECK(combined.scriptSig == complete12); - combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial1b), SignatureData(partial2b)); + combined = CombineSignatures(txFrom.vout[0], txTo, partial1_sigs, partial2_sigs); BOOST_CHECK(combined.scriptSig == complete12); - combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial3b), SignatureData(partial1b)); + combined = CombineSignatures(txFrom.vout[0], txTo, partial3_sigs, partial1_sigs); BOOST_CHECK(combined.scriptSig == complete13); - combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial2a), SignatureData(partial3a)); + combined = CombineSignatures(txFrom.vout[0], txTo, partial2_sigs, partial3_sigs); BOOST_CHECK(combined.scriptSig == complete23); - combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial3b), SignatureData(partial2b)); + combined = CombineSignatures(txFrom.vout[0], txTo, partial3_sigs, partial2_sigs); BOOST_CHECK(combined.scriptSig == complete23); - combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial3b), SignatureData(partial3a)); + combined = CombineSignatures(txFrom.vout[0], txTo, partial3_sigs, partial3_sigs); BOOST_CHECK(combined.scriptSig == partial3c); } diff --git a/src/test/streams_tests.cpp b/src/test/streams_tests.cpp index 5d057108b1..e1e77f7c92 100644 --- a/src/test/streams_tests.cpp +++ b/src/test/streams_tests.cpp @@ -6,11 +6,8 @@ #include <support/allocators/zeroafterfree.h> #include <test/test_bitcoin.h> -#include <boost/assign/std/vector.hpp> // for 'operator+=()' #include <boost/test/unit_test.hpp> -using namespace boost::assign; // bring 'operator+=()' into scope - BOOST_FIXTURE_TEST_SUITE(streams_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(streams_vector_writer) @@ -80,14 +77,17 @@ BOOST_AUTO_TEST_CASE(streams_serializedata_xor) // Degenerate case - key += '\x00','\x00'; + key.push_back('\x00'); + key.push_back('\x00'); ds.Xor(key); BOOST_CHECK_EQUAL( std::string(expected_xor.begin(), expected_xor.end()), std::string(ds.begin(), ds.end())); - in += '\x0f','\xf0'; - expected_xor += '\xf0','\x0f'; + in.push_back('\x0f'); + in.push_back('\xf0'); + expected_xor.push_back('\xf0'); + expected_xor.push_back('\x0f'); // Single character key @@ -95,7 +95,7 @@ BOOST_AUTO_TEST_CASE(streams_serializedata_xor) ds.insert(ds.begin(), in.begin(), in.end()); key.clear(); - key += '\xff'; + key.push_back('\xff'); ds.Xor(key); BOOST_CHECK_EQUAL( std::string(expected_xor.begin(), expected_xor.end()), @@ -105,14 +105,17 @@ BOOST_AUTO_TEST_CASE(streams_serializedata_xor) in.clear(); expected_xor.clear(); - in += '\xf0','\x0f'; - expected_xor += '\x0f','\x00'; + in.push_back('\xf0'); + in.push_back('\x0f'); + expected_xor.push_back('\x0f'); + expected_xor.push_back('\x00'); ds.clear(); ds.insert(ds.begin(), in.begin(), in.end()); key.clear(); - key += '\xff','\x0f'; + key.push_back('\xff'); + key.push_back('\x0f'); ds.Xor(key); BOOST_CHECK_EQUAL( diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 65c5b8ea1d..45dc0e3571 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -494,6 +494,15 @@ BOOST_AUTO_TEST_CASE(test_big_witness_transaction) { threadGroup.join_all(); } +SignatureData CombineSignatures(const CMutableTransaction& input1, const CMutableTransaction& input2, const CTransactionRef tx) +{ + SignatureData sigdata; + sigdata = DataFromTransaction(input1, 0, tx->vout[0]); + sigdata.MergeSignatureData(DataFromTransaction(input2, 0, tx->vout[0])); + ProduceSignature(DUMMY_SIGNING_PROVIDER, MutableTransactionSignatureCreator(&input1, 0, tx->vout[0].nValue), tx->vout[0].scriptPubKey, sigdata); + return sigdata; +} + BOOST_AUTO_TEST_CASE(test_witness) { CBasicKeyStore keystore, keystore2; @@ -629,7 +638,7 @@ BOOST_AUTO_TEST_CASE(test_witness) CreateCreditAndSpend(keystore2, scriptMulti, output2, input2, false); CheckWithFlag(output2, input2, 0, false); BOOST_CHECK(*output1 == *output2); - UpdateInput(input1.vin[0], CombineSignatures(output1->vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0, output1->vout[0].nValue), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0))); + UpdateInput(input1.vin[0], CombineSignatures(input1, input2, output1)); CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true); // P2SH 2-of-2 multisig @@ -640,7 +649,7 @@ BOOST_AUTO_TEST_CASE(test_witness) CheckWithFlag(output2, input2, 0, true); CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH, false); BOOST_CHECK(*output1 == *output2); - UpdateInput(input1.vin[0], CombineSignatures(output1->vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0, output1->vout[0].nValue), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0))); + UpdateInput(input1.vin[0], CombineSignatures(input1, input2, output1)); CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true); CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true); @@ -652,7 +661,7 @@ BOOST_AUTO_TEST_CASE(test_witness) CheckWithFlag(output2, input2, 0, true); CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false); BOOST_CHECK(*output1 == *output2); - UpdateInput(input1.vin[0], CombineSignatures(output1->vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0, output1->vout[0].nValue), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0))); + UpdateInput(input1.vin[0], CombineSignatures(input1, input2, output1)); CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true); CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true); @@ -664,7 +673,7 @@ BOOST_AUTO_TEST_CASE(test_witness) CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH, true); CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false); BOOST_CHECK(*output1 == *output2); - UpdateInput(input1.vin[0], CombineSignatures(output1->vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0, output1->vout[0].nValue), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0))); + UpdateInput(input1.vin[0], CombineSignatures(input1, input2, output1)); CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true); CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true); } diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp index 1791bfd7f7..1f42ab8fa8 100644 --- a/src/torcontrol.cpp +++ b/src/torcontrol.cpp @@ -528,8 +528,8 @@ void TorController::auth_cb(TorControlConnection& _conn, const TorControlReply& if (gArgs.GetArg("-onion", "") == "") { CService resolved(LookupNumeric("127.0.0.1", 9050)); proxyType addrOnion = proxyType(resolved, true); - SetProxy(NET_TOR, addrOnion); - SetLimited(NET_TOR, false); + SetProxy(NET_ONION, addrOnion); + SetLimited(NET_ONION, false); } // Finally - now create the service diff --git a/src/txdb.cpp b/src/txdb.cpp index b1d5879c83..3635d0ab4b 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2017 The Bitcoin Core developers +// Copyright (c) 2009-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. @@ -9,10 +9,10 @@ #include <hash.h> #include <random.h> #include <pow.h> +#include <shutdown.h> #include <uint256.h> #include <util.h> #include <ui_interface.h> -#include <init.h> #include <stdint.h> diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 1c6dba0c9c..8090172e3f 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -69,12 +69,12 @@ void CTxMemPool::UpdateForDescendants(txiter updateIt, cacheMap &cachedDescendan setAllDescendants.insert(cit); stageEntries.erase(cit); const setEntries &setChildren = GetMemPoolChildren(cit); - for (const txiter childEntry : setChildren) { + for (txiter childEntry : setChildren) { cacheMap::iterator cacheIt = cachedDescendants.find(childEntry); if (cacheIt != cachedDescendants.end()) { // We've already calculated this one, just add the entries for this set // but don't traverse again. - for (const txiter cacheEntry : cacheIt->second) { + for (txiter cacheEntry : cacheIt->second) { setAllDescendants.insert(cacheEntry); } } else if (!setAllDescendants.count(childEntry)) { diff --git a/src/txmempool.h b/src/txmempool.h index bda812b42f..ebfcf36e11 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -486,7 +486,7 @@ public: mutable CCriticalSection cs; indexed_transaction_set mapTx GUARDED_BY(cs); - typedef indexed_transaction_set::nth_index<0>::type::iterator txiter; + using txiter = indexed_transaction_set::nth_index<0>::type::const_iterator; std::vector<std::pair<uint256, txiter> > vTxHashes; //!< All tx witness hashes/entries in mapTx, in random order struct CompareIteratorByHash { diff --git a/src/validation.cpp b/src/validation.cpp index 7be12375a2..9921063a52 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2017 The Bitcoin Core developers +// Copyright (c) 2009-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. @@ -17,7 +17,6 @@ #include <cuckoocache.h> #include <hash.h> #include <index/txindex.h> -#include <init.h> #include <policy/fees.h> #include <policy/policy.h> #include <policy/rbf.h> @@ -29,6 +28,7 @@ #include <script/script.h> #include <script/sigcache.h> #include <script/standard.h> +#include <shutdown.h> #include <timedata.h> #include <tinyformat.h> #include <txdb.h> @@ -651,7 +651,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool view.SetBackend(viewMemPool); // do all inputs exist? - for (const CTxIn txin : tx.vin) { + for (const CTxIn& txin : tx.vin) { if (!pcoinsTip->HaveCoinInCache(txin.prevout)) { coins_to_uncache.push_back(txin.prevout); } @@ -957,7 +957,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool } // Remove conflicting transactions from the mempool - for (const CTxMemPool::txiter it : allConflicting) + for (CTxMemPool::txiter it : allConflicting) { LogPrint(BCLog::MEMPOOL, "replacing tx %s with %s for %s BTC additional fees, %d delta bytes\n", it->GetTx().GetHash().ToString(), @@ -3508,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)) { @@ -3525,6 +3524,7 @@ 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()) { @@ -3984,14 +3984,13 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, nCheckLevel = std::max(0, std::min(4, nCheckLevel)); LogPrintf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel); CCoinsViewCache coins(coinsview); - CBlockIndex* pindexState = chainActive.Tip(); + CBlockIndex* pindex; CBlockIndex* pindexFailure = nullptr; int nGoodTransactions = 0; CValidationState state; int reportDone = 0; LogPrintf("[0%%]..."); /* Continued */ - for (CBlockIndex* pindex = chainActive.Tip(); pindex && pindex->pprev; pindex = pindex->pprev) - { + for (pindex = chainActive.Tip(); pindex && pindex->pprev; pindex = pindex->pprev) { boost::this_thread::interruption_point(); int percentageDone = std::max(1, std::min(99, (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * (nCheckLevel >= 4 ? 50 : 100)))); if (reportDone < percentageDone/10) { @@ -4025,13 +4024,12 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, } } // check level 3: check for inconsistencies during memory-only disconnect of tip blocks - if (nCheckLevel >= 3 && pindex == pindexState && (coins.DynamicMemoryUsage() + pcoinsTip->DynamicMemoryUsage()) <= nCoinCacheUsage) { + if (nCheckLevel >= 3 && (coins.DynamicMemoryUsage() + pcoinsTip->DynamicMemoryUsage()) <= nCoinCacheUsage) { assert(coins.GetBestBlock() == pindex->GetBlockHash()); DisconnectResult res = g_chainstate.DisconnectBlock(block, pindex, coins); if (res == DISCONNECT_FAILED) { return error("VerifyDB(): *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); } - pindexState = pindex->pprev; if (res == DISCONNECT_UNCLEAN) { nGoodTransactions = 0; pindexFailure = pindex; @@ -4045,9 +4043,11 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, if (pindexFailure) return error("VerifyDB(): *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", chainActive.Height() - pindexFailure->nHeight + 1, nGoodTransactions); + // store block count as we move pindex at check level >= 4 + int block_count = chainActive.Height() - pindex->nHeight; + // check level 4: try reconnecting blocks if (nCheckLevel >= 4) { - CBlockIndex *pindex = pindexState; while (pindex != chainActive.Tip()) { boost::this_thread::interruption_point(); uiInterface.ShowProgress(_("Verifying blocks..."), std::max(1, std::min(99, 100 - (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * 50))), false); @@ -4061,7 +4061,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, } LogPrintf("[DONE].\n"); - LogPrintf("No coin database inconsistencies in last %i blocks (%i transactions)\n", chainActive.Height() - pindexState->nHeight, nGoodTransactions); + LogPrintf("No coin database inconsistencies in last %i blocks (%i transactions)\n", block_count, nGoodTransactions); return true; } diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp index f328d2d14b..55aa5c2cdf 100644 --- a/src/validationinterface.cpp +++ b/src/validationinterface.cpp @@ -5,7 +5,6 @@ #include <validationinterface.h> -#include <init.h> #include <primitives/block.h> #include <scheduler.h> #include <sync.h> 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/init.cpp b/src/wallet/init.cpp index daeff1d0e8..74312b7124 100644 --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -200,7 +200,7 @@ bool WalletInit::Verify() const // Keep track of each wallet absolute path to detect duplicates. std::set<fs::path> wallet_paths; - for (const auto wallet_file : wallet_files) { + for (const auto& wallet_file : wallet_files) { fs::path wallet_path = fs::absolute(wallet_file, GetWalletDir()); if (!wallet_paths.insert(wallet_path).second) { diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index d09af1dbd1..882ddbbe4e 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -441,7 +441,7 @@ UniValue importpubkey(const JSONRPCRequest& request) return NullUniValue; } - if (request.fHelp || request.params.size() < 1 || request.params.size() > 4) + if (request.fHelp || request.params.size() < 1 || request.params.size() > 3) throw std::runtime_error( "importpubkey \"pubkey\" ( \"label\" rescan )\n" "\nAdds a public key (in hex) that can be watched as if it were in your wallet but cannot be used to spend. Requires a new wallet backup.\n" diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index fd9e23cb64..b1d2532d86 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2017 The Bitcoin Core developers +// Copyright (c) 2009-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. @@ -20,6 +20,7 @@ #include <rpc/server.h> #include <rpc/util.h> #include <script/sign.h> +#include <shutdown.h> #include <timedata.h> #include <util.h> #include <utilmoneystr.h> @@ -30,8 +31,6 @@ #include <wallet/walletdb.h> #include <wallet/walletutil.h> -#include <init.h> // For StartShutdown - #include <stdint.h> #include <univalue.h> @@ -40,12 +39,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 +74,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)"); } @@ -1017,6 +1020,14 @@ static UniValue sendfrom(const JSONRPCRequest& request) return NullUniValue; } + if (!IsDeprecatedRPCEnabled("accounts")) { + if (request.fHelp) { + throw std::runtime_error("sendfrom (Deprecated, will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts)"); + } + throw JSONRPCError(RPC_METHOD_DEPRECATED, "sendfrom is deprecated and will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts."); + } + + if (request.fHelp || request.params.size() < 3 || request.params.size() > 6) throw std::runtime_error( "sendfrom \"fromaccount\" \"toaddress\" amount ( minconf \"comment\" \"comment_to\" )\n" @@ -1255,9 +1266,11 @@ static UniValue sendmany(const JSONRPCRequest& request) EnsureWalletIsUnlocked(pwallet); // Check funds - CAmount nBalance = pwallet->GetLegacyBalance(ISMINE_SPENDABLE, nMinDepth, &strAccount); - if (totalAmount > nBalance) + if (IsDeprecatedRPCEnabled("accounts") && totalAmount > pwallet->GetLegacyBalance(ISMINE_SPENDABLE, nMinDepth, &strAccount)) { throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); + } else if (!IsDeprecatedRPCEnabled("accounts") && totalAmount > pwallet->GetLegacyBalance(ISMINE_SPENDABLE, nMinDepth, nullptr)) { + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Wallet has insufficient funds"); + } // Shuffle recipient list std::shuffle(vecSend.begin(), vecSend.end(), FastRandomContext()); @@ -3053,7 +3066,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( @@ -3078,6 +3091,12 @@ UniValue loadwallet(const JSONRPCRequest& request) fs::path wallet_path = fs::absolute(wallet_file, GetWalletDir()); if (fs::symlink_status(wallet_path).type() == fs::file_not_found) { throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Wallet " + wallet_file + " not found."); + } else if (fs::is_directory(wallet_path)) { + // The given filename is a directory. Check that there's a wallet.dat file. + fs::path wallet_dat_file = wallet_path / "wallet.dat"; + if (fs::symlink_status(wallet_dat_file).type() == fs::file_not_found) { + throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Directory " + wallet_file + " does not contain a wallet.dat file."); + } } std::string warning; @@ -3100,7 +3119,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 +3166,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); @@ -4376,12 +4444,12 @@ static const CRPCCommand commands[] = { "wallet", "listwallets", &listwallets, {} }, { "wallet", "loadwallet", &loadwallet, {"filename"} }, { "wallet", "lockunspent", &lockunspent, {"unlock","transactions"} }, - { "wallet", "sendfrom", &sendfrom, {"fromaccount","toaddress","amount","minconf","comment","comment_to"} }, { "wallet", "sendmany", &sendmany, {"fromaccount|dummy","amounts","minconf","comment","subtractfeefrom","replaceable","conf_target","estimate_mode"} }, { "wallet", "sendtoaddress", &sendtoaddress, {"address","amount","comment","comment_to","subtractfeefromamount","replaceable","conf_target","estimate_mode"} }, { "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"} }, @@ -4397,6 +4465,7 @@ static const CRPCCommand commands[] = { "wallet", "listaccounts", &listaccounts, {"minconf","include_watchonly"} }, { "wallet", "listreceivedbyaccount", &listreceivedbylabel, {"minconf","include_empty","include_watchonly"} }, { "wallet", "setaccount", &setlabel, {"address","account"} }, + { "wallet", "sendfrom", &sendfrom, {"fromaccount","toaddress","amount","minconf","comment","comment_to"} }, { "wallet", "move", &movecmd, {"fromaccount","toaccount","amount","minconf","comment"} }, /** Label functions (to replace non-balance account functions) */ diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 014a59ce2b..adc48a8650 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -11,7 +11,6 @@ #include <consensus/consensus.h> #include <consensus/validation.h> #include <fs.h> -#include <init.h> #include <key.h> #include <key_io.h> #include <keystore.h> @@ -23,6 +22,7 @@ #include <primitives/block.h> #include <primitives/transaction.h> #include <script/script.h> +#include <shutdown.h> #include <timedata.h> #include <txmempool.h> #include <utilmoneystr.h> @@ -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")); @@ -544,7 +553,7 @@ void CWallet::SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator> ran for (TxSpends::iterator it = range.first; it != range.second; ++it) { const CWalletTx* wtx = &mapWallet.at(it->second); if (wtx->nOrderPos < nMinOrderPos) { - nMinOrderPos = wtx->nOrderPos;; + nMinOrderPos = wtx->nOrderPos; copyFrom = wtx; } } @@ -600,6 +609,8 @@ void CWallet::AddToSpends(const COutPoint& outpoint, const uint256& wtxid) { mapTxSpends.insert(std::make_pair(outpoint, wtxid)); + setLockedCoins.erase(outpoint); + std::pair<TxSpends::iterator, TxSpends::iterator> range; range = mapTxSpends.equal_range(outpoint); SyncMetaData(range); @@ -1296,7 +1307,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; } } @@ -3067,7 +3078,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransac tx = MakeTransactionRef(std::move(txNew)); // Limit size - if (GetTransactionWeight(*tx) >= MAX_STANDARD_TX_WEIGHT) + if (GetTransactionWeight(*tx) > MAX_STANDARD_TX_WEIGHT) { strFailReason = _("Transaction too large"); return false; @@ -4066,7 +4077,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) { @@ -4095,8 +4108,6 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name, } } - uiInterface.LoadWallet(walletInstance); - int prev_version = walletInstance->nWalletVersion; if (gArgs.GetBoolArg("-upgradewallet", fFirstRun)) { @@ -4346,6 +4357,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()); @@ -4525,9 +4538,7 @@ CTxDestination CWallet::AddAndGetDestinationForScript(const CScript& script, Out return CScriptID(script); case OutputType::P2SH_SEGWIT: case OutputType::BECH32: { - WitnessV0ScriptHash hash; - CSHA256().Write(script.data(), script.size()).Finalize(hash.begin()); - CTxDestination witdest = hash; + CTxDestination witdest = WitnessV0ScriptHash(script); CScript witprog = GetScriptForDestination(witdest); // Check if the resulting program is solvable (i.e. doesn't use an uncompressed key) if (!IsSolvable(*this, witprog)) return CScriptID(script); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index aacbdf81d6..b829394847 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1100,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/src/zmq/zmqnotificationinterface.cpp b/src/zmq/zmqnotificationinterface.cpp index 68b425fa08..8cbc969972 100644 --- a/src/zmq/zmqnotificationinterface.cpp +++ b/src/zmq/zmqnotificationinterface.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2017 The Bitcoin Core developers +// Copyright (c) 2015-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. @@ -29,6 +29,15 @@ CZMQNotificationInterface::~CZMQNotificationInterface() } } +std::list<const CZMQAbstractNotifier*> CZMQNotificationInterface::GetActiveNotifiers() const +{ + std::list<const CZMQAbstractNotifier*> result; + for (const auto* n : notifiers) { + result.push_back(n); + } + return result; +} + CZMQNotificationInterface* CZMQNotificationInterface::Create() { CZMQNotificationInterface* notificationInterface = nullptr; @@ -180,3 +189,5 @@ void CZMQNotificationInterface::BlockDisconnected(const std::shared_ptr<const CB TransactionAddedToMempool(ptx); } } + +CZMQNotificationInterface* g_zmq_notification_interface = nullptr; diff --git a/src/zmq/zmqnotificationinterface.h b/src/zmq/zmqnotificationinterface.h index dee926ea5e..a0cc26a162 100644 --- a/src/zmq/zmqnotificationinterface.h +++ b/src/zmq/zmqnotificationinterface.h @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2017 The Bitcoin Core developers +// Copyright (c) 2015-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. @@ -18,6 +18,8 @@ class CZMQNotificationInterface final : public CValidationInterface public: virtual ~CZMQNotificationInterface(); + std::list<const CZMQAbstractNotifier*> GetActiveNotifiers() const; + static CZMQNotificationInterface* Create(); protected: @@ -37,4 +39,6 @@ private: std::list<CZMQAbstractNotifier*> notifiers; }; +extern CZMQNotificationInterface* g_zmq_notification_interface; + #endif // BITCOIN_ZMQ_ZMQNOTIFICATIONINTERFACE_H diff --git a/src/zmq/zmqrpc.cpp b/src/zmq/zmqrpc.cpp new file mode 100644 index 0000000000..4f88bf4eb9 --- /dev/null +++ b/src/zmq/zmqrpc.cpp @@ -0,0 +1,61 @@ +// 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. + +#include <zmq/zmqrpc.h> + +#include <rpc/server.h> +#include <zmq/zmqabstractnotifier.h> +#include <zmq/zmqnotificationinterface.h> + +#include <univalue.h> + +namespace { + +UniValue getzmqnotifications(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() != 0) { + throw std::runtime_error( + "getzmqnotifications\n" + "\nReturns information about the active ZeroMQ notifications.\n" + "\nResult:\n" + "[\n" + " { (json object)\n" + " \"type\": \"pubhashtx\", (string) Type of notification\n" + " \"address\": \"...\" (string) Address of the publisher\n" + " },\n" + " ...\n" + "]\n" + "\nExamples:\n" + + HelpExampleCli("getzmqnotifications", "") + + HelpExampleRpc("getzmqnotifications", "") + ); + } + + UniValue result(UniValue::VARR); + if (g_zmq_notification_interface != nullptr) { + for (const auto* n : g_zmq_notification_interface->GetActiveNotifiers()) { + UniValue obj(UniValue::VOBJ); + obj.pushKV("type", n->GetType()); + obj.pushKV("address", n->GetAddress()); + result.push_back(obj); + } + } + + return result; +} + +const CRPCCommand commands[] = +{ // category name actor (function) argNames + // ----------------- ------------------------ ----------------------- ---------- + { "zmq", "getzmqnotifications", &getzmqnotifications, {} }, +}; + +} // anonymous namespace + +void RegisterZMQRPCCommands(CRPCTable& t) +{ + for (const auto& c : commands) { + t.appendCommand(c.name, &c); + } +} diff --git a/src/zmq/zmqrpc.h b/src/zmq/zmqrpc.h new file mode 100644 index 0000000000..5a810a16fb --- /dev/null +++ b/src/zmq/zmqrpc.h @@ -0,0 +1,12 @@ +// 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. + +#ifndef BITCOIN_ZMQ_ZMQRPC_H +#define BITCOIN_ZMQ_ZMQRPC_H + +class CRPCTable; + +void RegisterZMQRPCCommands(CRPCTable& t); + +#endif // BITCOIN_ZMQ_ZMRRPC_H diff --git a/test/functional/README.md b/test/functional/README.md index fdd7c339c5..e6365222ff 100644 --- a/test/functional/README.md +++ b/test/functional/README.md @@ -76,7 +76,7 @@ over the network (`CBlock`, `CTransaction`, etc, along with the network-level wrappers for them, `msg_block`, `msg_tx`, etc). - P2P tests have two threads. One thread handles all network communication -with the bitcoind(s) being tested (using python's asyncore package); the other +with the bitcoind(s) being tested in a callback-based event loop; the other implements the test logic. - `P2PConnection` is the class used to connect to a bitcoind. `P2PInterface` @@ -84,10 +84,6 @@ contains the higher level logic for processing P2P payloads and connecting to the Bitcoin Core node application logic. For custom behaviour, subclass the P2PInterface object and override the callback methods. -- Call `network_thread_start()` after all `P2PInterface` objects are created to -start the networking thread. (Continue with the test logic in your existing -thread.) - - Can be used to write tests where specific P2P protocol behavior is tested. Examples tests are `p2p_unrequested_blocks.py`, `p2p_compactblocks.py`. diff --git a/test/functional/example_test.py b/test/functional/example_test.py index 05d1c1bf4e..e2f1cc05b3 100755 --- a/test/functional/example_test.py +++ b/test/functional/example_test.py @@ -21,8 +21,6 @@ from test_framework.mininode import ( mininode_lock, msg_block, msg_getdata, - network_thread_join, - network_thread_start, ) from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( @@ -135,9 +133,6 @@ class ExampleTest(BitcoinTestFramework): # Create P2P connections to two of the nodes self.nodes[0].add_p2p_connection(BaseNode()) - # Start up network handling in another thread. This needs to be called - # after the P2P connections have been created. - network_thread_start() # wait_for_verack ensures that the P2P connection is fully up. self.nodes[0].p2p.wait_for_verack() @@ -189,14 +184,9 @@ class ExampleTest(BitcoinTestFramework): connect_nodes(self.nodes[1], 2) self.log.info("Add P2P connection to node2") - # We can't add additional P2P connections once the network thread has started. Disconnect the connection - # to node0, wait for the network thread to terminate, then connect to node2. This is specific to - # the current implementation of the network thread and may be improved in future. self.nodes[0].disconnect_p2ps() - network_thread_join() self.nodes[2].add_p2p_connection(BaseNode()) - network_thread_start() self.nodes[2].p2p.wait_for_verack() self.log.info("Wait for node2 reach current tip. Test that it has propagated all the blocks to us") diff --git a/test/functional/feature_assumevalid.py b/test/functional/feature_assumevalid.py index 5a09142412..933a4740dd 100755 --- a/test/functional/feature_assumevalid.py +++ b/test/functional/feature_assumevalid.py @@ -33,16 +33,16 @@ import time from test_framework.blocktools import (create_block, create_coinbase) from test_framework.key import CECKey -from test_framework.mininode import (CBlockHeader, - COutPoint, - CTransaction, - CTxIn, - CTxOut, - network_thread_join, - network_thread_start, - P2PInterface, - msg_block, - msg_headers) +from test_framework.messages import ( + CBlockHeader, + COutPoint, + CTransaction, + CTxIn, + CTxOut, + msg_block, + msg_headers +) +from test_framework.mininode import P2PInterface from test_framework.script import (CScript, OP_TRUE) from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal @@ -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): @@ -98,8 +98,6 @@ class AssumeValidTest(BitcoinTestFramework): # Connect to node0 p2p0 = self.nodes[0].add_p2p_connection(BaseNode()) - - network_thread_start() self.nodes[0].p2p.wait_for_verack() # Build the blockchain @@ -160,9 +158,7 @@ class AssumeValidTest(BitcoinTestFramework): self.block_time += 1 height += 1 - # We're adding new connections so terminate the network thread self.nodes[0].disconnect_p2ps() - network_thread_join() # Start node1 and node2 with assumevalid so they accept a block with a bad signature. self.start_node(1, extra_args=["-assumevalid=" + hex(block102.sha256)]) @@ -172,8 +168,6 @@ class AssumeValidTest(BitcoinTestFramework): p2p1 = self.nodes[1].add_p2p_connection(BaseNode()) p2p2 = self.nodes[2].add_p2p_connection(BaseNode()) - network_thread_start() - p2p0.wait_for_verack() p2p1.wait_for_verack() p2p2.wait_for_verack() diff --git a/test/functional/feature_block.py b/test/functional/feature_block.py index f943fdf176..62c0582381 100755 --- a/test/functional/feature_block.py +++ b/test/functional/feature_block.py @@ -20,7 +20,7 @@ from test_framework.messages import ( uint256_from_compact, uint256_from_str, ) -from test_framework.mininode import P2PDataStore, network_thread_start, network_thread_join +from test_framework.mininode import P2PDataStore from test_framework.script import ( CScript, MAX_SCRIPT_ELEMENT_SIZE, @@ -1299,7 +1299,6 @@ class FullBlockTest(BitcoinTestFramework): Helper to connect and wait for version handshake.""" self.nodes[0].add_p2p_connection(P2PDataStore()) - network_thread_start() # We need to wait for the initial getheaders from the peer before we # start populating our blockstore. If we don't, then we may run ahead # to the next subtest before we receive the getheaders. We'd then send @@ -1314,7 +1313,6 @@ class FullBlockTest(BitcoinTestFramework): The node gets disconnected several times in this test. This helper method reconnects the p2p and restarts the network thread.""" self.nodes[0].disconnect_p2ps() - network_thread_join() self.bootstrap_p2p() def sync_blocks(self, blocks, success=True, reject_code=None, reject_reason=None, request_block=True, reconnect=False, timeout=60): diff --git a/test/functional/feature_cltv.py b/test/functional/feature_cltv.py index e9a8945e76..b484bffe0d 100755 --- a/test/functional/feature_cltv.py +++ b/test/functional/feature_cltv.py @@ -67,10 +67,6 @@ class BIP65Test(BitcoinTestFramework): def run_test(self): self.nodes[0].add_p2p_connection(P2PInterface()) - - network_thread_start() - - # wait_for_verack ensures that the P2P connection is fully up. self.nodes[0].p2p.wait_for_verack() self.log.info("Mining %d blocks", CLTV_HEIGHT - 2) diff --git a/test/functional/feature_csv_activation.py b/test/functional/feature_csv_activation.py index 37d60aad61..2499214fbd 100755 --- a/test/functional/feature_csv_activation.py +++ b/test/functional/feature_csv_activation.py @@ -49,7 +49,7 @@ import time from test_framework.blocktools import create_coinbase, create_block from test_framework.messages import ToHex, CTransaction -from test_framework.mininode import network_thread_start, P2PDataStore +from test_framework.mininode import P2PDataStore from test_framework.script import ( CScript, OP_CHECKSEQUENCEVERIFY, @@ -183,7 +183,6 @@ class BIP68_112_113Test(BitcoinTestFramework): def run_test(self): self.nodes[0].add_p2p_connection(P2PDataStore()) - network_thread_start() self.nodes[0].p2p.wait_for_verack() self.log.info("Generate blocks in the past for coinbase outputs.") diff --git a/test/functional/feature_dersig.py b/test/functional/feature_dersig.py index 02dcc3e55d..13224466d3 100755 --- a/test/functional/feature_dersig.py +++ b/test/functional/feature_dersig.py @@ -56,8 +56,6 @@ class BIP66Test(BitcoinTestFramework): def run_test(self): self.nodes[0].add_p2p_connection(P2PInterface()) - network_thread_start() - # wait_for_verack ensures that the P2P connection is fully up. self.nodes[0].p2p.wait_for_verack() diff --git a/test/functional/feature_maxuploadtarget.py b/test/functional/feature_maxuploadtarget.py index 0946f27b90..c413ecf705 100755 --- a/test/functional/feature_maxuploadtarget.py +++ b/test/functional/feature_maxuploadtarget.py @@ -57,7 +57,6 @@ class MaxUploadTest(BitcoinTestFramework): for _ in range(3): p2p_conns.append(self.nodes[0].add_p2p_connection(TestP2PConn())) - network_thread_start() for p2pc in p2p_conns: p2pc.wait_for_verack() @@ -148,8 +147,6 @@ class MaxUploadTest(BitcoinTestFramework): # Reconnect to self.nodes[0] self.nodes[0].add_p2p_connection(TestP2PConn()) - - network_thread_start() self.nodes[0].p2p.wait_for_verack() #retrieve 20 blocks which should be enough to break the 1MB limit diff --git a/test/functional/feature_nulldummy.py b/test/functional/feature_nulldummy.py index 6577d83f5c..24659eac77 100755 --- a/test/functional/feature_nulldummy.py +++ b/test/functional/feature_nulldummy.py @@ -15,7 +15,7 @@ Generate 427 more blocks. from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * -from test_framework.mininode import CTransaction, network_thread_start +from test_framework.messages import CTransaction from test_framework.blocktools import create_coinbase, create_block, add_witness_commitment from test_framework.script import CScript from io import BytesIO @@ -50,7 +50,6 @@ class NULLDUMMYTest(BitcoinTestFramework): self.wit_address = self.nodes[0].addwitnessaddress(self.address) self.wit_ms_address = self.nodes[0].addmultisigaddress(1, [self.address], '', 'p2sh-segwit')['address'] - network_thread_start() self.coinbase_blocks = self.nodes[0].generate(2) # Block 2 coinbase_txid = [] for i in self.coinbase_blocks: diff --git a/test/functional/feature_versionbits_warning.py b/test/functional/feature_versionbits_warning.py index 2985569a8f..a03c20b088 100755 --- a/test/functional/feature_versionbits_warning.py +++ b/test/functional/feature_versionbits_warning.py @@ -12,7 +12,7 @@ import re from test_framework.blocktools import create_block, create_coinbase from test_framework.messages import msg_block -from test_framework.mininode import P2PInterface, network_thread_start, mininode_lock +from test_framework.mininode import P2PInterface, mininode_lock from test_framework.test_framework import BitcoinTestFramework from test_framework.util import wait_until @@ -65,7 +65,6 @@ class VersionBitsWarningTest(BitcoinTestFramework): # Handy alias node = self.nodes[0] node.add_p2p_connection(P2PInterface()) - network_thread_start() node.p2p.wait_for_verack() # Mine one period worth of blocks diff --git a/test/functional/p2p_compactblocks.py b/test/functional/p2p_compactblocks.py index cb4c9867a3..17aacd8152 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): @@ -788,13 +788,11 @@ class CompactBlocksTest(BitcoinTestFramework): assert_equal(int(node.getbestblockhash(), 16), block.sha256) def run_test(self): - # Setup the p2p connections and start up the network thread. + # Setup the p2p connections self.test_node = self.nodes[0].add_p2p_connection(TestP2PConn()) self.segwit_node = self.nodes[1].add_p2p_connection(TestP2PConn(), services=NODE_NETWORK|NODE_WITNESS) self.old_node = self.nodes[1].add_p2p_connection(TestP2PConn(), services=NODE_NETWORK) - network_thread_start() - self.test_node.wait_for_verack() # We will need UTXOs to construct transactions in later tests. diff --git a/test/functional/p2p_feefilter.py b/test/functional/p2p_feefilter.py index c304bbba85..5b3fa0186a 100755 --- a/test/functional/p2p_feefilter.py +++ b/test/functional/p2p_feefilter.py @@ -47,9 +47,8 @@ class FeeFilterTest(BitcoinTestFramework): node1.generate(1) sync_blocks(self.nodes) - # Setup the p2p connections and start up the network thread. + # Setup the p2p connections self.nodes[0].add_p2p_connection(TestP2PConn()) - network_thread_start() self.nodes[0].p2p.wait_for_verack() # Test that invs are received for all txs at feerate of 20 sat/byte diff --git a/test/functional/p2p_fingerprint.py b/test/functional/p2p_fingerprint.py index 516ce8555b..61f9ec014b 100755 --- a/test/functional/p2p_fingerprint.py +++ b/test/functional/p2p_fingerprint.py @@ -18,7 +18,6 @@ from test_framework.mininode import ( msg_block, msg_getdata, msg_getheaders, - network_thread_start, wait_until, ) from test_framework.test_framework import BitcoinTestFramework @@ -76,8 +75,6 @@ class P2PFingerprintTest(BitcoinTestFramework): # last month but that have over a month's worth of work are also withheld. def run_test(self): node0 = self.nodes[0].add_p2p_connection(P2PInterface()) - - network_thread_start() node0.wait_for_verack() # Set node time to 60 days ago diff --git a/test/functional/p2p_invalid_block.py b/test/functional/p2p_invalid_block.py index e1f328ba77..c981968026 100755 --- a/test/functional/p2p_invalid_block.py +++ b/test/functional/p2p_invalid_block.py @@ -14,7 +14,7 @@ import copy from test_framework.blocktools import create_block, create_coinbase, create_transaction from test_framework.messages import COIN -from test_framework.mininode import network_thread_start, P2PDataStore +from test_framework.mininode import P2PDataStore from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal @@ -28,8 +28,6 @@ class InvalidBlockRequestTest(BitcoinTestFramework): # Add p2p connection to node0 node = self.nodes[0] # convenience reference to the node node.add_p2p_connection(P2PDataStore()) - - network_thread_start() node.p2p.wait_for_verack() best_block = node.getblock(node.getbestblockhash()) diff --git a/test/functional/p2p_invalid_tx.py b/test/functional/p2p_invalid_tx.py index 3fed872ccc..a7a86f89fd 100755 --- a/test/functional/p2p_invalid_tx.py +++ b/test/functional/p2p_invalid_tx.py @@ -13,7 +13,7 @@ from test_framework.messages import ( CTxIn, CTxOut, ) -from test_framework.mininode import network_thread_start, P2PDataStore, network_thread_join +from test_framework.mininode import P2PDataStore from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, @@ -32,7 +32,6 @@ class InvalidTxRequestTest(BitcoinTestFramework): Helper to connect and wait for version handshake.""" for _ in range(num_connections): self.nodes[0].add_p2p_connection(P2PDataStore()) - network_thread_start() self.nodes[0].p2p.wait_for_verack() def reconnect_p2p(self, **kwargs): @@ -41,7 +40,6 @@ class InvalidTxRequestTest(BitcoinTestFramework): The node gets disconnected several times in this test. This helper method reconnects the p2p and restarts the network thread.""" self.nodes[0].disconnect_p2ps() - network_thread_join() self.bootstrap_p2p(**kwargs) def run_test(self): diff --git a/test/functional/p2p_leak.py b/test/functional/p2p_leak.py index 198dcc1490..ecb9a56fe1 100755 --- a/test/functional/p2p_leak.py +++ b/test/functional/p2p_leak.py @@ -103,8 +103,6 @@ class P2PLeakTest(BitcoinTestFramework): unsupported_service_bit5_node = self.nodes[0].add_p2p_connection(CLazyNode(), services=NODE_NETWORK|NODE_UNSUPPORTED_SERVICE_BIT_5) unsupported_service_bit7_node = self.nodes[0].add_p2p_connection(CLazyNode(), services=NODE_NETWORK|NODE_UNSUPPORTED_SERVICE_BIT_7) - network_thread_start() - wait_until(lambda: no_version_bannode.ever_connected, timeout=10, lock=mininode_lock) wait_until(lambda: no_version_idlenode.ever_connected, timeout=10, lock=mininode_lock) wait_until(lambda: no_verack_idlenode.version_received, timeout=10, lock=mininode_lock) @@ -118,17 +116,16 @@ 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() - # Wait until all connections are closed and the network thread has terminated + # Wait until all connections are closed wait_until(lambda: len(self.nodes[0].getpeerinfo()) == 0) - network_thread_join() # Make sure no unexpected messages came in assert(no_version_bannode.unexpected_msg == False) @@ -143,11 +140,9 @@ class P2PLeakTest(BitcoinTestFramework): allowed_service_bit5_node = self.nodes[0].add_p2p_connection(P2PInterface(), services=NODE_NETWORK|NODE_UNSUPPORTED_SERVICE_BIT_5) allowed_service_bit7_node = self.nodes[0].add_p2p_connection(P2PInterface(), services=NODE_NETWORK|NODE_UNSUPPORTED_SERVICE_BIT_7) - # Network thread stopped when all previous P2PInterfaces disconnected. Restart it - network_thread_start() - wait_until(lambda: allowed_service_bit5_node.message_count["verack"], lock=mininode_lock) wait_until(lambda: allowed_service_bit7_node.message_count["verack"], lock=mininode_lock) + if __name__ == '__main__': P2PLeakTest().main() diff --git a/test/functional/p2p_mempool.py b/test/functional/p2p_mempool.py index e54843b26f..5a1fb60fb5 100755 --- a/test/functional/p2p_mempool.py +++ b/test/functional/p2p_mempool.py @@ -21,7 +21,6 @@ class P2PMempoolTests(BitcoinTestFramework): def run_test(self): # Add a p2p connection self.nodes[0].add_p2p_connection(P2PInterface()) - network_thread_start() self.nodes[0].p2p.wait_for_verack() #request mempool diff --git a/test/functional/p2p_node_network_limited.py b/test/functional/p2p_node_network_limited.py index 301d8c181a..4a24e24daf 100755 --- a/test/functional/p2p_node_network_limited.py +++ b/test/functional/p2p_node_network_limited.py @@ -9,7 +9,7 @@ and that it responds to getdata requests for blocks correctly: - send a block within 288 + 2 of the tip - disconnect peers who request blocks older than that.""" from test_framework.messages import CInv, msg_getdata, msg_verack -from test_framework.mininode import NODE_BLOOM, NODE_NETWORK_LIMITED, NODE_WITNESS, P2PInterface, wait_until, mininode_lock, network_thread_start, network_thread_join +from test_framework.mininode import NODE_BLOOM, NODE_NETWORK_LIMITED, NODE_WITNESS, P2PInterface, wait_until, mininode_lock from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, disconnect_nodes, connect_nodes_bi, sync_blocks @@ -48,7 +48,6 @@ class NodeNetworkLimitedTest(BitcoinTestFramework): def run_test(self): node = self.nodes[0].add_p2p_connection(P2PIgnoreInv()) - network_thread_start() node.wait_for_verack() expected_services = NODE_BLOOM | NODE_WITNESS | NODE_NETWORK_LIMITED @@ -74,9 +73,7 @@ class NodeNetworkLimitedTest(BitcoinTestFramework): self.log.info("Check local address relay, do a fresh connection.") self.nodes[0].disconnect_p2ps() - network_thread_join() node1 = self.nodes[0].add_p2p_connection(P2PIgnoreInv()) - network_thread_start() node1.wait_for_verack() node1.send_message(msg_verack()) diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py index 940d085e89..727f2d1c6e 100755 --- a/test/functional/p2p_segwit.py +++ b/test/functional/p2p_segwit.py @@ -3,17 +3,84 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test segwit transactions and blocks on P2P network.""" +from binascii import hexlify +import math +import random +import struct +import time -from test_framework.mininode import * -from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * -from test_framework.script import * from test_framework.blocktools import create_block, create_coinbase, add_witness_commitment, get_witness_script, WITNESS_COMMITMENT_HEADER from test_framework.key import CECKey, CPubKey -import math -import time -import random -from binascii import hexlify +from test_framework.messages import ( + BIP125_SEQUENCE_NUMBER, + CBlock, + CBlockHeader, + CInv, + COutPoint, + CTransaction, + CTxIn, + CTxInWitness, + CTxOut, + CTxWitness, + MAX_BLOCK_BASE_SIZE, + MSG_WITNESS_FLAG, + NODE_NETWORK, + NODE_WITNESS, + msg_block, + msg_getdata, + msg_headers, + msg_inv, + msg_tx, + msg_witness_block, + msg_witness_tx, + ser_uint256, + ser_vector, + sha256, + uint256_from_str, +) +from test_framework.mininode import ( + P2PInterface, + mininode_lock, +) +from test_framework.script import ( + CScript, + CScriptNum, + CScriptOp, + OP_0, + OP_1, + OP_16, + OP_2DROP, + OP_CHECKMULTISIG, + OP_CHECKSIG, + OP_DROP, + OP_DUP, + OP_ELSE, + OP_ENDIF, + OP_EQUAL, + OP_EQUALVERIFY, + OP_HASH160, + OP_IF, + OP_RETURN, + OP_TRUE, + SIGHASH_ALL, + SIGHASH_ANYONECANPAY, + SIGHASH_NONE, + SIGHASH_SINGLE, + SegwitVersion1SignatureHash, + SignatureHash, + hash160, +) +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import ( + assert_equal, + bytes_to_hex_str, + connect_nodes, + disconnect_nodes, + get_bip9_status, + hex_str_to_bytes, + sync_blocks, + sync_mempools, +) # The versionbit bit used to signal activation of SegWit VB_WITNESS_BIT = 1 @@ -22,14 +89,32 @@ VB_TOP_BITS = 0x20000000 MAX_SIGOP_COST = 80000 +class UTXO(): + """Used to keep track of anyone-can-spend outputs that we can use in the tests.""" + def __init__(self, sha256, n, value): + self.sha256 = sha256 + self.n = n + self.nValue = value + +def get_p2pkh_script(pubkeyhash): + """Get the script associated with a P2PKH.""" + return CScript([CScriptOp(OP_DUP), CScriptOp(OP_HASH160), pubkeyhash, CScriptOp(OP_EQUALVERIFY), CScriptOp(OP_CHECKSIG)]) + +def sign_p2pk_witness_input(script, tx_to, in_idx, hashtype, value, key): + """Add signature for a P2PK witness program.""" + tx_hash = SegwitVersion1SignatureHash(script, tx_to, in_idx, hashtype, value) + signature = key.sign(tx_hash) + chr(hashtype).encode('latin-1') + tx_to.wit.vtxinwit[in_idx].scriptWitness.stack = [signature, script] + tx_to.rehash() -# Calculate the virtual size of a witness block: -# (base + witness/4) def get_virtual_size(witness_block): + """Calculate the virtual size of a witness block. + + Virtual size is base + witness/4.""" base_size = len(witness_block.serialize(with_witness=False)) total_size = len(witness_block.serialize(with_witness=True)) # the "+3" is so we round up - vsize = int((3*base_size + total_size + 3)/4) + vsize = int((3 * base_size + total_size + 3) / 4) return vsize def test_transaction_acceptance(rpc, p2p, tx, with_witness, accepted, reason=None): @@ -43,7 +128,7 @@ def test_transaction_acceptance(rpc, p2p, tx, with_witness, accepted, reason=Non p2p.send_message(tx_message) p2p.sync_with_ping() assert_equal(tx.hash in rpc.getrawmempool(), accepted) - if (reason != None and not accepted): + if (reason is not None and not accepted): # Check the rejection reason as well. with mininode_lock: assert_equal(p2p.last_message["reject"].reason, reason) @@ -59,7 +144,7 @@ def test_witness_block(rpc, p2p, block, accepted, with_witness=True, reason=None p2p.send_message(msg_block(block)) p2p.sync_with_ping() assert_equal(rpc.getbestblockhash() == block.hash, accepted) - if (reason != None and not accepted): + if (reason is not None and not accepted): # Check the rejection reason as well. with mininode_lock: assert_equal(p2p.last_message["reject"].reason, reason) @@ -88,7 +173,7 @@ class TestP2PConn(P2PInterface): self.last_message.pop("getdata", None) self.last_message.pop("getheaders", None) msg = msg_headers() - msg.headers = [ CBlockHeader(block) ] + msg.headers = [CBlockHeader(block)] if use_header: self.send_message(msg) else: @@ -104,25 +189,6 @@ class TestP2PConn(P2PInterface): self.wait_for_block(blockhash, timeout) return self.last_message["block"].block -# Used to keep track of anyone-can-spend outputs that we can use in the tests -class UTXO(): - def __init__(self, sha256, n, nValue): - self.sha256 = sha256 - self.n = n - self.nValue = nValue - -# Helper for getting the script associated with a P2PKH -def GetP2PKHScript(pubkeyhash): - return CScript([CScriptOp(OP_DUP), CScriptOp(OP_HASH160), pubkeyhash, CScriptOp(OP_EQUALVERIFY), CScriptOp(OP_CHECKSIG)]) - -# Add signature for a P2PK witness program. -def sign_P2PK_witness_input(script, txTo, inIdx, hashtype, value, key): - tx_hash = SegwitVersion1SignatureHash(script, txTo, inIdx, hashtype, value) - signature = key.sign(tx_hash) + chr(hashtype).encode('latin-1') - txTo.wit.vtxinwit[inIdx].scriptWitness.stack = [signature, script] - txTo.rehash() - - class SegWitTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True @@ -136,43 +202,116 @@ class SegWitTest(BitcoinTestFramework): connect_nodes(self.nodes[0], 2) self.sync_all() - ''' Helpers ''' - # Build a block on top of node0's tip. - def build_next_block(self, nVersion=4): + # Helper functions + + def build_next_block(self, version=4): + """Build a block on top of node0's tip.""" tip = self.nodes[0].getbestblockhash() height = self.nodes[0].getblockcount() + 1 block_time = self.nodes[0].getblockheader(tip)["mediantime"] + 1 block = create_block(int(tip, 16), create_coinbase(height), block_time) - block.nVersion = nVersion + block.version = version block.rehash() return block - # Adds list of transactions to block, adds witness commitment, then solves. def update_witness_block_with_transactions(self, block, tx_list, nonce=0): + """Add list of transactions to block, adds witness commitment, then solves.""" block.vtx.extend(tx_list) add_witness_commitment(block, nonce) block.solve() - return - ''' Individual tests ''' - def test_witness_services(self): - self.log.info("Verifying NODE_WITNESS service bit") - assert((self.test_node.nServices & NODE_WITNESS) != 0) + def run_test(self): + # Setup the p2p connections and start up the network thread. + # self.test_node sets NODE_WITNESS|NODE_NETWORK + self.test_node = self.nodes[0].add_p2p_connection(TestP2PConn(), services=NODE_NETWORK | NODE_WITNESS) + # self.old_node sets only NODE_NETWORK + self.old_node = self.nodes[0].add_p2p_connection(TestP2PConn(), services=NODE_NETWORK) + # self.std_node is for testing node1 (fRequireStandard=true) + self.std_node = self.nodes[1].add_p2p_connection(TestP2PConn(), services=NODE_NETWORK | NODE_WITNESS) + + for conn in (self.test_node, self.old_node, self.std_node): + conn.wait_for_verack() + + assert self.test_node.nServices & NODE_WITNESS != 0 + + # Keep a place to store utxo's that can be used in later tests + self.utxo = [] + + # Segwit status 'defined' + self.segwit_status = 'defined' + + self.test_non_witness_transaction() + self.test_unnecessary_witness_before_segwit_activation() + self.test_v0_outputs_arent_spendable() + self.test_block_relay() + self.advance_to_segwit_started() + + # Segwit status 'started' + + self.test_getblocktemplate_before_lockin() + self.advance_to_segwit_lockin() + + # Segwit status 'locked_in' + + self.test_unnecessary_witness_before_segwit_activation() + self.test_witness_tx_relay_before_segwit_activation() + self.test_block_relay() + self.test_standardness_v0() + self.advance_to_segwit_active() + + # Segwit status 'active' + + self.test_p2sh_witness() + self.test_witness_commitments() + self.test_block_malleability() + self.test_witness_block_size() + self.test_submit_block() + self.test_extra_witness_data() + self.test_max_witness_push_length() + self.test_max_witness_program_length() + self.test_witness_input_length() + self.test_block_relay() + self.test_tx_relay_after_segwit_activation() + self.test_standardness_v0() + self.test_segwit_versions() + self.test_premature_coinbase_witness_spend() + self.test_uncompressed_pubkey() + self.test_signature_version_1() + self.test_non_standard_witness_blinding() + self.test_non_standard_witness() + self.test_upgrade_after_activation() + self.test_witness_sigops() + + # Individual tests + def subtest(func): # noqa: N805 + """Wraps the subtests for logging and state assertions.""" + def func_wrapper(self, *args, **kwargs): + self.log.info("Subtest: {} (Segwit status = {})".format(func.__name__, self.segwit_status)) + # Assert segwit status is as expected + assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], self.segwit_status) + func(self, *args, **kwargs) + # Each subtest should leave some utxos for the next subtest + assert self.utxo + sync_blocks(self.nodes) + # Assert segwit status is as expected at end of subtest + assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], self.segwit_status) - # See if sending a regular transaction works, and create a utxo - # to use in later tests. + return func_wrapper + + @subtest def test_non_witness_transaction(self): + """See if sending a regular transaction works, and create a utxo to use in later tests.""" # Mine a block with an anyone-can-spend coinbase, # let it mature, then try to spend it. - self.log.info("Testing non-witness transaction") - block = self.build_next_block(nVersion=1) + + block = self.build_next_block(version=1) block.solve() self.test_node.send_message(msg_block(block)) - self.test_node.sync_with_ping() # make sure the block was processed + self.test_node.sync_with_ping() # make sure the block was processed txid = block.vtx[0].sha256 - self.nodes[0].generate(99) # let the block mature + self.nodes[0].generate(99) # let the block mature # Create a transaction that spends the coinbase tx = CTransaction() @@ -185,24 +324,19 @@ class SegWitTest(BitcoinTestFramework): assert_equal(msg_tx(tx).serialize(), msg_witness_tx(tx).serialize()) self.test_node.send_message(msg_witness_tx(tx)) - self.test_node.sync_with_ping() # make sure the tx was processed + self.test_node.sync_with_ping() # make sure the tx was processed assert(tx.hash in self.nodes[0].getrawmempool()) # Save this transaction for later - self.utxo.append(UTXO(tx.sha256, 0, 49*100000000)) + self.utxo.append(UTXO(tx.sha256, 0, 49 * 100000000)) self.nodes[0].generate(1) - - # Verify that blocks with witnesses are rejected before activation. + @subtest def test_unnecessary_witness_before_segwit_activation(self): - self.log.info("Testing behavior of unnecessary witnesses") - # For now, rely on earlier tests to have created at least one utxo for - # us to use - assert(len(self.utxo) > 0) - assert(get_bip9_status(self.nodes[0], 'segwit')['status'] != 'active') + """Verify that blocks with witnesses are rejected before activation.""" tx = CTransaction() tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")) - tx.vout.append(CTxOut(self.utxo[0].nValue-1000, CScript([OP_TRUE]))) + tx.vout.append(CTxOut(self.utxo[0].nValue - 1000, CScript([OP_TRUE]))) tx.wit.vtxinwit.append(CTxInWitness()) tx.wit.vtxinwit[0].scriptWitness.stack = [CScript([CScriptNum(1)])] @@ -212,7 +346,7 @@ class SegWitTest(BitcoinTestFramework): assert(tx.sha256 != tx.calc_sha256(with_witness=True)) # Construct a segwit-signaling block that includes the transaction. - block = self.build_next_block(nVersion=(VB_TOP_BITS|(1 << VB_WITNESS_BIT))) + block = self.build_next_block(version=(VB_TOP_BITS | (1 << VB_WITNESS_BIT))) self.update_witness_block_with_transactions(block, [tx]) # Sending witness data before activation is not allowed (anti-spam # rule). @@ -220,7 +354,7 @@ class SegWitTest(BitcoinTestFramework): # TODO: fix synchronization so we can test reject reason # Right now, bitcoind delays sending reject messages for blocks # until the future, making synchronization here difficult. - #assert_equal(self.test_node.last_message["reject"].reason, "unexpected-witness") + # assert_equal(self.test_node.last_message["reject"].reason, "unexpected-witness") # But it should not be permanently marked bad... # Resend without witness information. @@ -228,37 +362,136 @@ class SegWitTest(BitcoinTestFramework): self.test_node.sync_with_ping() assert_equal(self.nodes[0].getbestblockhash(), block.hash) - sync_blocks(self.nodes) - # Update our utxo list; we spent the first entry. self.utxo.pop(0) self.utxo.append(UTXO(tx.sha256, 0, tx.vout[0].nValue)) - # ~6 months after segwit activation, the SCRIPT_VERIFY_WITNESS flag was - # backdated so that it applies to all blocks, going back to the genesis - # block. - # - # Consequently, version 0 witness outputs are never spendable without - # witness, and so can't be spent before segwit activation (the point at which - # blocks are permitted to contain witnesses). + @subtest + def test_block_relay(self): + """Test that block requests to NODE_WITNESS peer are with MSG_WITNESS_FLAG. + + This is true regardless of segwit activation. + Also test that we don't ask for blocks from unupgraded peers.""" + + blocktype = 2 | MSG_WITNESS_FLAG + + # test_node has set NODE_WITNESS, so all getdata requests should be for + # witness blocks. + # Test announcing a block via inv results in a getdata, and that + # announcing a version 4 or random VB block with a header results in a getdata + block1 = self.build_next_block() + block1.solve() + + self.test_node.announce_block_and_wait_for_getdata(block1, use_header=False) + assert(self.test_node.last_message["getdata"].inv[0].type == blocktype) + test_witness_block(self.nodes[0].rpc, self.test_node, block1, True) + + block2 = self.build_next_block(version=4) + block2.solve() + + self.test_node.announce_block_and_wait_for_getdata(block2, use_header=True) + assert(self.test_node.last_message["getdata"].inv[0].type == blocktype) + test_witness_block(self.nodes[0].rpc, self.test_node, block2, True) + + block3 = self.build_next_block(version=(VB_TOP_BITS | (1 << 15))) + block3.solve() + self.test_node.announce_block_and_wait_for_getdata(block3, use_header=True) + assert(self.test_node.last_message["getdata"].inv[0].type == blocktype) + test_witness_block(self.nodes[0].rpc, self.test_node, block3, True) + + # Check that we can getdata for witness blocks or regular blocks, + # and the right thing happens. + if self.segwit_status != 'active': + # Before activation, we should be able to request old blocks with + # or without witness, and they should be the same. + chain_height = self.nodes[0].getblockcount() + # Pick 10 random blocks on main chain, and verify that getdata's + # for MSG_BLOCK, MSG_WITNESS_BLOCK, and rpc getblock() are equal. + all_heights = list(range(chain_height + 1)) + random.shuffle(all_heights) + all_heights = all_heights[0:10] + for height in all_heights: + block_hash = self.nodes[0].getblockhash(height) + rpc_block = self.nodes[0].getblock(block_hash, False) + block_hash = int(block_hash, 16) + block = self.test_node.request_block(block_hash, 2) + wit_block = self.test_node.request_block(block_hash, 2 | MSG_WITNESS_FLAG) + assert_equal(block.serialize(True), wit_block.serialize(True)) + assert_equal(block.serialize(), hex_str_to_bytes(rpc_block)) + else: + # After activation, witness blocks and non-witness blocks should + # be different. Verify rpc getblock() returns witness blocks, while + # getdata respects the requested type. + block = self.build_next_block() + self.update_witness_block_with_transactions(block, []) + # This gives us a witness commitment. + assert(len(block.vtx[0].wit.vtxinwit) == 1) + assert(len(block.vtx[0].wit.vtxinwit[0].scriptWitness.stack) == 1) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) + # Now try to retrieve it... + rpc_block = self.nodes[0].getblock(block.hash, False) + non_wit_block = self.test_node.request_block(block.sha256, 2) + wit_block = self.test_node.request_block(block.sha256, 2 | MSG_WITNESS_FLAG) + assert_equal(wit_block.serialize(True), hex_str_to_bytes(rpc_block)) + assert_equal(wit_block.serialize(False), non_wit_block.serialize()) + assert_equal(wit_block.serialize(True), block.serialize(True)) + + # Test size, vsize, weight + rpc_details = self.nodes[0].getblock(block.hash, True) + assert_equal(rpc_details["size"], len(block.serialize(True))) + assert_equal(rpc_details["strippedsize"], len(block.serialize(False))) + weight = 3 * len(block.serialize(False)) + len(block.serialize(True)) + assert_equal(rpc_details["weight"], weight) + + # Upgraded node should not ask for blocks from unupgraded + block4 = self.build_next_block(version=4) + block4.solve() + self.old_node.getdataset = set() + + # Blocks can be requested via direct-fetch (immediately upon processing the announcement) + # or via parallel download (with an indeterminate delay from processing the announcement) + # so to test that a block is NOT requested, we could guess a time period to sleep for, + # and then check. We can avoid the sleep() by taking advantage of transaction getdata's + # being processed after block getdata's, and announce a transaction as well, + # and then check to see if that particular getdata has been received. + # Since 0.14, inv's will only be responded to with a getheaders, so send a header + # to announce this block. + msg = msg_headers() + msg.headers = [CBlockHeader(block4)] + self.old_node.send_message(msg) + self.old_node.announce_tx_and_wait_for_getdata(block4.vtx[0]) + assert(block4.sha256 not in self.old_node.getdataset) + + @subtest def test_v0_outputs_arent_spendable(self): - self.log.info("Testing that v0 witness program outputs aren't spendable before activation") + """Test that v0 outputs aren't spendable before segwit activation. - assert len(self.utxo), "self.utxo is empty" + ~6 months after segwit activation, the SCRIPT_VERIFY_WITNESS flag was + backdated so that it applies to all blocks, going back to the genesis + block. + + Consequently, version 0 witness outputs are never spendable without + witness, and so can't be spent before segwit activation (the point at which + blocks are permitted to contain witnesses).""" + + # node2 doesn't need to be connected for this test. + # (If it's connected, node0 may propogate an invalid block to it over + # compact blocks and the nodes would have inconsistent tips.) + disconnect_nodes(self.nodes[0], 2) # Create two outputs, a p2wsh and p2sh-p2wsh witness_program = CScript([OP_TRUE]) witness_hash = sha256(witness_program) - scriptPubKey = CScript([OP_0, witness_hash]) + script_pubkey = CScript([OP_0, witness_hash]) - p2sh_pubkey = hash160(scriptPubKey) - p2sh_scriptPubKey = CScript([OP_HASH160, p2sh_pubkey, OP_EQUAL]) + p2sh_pubkey = hash160(script_pubkey) + p2sh_script_pubkey = CScript([OP_HASH160, p2sh_pubkey, OP_EQUAL]) value = self.utxo[0].nValue // 3 tx = CTransaction() tx.vin = [CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b'')] - tx.vout = [CTxOut(value, scriptPubKey), CTxOut(value, p2sh_scriptPubKey)] + tx.vout = [CTxOut(value, script_pubkey), CTxOut(value, p2sh_script_pubkey)] tx.vout.append(CTxOut(value, CScript([OP_TRUE]))) tx.rehash() txid = tx.sha256 @@ -268,7 +501,7 @@ class SegWitTest(BitcoinTestFramework): self.update_witness_block_with_transactions(block, [tx]) # Verify that segwit isn't activated. A block serialized with witness # should be rejected prior to activation. - test_witness_block(self.nodes[0], self.test_node, block, accepted=False, with_witness=True, reason = b'unexpected-witness') + test_witness_block(self.nodes[0], self.test_node, block, accepted=False, with_witness=True, reason=b'unexpected-witness') # Now send the block without witness. It should be accepted test_witness_block(self.nodes[0], self.test_node, block, accepted=True, with_witness=False) @@ -281,7 +514,7 @@ class SegWitTest(BitcoinTestFramework): p2wsh_tx.rehash() p2sh_p2wsh_tx = CTransaction() - p2sh_p2wsh_tx.vin = [CTxIn(COutPoint(txid, 1), CScript([scriptPubKey]))] + p2sh_p2wsh_tx.vin = [CTxIn(COutPoint(txid, 1), CScript([script_pubkey]))] p2sh_p2wsh_tx.vout = [CTxOut(value, CScript([OP_TRUE]))] p2sh_p2wsh_tx.wit.vtxinwit.append(CTxInWitness()) p2sh_p2wsh_tx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])] @@ -306,50 +539,288 @@ class SegWitTest(BitcoinTestFramework): # TODO: support multiple acceptable reject reasons. test_witness_block(self.nodes[0], self.test_node, block, accepted=False, with_witness=False) + connect_nodes(self.nodes[0], 2) + self.utxo.pop(0) self.utxo.append(UTXO(txid, 2, value)) - # Mine enough blocks for segwit's vb state to be 'started'. + @subtest def advance_to_segwit_started(self): + """Mine enough blocks for segwit's vb state to be 'started'.""" height = self.nodes[0].getblockcount() # Will need to rewrite the tests here if we are past the first period assert(height < VB_PERIOD - 1) - # Genesis block is 'defined'. - assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], 'defined') # Advance to end of period, status should now be 'started' - self.nodes[0].generate(VB_PERIOD-height-1) + self.nodes[0].generate(VB_PERIOD - height - 1) assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], 'started') + self.segwit_status = 'started' + + @subtest + def test_getblocktemplate_before_lockin(self): + # Node0 is segwit aware, node2 is not. + for node in [self.nodes[0], self.nodes[2]]: + gbt_results = node.getblocktemplate() + block_version = gbt_results['version'] + # If we're not indicating segwit support, we will still be + # signalling for segwit activation. + assert_equal((block_version & (1 << VB_WITNESS_BIT) != 0), node == self.nodes[0]) + # If we don't specify the segwit rule, then we won't get a default + # commitment. + assert('default_witness_commitment' not in gbt_results) + + # Workaround: + # Can either change the tip, or change the mempool and wait 5 seconds + # to trigger a recomputation of getblocktemplate. + txid = int(self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1), 16) + # Using mocktime lets us avoid sleep() + sync_mempools(self.nodes) + self.nodes[0].setmocktime(int(time.time()) + 10) + self.nodes[2].setmocktime(int(time.time()) + 10) + + for node in [self.nodes[0], self.nodes[2]]: + gbt_results = node.getblocktemplate({"rules": ["segwit"]}) + block_version = gbt_results['version'] + if node == self.nodes[2]: + # If this is a non-segwit node, we should still not get a witness + # commitment, nor a version bit signalling segwit. + assert_equal(block_version & (1 << VB_WITNESS_BIT), 0) + assert('default_witness_commitment' not in gbt_results) + else: + # For segwit-aware nodes, check the version bit and the witness + # commitment are correct. + assert(block_version & (1 << VB_WITNESS_BIT) != 0) + assert('default_witness_commitment' in gbt_results) + witness_commitment = gbt_results['default_witness_commitment'] - # Mine enough blocks to lock in segwit, but don't activate. - # TODO: we could verify that lockin only happens at the right threshold of - # signalling blocks, rather than just at the right period boundary. + # Check that default_witness_commitment is present. + witness_root = CBlock.get_merkle_root([ser_uint256(0), + ser_uint256(txid)]) + script = get_witness_script(witness_root, 0) + assert_equal(witness_commitment, bytes_to_hex_str(script)) + + # undo mocktime + self.nodes[0].setmocktime(0) + self.nodes[2].setmocktime(0) + + @subtest def advance_to_segwit_lockin(self): + """Mine enough blocks to lock in segwit, but don't activate.""" + # TODO: we could verify that lockin only happens at the right threshold of + # signalling blocks, rather than just at the right period boundary. + height = self.nodes[0].getblockcount() - assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], 'started') # Advance to end of period, and verify lock-in happens at the end - self.nodes[0].generate(VB_PERIOD-1) + self.nodes[0].generate(VB_PERIOD - 1) height = self.nodes[0].getblockcount() assert((height % VB_PERIOD) == VB_PERIOD - 2) assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], 'started') self.nodes[0].generate(1) assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], 'locked_in') + self.segwit_status = 'locked_in' + @subtest + def test_witness_tx_relay_before_segwit_activation(self): - # Mine enough blocks to activate segwit. - # TODO: we could verify that activation only happens at the right threshold - # of signalling blocks, rather than just at the right period boundary. + # Generate a transaction that doesn't require a witness, but send it + # with a witness. Should be rejected for premature-witness, but should + # not be added to recently rejected list. + tx = CTransaction() + tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")) + tx.vout.append(CTxOut(self.utxo[0].nValue - 1000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))) + tx.wit.vtxinwit.append(CTxInWitness()) + tx.wit.vtxinwit[0].scriptWitness.stack = [b'a'] + tx.rehash() + + tx_hash = tx.sha256 + tx_value = tx.vout[0].nValue + + # Verify that if a peer doesn't set nServices to include NODE_WITNESS, + # the getdata is just for the non-witness portion. + self.old_node.announce_tx_and_wait_for_getdata(tx) + assert(self.old_node.last_message["getdata"].inv[0].type == 1) + + # Since we haven't delivered the tx yet, inv'ing the same tx from + # a witness transaction ought not result in a getdata. + self.test_node.announce_tx_and_wait_for_getdata(tx, timeout=2, success=False) + + # Delivering this transaction with witness should fail (no matter who + # its from) + assert_equal(len(self.nodes[0].getrawmempool()), 0) + assert_equal(len(self.nodes[1].getrawmempool()), 0) + test_transaction_acceptance(self.nodes[0].rpc, self.old_node, tx, with_witness=True, accepted=False) + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx, with_witness=True, accepted=False) + + # But eliminating the witness should fix it + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx, with_witness=False, accepted=True) + + # Cleanup: mine the first transaction and update utxo + self.nodes[0].generate(1) + assert_equal(len(self.nodes[0].getrawmempool()), 0) + + self.utxo.pop(0) + self.utxo.append(UTXO(tx_hash, 0, tx_value)) + + @subtest + def test_standardness_v0(self): + """Test V0 txout standardness. + + V0 segwit outputs and inputs are always standard. + V0 segwit inputs may only be mined after activation, but not before.""" + + witness_program = CScript([OP_TRUE]) + witness_hash = sha256(witness_program) + script_pubkey = CScript([OP_0, witness_hash]) + + p2sh_pubkey = hash160(witness_program) + p2sh_script_pubkey = CScript([OP_HASH160, p2sh_pubkey, OP_EQUAL]) + + # First prepare a p2sh output (so that spending it will pass standardness) + p2sh_tx = CTransaction() + p2sh_tx.vin = [CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")] + p2sh_tx.vout = [CTxOut(self.utxo[0].nValue - 1000, p2sh_script_pubkey)] + p2sh_tx.rehash() + + # Mine it on test_node to create the confirmed output. + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, p2sh_tx, with_witness=True, accepted=True) + self.nodes[0].generate(1) + sync_blocks(self.nodes) + + # Now test standardness of v0 P2WSH outputs. + # Start by creating a transaction with two outputs. + tx = CTransaction() + tx.vin = [CTxIn(COutPoint(p2sh_tx.sha256, 0), CScript([witness_program]))] + tx.vout = [CTxOut(p2sh_tx.vout[0].nValue - 10000, script_pubkey)] + tx.vout.append(CTxOut(8000, script_pubkey)) # Might burn this later + tx.vin[0].nSequence = BIP125_SEQUENCE_NUMBER # Just to have the option to bump this tx from the mempool + tx.rehash() + + # This is always accepted, since the mempool policy is to consider segwit as always active + # and thus allow segwit outputs + test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx, with_witness=True, accepted=True) + + # Now create something that looks like a P2PKH output. This won't be spendable. + script_pubkey = CScript([OP_0, hash160(witness_hash)]) + tx2 = CTransaction() + # tx was accepted, so we spend the second output. + tx2.vin = [CTxIn(COutPoint(tx.sha256, 1), b"")] + tx2.vout = [CTxOut(7000, script_pubkey)] + tx2.wit.vtxinwit.append(CTxInWitness()) + tx2.wit.vtxinwit[0].scriptWitness.stack = [witness_program] + tx2.rehash() + + test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx2, with_witness=True, accepted=True) + + # Now update self.utxo for later tests. + tx3 = CTransaction() + # tx and tx2 were both accepted. Don't bother trying to reclaim the + # P2PKH output; just send tx's first output back to an anyone-can-spend. + sync_mempools([self.nodes[0], self.nodes[1]]) + tx3.vin = [CTxIn(COutPoint(tx.sha256, 0), b"")] + tx3.vout = [CTxOut(tx.vout[0].nValue - 1000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))] + tx3.wit.vtxinwit.append(CTxInWitness()) + tx3.wit.vtxinwit[0].scriptWitness.stack = [witness_program] + tx3.rehash() + if self.segwit_status != 'active': + # Just check mempool acceptance, but don't add the transaction to the mempool, since witness is disallowed + # in blocks and the tx is impossible to mine right now. + assert_equal(self.nodes[0].testmempoolaccept([bytes_to_hex_str(tx3.serialize_with_witness())]), [{'txid': tx3.hash, 'allowed': True}]) + # Create the same output as tx3, but by replacing tx + tx3_out = tx3.vout[0] + tx3 = tx + tx3.vout = [tx3_out] + tx3.rehash() + assert_equal(self.nodes[0].testmempoolaccept([bytes_to_hex_str(tx3.serialize_with_witness())]), [{'txid': tx3.hash, 'allowed': True}]) + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx3, with_witness=True, accepted=True) + + self.nodes[0].generate(1) + sync_blocks(self.nodes) + self.utxo.pop(0) + self.utxo.append(UTXO(tx3.sha256, 0, tx3.vout[0].nValue)) + assert_equal(len(self.nodes[1].getrawmempool()), 0) + + @subtest def advance_to_segwit_active(self): - assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], 'locked_in') + """Mine enough blocks to activate segwit.""" + # TODO: we could verify that activation only happens at the right threshold + # of signalling blocks, rather than just at the right period boundary. + height = self.nodes[0].getblockcount() - self.nodes[0].generate(VB_PERIOD - (height%VB_PERIOD) - 2) + self.nodes[0].generate(VB_PERIOD - (height % VB_PERIOD) - 2) assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], 'locked_in') self.nodes[0].generate(1) assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], 'active') + self.segwit_status = 'active' + + @subtest + def test_p2sh_witness(self): + """Test P2SH wrapped witness programs.""" + + # Prepare the p2sh-wrapped witness output + witness_program = CScript([OP_DROP, OP_TRUE]) + witness_hash = sha256(witness_program) + p2wsh_pubkey = CScript([OP_0, witness_hash]) + p2sh_witness_hash = hash160(p2wsh_pubkey) + script_pubkey = CScript([OP_HASH160, p2sh_witness_hash, OP_EQUAL]) + script_sig = CScript([p2wsh_pubkey]) # a push of the redeem script + + # Fund the P2SH output + tx = CTransaction() + tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")) + tx.vout.append(CTxOut(self.utxo[0].nValue - 1000, script_pubkey)) + tx.rehash() + # Verify mempool acceptance and block validity + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx, with_witness=False, accepted=True) + block = self.build_next_block() + self.update_witness_block_with_transactions(block, [tx]) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True, with_witness=True) + sync_blocks(self.nodes) + + # Now test attempts to spend the output. + spend_tx = CTransaction() + spend_tx.vin.append(CTxIn(COutPoint(tx.sha256, 0), script_sig)) + spend_tx.vout.append(CTxOut(tx.vout[0].nValue - 1000, CScript([OP_TRUE]))) + spend_tx.rehash() + + # This transaction should not be accepted into the mempool pre- or + # post-segwit. Mempool acceptance will use SCRIPT_VERIFY_WITNESS which + # will require a witness to spend a witness program regardless of + # segwit activation. Note that older bitcoind's that are not + # segwit-aware would also reject this for failing CLEANSTACK. + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, spend_tx, with_witness=False, accepted=False) - # This test can only be run after segwit has activated + # Try to put the witness script in the script_sig, should also fail. + spend_tx.vin[0].script_sig = CScript([p2wsh_pubkey, b'a']) + spend_tx.rehash() + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, spend_tx, with_witness=False, accepted=False) + + # Now put the witness script in the witness, should succeed after + # segwit activates. + spend_tx.vin[0].scriptSig = script_sig + spend_tx.rehash() + spend_tx.wit.vtxinwit.append(CTxInWitness()) + spend_tx.wit.vtxinwit[0].scriptWitness.stack = [b'a', witness_program] + + # Verify mempool acceptance + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, spend_tx, with_witness=True, accepted=True) + block = self.build_next_block() + self.update_witness_block_with_transactions(block, [spend_tx]) + + # If we're after activation, then sending this with witnesses should be valid. + # This no longer works before activation, because SCRIPT_VERIFY_WITNESS + # is always set. + # TODO: rewrite this test to make clear that it only works after activation. + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) + + # Update self.utxo + self.utxo.pop(0) + self.utxo.append(UTXO(spend_tx.sha256, 0, spend_tx.vout[0].nValue)) + + @subtest def test_witness_commitments(self): - self.log.info("Testing witness commitments") + """Test witness commitments. + + This test can only be run after segwit has activated.""" # First try a correct witness commitment. block = self.build_next_block() @@ -374,21 +845,20 @@ class SegWitTest(BitcoinTestFramework): test_witness_block(self.nodes[0].rpc, self.test_node, block_2, accepted=True) # Now test commitments with actual transactions - assert (len(self.utxo) > 0) tx = CTransaction() tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")) # Let's construct a witness program witness_program = CScript([OP_TRUE]) witness_hash = sha256(witness_program) - scriptPubKey = CScript([OP_0, witness_hash]) - tx.vout.append(CTxOut(self.utxo[0].nValue-1000, scriptPubKey)) + script_pubkey = CScript([OP_0, witness_hash]) + tx.vout.append(CTxOut(self.utxo[0].nValue - 1000, script_pubkey)) tx.rehash() # tx2 will spend tx1, and send back to a regular anyone-can-spend address tx2 = CTransaction() tx2.vin.append(CTxIn(COutPoint(tx.sha256, 0), b"")) - tx2.vout.append(CTxOut(tx.vout[0].nValue-1000, witness_program)) + tx2.vout.append(CTxOut(tx.vout[0].nValue - 1000, witness_program)) tx2.wit.vtxinwit.append(CTxInWitness()) tx2.wit.vtxinwit[0].scriptWitness.stack = [witness_program] tx2.rehash() @@ -416,7 +886,7 @@ class SegWitTest(BitcoinTestFramework): block_3.vtx[0].rehash() block_3.hashMerkleRoot = block_3.calc_merkle_root() block_3.rehash() - assert(len(block_3.vtx[0].vout) == 4) # 3 OP_returns + assert(len(block_3.vtx[0].vout) == 4) # 3 OP_returns block_3.solve() test_witness_block(self.nodes[0].rpc, self.test_node, block_3, accepted=True) @@ -425,7 +895,7 @@ class SegWitTest(BitcoinTestFramework): block_4 = self.build_next_block() tx3 = CTransaction() tx3.vin.append(CTxIn(COutPoint(tx2.sha256, 0), b"")) - tx3.vout.append(CTxOut(tx.vout[0].nValue-1000, witness_program)) + tx3.vout.append(CTxOut(tx.vout[0].nValue - 1000, witness_program)) tx3.rehash() block_4.vtx.append(tx3) block_4.hashMerkleRoot = block_4.calc_merkle_root() @@ -436,9 +906,8 @@ class SegWitTest(BitcoinTestFramework): self.utxo.pop(0) self.utxo.append(UTXO(tx3.sha256, 0, tx3.vout[0].nValue)) - + @subtest def test_block_malleability(self): - self.log.info("Testing witness block malleability") # Make sure that a block that has too big a virtual size # because of a too-large coinbase witness is not permanently @@ -447,7 +916,7 @@ class SegWitTest(BitcoinTestFramework): add_witness_commitment(block) block.solve() - block.vtx[0].wit.vtxinwit[0].scriptWitness.stack.append(b'a'*5000000) + block.vtx[0].wit.vtxinwit[0].scriptWitness.stack.append(b'a' * 5000000) assert(get_virtual_size(block) > MAX_BLOCK_BASE_SIZE) # We can't send over the p2p network, because this is too big to relay @@ -470,16 +939,15 @@ class SegWitTest(BitcoinTestFramework): # Change the nonce -- should not cause the block to be permanently # failed - block.vtx[0].wit.vtxinwit[0].scriptWitness.stack = [ ser_uint256(1) ] + block.vtx[0].wit.vtxinwit[0].scriptWitness.stack = [ser_uint256(1)] test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False) # Changing the witness reserved value doesn't change the block hash - block.vtx[0].wit.vtxinwit[0].scriptWitness.stack = [ ser_uint256(0) ] + block.vtx[0].wit.vtxinwit[0].scriptWitness.stack = [ser_uint256(0)] test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) - + @subtest def test_witness_block_size(self): - self.log.info("Testing witness block size limit") # TODO: Test that non-witness carrying blocks can't exceed 1MB # Skipping this test for now; this is covered in p2p-fullblocktest.py @@ -492,21 +960,21 @@ class SegWitTest(BitcoinTestFramework): # The witness program will be a bunch of OP_2DROP's, followed by OP_TRUE. # This should give us plenty of room to tweak the spending tx's # virtual size. - NUM_DROPS = 200 # 201 max ops per script! + NUM_DROPS = 200 # 201 max ops per script! NUM_OUTPUTS = 50 - witness_program = CScript([OP_2DROP]*NUM_DROPS + [OP_TRUE]) + witness_program = CScript([OP_2DROP] * NUM_DROPS + [OP_TRUE]) witness_hash = uint256_from_str(sha256(witness_program)) - scriptPubKey = CScript([OP_0, ser_uint256(witness_hash)]) + script_pubkey = CScript([OP_0, ser_uint256(witness_hash)]) prevout = COutPoint(self.utxo[0].sha256, self.utxo[0].n) value = self.utxo[0].nValue parent_tx = CTransaction() parent_tx.vin.append(CTxIn(prevout, b"")) - child_value = int(value/NUM_OUTPUTS) + child_value = int(value / NUM_OUTPUTS) for i in range(NUM_OUTPUTS): - parent_tx.vout.append(CTxOut(child_value, scriptPubKey)) + parent_tx.vout.append(CTxOut(child_value, script_pubkey)) parent_tx.vout[0].nValue -= 50000 assert(parent_tx.vout[0].nValue > 0) parent_tx.rehash() @@ -517,17 +985,17 @@ class SegWitTest(BitcoinTestFramework): child_tx.vout = [CTxOut(value - 100000, CScript([OP_TRUE]))] for i in range(NUM_OUTPUTS): child_tx.wit.vtxinwit.append(CTxInWitness()) - child_tx.wit.vtxinwit[-1].scriptWitness.stack = [b'a'*195]*(2*NUM_DROPS) + [witness_program] + child_tx.wit.vtxinwit[-1].scriptWitness.stack = [b'a' * 195] * (2 * NUM_DROPS) + [witness_program] child_tx.rehash() self.update_witness_block_with_transactions(block, [parent_tx, child_tx]) vsize = get_virtual_size(block) - additional_bytes = (MAX_BLOCK_BASE_SIZE - vsize)*4 + additional_bytes = (MAX_BLOCK_BASE_SIZE - vsize) * 4 i = 0 while additional_bytes > 0: # Add some more bytes to each input until we hit MAX_BLOCK_BASE_SIZE+1 - extra_bytes = min(additional_bytes+1, 55) - block.vtx[-1].wit.vtxinwit[int(i/(2*NUM_DROPS))].scriptWitness.stack[i%(2*NUM_DROPS)] = b'a'*(195+extra_bytes) + extra_bytes = min(additional_bytes + 1, 55) + block.vtx[-1].wit.vtxinwit[int(i / (2 * NUM_DROPS))].scriptWitness.stack[i % (2 * NUM_DROPS)] = b'a' * (195 + extra_bytes) additional_bytes -= extra_bytes i += 1 @@ -538,13 +1006,13 @@ class SegWitTest(BitcoinTestFramework): assert_equal(vsize, MAX_BLOCK_BASE_SIZE + 1) # Make sure that our test case would exceed the old max-network-message # limit - assert(len(block.serialize(True)) > 2*1024*1024) + assert(len(block.serialize(True)) > 2 * 1024 * 1024) test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False) # Now resize the second transaction to make the block fit. cur_length = len(block.vtx[-1].wit.vtxinwit[0].scriptWitness.stack[0]) - block.vtx[-1].wit.vtxinwit[0].scriptWitness.stack[0] = b'a'*(cur_length-1) + block.vtx[-1].wit.vtxinwit[0].scriptWitness.stack[0] = b'a' * (cur_length - 1) block.vtx[0].vout.pop() add_witness_commitment(block) block.solve() @@ -556,16 +1024,15 @@ class SegWitTest(BitcoinTestFramework): self.utxo.pop(0) self.utxo.append(UTXO(block.vtx[-1].sha256, 0, block.vtx[-1].vout[0].nValue)) - - # submitblock will try to add the nonce automatically, so that mining - # software doesn't need to worry about doing so itself. + @subtest def test_submit_block(self): + """Test that submitblock adds the nonce automatically when possible.""" block = self.build_next_block() # Try using a custom nonce and then don't supply it. # This shouldn't possibly work. add_witness_commitment(block, nonce=1) - block.vtx[0].wit = CTxWitness() # drop the nonce + block.vtx[0].wit = CTxWitness() # drop the nonce block.solve() self.nodes[0].submitblock(bytes_to_hex_str(block.serialize(True))) assert(self.nodes[0].getbestblockhash() != block.hash) @@ -593,24 +1060,21 @@ class SegWitTest(BitcoinTestFramework): # Tip should not advance! assert(self.nodes[0].getbestblockhash() != block_2.hash) - - # Consensus tests of extra witness data in a transaction. + @subtest def test_extra_witness_data(self): - self.log.info("Testing extra witness data in tx") - - assert(len(self.utxo) > 0) + """Test extra witness data in a transaction.""" block = self.build_next_block() witness_program = CScript([OP_DROP, OP_TRUE]) witness_hash = sha256(witness_program) - scriptPubKey = CScript([OP_0, witness_hash]) + script_pubkey = CScript([OP_0, witness_hash]) # First try extra witness data on a tx that doesn't require a witness tx = CTransaction() tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")) - tx.vout.append(CTxOut(self.utxo[0].nValue-2000, scriptPubKey)) - tx.vout.append(CTxOut(1000, CScript([OP_TRUE]))) # non-witness output + tx.vout.append(CTxOut(self.utxo[0].nValue - 2000, script_pubkey)) + tx.vout.append(CTxOut(1000, CScript([OP_TRUE]))) # non-witness output tx.wit.vtxinwit.append(CTxInWitness()) tx.wit.vtxinwit[0].scriptWitness.stack = [CScript([])] tx.rehash() @@ -631,12 +1095,12 @@ class SegWitTest(BitcoinTestFramework): # Now try extra witness/signature data on an input that DOES require a # witness tx2 = CTransaction() - tx2.vin.append(CTxIn(COutPoint(tx.sha256, 0), b"")) # witness output - tx2.vin.append(CTxIn(COutPoint(tx.sha256, 1), b"")) # non-witness + tx2.vin.append(CTxIn(COutPoint(tx.sha256, 0), b"")) # witness output + tx2.vin.append(CTxIn(COutPoint(tx.sha256, 1), b"")) # non-witness tx2.vout.append(CTxOut(tx.vout[0].nValue, CScript([OP_TRUE]))) tx2.wit.vtxinwit.extend([CTxInWitness(), CTxInWitness()]) - tx2.wit.vtxinwit[0].scriptWitness.stack = [ CScript([CScriptNum(1)]), CScript([CScriptNum(1)]), witness_program ] - tx2.wit.vtxinwit[1].scriptWitness.stack = [ CScript([OP_TRUE]) ] + tx2.wit.vtxinwit[0].scriptWitness.stack = [CScript([CScriptNum(1)]), CScript([CScriptNum(1)]), witness_program] + tx2.wit.vtxinwit[1].scriptWitness.stack = [CScript([OP_TRUE])] block = self.build_next_block() self.update_witness_block_with_transactions(block, [tx2]) @@ -669,37 +1133,36 @@ class SegWitTest(BitcoinTestFramework): self.utxo.pop(0) self.utxo.append(UTXO(tx2.sha256, 0, tx2.vout[0].nValue)) - + @subtest def test_max_witness_push_length(self): - ''' Should only allow up to 520 byte pushes in witness stack ''' - self.log.info("Testing maximum witness push size") + """Test that witness stack can only allow up to 520 byte pushes.""" + MAX_SCRIPT_ELEMENT_SIZE = 520 - assert(len(self.utxo)) block = self.build_next_block() witness_program = CScript([OP_DROP, OP_TRUE]) witness_hash = sha256(witness_program) - scriptPubKey = CScript([OP_0, witness_hash]) + script_pubkey = CScript([OP_0, witness_hash]) tx = CTransaction() tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")) - tx.vout.append(CTxOut(self.utxo[0].nValue-1000, scriptPubKey)) + tx.vout.append(CTxOut(self.utxo[0].nValue - 1000, script_pubkey)) tx.rehash() tx2 = CTransaction() tx2.vin.append(CTxIn(COutPoint(tx.sha256, 0), b"")) - tx2.vout.append(CTxOut(tx.vout[0].nValue-1000, CScript([OP_TRUE]))) + tx2.vout.append(CTxOut(tx.vout[0].nValue - 1000, CScript([OP_TRUE]))) tx2.wit.vtxinwit.append(CTxInWitness()) # First try a 521-byte stack element - tx2.wit.vtxinwit[0].scriptWitness.stack = [ b'a'*(MAX_SCRIPT_ELEMENT_SIZE+1), witness_program ] + tx2.wit.vtxinwit[0].scriptWitness.stack = [b'a' * (MAX_SCRIPT_ELEMENT_SIZE + 1), witness_program] tx2.rehash() self.update_witness_block_with_transactions(block, [tx, tx2]) test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False) # Now reduce the length of the stack element - tx2.wit.vtxinwit[0].scriptWitness.stack[0] = b'a'*(MAX_SCRIPT_ELEMENT_SIZE) + tx2.wit.vtxinwit[0].scriptWitness.stack[0] = b'a' * (MAX_SCRIPT_ELEMENT_SIZE) add_witness_commitment(block) block.solve() @@ -709,31 +1172,30 @@ class SegWitTest(BitcoinTestFramework): self.utxo.pop() self.utxo.append(UTXO(tx2.sha256, 0, tx2.vout[0].nValue)) + @subtest def test_max_witness_program_length(self): - # Can create witness outputs that are long, but can't be greater than - # 10k bytes to successfully spend - self.log.info("Testing maximum witness program length") - assert(len(self.utxo)) + """Test that witness outputs greater than 10kB can't be spent.""" + MAX_PROGRAM_LENGTH = 10000 # This program is 19 max pushes (9937 bytes), then 64 more opcode-bytes. - long_witness_program = CScript([b'a'*520]*19 + [OP_DROP]*63 + [OP_TRUE]) - assert(len(long_witness_program) == MAX_PROGRAM_LENGTH+1) + long_witness_program = CScript([b'a' * 520] * 19 + [OP_DROP] * 63 + [OP_TRUE]) + assert(len(long_witness_program) == MAX_PROGRAM_LENGTH + 1) long_witness_hash = sha256(long_witness_program) - long_scriptPubKey = CScript([OP_0, long_witness_hash]) + long_script_pubkey = CScript([OP_0, long_witness_hash]) block = self.build_next_block() tx = CTransaction() tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")) - tx.vout.append(CTxOut(self.utxo[0].nValue-1000, long_scriptPubKey)) + tx.vout.append(CTxOut(self.utxo[0].nValue - 1000, long_script_pubkey)) tx.rehash() tx2 = CTransaction() tx2.vin.append(CTxIn(COutPoint(tx.sha256, 0), b"")) - tx2.vout.append(CTxOut(tx.vout[0].nValue-1000, CScript([OP_TRUE]))) + tx2.vout.append(CTxOut(tx.vout[0].nValue - 1000, CScript([OP_TRUE]))) tx2.wit.vtxinwit.append(CTxInWitness()) - tx2.wit.vtxinwit[0].scriptWitness.stack = [b'a']*44 + [long_witness_program] + tx2.wit.vtxinwit[0].scriptWitness.stack = [b'a'] * 44 + [long_witness_program] tx2.rehash() self.update_witness_block_with_transactions(block, [tx, tx2]) @@ -741,15 +1203,15 @@ class SegWitTest(BitcoinTestFramework): test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False) # Try again with one less byte in the witness program - witness_program = CScript([b'a'*520]*19 + [OP_DROP]*62 + [OP_TRUE]) + witness_program = CScript([b'a' * 520] * 19 + [OP_DROP] * 62 + [OP_TRUE]) assert(len(witness_program) == MAX_PROGRAM_LENGTH) witness_hash = sha256(witness_program) - scriptPubKey = CScript([OP_0, witness_hash]) + script_pubkey = CScript([OP_0, witness_hash]) - tx.vout[0] = CTxOut(tx.vout[0].nValue, scriptPubKey) + tx.vout[0] = CTxOut(tx.vout[0].nValue, script_pubkey) tx.rehash() tx2.vin[0].prevout.hash = tx.sha256 - tx2.wit.vtxinwit[0].scriptWitness.stack = [b'a']*43 + [witness_program] + tx2.wit.vtxinwit[0].scriptWitness.stack = [b'a'] * 43 + [witness_program] tx2.rehash() block.vtx = [block.vtx[0]] self.update_witness_block_with_transactions(block, [tx, tx2]) @@ -758,22 +1220,20 @@ class SegWitTest(BitcoinTestFramework): self.utxo.pop() self.utxo.append(UTXO(tx2.sha256, 0, tx2.vout[0].nValue)) - + @subtest def test_witness_input_length(self): - ''' Ensure that vin length must match vtxinwit length ''' - self.log.info("Testing witness input length") - assert(len(self.utxo)) + """Test that vin length must match vtxinwit length.""" witness_program = CScript([OP_DROP, OP_TRUE]) witness_hash = sha256(witness_program) - scriptPubKey = CScript([OP_0, witness_hash]) + script_pubkey = CScript([OP_0, witness_hash]) # Create a transaction that splits our utxo into many outputs tx = CTransaction() tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")) - nValue = self.utxo[0].nValue + value = self.utxo[0].nValue for i in range(10): - tx.vout.append(CTxOut(int(nValue/10), scriptPubKey)) + tx.vout.append(CTxOut(int(value / 10), script_pubkey)) tx.vout[0].nValue -= 1000 assert(tx.vout[0].nValue >= 0) @@ -805,7 +1265,7 @@ class SegWitTest(BitcoinTestFramework): tx2 = BrokenCTransaction() for i in range(10): tx2.vin.append(CTxIn(COutPoint(tx.sha256, i), b"")) - tx2.vout.append(CTxOut(nValue-3000, CScript([OP_TRUE]))) + tx2.vout.append(CTxOut(value - 3000, CScript([OP_TRUE]))) # First try using a too long vtxinwit for i in range(11): @@ -827,7 +1287,7 @@ class SegWitTest(BitcoinTestFramework): # Now make one of the intermediate witnesses be incorrect tx2.wit.vtxinwit.append(CTxInWitness()) tx2.wit.vtxinwit[-1].scriptWitness.stack = [b'a', witness_program] - tx2.wit.vtxinwit[5].scriptWitness.stack = [ witness_program ] + tx2.wit.vtxinwit[5].scriptWitness.stack = [witness_program] block.vtx = [block.vtx[0]] self.update_witness_block_with_transactions(block, [tx2]) @@ -842,65 +1302,23 @@ class SegWitTest(BitcoinTestFramework): self.utxo.pop() self.utxo.append(UTXO(tx2.sha256, 0, tx2.vout[0].nValue)) + @subtest + def test_tx_relay_after_segwit_activation(self): + """Test transaction relay after segwit activation. - def test_witness_tx_relay_before_segwit_activation(self): - self.log.info("Testing relay of witness transactions") - # Generate a transaction that doesn't require a witness, but send it - # with a witness. Should be rejected for premature-witness, but should - # not be added to recently rejected list. - assert(len(self.utxo)) - tx = CTransaction() - tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")) - tx.vout.append(CTxOut(self.utxo[0].nValue - 1000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))) - tx.wit.vtxinwit.append(CTxInWitness()) - tx.wit.vtxinwit[0].scriptWitness.stack = [ b'a' ] - tx.rehash() - - tx_hash = tx.sha256 - tx_value = tx.vout[0].nValue - - # Verify that if a peer doesn't set nServices to include NODE_WITNESS, - # the getdata is just for the non-witness portion. - self.old_node.announce_tx_and_wait_for_getdata(tx) - assert(self.old_node.last_message["getdata"].inv[0].type == 1) - - # Since we haven't delivered the tx yet, inv'ing the same tx from - # a witness transaction ought not result in a getdata. - self.test_node.announce_tx_and_wait_for_getdata(tx, timeout=2, success=False) - - # Delivering this transaction with witness should fail (no matter who - # its from) - assert_equal(len(self.nodes[0].getrawmempool()), 0) - assert_equal(len(self.nodes[1].getrawmempool()), 0) - test_transaction_acceptance(self.nodes[0].rpc, self.old_node, tx, with_witness=True, accepted=False) - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx, with_witness=True, accepted=False) - - # But eliminating the witness should fix it - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx, with_witness=False, accepted=True) - - # Cleanup: mine the first transaction and update utxo - self.nodes[0].generate(1) - assert_equal(len(self.nodes[0].getrawmempool()), 0) - - self.utxo.pop(0) - self.utxo.append(UTXO(tx_hash, 0, tx_value)) - + After segwit activates, verify that mempool: + - rejects transactions with unnecessary/extra witnesses + - accepts transactions with valid witnesses + and that witness transactions are relayed to non-upgraded peers.""" - # After segwit activates, verify that mempool: - # - rejects transactions with unnecessary/extra witnesses - # - accepts transactions with valid witnesses - # and that witness transactions are relayed to non-upgraded peers. - def test_tx_relay_after_segwit_activation(self): - self.log.info("Testing relay of witness transactions") # Generate a transaction that doesn't require a witness, but send it # with a witness. Should be rejected because we can't use a witness # when spending a non-witness output. - assert(len(self.utxo)) tx = CTransaction() tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")) tx.vout.append(CTxOut(self.utxo[0].nValue - 1000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))) tx.wit.vtxinwit.append(CTxInWitness()) - tx.wit.vtxinwit[0].scriptWitness.stack = [ b'a' ] + tx.wit.vtxinwit[0].scriptWitness.stack = [b'a'] tx.rehash() tx_hash = tx.sha256 @@ -917,10 +1335,10 @@ class SegWitTest(BitcoinTestFramework): # Now try to add extra witness data to a valid witness tx. witness_program = CScript([OP_TRUE]) witness_hash = sha256(witness_program) - scriptPubKey = CScript([OP_0, witness_hash]) + script_pubkey = CScript([OP_0, witness_hash]) tx2 = CTransaction() tx2.vin.append(CTxIn(COutPoint(tx_hash, 0), b"")) - tx2.vout.append(CTxOut(tx.vout[0].nValue-1000, scriptPubKey)) + tx2.vout.append(CTxOut(tx.vout[0].nValue - 1000, script_pubkey)) tx2.rehash() tx3 = CTransaction() @@ -930,8 +1348,8 @@ class SegWitTest(BitcoinTestFramework): # Add too-large for IsStandard witness and check that it does not enter reject filter p2sh_program = CScript([OP_TRUE]) p2sh_pubkey = hash160(p2sh_program) - witness_program2 = CScript([b'a'*400000]) - tx3.vout.append(CTxOut(tx2.vout[0].nValue-1000, CScript([OP_HASH160, p2sh_pubkey, OP_EQUAL]))) + witness_program2 = CScript([b'a' * 400000]) + tx3.vout.append(CTxOut(tx2.vout[0].nValue - 1000, CScript([OP_HASH160, p2sh_pubkey, OP_EQUAL]))) tx3.wit.vtxinwit[0].scriptWitness.stack = [witness_program2] tx3.rehash() @@ -943,17 +1361,17 @@ class SegWitTest(BitcoinTestFramework): # Remove witness stuffing, instead add extra witness push on stack tx3.vout[0] = CTxOut(tx2.vout[0].nValue - 1000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE])) - tx3.wit.vtxinwit[0].scriptWitness.stack = [CScript([CScriptNum(1)]), witness_program ] + tx3.wit.vtxinwit[0].scriptWitness.stack = [CScript([CScriptNum(1)]), witness_program] tx3.rehash() test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx2, with_witness=True, accepted=True) test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx3, with_witness=True, accepted=False) # Get rid of the extra witness, and verify acceptance. - tx3.wit.vtxinwit[0].scriptWitness.stack = [ witness_program ] + tx3.wit.vtxinwit[0].scriptWitness.stack = [witness_program] # Also check that old_node gets a tx announcement, even though this is # a witness transaction. - self.old_node.wait_for_inv([CInv(1, tx2.sha256)]) # wait until tx2 was inv'ed + self.old_node.wait_for_inv([CInv(1, tx2.sha256)]) # wait until tx2 was inv'ed test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx3, with_witness=True, accepted=True) self.old_node.wait_for_inv([CInv(1, tx3.sha256)]) @@ -962,7 +1380,7 @@ class SegWitTest(BitcoinTestFramework): raw_tx = self.nodes[0].getrawtransaction(tx3.hash, 1) assert_equal(int(raw_tx["hash"], 16), tx3.calc_sha256(True)) assert_equal(raw_tx["size"], len(tx3.serialize_with_witness())) - weight = len(tx3.serialize_with_witness()) + 3*len(tx3.serialize_without_witness()) + weight = len(tx3.serialize_with_witness()) + 3 * len(tx3.serialize_without_witness()) vsize = math.ceil(weight / 4) assert_equal(raw_tx["vsize"], vsize) assert_equal(raw_tx["weight"], weight) @@ -972,202 +1390,31 @@ class SegWitTest(BitcoinTestFramework): # Cleanup: mine the transactions and update utxo for next test self.nodes[0].generate(1) - assert_equal(len(self.nodes[0].getrawmempool()), 0) + assert_equal(len(self.nodes[0].getrawmempool()), 0) self.utxo.pop(0) self.utxo.append(UTXO(tx3.sha256, 0, tx3.vout[0].nValue)) + @subtest + def test_segwit_versions(self): + """Test validity of future segwit version transactions. - # Test that block requests to NODE_WITNESS peer are with MSG_WITNESS_FLAG - # This is true regardless of segwit activation. - # Also test that we don't ask for blocks from unupgraded peers - def test_block_relay(self, segwit_activated): - self.log.info("Testing block relay") - - blocktype = 2|MSG_WITNESS_FLAG - - # test_node has set NODE_WITNESS, so all getdata requests should be for - # witness blocks. - # Test announcing a block via inv results in a getdata, and that - # announcing a version 4 or random VB block with a header results in a getdata - block1 = self.build_next_block() - block1.solve() - - self.test_node.announce_block_and_wait_for_getdata(block1, use_header=False) - assert(self.test_node.last_message["getdata"].inv[0].type == blocktype) - test_witness_block(self.nodes[0].rpc, self.test_node, block1, True) - - block2 = self.build_next_block(nVersion=4) - block2.solve() - - self.test_node.announce_block_and_wait_for_getdata(block2, use_header=True) - assert(self.test_node.last_message["getdata"].inv[0].type == blocktype) - test_witness_block(self.nodes[0].rpc, self.test_node, block2, True) - - block3 = self.build_next_block(nVersion=(VB_TOP_BITS | (1<<15))) - block3.solve() - self.test_node.announce_block_and_wait_for_getdata(block3, use_header=True) - assert(self.test_node.last_message["getdata"].inv[0].type == blocktype) - test_witness_block(self.nodes[0].rpc, self.test_node, block3, True) - - # Check that we can getdata for witness blocks or regular blocks, - # and the right thing happens. - if segwit_activated == False: - # Before activation, we should be able to request old blocks with - # or without witness, and they should be the same. - chain_height = self.nodes[0].getblockcount() - # Pick 10 random blocks on main chain, and verify that getdata's - # for MSG_BLOCK, MSG_WITNESS_BLOCK, and rpc getblock() are equal. - all_heights = list(range(chain_height+1)) - random.shuffle(all_heights) - all_heights = all_heights[0:10] - for height in all_heights: - block_hash = self.nodes[0].getblockhash(height) - rpc_block = self.nodes[0].getblock(block_hash, False) - block_hash = int(block_hash, 16) - block = self.test_node.request_block(block_hash, 2) - wit_block = self.test_node.request_block(block_hash, 2|MSG_WITNESS_FLAG) - assert_equal(block.serialize(True), wit_block.serialize(True)) - assert_equal(block.serialize(), hex_str_to_bytes(rpc_block)) - else: - # After activation, witness blocks and non-witness blocks should - # be different. Verify rpc getblock() returns witness blocks, while - # getdata respects the requested type. - block = self.build_next_block() - self.update_witness_block_with_transactions(block, []) - # This gives us a witness commitment. - assert(len(block.vtx[0].wit.vtxinwit) == 1) - assert(len(block.vtx[0].wit.vtxinwit[0].scriptWitness.stack) == 1) - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) - # Now try to retrieve it... - rpc_block = self.nodes[0].getblock(block.hash, False) - non_wit_block = self.test_node.request_block(block.sha256, 2) - wit_block = self.test_node.request_block(block.sha256, 2|MSG_WITNESS_FLAG) - assert_equal(wit_block.serialize(True), hex_str_to_bytes(rpc_block)) - assert_equal(wit_block.serialize(False), non_wit_block.serialize()) - assert_equal(wit_block.serialize(True), block.serialize(True)) - - # Test size, vsize, weight - rpc_details = self.nodes[0].getblock(block.hash, True) - assert_equal(rpc_details["size"], len(block.serialize(True))) - assert_equal(rpc_details["strippedsize"], len(block.serialize(False))) - weight = 3*len(block.serialize(False)) + len(block.serialize(True)) - assert_equal(rpc_details["weight"], weight) - - # Upgraded node should not ask for blocks from unupgraded - block4 = self.build_next_block(nVersion=4) - block4.solve() - self.old_node.getdataset = set() - - # Blocks can be requested via direct-fetch (immediately upon processing the announcement) - # or via parallel download (with an indeterminate delay from processing the announcement) - # so to test that a block is NOT requested, we could guess a time period to sleep for, - # and then check. We can avoid the sleep() by taking advantage of transaction getdata's - # being processed after block getdata's, and announce a transaction as well, - # and then check to see if that particular getdata has been received. - # Since 0.14, inv's will only be responded to with a getheaders, so send a header - # to announce this block. - msg = msg_headers() - msg.headers = [ CBlockHeader(block4) ] - self.old_node.send_message(msg) - self.old_node.announce_tx_and_wait_for_getdata(block4.vtx[0]) - assert(block4.sha256 not in self.old_node.getdataset) - - # V0 segwit outputs and inputs are always standard. V0 segwit inputs may only be mined after activation, but not before. - def test_standardness_v0(self, segwit_activated): - self.log.info("Testing standardness of v0 outputs (%s activation)" % ("after" if segwit_activated else "before")) - assert(len(self.utxo)) - - witness_program = CScript([OP_TRUE]) - witness_hash = sha256(witness_program) - scriptPubKey = CScript([OP_0, witness_hash]) - - p2sh_pubkey = hash160(witness_program) - p2sh_scriptPubKey = CScript([OP_HASH160, p2sh_pubkey, OP_EQUAL]) - - # First prepare a p2sh output (so that spending it will pass standardness) - p2sh_tx = CTransaction() - p2sh_tx.vin = [CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")] - p2sh_tx.vout = [CTxOut(self.utxo[0].nValue-1000, p2sh_scriptPubKey)] - p2sh_tx.rehash() - - # Mine it on test_node to create the confirmed output. - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, p2sh_tx, with_witness=True, accepted=True) - self.nodes[0].generate(1) - sync_blocks(self.nodes) - - # Now test standardness of v0 P2WSH outputs. - # Start by creating a transaction with two outputs. - tx = CTransaction() - tx.vin = [CTxIn(COutPoint(p2sh_tx.sha256, 0), CScript([witness_program]))] - tx.vout = [CTxOut(p2sh_tx.vout[0].nValue-10000, scriptPubKey)] - tx.vout.append(CTxOut(8000, scriptPubKey)) # Might burn this later - tx.vin[0].nSequence = BIP125_SEQUENCE_NUMBER # Just to have the option to bump this tx from the mempool - tx.rehash() - - # This is always accepted, since the mempool policy is to consider segwit as always active - # and thus allow segwit outputs - test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx, with_witness=True, accepted=True) - - # Now create something that looks like a P2PKH output. This won't be spendable. - scriptPubKey = CScript([OP_0, hash160(witness_hash)]) - tx2 = CTransaction() - # tx was accepted, so we spend the second output. - tx2.vin = [CTxIn(COutPoint(tx.sha256, 1), b"")] - tx2.vout = [CTxOut(7000, scriptPubKey)] - tx2.wit.vtxinwit.append(CTxInWitness()) - tx2.wit.vtxinwit[0].scriptWitness.stack = [witness_program] - tx2.rehash() - - test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx2, with_witness=True, accepted=True) - - # Now update self.utxo for later tests. - tx3 = CTransaction() - # tx and tx2 were both accepted. Don't bother trying to reclaim the - # P2PKH output; just send tx's first output back to an anyone-can-spend. - sync_mempools([self.nodes[0], self.nodes[1]]) - tx3.vin = [CTxIn(COutPoint(tx.sha256, 0), b"")] - tx3.vout = [CTxOut(tx.vout[0].nValue - 1000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))] - tx3.wit.vtxinwit.append(CTxInWitness()) - tx3.wit.vtxinwit[0].scriptWitness.stack = [witness_program] - tx3.rehash() - if not segwit_activated: - # Just check mempool acceptance, but don't add the transaction to the mempool, since witness is disallowed - # in blocks and the tx is impossible to mine right now. - assert_equal(self.nodes[0].testmempoolaccept([bytes_to_hex_str(tx3.serialize_with_witness())]), [{'txid': tx3.hash, 'allowed': True}]) - # Create the same output as tx3, but by replacing tx - tx3_out = tx3.vout[0] - tx3 = tx - tx3.vout = [tx3_out] - tx3.rehash() - assert_equal(self.nodes[0].testmempoolaccept([bytes_to_hex_str(tx3.serialize_with_witness())]), [{'txid': tx3.hash, 'allowed': True}]) - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx3, with_witness=True, accepted=True) - - self.nodes[0].generate(1) - sync_blocks(self.nodes) - self.utxo.pop(0) - self.utxo.append(UTXO(tx3.sha256, 0, tx3.vout[0].nValue)) - assert_equal(len(self.nodes[1].getrawmempool()), 0) - + Future segwit version transactions are non-standard, but valid in blocks. + Can run this before and after segwit activation.""" - # Verify that future segwit upgraded transactions are non-standard, - # but valid in blocks. Can run this before and after segwit activation. - def test_segwit_versions(self): - self.log.info("Testing standardness/consensus for segwit versions (0-16)") - assert(len(self.utxo)) - NUM_TESTS = 17 # will test OP_0, OP1, ..., OP_16 - if (len(self.utxo) < NUM_TESTS): + num_tests = 17 # will test OP_0, OP1, ..., OP_16 + if (len(self.utxo) < num_tests): tx = CTransaction() tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")) - split_value = (self.utxo[0].nValue - 4000) // NUM_TESTS - for i in range(NUM_TESTS): + split_value = (self.utxo[0].nValue - 4000) // num_tests + for i in range(num_tests): tx.vout.append(CTxOut(split_value, CScript([OP_TRUE]))) tx.rehash() block = self.build_next_block() self.update_witness_block_with_transactions(block, [tx]) test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) self.utxo.pop(0) - for i in range(NUM_TESTS): + for i in range(num_tests): self.utxo.append(UTXO(tx.sha256, i, split_value)) sync_blocks(self.nodes) @@ -1177,36 +1424,36 @@ class SegWitTest(BitcoinTestFramework): witness_program = CScript([OP_TRUE]) witness_hash = sha256(witness_program) assert_equal(len(self.nodes[1].getrawmempool()), 0) - for version in list(range(OP_1, OP_16+1)) + [OP_0]: + for version in list(range(OP_1, OP_16 + 1)) + [OP_0]: count += 1 - # First try to spend to a future version segwit scriptPubKey. - scriptPubKey = CScript([CScriptOp(version), witness_hash]) + # First try to spend to a future version segwit script_pubkey. + script_pubkey = CScript([CScriptOp(version), witness_hash]) tx.vin = [CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")] - tx.vout = [CTxOut(self.utxo[0].nValue-1000, scriptPubKey)] + tx.vout = [CTxOut(self.utxo[0].nValue - 1000, script_pubkey)] tx.rehash() test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx, with_witness=True, accepted=False) test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx, with_witness=True, accepted=True) self.utxo.pop(0) temp_utxo.append(UTXO(tx.sha256, 0, tx.vout[0].nValue)) - self.nodes[0].generate(1) # Mine all the transactions + self.nodes[0].generate(1) # Mine all the transactions sync_blocks(self.nodes) assert(len(self.nodes[0].getrawmempool()) == 0) # Finally, verify that version 0 -> version 1 transactions # are non-standard - scriptPubKey = CScript([CScriptOp(OP_1), witness_hash]) + script_pubkey = CScript([CScriptOp(OP_1), witness_hash]) tx2 = CTransaction() tx2.vin = [CTxIn(COutPoint(tx.sha256, 0), b"")] - tx2.vout = [CTxOut(tx.vout[0].nValue-1000, scriptPubKey)] + tx2.vout = [CTxOut(tx.vout[0].nValue - 1000, script_pubkey)] tx2.wit.vtxinwit.append(CTxInWitness()) - tx2.wit.vtxinwit[0].scriptWitness.stack = [ witness_program ] + tx2.wit.vtxinwit[0].scriptWitness.stack = [witness_program] tx2.rehash() # Gets accepted to test_node, because standardness of outputs isn't # checked with fRequireStandard test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx2, with_witness=True, accepted=True) test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx2, with_witness=True, accepted=False) - temp_utxo.pop() # last entry in temp_utxo was the output we just spent + temp_utxo.pop() # last entry in temp_utxo was the output we just spent temp_utxo.append(UTXO(tx2.sha256, 0, tx2.vout[0].nValue)) # Spend everything in temp_utxo back to an OP_TRUE output. @@ -1235,15 +1482,15 @@ class SegWitTest(BitcoinTestFramework): # Add utxo to our list self.utxo.append(UTXO(tx3.sha256, 0, tx3.vout[0].nValue)) - + @subtest def test_premature_coinbase_witness_spend(self): - self.log.info("Testing premature coinbase witness spend") + block = self.build_next_block() # Change the output of the block to be a witness output. witness_program = CScript([OP_TRUE]) witness_hash = sha256(witness_program) - scriptPubKey = CScript([OP_0, witness_hash]) - block.vtx[0].vout[0].scriptPubKey = scriptPubKey + script_pubkey = CScript([OP_0, witness_hash]) + block.vtx[0].vout[0].scriptPubKey = script_pubkey # This next line will rehash the coinbase and update the merkle # root, and solve. self.update_witness_block_with_transactions(block, []) @@ -1253,7 +1500,7 @@ class SegWitTest(BitcoinTestFramework): spend_tx.vin = [CTxIn(COutPoint(block.vtx[0].sha256, 0), b"")] spend_tx.vout = [CTxOut(block.vtx[0].vout[0].nValue, witness_program)] spend_tx.wit.vtxinwit.append(CTxInWitness()) - spend_tx.wit.vtxinwit[0].scriptWitness.stack = [ witness_program ] + spend_tx.wit.vtxinwit[0].scriptWitness.stack = [witness_program] spend_tx.rehash() # Now test a premature spend. @@ -1270,22 +1517,127 @@ class SegWitTest(BitcoinTestFramework): test_witness_block(self.nodes[0].rpc, self.test_node, block2, accepted=True) sync_blocks(self.nodes) + @subtest + def test_uncompressed_pubkey(self): + """Test uncompressed pubkey validity in segwit transactions. + + Uncompressed pubkeys are no longer supported in default relay policy, + but (for now) are still valid in blocks.""" + + # Segwit transactions using uncompressed pubkeys are not accepted + # under default policy, but should still pass consensus. + key = CECKey() + key.set_secretbytes(b"9") + key.set_compressed(False) + pubkey = CPubKey(key.get_pubkey()) + assert_equal(len(pubkey), 65) # This should be an uncompressed pubkey + + utxo = self.utxo.pop(0) + + # Test 1: P2WPKH + # First create a P2WPKH output that uses an uncompressed pubkey + pubkeyhash = hash160(pubkey) + script_pkh = CScript([OP_0, pubkeyhash]) + tx = CTransaction() + tx.vin.append(CTxIn(COutPoint(utxo.sha256, utxo.n), b"")) + tx.vout.append(CTxOut(utxo.nValue - 1000, script_pkh)) + tx.rehash() + + # Confirm it in a block. + block = self.build_next_block() + self.update_witness_block_with_transactions(block, [tx]) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) + + # Now try to spend it. Send it to a P2WSH output, which we'll + # use in the next test. + witness_program = CScript([pubkey, CScriptOp(OP_CHECKSIG)]) + witness_hash = sha256(witness_program) + script_wsh = CScript([OP_0, witness_hash]) + + tx2 = CTransaction() + tx2.vin.append(CTxIn(COutPoint(tx.sha256, 0), b"")) + tx2.vout.append(CTxOut(tx.vout[0].nValue - 1000, script_wsh)) + script = get_p2pkh_script(pubkeyhash) + sig_hash = SegwitVersion1SignatureHash(script, tx2, 0, SIGHASH_ALL, tx.vout[0].nValue) + signature = key.sign(sig_hash) + b'\x01' # 0x1 is SIGHASH_ALL + tx2.wit.vtxinwit.append(CTxInWitness()) + tx2.wit.vtxinwit[0].scriptWitness.stack = [signature, pubkey] + tx2.rehash() + + # Should fail policy test. + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx2, True, False, b'non-mandatory-script-verify-flag (Using non-compressed keys in segwit)') + # But passes consensus. + block = self.build_next_block() + self.update_witness_block_with_transactions(block, [tx2]) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) + + # Test 2: P2WSH + # Try to spend the P2WSH output created in last test. + # Send it to a P2SH(P2WSH) output, which we'll use in the next test. + p2sh_witness_hash = hash160(script_wsh) + script_p2sh = CScript([OP_HASH160, p2sh_witness_hash, OP_EQUAL]) + script_sig = CScript([script_wsh]) + + tx3 = CTransaction() + tx3.vin.append(CTxIn(COutPoint(tx2.sha256, 0), b"")) + tx3.vout.append(CTxOut(tx2.vout[0].nValue - 1000, script_p2sh)) + tx3.wit.vtxinwit.append(CTxInWitness()) + sign_p2pk_witness_input(witness_program, tx3, 0, SIGHASH_ALL, tx2.vout[0].nValue, key) + + # Should fail policy test. + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx3, True, False, b'non-mandatory-script-verify-flag (Using non-compressed keys in segwit)') + # But passes consensus. + block = self.build_next_block() + self.update_witness_block_with_transactions(block, [tx3]) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) + + # Test 3: P2SH(P2WSH) + # Try to spend the P2SH output created in the last test. + # Send it to a P2PKH output, which we'll use in the next test. + script_pubkey = get_p2pkh_script(pubkeyhash) + tx4 = CTransaction() + tx4.vin.append(CTxIn(COutPoint(tx3.sha256, 0), script_sig)) + tx4.vout.append(CTxOut(tx3.vout[0].nValue - 1000, script_pubkey)) + tx4.wit.vtxinwit.append(CTxInWitness()) + sign_p2pk_witness_input(witness_program, tx4, 0, SIGHASH_ALL, tx3.vout[0].nValue, key) + + # Should fail policy test. + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx4, True, False, b'non-mandatory-script-verify-flag (Using non-compressed keys in segwit)') + block = self.build_next_block() + self.update_witness_block_with_transactions(block, [tx4]) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) + + # Test 4: Uncompressed pubkeys should still be valid in non-segwit + # transactions. + tx5 = CTransaction() + tx5.vin.append(CTxIn(COutPoint(tx4.sha256, 0), b"")) + tx5.vout.append(CTxOut(tx4.vout[0].nValue - 1000, CScript([OP_TRUE]))) + (sig_hash, err) = SignatureHash(script_pubkey, tx5, 0, SIGHASH_ALL) + signature = key.sign(sig_hash) + b'\x01' # 0x1 is SIGHASH_ALL + tx5.vin[0].scriptSig = CScript([signature, pubkey]) + tx5.rehash() + # Should pass policy and consensus. + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx5, True, True) + block = self.build_next_block() + self.update_witness_block_with_transactions(block, [tx5]) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) + self.utxo.append(UTXO(tx5.sha256, 0, tx5.vout[0].nValue)) + @subtest def test_signature_version_1(self): - self.log.info("Testing segwit signature hash version 1") + key = CECKey() key.set_secretbytes(b"9") pubkey = CPubKey(key.get_pubkey()) witness_program = CScript([pubkey, CScriptOp(OP_CHECKSIG)]) witness_hash = sha256(witness_program) - scriptPubKey = CScript([OP_0, witness_hash]) + script_pubkey = CScript([OP_0, witness_hash]) # First create a witness output for use in the tests. - assert(len(self.utxo)) tx = CTransaction() tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")) - tx.vout.append(CTxOut(self.utxo[0].nValue-1000, scriptPubKey)) + tx.vout.append(CTxOut(self.utxo[0].nValue - 1000, script_pubkey)) tx.rehash() test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx, with_witness=True, accepted=True) @@ -1298,27 +1650,27 @@ class SegWitTest(BitcoinTestFramework): # Test each hashtype prev_utxo = UTXO(tx.sha256, 0, tx.vout[0].nValue) - for sigflag in [ 0, SIGHASH_ANYONECANPAY ]: + for sigflag in [0, SIGHASH_ANYONECANPAY]: for hashtype in [SIGHASH_ALL, SIGHASH_NONE, SIGHASH_SINGLE]: hashtype |= sigflag block = self.build_next_block() tx = CTransaction() tx.vin.append(CTxIn(COutPoint(prev_utxo.sha256, prev_utxo.n), b"")) - tx.vout.append(CTxOut(prev_utxo.nValue - 1000, scriptPubKey)) + tx.vout.append(CTxOut(prev_utxo.nValue - 1000, script_pubkey)) tx.wit.vtxinwit.append(CTxInWitness()) # Too-large input value - sign_P2PK_witness_input(witness_program, tx, 0, hashtype, prev_utxo.nValue+1, key) + sign_p2pk_witness_input(witness_program, tx, 0, hashtype, prev_utxo.nValue + 1, key) self.update_witness_block_with_transactions(block, [tx]) test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False) # Too-small input value - sign_P2PK_witness_input(witness_program, tx, 0, hashtype, prev_utxo.nValue-1, key) - block.vtx.pop() # remove last tx + sign_p2pk_witness_input(witness_program, tx, 0, hashtype, prev_utxo.nValue - 1, key) + block.vtx.pop() # remove last tx self.update_witness_block_with_transactions(block, [tx]) test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False) # Now try correct value - sign_P2PK_witness_input(witness_program, tx, 0, hashtype, prev_utxo.nValue, key) + sign_p2pk_witness_input(witness_program, tx, 0, hashtype, prev_utxo.nValue, key) block.vtx.pop() self.update_witness_block_with_transactions(block, [tx]) test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) @@ -1328,19 +1680,19 @@ class SegWitTest(BitcoinTestFramework): # Test combinations of signature hashes. # Split the utxo into a lot of outputs. # Randomly choose up to 10 to spend, sign with different hashtypes, and - # output to a random number of outputs. Repeat NUM_TESTS times. + # output to a random number of outputs. Repeat num_tests times. # Ensure that we've tested a situation where we use SIGHASH_SINGLE with # an input index > number of outputs. - NUM_TESTS = 500 + num_tests = 500 temp_utxos = [] tx = CTransaction() tx.vin.append(CTxIn(COutPoint(prev_utxo.sha256, prev_utxo.n), b"")) - split_value = prev_utxo.nValue // NUM_TESTS - for i in range(NUM_TESTS): - tx.vout.append(CTxOut(split_value, scriptPubKey)) + split_value = prev_utxo.nValue // num_tests + for i in range(num_tests): + tx.vout.append(CTxOut(split_value, script_pubkey)) tx.wit.vtxinwit.append(CTxInWitness()) - sign_P2PK_witness_input(witness_program, tx, 0, SIGHASH_ALL, prev_utxo.nValue, key) - for i in range(NUM_TESTS): + sign_p2pk_witness_input(witness_program, tx, 0, SIGHASH_ALL, prev_utxo.nValue, key) + for i in range(num_tests): temp_utxos.append(UTXO(tx.sha256, i, split_value)) block = self.build_next_block() @@ -1349,7 +1701,7 @@ class SegWitTest(BitcoinTestFramework): block = self.build_next_block() used_sighash_single_out_of_bounds = False - for i in range(NUM_TESTS): + for i in range(num_tests): # Ping regularly to keep the connection alive if (not i % 100): self.test_node.sync_with_ping() @@ -1367,14 +1719,14 @@ class SegWitTest(BitcoinTestFramework): total_value += temp_utxos[i].nValue split_value = total_value // num_outputs for i in range(num_outputs): - tx.vout.append(CTxOut(split_value, scriptPubKey)) + tx.vout.append(CTxOut(split_value, script_pubkey)) for i in range(num_inputs): # Now try to sign each input, using a random hashtype. anyonecanpay = 0 if random.randint(0, 1): anyonecanpay = SIGHASH_ANYONECANPAY hashtype = random.randint(1, 3) | anyonecanpay - sign_P2PK_witness_input(witness_program, tx, i, hashtype, temp_utxos[i].nValue, key) + sign_p2pk_witness_input(witness_program, tx, i, hashtype, temp_utxos[i].nValue, key) if (hashtype == SIGHASH_SINGLE and i >= num_outputs): used_sighash_single_out_of_bounds = True tx.rehash() @@ -1399,19 +1751,19 @@ class SegWitTest(BitcoinTestFramework): # Now test witness version 0 P2PKH transactions pubkeyhash = hash160(pubkey) - scriptPKH = CScript([OP_0, pubkeyhash]) + script_pkh = CScript([OP_0, pubkeyhash]) tx = CTransaction() tx.vin.append(CTxIn(COutPoint(temp_utxos[0].sha256, temp_utxos[0].n), b"")) - tx.vout.append(CTxOut(temp_utxos[0].nValue, scriptPKH)) + tx.vout.append(CTxOut(temp_utxos[0].nValue, script_pkh)) tx.wit.vtxinwit.append(CTxInWitness()) - sign_P2PK_witness_input(witness_program, tx, 0, SIGHASH_ALL, temp_utxos[0].nValue, key) + sign_p2pk_witness_input(witness_program, tx, 0, SIGHASH_ALL, temp_utxos[0].nValue, key) tx2 = CTransaction() tx2.vin.append(CTxIn(COutPoint(tx.sha256, 0), b"")) tx2.vout.append(CTxOut(tx.vout[0].nValue, CScript([OP_TRUE]))) - script = GetP2PKHScript(pubkeyhash) + script = get_p2pkh_script(pubkeyhash) sig_hash = SegwitVersion1SignatureHash(script, tx2, 0, SIGHASH_ALL, tx.vout[0].nValue) - signature = key.sign(sig_hash) + b'\x01' # 0x1 is SIGHASH_ALL + signature = key.sign(sig_hash) + b'\x01' # 0x1 is SIGHASH_ALL # Check that we can't have a scriptSig tx2.vin[0].scriptSig = CScript([signature, pubkey]) @@ -1444,7 +1796,7 @@ class SegWitTest(BitcoinTestFramework): # the signatures as we go. tx.vin.append(CTxIn(COutPoint(i.sha256, i.n), b"")) tx.wit.vtxinwit.append(CTxInWitness()) - sign_P2PK_witness_input(witness_program, tx, index, SIGHASH_ALL|SIGHASH_ANYONECANPAY, i.nValue, key) + sign_p2pk_witness_input(witness_program, tx, index, SIGHASH_ALL | SIGHASH_ANYONECANPAY, i.nValue, key) index += 1 block = self.build_next_block() self.update_witness_block_with_transactions(block, [tx]) @@ -1453,379 +1805,22 @@ class SegWitTest(BitcoinTestFramework): for i in range(len(tx.vout)): self.utxo.append(UTXO(tx.sha256, i, tx.vout[i].nValue)) - - # Test P2SH wrapped witness programs. - def test_p2sh_witness(self, segwit_activated): - self.log.info("Testing P2SH witness transactions") - - assert(len(self.utxo)) - - # Prepare the p2sh-wrapped witness output - witness_program = CScript([OP_DROP, OP_TRUE]) - witness_hash = sha256(witness_program) - p2wsh_pubkey = CScript([OP_0, witness_hash]) - p2sh_witness_hash = hash160(p2wsh_pubkey) - scriptPubKey = CScript([OP_HASH160, p2sh_witness_hash, OP_EQUAL]) - scriptSig = CScript([p2wsh_pubkey]) # a push of the redeem script - - # Fund the P2SH output - tx = CTransaction() - tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")) - tx.vout.append(CTxOut(self.utxo[0].nValue-1000, scriptPubKey)) - tx.rehash() - - # Verify mempool acceptance and block validity - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx, with_witness=False, accepted=True) - block = self.build_next_block() - self.update_witness_block_with_transactions(block, [tx]) - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True, with_witness=segwit_activated) - sync_blocks(self.nodes) - - # Now test attempts to spend the output. - spend_tx = CTransaction() - spend_tx.vin.append(CTxIn(COutPoint(tx.sha256, 0), scriptSig)) - spend_tx.vout.append(CTxOut(tx.vout[0].nValue-1000, CScript([OP_TRUE]))) - spend_tx.rehash() - - # This transaction should not be accepted into the mempool pre- or - # post-segwit. Mempool acceptance will use SCRIPT_VERIFY_WITNESS which - # will require a witness to spend a witness program regardless of - # segwit activation. Note that older bitcoind's that are not - # segwit-aware would also reject this for failing CLEANSTACK. - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, spend_tx, with_witness=False, accepted=False) - - # Try to put the witness script in the scriptSig, should also fail. - spend_tx.vin[0].scriptSig = CScript([p2wsh_pubkey, b'a']) - spend_tx.rehash() - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, spend_tx, with_witness=False, accepted=False) - - # Now put the witness script in the witness, should succeed after - # segwit activates. - spend_tx.vin[0].scriptSig = scriptSig - spend_tx.rehash() - spend_tx.wit.vtxinwit.append(CTxInWitness()) - spend_tx.wit.vtxinwit[0].scriptWitness.stack = [ b'a', witness_program ] - - # Verify mempool acceptance - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, spend_tx, with_witness=True, accepted=segwit_activated) - block = self.build_next_block() - self.update_witness_block_with_transactions(block, [spend_tx]) - - # If we're after activation, then sending this with witnesses should be valid. - # This no longer works before activation, because SCRIPT_VERIFY_WITNESS - # is always set. - # TODO: rewrite this test to make clear that it only works after activation. - if segwit_activated: - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) - else: - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True, with_witness=False) - - # Update self.utxo - self.utxo.pop(0) - self.utxo.append(UTXO(spend_tx.sha256, 0, spend_tx.vout[0].nValue)) - - # Test the behavior of starting up a segwit-aware node after the softfork - # has activated. As segwit requires different block data than pre-segwit - # nodes would have stored, this requires special handling. - # To enable this test, pass --oldbinary=<path-to-pre-segwit-bitcoind> to - # the test. - def test_upgrade_after_activation(self, node_id): - self.log.info("Testing software upgrade after softfork activation") - - assert(node_id != 0) # node0 is assumed to be a segwit-active bitcoind - - # Make sure the nodes are all up - sync_blocks(self.nodes) - - # Restart with the new binary - self.stop_node(node_id) - self.start_node(node_id, extra_args=["-vbparams=segwit:0:999999999999"]) - connect_nodes(self.nodes[0], node_id) - - sync_blocks(self.nodes) - - # Make sure that this peer thinks segwit has activated. - assert(get_bip9_status(self.nodes[node_id], 'segwit')['status'] == "active") - - # Make sure this peer's blocks match those of node0. - height = self.nodes[node_id].getblockcount() - while height >= 0: - block_hash = self.nodes[node_id].getblockhash(height) - assert_equal(block_hash, self.nodes[0].getblockhash(height)) - assert_equal(self.nodes[0].getblock(block_hash), self.nodes[node_id].getblock(block_hash)) - height -= 1 - - - def test_witness_sigops(self): - '''Ensure sigop counting is correct inside witnesses.''' - self.log.info("Testing sigops limit") - - assert(len(self.utxo)) - - # Keep this under MAX_OPS_PER_SCRIPT (201) - witness_program = CScript([OP_TRUE, OP_IF, OP_TRUE, OP_ELSE] + [OP_CHECKMULTISIG]*5 + [OP_CHECKSIG]*193 + [OP_ENDIF]) - witness_hash = sha256(witness_program) - scriptPubKey = CScript([OP_0, witness_hash]) - - sigops_per_script = 20*5 + 193*1 - # We'll produce 2 extra outputs, one with a program that would take us - # over max sig ops, and one with a program that would exactly reach max - # sig ops - outputs = (MAX_SIGOP_COST // sigops_per_script) + 2 - extra_sigops_available = MAX_SIGOP_COST % sigops_per_script - - # We chose the number of checkmultisigs/checksigs to make this work: - assert(extra_sigops_available < 100) # steer clear of MAX_OPS_PER_SCRIPT - - # This script, when spent with the first - # N(=MAX_SIGOP_COST//sigops_per_script) outputs of our transaction, - # would push us just over the block sigop limit. - witness_program_toomany = CScript([OP_TRUE, OP_IF, OP_TRUE, OP_ELSE] + [OP_CHECKSIG]*(extra_sigops_available + 1) + [OP_ENDIF]) - witness_hash_toomany = sha256(witness_program_toomany) - scriptPubKey_toomany = CScript([OP_0, witness_hash_toomany]) - - # If we spend this script instead, we would exactly reach our sigop - # limit (for witness sigops). - witness_program_justright = CScript([OP_TRUE, OP_IF, OP_TRUE, OP_ELSE] + [OP_CHECKSIG]*(extra_sigops_available) + [OP_ENDIF]) - witness_hash_justright = sha256(witness_program_justright) - scriptPubKey_justright = CScript([OP_0, witness_hash_justright]) - - # First split our available utxo into a bunch of outputs - split_value = self.utxo[0].nValue // outputs - tx = CTransaction() - tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")) - for i in range(outputs): - tx.vout.append(CTxOut(split_value, scriptPubKey)) - tx.vout[-2].scriptPubKey = scriptPubKey_toomany - tx.vout[-1].scriptPubKey = scriptPubKey_justright - tx.rehash() - - block_1 = self.build_next_block() - self.update_witness_block_with_transactions(block_1, [tx]) - test_witness_block(self.nodes[0].rpc, self.test_node, block_1, accepted=True) - - tx2 = CTransaction() - # If we try to spend the first n-1 outputs from tx, that should be - # too many sigops. - total_value = 0 - for i in range(outputs-1): - tx2.vin.append(CTxIn(COutPoint(tx.sha256, i), b"")) - tx2.wit.vtxinwit.append(CTxInWitness()) - tx2.wit.vtxinwit[-1].scriptWitness.stack = [ witness_program ] - total_value += tx.vout[i].nValue - tx2.wit.vtxinwit[-1].scriptWitness.stack = [ witness_program_toomany ] - tx2.vout.append(CTxOut(total_value, CScript([OP_TRUE]))) - tx2.rehash() - - block_2 = self.build_next_block() - self.update_witness_block_with_transactions(block_2, [tx2]) - test_witness_block(self.nodes[0].rpc, self.test_node, block_2, accepted=False) - - # Try dropping the last input in tx2, and add an output that has - # too many sigops (contributing to legacy sigop count). - checksig_count = (extra_sigops_available // 4) + 1 - scriptPubKey_checksigs = CScript([OP_CHECKSIG]*checksig_count) - tx2.vout.append(CTxOut(0, scriptPubKey_checksigs)) - tx2.vin.pop() - tx2.wit.vtxinwit.pop() - tx2.vout[0].nValue -= tx.vout[-2].nValue - tx2.rehash() - block_3 = self.build_next_block() - self.update_witness_block_with_transactions(block_3, [tx2]) - test_witness_block(self.nodes[0].rpc, self.test_node, block_3, accepted=False) - - # If we drop the last checksig in this output, the tx should succeed. - block_4 = self.build_next_block() - tx2.vout[-1].scriptPubKey = CScript([OP_CHECKSIG]*(checksig_count-1)) - tx2.rehash() - self.update_witness_block_with_transactions(block_4, [tx2]) - test_witness_block(self.nodes[0].rpc, self.test_node, block_4, accepted=True) - - # Reset the tip back down for the next test - sync_blocks(self.nodes) - for x in self.nodes: - x.invalidateblock(block_4.hash) - - # Try replacing the last input of tx2 to be spending the last - # output of tx - block_5 = self.build_next_block() - tx2.vout.pop() - tx2.vin.append(CTxIn(COutPoint(tx.sha256, outputs-1), b"")) - tx2.wit.vtxinwit.append(CTxInWitness()) - tx2.wit.vtxinwit[-1].scriptWitness.stack = [ witness_program_justright ] - tx2.rehash() - self.update_witness_block_with_transactions(block_5, [tx2]) - test_witness_block(self.nodes[0].rpc, self.test_node, block_5, accepted=True) - - # TODO: test p2sh sigop counting - - def test_getblocktemplate_before_lockin(self): - self.log.info("Testing getblocktemplate setting of segwit versionbit (before lockin)") - # Node0 is segwit aware, node2 is not. - for node in [self.nodes[0], self.nodes[2]]: - gbt_results = node.getblocktemplate() - block_version = gbt_results['version'] - # If we're not indicating segwit support, we will still be - # signalling for segwit activation. - assert_equal((block_version & (1 << VB_WITNESS_BIT) != 0), node == self.nodes[0]) - # If we don't specify the segwit rule, then we won't get a default - # commitment. - assert('default_witness_commitment' not in gbt_results) - - # Workaround: - # Can either change the tip, or change the mempool and wait 5 seconds - # to trigger a recomputation of getblocktemplate. - txid = int(self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1), 16) - # Using mocktime lets us avoid sleep() - sync_mempools(self.nodes) - self.nodes[0].setmocktime(int(time.time())+10) - self.nodes[2].setmocktime(int(time.time())+10) - - for node in [self.nodes[0], self.nodes[2]]: - gbt_results = node.getblocktemplate({"rules" : ["segwit"]}) - block_version = gbt_results['version'] - if node == self.nodes[2]: - # If this is a non-segwit node, we should still not get a witness - # commitment, nor a version bit signalling segwit. - assert_equal(block_version & (1 << VB_WITNESS_BIT), 0) - assert('default_witness_commitment' not in gbt_results) - else: - # For segwit-aware nodes, check the version bit and the witness - # commitment are correct. - assert(block_version & (1 << VB_WITNESS_BIT) != 0) - assert('default_witness_commitment' in gbt_results) - witness_commitment = gbt_results['default_witness_commitment'] - - # Check that default_witness_commitment is present. - witness_root = CBlock.get_merkle_root([ser_uint256(0), - ser_uint256(txid)]) - script = get_witness_script(witness_root, 0) - assert_equal(witness_commitment, bytes_to_hex_str(script)) - - # undo mocktime - self.nodes[0].setmocktime(0) - self.nodes[2].setmocktime(0) - - # Uncompressed pubkeys are no longer supported in default relay policy, - # but (for now) are still valid in blocks. - def test_uncompressed_pubkey(self): - self.log.info("Testing uncompressed pubkeys") - # Segwit transactions using uncompressed pubkeys are not accepted - # under default policy, but should still pass consensus. - key = CECKey() - key.set_secretbytes(b"9") - key.set_compressed(False) - pubkey = CPubKey(key.get_pubkey()) - assert_equal(len(pubkey), 65) # This should be an uncompressed pubkey - - assert(len(self.utxo) > 0) - utxo = self.utxo.pop(0) - - # Test 1: P2WPKH - # First create a P2WPKH output that uses an uncompressed pubkey - pubkeyhash = hash160(pubkey) - scriptPKH = CScript([OP_0, pubkeyhash]) - tx = CTransaction() - tx.vin.append(CTxIn(COutPoint(utxo.sha256, utxo.n), b"")) - tx.vout.append(CTxOut(utxo.nValue-1000, scriptPKH)) - tx.rehash() - - # Confirm it in a block. - block = self.build_next_block() - self.update_witness_block_with_transactions(block, [tx]) - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) - - # Now try to spend it. Send it to a P2WSH output, which we'll - # use in the next test. - witness_program = CScript([pubkey, CScriptOp(OP_CHECKSIG)]) - witness_hash = sha256(witness_program) - scriptWSH = CScript([OP_0, witness_hash]) - - tx2 = CTransaction() - tx2.vin.append(CTxIn(COutPoint(tx.sha256, 0), b"")) - tx2.vout.append(CTxOut(tx.vout[0].nValue-1000, scriptWSH)) - script = GetP2PKHScript(pubkeyhash) - sig_hash = SegwitVersion1SignatureHash(script, tx2, 0, SIGHASH_ALL, tx.vout[0].nValue) - signature = key.sign(sig_hash) + b'\x01' # 0x1 is SIGHASH_ALL - tx2.wit.vtxinwit.append(CTxInWitness()) - tx2.wit.vtxinwit[0].scriptWitness.stack = [ signature, pubkey ] - tx2.rehash() - - # Should fail policy test. - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx2, True, False, b'non-mandatory-script-verify-flag (Using non-compressed keys in segwit)') - # But passes consensus. - block = self.build_next_block() - self.update_witness_block_with_transactions(block, [tx2]) - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) - - # Test 2: P2WSH - # Try to spend the P2WSH output created in last test. - # Send it to a P2SH(P2WSH) output, which we'll use in the next test. - p2sh_witness_hash = hash160(scriptWSH) - scriptP2SH = CScript([OP_HASH160, p2sh_witness_hash, OP_EQUAL]) - scriptSig = CScript([scriptWSH]) - - tx3 = CTransaction() - tx3.vin.append(CTxIn(COutPoint(tx2.sha256, 0), b"")) - tx3.vout.append(CTxOut(tx2.vout[0].nValue-1000, scriptP2SH)) - tx3.wit.vtxinwit.append(CTxInWitness()) - sign_P2PK_witness_input(witness_program, tx3, 0, SIGHASH_ALL, tx2.vout[0].nValue, key) - - # Should fail policy test. - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx3, True, False, b'non-mandatory-script-verify-flag (Using non-compressed keys in segwit)') - # But passes consensus. - block = self.build_next_block() - self.update_witness_block_with_transactions(block, [tx3]) - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) - - # Test 3: P2SH(P2WSH) - # Try to spend the P2SH output created in the last test. - # Send it to a P2PKH output, which we'll use in the next test. - scriptPubKey = GetP2PKHScript(pubkeyhash) - tx4 = CTransaction() - tx4.vin.append(CTxIn(COutPoint(tx3.sha256, 0), scriptSig)) - tx4.vout.append(CTxOut(tx3.vout[0].nValue-1000, scriptPubKey)) - tx4.wit.vtxinwit.append(CTxInWitness()) - sign_P2PK_witness_input(witness_program, tx4, 0, SIGHASH_ALL, tx3.vout[0].nValue, key) - - # Should fail policy test. - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx4, True, False, b'non-mandatory-script-verify-flag (Using non-compressed keys in segwit)') - block = self.build_next_block() - self.update_witness_block_with_transactions(block, [tx4]) - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) - - # Test 4: Uncompressed pubkeys should still be valid in non-segwit - # transactions. - tx5 = CTransaction() - tx5.vin.append(CTxIn(COutPoint(tx4.sha256, 0), b"")) - tx5.vout.append(CTxOut(tx4.vout[0].nValue-1000, CScript([OP_TRUE]))) - (sig_hash, err) = SignatureHash(scriptPubKey, tx5, 0, SIGHASH_ALL) - signature = key.sign(sig_hash) + b'\x01' # 0x1 is SIGHASH_ALL - tx5.vin[0].scriptSig = CScript([signature, pubkey]) - tx5.rehash() - # Should pass policy and consensus. - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx5, True, True) - block = self.build_next_block() - self.update_witness_block_with_transactions(block, [tx5]) - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) - self.utxo.append(UTXO(tx5.sha256, 0, tx5.vout[0].nValue)) - + @subtest def test_non_standard_witness_blinding(self): - self.log.info("Testing behavior of unnecessary witnesses in transactions does not blind the node for the transaction") - assert (len(self.utxo) > 0) + """Test behavior of unnecessary witnesses in transactions does not blind the node for the transaction""" # Create a p2sh output -- this is so we can pass the standardness # rules (an anyone-can-spend OP_TRUE would be rejected, if not wrapped # in P2SH). p2sh_program = CScript([OP_TRUE]) p2sh_pubkey = hash160(p2sh_program) - scriptPubKey = CScript([OP_HASH160, p2sh_pubkey, OP_EQUAL]) + script_pubkey = CScript([OP_HASH160, p2sh_pubkey, OP_EQUAL]) # Now check that unnecessary witnesses can't be used to blind a node # to a transaction, eg by violating standardness checks. tx = CTransaction() tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")) - tx.vout.append(CTxOut(self.utxo[0].nValue - 1000, scriptPubKey)) + tx.vout.append(CTxOut(self.utxo[0].nValue - 1000, script_pubkey)) tx.rehash() test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx, False, True) self.nodes[0].generate(1) @@ -1838,7 +1833,7 @@ class SegWitTest(BitcoinTestFramework): # to the rejection cache. tx2 = CTransaction() tx2.vin.append(CTxIn(COutPoint(tx.sha256, 0), CScript([p2sh_program]))) - tx2.vout.append(CTxOut(tx.vout[0].nValue - 1000, scriptPubKey)) + tx2.vout.append(CTxOut(tx.vout[0].nValue - 1000, script_pubkey)) tx2.wit.vtxinwit.append(CTxInWitness()) tx2.wit.vtxinwit[0].scriptWitness.stack = [b'a' * 400] tx2.rehash() @@ -1864,8 +1859,9 @@ class SegWitTest(BitcoinTestFramework): self.utxo.pop(0) self.utxo.append(UTXO(tx3.sha256, 0, tx3.vout[0].nValue)) + @subtest def test_non_standard_witness(self): - self.log.info("Testing detection of non-standard P2WSH witness") + """Test detection of non-standard P2WSH witness""" pad = chr(1).encode('latin-1') # Create scripts for tests @@ -1877,7 +1873,6 @@ class SegWitTest(BitcoinTestFramework): p2wsh_scripts = [] - assert(len(self.utxo)) tx = CTransaction() tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")) @@ -1901,13 +1896,13 @@ class SegWitTest(BitcoinTestFramework): p2sh_txs = [] for i in range(len(scripts)): p2wsh_tx = CTransaction() - p2wsh_tx.vin.append(CTxIn(COutPoint(txid,i*2))) + p2wsh_tx.vin.append(CTxIn(COutPoint(txid, i * 2))) p2wsh_tx.vout.append(CTxOut(outputvalue - 5000, CScript([OP_0, hash160(hex_str_to_bytes(""))]))) p2wsh_tx.wit.vtxinwit.append(CTxInWitness()) p2wsh_tx.rehash() p2wsh_txs.append(p2wsh_tx) p2sh_tx = CTransaction() - p2sh_tx.vin.append(CTxIn(COutPoint(txid,i*2+1), CScript([p2wsh_scripts[i]]))) + p2sh_tx.vin.append(CTxIn(COutPoint(txid, i * 2 + 1), CScript([p2wsh_scripts[i]]))) p2sh_tx.vout.append(CTxOut(outputvalue - 5000, CScript([OP_0, hash160(hex_str_to_bytes(""))]))) p2sh_tx.wit.vtxinwit.append(CTxInWitness()) p2sh_tx.rehash() @@ -1964,80 +1959,128 @@ class SegWitTest(BitcoinTestFramework): self.utxo.pop(0) + @subtest + def test_upgrade_after_activation(self): + """Test the behavior of starting up a segwit-aware node after the softfork has activated.""" - def run_test(self): - # Setup the p2p connections and start up the network thread. - # self.test_node sets NODE_WITNESS|NODE_NETWORK - self.test_node = self.nodes[0].add_p2p_connection(TestP2PConn(), services=NODE_NETWORK|NODE_WITNESS) - # self.old_node sets only NODE_NETWORK - self.old_node = self.nodes[0].add_p2p_connection(TestP2PConn(), services=NODE_NETWORK) - # self.std_node is for testing node1 (fRequireStandard=true) - self.std_node = self.nodes[1].add_p2p_connection(TestP2PConn(), services=NODE_NETWORK|NODE_WITNESS) + # Restart with the new binary + self.stop_node(2) + self.start_node(2, extra_args=["-vbparams=segwit:0:999999999999"]) + connect_nodes(self.nodes[0], 2) - network_thread_start() + sync_blocks(self.nodes) - # Keep a place to store utxo's that can be used in later tests - self.utxo = [] + # Make sure that this peer thinks segwit has activated. + assert(get_bip9_status(self.nodes[2], 'segwit')['status'] == "active") - # Test logic begins here - self.test_node.wait_for_verack() + # Make sure this peer's blocks match those of node0. + height = self.nodes[2].getblockcount() + while height >= 0: + block_hash = self.nodes[2].getblockhash(height) + assert_equal(block_hash, self.nodes[0].getblockhash(height)) + assert_equal(self.nodes[0].getblock(block_hash), self.nodes[2].getblock(block_hash)) + height -= 1 - self.log.info("Starting tests before segwit lock in:") + @subtest + def test_witness_sigops(self): + """Test sigop counting is correct inside witnesses.""" - self.test_witness_services() # Verifies NODE_WITNESS - self.test_non_witness_transaction() # non-witness tx's are accepted - self.test_unnecessary_witness_before_segwit_activation() - self.test_v0_outputs_arent_spendable() - self.test_block_relay(segwit_activated=False) + # Keep this under MAX_OPS_PER_SCRIPT (201) + witness_program = CScript([OP_TRUE, OP_IF, OP_TRUE, OP_ELSE] + [OP_CHECKMULTISIG] * 5 + [OP_CHECKSIG] * 193 + [OP_ENDIF]) + witness_hash = sha256(witness_program) + script_pubkey = CScript([OP_0, witness_hash]) - # Advance to segwit being 'started' - self.advance_to_segwit_started() - sync_blocks(self.nodes) - self.test_getblocktemplate_before_lockin() + sigops_per_script = 20 * 5 + 193 * 1 + # We'll produce 2 extra outputs, one with a program that would take us + # over max sig ops, and one with a program that would exactly reach max + # sig ops + outputs = (MAX_SIGOP_COST // sigops_per_script) + 2 + extra_sigops_available = MAX_SIGOP_COST % sigops_per_script - sync_blocks(self.nodes) + # We chose the number of checkmultisigs/checksigs to make this work: + assert(extra_sigops_available < 100) # steer clear of MAX_OPS_PER_SCRIPT - # At lockin, nothing should change. - self.log.info("Testing behavior post lockin, pre-activation") - self.advance_to_segwit_lockin() + # This script, when spent with the first + # N(=MAX_SIGOP_COST//sigops_per_script) outputs of our transaction, + # would push us just over the block sigop limit. + witness_program_toomany = CScript([OP_TRUE, OP_IF, OP_TRUE, OP_ELSE] + [OP_CHECKSIG] * (extra_sigops_available + 1) + [OP_ENDIF]) + witness_hash_toomany = sha256(witness_program_toomany) + script_pubkey_toomany = CScript([OP_0, witness_hash_toomany]) - # Retest unnecessary witnesses - self.test_unnecessary_witness_before_segwit_activation() - self.test_witness_tx_relay_before_segwit_activation() - self.test_block_relay(segwit_activated=False) - self.test_standardness_v0(segwit_activated=False) + # If we spend this script instead, we would exactly reach our sigop + # limit (for witness sigops). + witness_program_justright = CScript([OP_TRUE, OP_IF, OP_TRUE, OP_ELSE] + [OP_CHECKSIG] * (extra_sigops_available) + [OP_ENDIF]) + witness_hash_justright = sha256(witness_program_justright) + script_pubkey_justright = CScript([OP_0, witness_hash_justright]) - sync_blocks(self.nodes) + # First split our available utxo into a bunch of outputs + split_value = self.utxo[0].nValue // outputs + tx = CTransaction() + tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")) + for i in range(outputs): + tx.vout.append(CTxOut(split_value, script_pubkey)) + tx.vout[-2].scriptPubKey = script_pubkey_toomany + tx.vout[-1].scriptPubKey = script_pubkey_justright + tx.rehash() - # Now activate segwit - self.log.info("Testing behavior after segwit activation") - self.advance_to_segwit_active() + block_1 = self.build_next_block() + self.update_witness_block_with_transactions(block_1, [tx]) + test_witness_block(self.nodes[0].rpc, self.test_node, block_1, accepted=True) - sync_blocks(self.nodes) + tx2 = CTransaction() + # If we try to spend the first n-1 outputs from tx, that should be + # too many sigops. + total_value = 0 + for i in range(outputs - 1): + tx2.vin.append(CTxIn(COutPoint(tx.sha256, i), b"")) + tx2.wit.vtxinwit.append(CTxInWitness()) + tx2.wit.vtxinwit[-1].scriptWitness.stack = [witness_program] + total_value += tx.vout[i].nValue + tx2.wit.vtxinwit[-1].scriptWitness.stack = [witness_program_toomany] + tx2.vout.append(CTxOut(total_value, CScript([OP_TRUE]))) + tx2.rehash() - # Test P2SH witness handling again - self.test_p2sh_witness(segwit_activated=True) - self.test_witness_commitments() - self.test_block_malleability() - self.test_witness_block_size() - self.test_submit_block() - self.test_extra_witness_data() - self.test_max_witness_push_length() - self.test_max_witness_program_length() - self.test_witness_input_length() - self.test_block_relay(segwit_activated=True) - self.test_tx_relay_after_segwit_activation() - self.test_standardness_v0(segwit_activated=True) - self.test_segwit_versions() - self.test_premature_coinbase_witness_spend() - self.test_uncompressed_pubkey() - self.test_signature_version_1() - self.test_non_standard_witness_blinding() - self.test_non_standard_witness() + block_2 = self.build_next_block() + self.update_witness_block_with_transactions(block_2, [tx2]) + test_witness_block(self.nodes[0].rpc, self.test_node, block_2, accepted=False) + + # Try dropping the last input in tx2, and add an output that has + # too many sigops (contributing to legacy sigop count). + checksig_count = (extra_sigops_available // 4) + 1 + script_pubkey_checksigs = CScript([OP_CHECKSIG] * checksig_count) + tx2.vout.append(CTxOut(0, script_pubkey_checksigs)) + tx2.vin.pop() + tx2.wit.vtxinwit.pop() + tx2.vout[0].nValue -= tx.vout[-2].nValue + tx2.rehash() + block_3 = self.build_next_block() + self.update_witness_block_with_transactions(block_3, [tx2]) + test_witness_block(self.nodes[0].rpc, self.test_node, block_3, accepted=False) + + # If we drop the last checksig in this output, the tx should succeed. + block_4 = self.build_next_block() + tx2.vout[-1].scriptPubKey = CScript([OP_CHECKSIG] * (checksig_count - 1)) + tx2.rehash() + self.update_witness_block_with_transactions(block_4, [tx2]) + test_witness_block(self.nodes[0].rpc, self.test_node, block_4, accepted=True) + + # Reset the tip back down for the next test sync_blocks(self.nodes) - self.test_upgrade_after_activation(node_id=2) - self.test_witness_sigops() + for x in self.nodes: + x.invalidateblock(block_4.hash) + + # Try replacing the last input of tx2 to be spending the last + # output of tx + block_5 = self.build_next_block() + tx2.vout.pop() + tx2.vin.append(CTxIn(COutPoint(tx.sha256, outputs - 1), b"")) + tx2.wit.vtxinwit.append(CTxInWitness()) + tx2.wit.vtxinwit[-1].scriptWitness.stack = [witness_program_justright] + tx2.rehash() + self.update_witness_block_with_transactions(block_5, [tx2]) + test_witness_block(self.nodes[0].rpc, self.test_node, block_5, accepted=True) + # TODO: test p2sh sigop counting if __name__ == '__main__': SegWitTest().main() diff --git a/test/functional/p2p_sendheaders.py b/test/functional/p2p_sendheaders.py index 7ee8168e2f..2788d8995e 100755 --- a/test/functional/p2p_sendheaders.py +++ b/test/functional/p2p_sendheaders.py @@ -90,7 +90,6 @@ from test_framework.mininode import ( CBlockHeader, CInv, NODE_WITNESS, - network_thread_start, P2PInterface, mininode_lock, msg_block, @@ -238,15 +237,11 @@ class SendHeadersTest(BitcoinTestFramework): return [int(x, 16) for x in all_hashes] def run_test(self): - # Setup the p2p connections and start up the network thread. + # Setup the p2p connections inv_node = self.nodes[0].add_p2p_connection(BaseNode()) # Make sure NODE_NETWORK is not set for test_node, so no block download # will occur outside of direct fetching test_node = self.nodes[0].add_p2p_connection(BaseNode(), services=NODE_WITNESS) - - network_thread_start() - - # Test logic begins here inv_node.wait_for_verack() test_node.wait_for_verack() @@ -306,6 +301,7 @@ class SendHeadersTest(BitcoinTestFramework): test_node.clear_block_announcements() # since we requested headers... elif i == 2: # this time announce own block via headers + inv_node.clear_block_announcements() height = self.nodes[0].getblockcount() last_time = self.nodes[0].getblock(self.nodes[0].getbestblockhash())['time'] block_time = last_time + 1 @@ -315,6 +311,7 @@ class SendHeadersTest(BitcoinTestFramework): test_node.wait_for_getdata([new_block.sha256]) test_node.send_message(msg_block(new_block)) test_node.sync_with_ping() # make sure this block is processed + wait_until(lambda: inv_node.block_announced, timeout=60, lock=mininode_lock) inv_node.clear_block_announcements() test_node.clear_block_announcements() diff --git a/test/functional/p2p_timeouts.py b/test/functional/p2p_timeouts.py index 6a21b693b4..7a4ef1c05c 100755 --- a/test/functional/p2p_timeouts.py +++ b/test/functional/p2p_timeouts.py @@ -38,18 +38,16 @@ class TimeoutsTest(BitcoinTestFramework): self.num_nodes = 1 def run_test(self): - # Setup the p2p connections and start up the network thread. + # Setup the p2p connections no_verack_node = self.nodes[0].add_p2p_connection(TestP2PConn()) no_version_node = self.nodes[0].add_p2p_connection(TestP2PConn(), send_version=False) no_send_node = self.nodes[0].add_p2p_connection(TestP2PConn(), send_version=False) - network_thread_start() - 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 +56,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/p2p_unrequested_blocks.py b/test/functional/p2p_unrequested_blocks.py index 49c619a4ce..5f2d65c3f5 100755 --- a/test/functional/p2p_unrequested_blocks.py +++ b/test/functional/p2p_unrequested_blocks.py @@ -73,15 +73,11 @@ class AcceptBlockTest(BitcoinTestFramework): self.setup_nodes() def run_test(self): - # Setup the p2p connections and start up the network thread. + # Setup the p2p connections # test_node connects to node0 (not whitelisted) test_node = self.nodes[0].add_p2p_connection(P2PInterface()) # min_work_node connects to node1 (whitelisted) min_work_node = self.nodes[1].add_p2p_connection(P2PInterface()) - - network_thread_start() - - # Test logic begins here test_node.wait_for_verack() min_work_node.wait_for_verack() @@ -204,10 +200,8 @@ class AcceptBlockTest(BitcoinTestFramework): self.nodes[0].disconnect_p2ps() self.nodes[1].disconnect_p2ps() - network_thread_join() test_node = self.nodes[0].add_p2p_connection(P2PInterface()) - network_thread_start() test_node.wait_for_verack() test_node.send_message(msg_block(block_h1f)) @@ -293,8 +287,6 @@ class AcceptBlockTest(BitcoinTestFramework): self.nodes[0].disconnect_p2ps() test_node = self.nodes[0].add_p2p_connection(P2PInterface()) - - network_thread_start() test_node.wait_for_verack() # We should have failed reorg and switched back to 290 (but have block 291) diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py index 7acc59c2c6..155d30317a 100755 --- a/test/functional/rpc_blockchain.py +++ b/test/functional/rpc_blockchain.py @@ -41,7 +41,6 @@ from test_framework.messages import ( ) from test_framework.mininode import ( P2PInterface, - network_thread_start, ) @@ -262,7 +261,6 @@ class BlockchainTest(BitcoinTestFramework): # Start a P2P connection since we'll need to create some blocks. node.add_p2p_connection(P2PInterface()) - network_thread_start() node.p2p.wait_for_verack() current_height = node.getblock(node.getbestblockhash())['height'] diff --git a/test/functional/rpc_txoutproof.py b/test/functional/rpc_txoutproof.py index c52a7397dc..e5a63f0c46 100755 --- a/test/functional/rpc_txoutproof.py +++ b/test/functional/rpc_txoutproof.py @@ -6,6 +6,8 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * +from test_framework.mininode import FromHex, ToHex +from test_framework.messages import CMerkleBlock class MerkleBlockTest(BitcoinTestFramework): def set_test_params(self): @@ -78,6 +80,27 @@ class MerkleBlockTest(BitcoinTestFramework): # We can't get a proof if we specify transactions from different blocks assert_raises_rpc_error(-5, "Not all transactions found in specified or retrieved block", self.nodes[2].gettxoutproof, [txid1, txid3]) + # Now we'll try tweaking a proof. + proof = self.nodes[3].gettxoutproof([txid1, txid2]) + assert txid1 in self.nodes[0].verifytxoutproof(proof) + assert txid2 in self.nodes[1].verifytxoutproof(proof) + + tweaked_proof = FromHex(CMerkleBlock(), proof) + + # Make sure that our serialization/deserialization is working + assert txid1 in self.nodes[2].verifytxoutproof(ToHex(tweaked_proof)) + + # Check to see if we can go up the merkle tree and pass this off as a + # single-transaction block + tweaked_proof.txn.nTransactions = 1 + tweaked_proof.txn.vHash = [tweaked_proof.header.hashMerkleRoot] + tweaked_proof.txn.vBits = [True] + [False]*7 + + for n in self.nodes: + assert not n.verifytxoutproof(ToHex(tweaked_proof)) + + # TODO: try more variants, eg transactions at different depths, and + # verify that the proofs are invalid if __name__ == '__main__': MerkleBlockTest().main() diff --git a/test/functional/rpc_zmq.py b/test/functional/rpc_zmq.py new file mode 100755 index 0000000000..eb789face2 --- /dev/null +++ b/test/functional/rpc_zmq.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 +# 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. +"""Test for the ZMQ RPC methods.""" + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import assert_equal + + +class RPCZMQTest(BitcoinTestFramework): + + address = "tcp://127.0.0.1:28332" + + def set_test_params(self): + self.num_nodes = 1 + self.setup_clean_chain = True + + def run_test(self): + self._test_getzmqnotifications() + + def _test_getzmqnotifications(self): + self.restart_node(0, extra_args=[]) + assert_equal(self.nodes[0].getzmqnotifications(), []) + + self.restart_node(0, extra_args=["-zmqpubhashtx=%s" % self.address]) + assert_equal(self.nodes[0].getzmqnotifications(), [ + {"type": "pubhashtx", "address": self.address}, + ]) + + +if __name__ == '__main__': + RPCZMQTest().main() diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py index ca2e425bd6..df8d424d01 100755 --- a/test/functional/test_framework/messages.py +++ b/test/functional/test_framework/messages.py @@ -841,6 +841,52 @@ class BlockTransactions(): def __repr__(self): return "BlockTransactions(hash=%064x transactions=%s)" % (self.blockhash, repr(self.transactions)) +class CPartialMerkleTree(): + def __init__(self): + self.nTransactions = 0 + self.vHash = [] + self.vBits = [] + self.fBad = False + + def deserialize(self, f): + self.nTransactions = struct.unpack("<i", f.read(4))[0] + self.vHash = deser_uint256_vector(f) + vBytes = deser_string(f) + self.vBits = [] + for i in range(len(vBytes) * 8): + self.vBits.append(vBytes[i//8] & (1 << (i % 8)) != 0) + + def serialize(self): + r = b"" + r += struct.pack("<i", self.nTransactions) + r += ser_uint256_vector(self.vHash) + vBytesArray = bytearray([0x00] * ((len(self.vBits) + 7)//8)) + for i in range(len(self.vBits)): + vBytesArray[i // 8] |= self.vBits[i] << (i % 8) + r += ser_string(bytes(vBytesArray)) + return r + + def __repr__(self): + return "CPartialMerkleTree(nTransactions=%d, vHash=%s, vBits=%s)" % (self.nTransactions, repr(self.vHash), repr(self.vBits)) + +class CMerkleBlock(): + def __init__(self): + self.header = CBlockHeader() + self.txn = CPartialMerkleTree() + + def deserialize(self, f): + self.header.deserialize(f) + self.txn.deserialize(f) + + def serialize(self): + r = b"" + r += self.header.serialize() + r += self.txn.serialize() + return r + + def __repr__(self): + return "CMerkleBlock(header=%s, txn=%s)" % (repr(self.header), repr(self.txn)) + # Objects that correspond to messages on the wire class msg_version(): diff --git a/test/functional/test_framework/mininode.py b/test/functional/test_framework/mininode.py index 7c2125a177..29bf33fa5b 100755 --- a/test/functional/test_framework/mininode.py +++ b/test/functional/test_framework/mininode.py @@ -13,11 +13,10 @@ P2PConnection: A low-level connection object to a node's P2P interface P2PInterface: A high-level interface object for communicating to a node over P2P P2PDataStore: A p2p interface class that keeps a store of transactions and blocks and can respond correctly to getdata and getheaders messages""" -import asyncore +import asyncio from collections import defaultdict from io import BytesIO import logging -import socket import struct import sys import threading @@ -57,7 +56,8 @@ MAGIC_BYTES = { "regtest": b"\xfa\xbf\xb5\xda", # regtest } -class P2PConnection(asyncore.dispatcher): + +class P2PConnection(asyncio.Protocol): """A low-level connection object to a node's P2P interface. This class is responsible for: @@ -71,68 +71,59 @@ class P2PConnection(asyncore.dispatcher): sub-classed and the on_message() callback overridden.""" def __init__(self): - # All P2PConnections must be created before starting the NetworkThread. - # assert that the network thread is not running. - assert not network_thread_running() + # The underlying transport of the connection. + # Should only call methods on this from the NetworkThread, c.f. call_soon_threadsafe + self._transport = None - super().__init__(map=mininode_socket_map) + @property + def is_connected(self): + return self._transport is not None def peer_connect(self, dstaddr, dstport, net="regtest"): + assert not self.is_connected self.dstaddr = dstaddr self.dstport = dstport - self.create_socket(socket.AF_INET, socket.SOCK_STREAM) - self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) - self.sendbuf = b"" + # The initial message to send after the connection was made: + self.on_connection_send_msg = None self.recvbuf = b"" - self.state = "connecting" self.network = net - self.disconnect = False - logger.debug('Connecting to Bitcoin Node: %s:%d' % (self.dstaddr, self.dstport)) - try: - self.connect((dstaddr, dstport)) - except: - self.handle_close() + loop = NetworkThread.network_event_loop + conn_gen_unsafe = loop.create_connection(lambda: self, host=self.dstaddr, port=self.dstport) + conn_gen = lambda: loop.call_soon_threadsafe(loop.create_task, conn_gen_unsafe) + return conn_gen def peer_disconnect(self): # Connection could have already been closed by other end. - if self.state == "connected": - self.disconnect_node() + NetworkThread.network_event_loop.call_soon_threadsafe(lambda: self._transport and self._transport.abort()) # Connection and disconnection methods - def handle_connect(self): - """asyncore callback when a connection is opened.""" - if self.state != "connected": - logger.debug("Connected & Listening: %s:%d" % (self.dstaddr, self.dstport)) - self.state = "connected" - 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" + def connection_made(self, transport): + """asyncio callback when a connection is opened.""" + assert not self._transport + logger.debug("Connected & Listening: %s:%d" % (self.dstaddr, self.dstport)) + self._transport = transport + if self.on_connection_send_msg: + self.send_message(self.on_connection_send_msg) + self.on_connection_send_msg = None # Never used again + self.on_open() + + def connection_lost(self, exc): + """asyncio callback when a connection is closed.""" + if exc: + logger.warning("Connection lost to {}:{} due to {}".format(self.dstaddr, self.dstport, exc)) + else: + logger.debug("Closed connection to: %s:%d" % (self.dstaddr, self.dstport)) + self._transport = None self.recvbuf = b"" - self.sendbuf = b"" - try: - self.close() - except: - 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): - """asyncore callback when data is read from the socket.""" - t = self.recv(8192) + def data_received(self, t): + """asyncio callback when data is read from the socket.""" if len(t) > 0: self.recvbuf += t self._on_data() @@ -179,39 +170,21 @@ class P2PConnection(asyncore.dispatcher): # Socket write methods - 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) - - def handle_write(self): - """asyncore callback when data should be written to the socket.""" - with mininode_lock: - # 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": - self.handle_connect() - if not self.writable(): - return - - try: - sent = self.send(self.sendbuf) - except: - self.handle_close() - 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) + tmsg = self._build_message(message) + NetworkThread.network_event_loop.call_soon_threadsafe(lambda: self._transport and self._transport.write(tmsg)) + + # Class utility methods + + def _build_message(self, message): + """Build a serialized P2P message""" command = message.command data = message.serialize() tmsg = MAGIC_BYTES[self.network] @@ -222,17 +195,7 @@ class P2PConnection(asyncore.dispatcher): h = sha256(th) tmsg += h[:4] tmsg += data - with mininode_lock: - if (len(self.sendbuf) == 0 and not pushbuf): - try: - sent = self.send(tmsg) - self.sendbuf = tmsg[sent:] - except BlockingIOError: - self.sendbuf = tmsg - else: - self.sendbuf += tmsg - - # Class utility methods + return tmsg def _log_message(self, direction, msg): """Logs a message being sent or received over the connection.""" @@ -270,7 +233,7 @@ class P2PInterface(P2PConnection): self.nServices = 0 def peer_connect(self, *args, services=NODE_NETWORK|NODE_WITNESS, send_version=True, **kwargs): - super().peer_connect(*args, **kwargs) + create_conn = super().peer_connect(*args, **kwargs) if send_version: # Send a version msg @@ -280,7 +243,9 @@ 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.on_connection_send_msg = vt # Will be sent soon after connection_made + + return create_conn # Message receiving methods @@ -348,7 +313,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 @@ -404,56 +369,35 @@ class P2PInterface(P2PConnection): self.ping_counter += 1 -# Keep our own socket map for asyncore, so that we can track disconnects -# ourselves (to work around an issue with closing an asyncore socket when -# using select) -mininode_socket_map = dict() - -# One lock for synchronizing all data access between the networking thread (see +# One lock for synchronizing all data access between the network event loop (see # NetworkThread below) and the thread running the test logic. For simplicity, -# P2PConnection acquires this lock whenever delivering a message to a P2PInterface, -# and whenever adding anything to the send buffer (in send_message()). This -# lock should be acquired in the thread running the test logic to synchronize +# P2PConnection acquires this lock whenever delivering a message to a P2PInterface. +# This lock should be acquired in the thread running the test logic to synchronize # access to any data shared with the P2PInterface or P2PConnection. mininode_lock = threading.RLock() + class NetworkThread(threading.Thread): + network_event_loop = None + def __init__(self): super().__init__(name="NetworkThread") + # There is only one event loop and no more than one thread must be created + assert not self.network_event_loop + + NetworkThread.network_event_loop = asyncio.new_event_loop() def run(self): - while mininode_socket_map: - # We check for whether to disconnect outside of the asyncore - # loop to work around the behavior of asyncore when using - # select - disconnected = [] - for fd, obj in mininode_socket_map.items(): - if obj.disconnect: - disconnected.append(obj) - [obj.handle_close() for obj in disconnected] - asyncore.loop(0.1, use_poll=True, map=mininode_socket_map, count=1) - logger.debug("Network thread closing") - -def network_thread_start(): - """Start the network thread.""" - # Only one network thread may run at a time - assert not network_thread_running() - - NetworkThread().start() - -def network_thread_running(): - """Return whether the network thread is running.""" - return any([thread.name == "NetworkThread" for thread in threading.enumerate()]) - -def network_thread_join(timeout=10): - """Wait timeout seconds for the network thread to terminate. - - Throw if the network thread doesn't terminate in timeout seconds.""" - network_threads = [thread for thread in threading.enumerate() if thread.name == "NetworkThread"] - assert len(network_threads) <= 1 - for thread in network_threads: - thread.join(timeout) - assert not thread.is_alive() + """Start the network thread.""" + self.network_event_loop.run_forever() + + def close(self, timeout=10): + """Close the connections and network event loop.""" + self.network_event_loop.call_soon_threadsafe(self.network_event_loop.stop) + wait_until(lambda: not self.network_event_loop.is_running(), timeout=timeout) + self.network_event_loop.close() + self.join(timeout) + class P2PDataStore(P2PInterface): """A P2P data store class. diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index 5c2555c1ff..abe8d12e59 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -18,6 +18,7 @@ import time from .authproxy import JSONRPCException from . import coverage from .test_node import TestNode +from .mininode import NetworkThread from .util import ( MAX_NODES, PortSeed, @@ -83,6 +84,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): """Sets test framework defaults. Do not override this method. Instead, override the set_test_params() method""" self.setup_clean_chain = False self.nodes = [] + self.network_thread = None self.mocktime = 0 self.supports_cli = False self.bind_to_localhost_only = True @@ -144,6 +146,10 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): self.options.tmpdir = tempfile.mkdtemp(prefix="test") self._start_logging() + self.log.debug('Setting up network thread') + self.network_thread = NetworkThread() + self.network_thread.start() + success = TestStatus.FAILED try: @@ -171,6 +177,8 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): print("Testcase failed. Attaching python debugger. Enter ? for help") pdb.set_trace() + self.log.debug('Closing down network thread') + self.network_thread.close() if not self.options.noshutdown: self.log.info("Stopping nodes") if self.nodes: diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py index 0353fc0712..287dc0e53e 100755 --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -289,7 +289,7 @@ class TestNode(): if 'dstaddr' not in kwargs: kwargs['dstaddr'] = '127.0.0.1' - p2p_conn.peer_connect(*args, **kwargs) + p2p_conn.peer_connect(*args, **kwargs)() self.p2ps.append(p2p_conn) return p2p_conn @@ -343,10 +343,10 @@ class TestNodeCLI(): def batch(self, requests): results = [] for request in requests: - try: - results.append(dict(result=request())) - except JSONRPCException as e: - results.append(dict(error=e)) + try: + results.append(dict(result=request())) + except JSONRPCException as e: + results.append(dict(error=e)) return results def send_cli(self, command=None, *args, **kwargs): diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 36101d9f57..c3a5468296 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -116,6 +116,7 @@ BASE_SCRIPTS = [ 'feature_versionbits_warning.py', 'rpc_preciousblock.py', 'wallet_importprunedfunds.py', + 'rpc_zmq.py', 'rpc_signmessage.py', 'feature_nulldummy.py', 'mempool_accept.py', diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py index 9c58b84819..0353905142 100755 --- a/test/functional/wallet_basic.py +++ b/test/functional/wallet_basic.py @@ -129,6 +129,15 @@ class WalletTest(BitcoinTestFramework): self.nodes[2].lockunspent, False, [{"txid": unspent_0["txid"], "vout": 999}]) + # An output should be unlocked when spent + unspent_0 = self.nodes[1].listunspent()[0] + self.nodes[1].lockunspent(False, [unspent_0]) + tx = self.nodes[1].createrawtransaction([unspent_0], { self.nodes[1].getnewaddress() : 1 }) + tx = self.nodes[1].fundrawtransaction(tx)['hex'] + tx = self.nodes[1].signrawtransactionwithwallet(tx)["hex"] + self.nodes[1].sendrawtransaction(tx) + assert_equal(len(self.nodes[1].listlockunspent()), 0) + # Have node1 generate 100 blocks (so node0 can recover the fee) self.nodes[1].generate(100) self.sync_all([self.nodes[0:3]]) @@ -230,8 +239,8 @@ class WalletTest(BitcoinTestFramework): # 2. hex-changed one output to 0.0 # 3. sign and send # 4. check if recipient (node0) can list the zero value tx - usp = self.nodes[1].listunspent() - inputs = [{"txid": usp[0]['txid'], "vout": usp[0]['vout']}] + usp = self.nodes[1].listunspent(query_options={'minimumAmount': '49.998'})[0] + inputs = [{"txid": usp['txid'], "vout": usp['vout']}] outputs = {self.nodes[1].getnewaddress(): 49.998, self.nodes[0].getnewaddress(): 11.11} raw_tx = self.nodes[1].createrawtransaction(inputs, outputs).replace("c0833842", "00000000") # replace 11.11 with 0.0 (int32) diff --git a/test/functional/wallet_fallbackfee.py b/test/functional/wallet_fallbackfee.py index e9cd052344..cc0a5175d5 100755 --- a/test/functional/wallet_fallbackfee.py +++ b/test/functional/wallet_fallbackfee.py @@ -21,7 +21,6 @@ class WalletRBFTest(BitcoinTestFramework): self.restart_node(0, extra_args=["-fallbackfee=0"]) assert_raises_rpc_error(-4, "Fee estimation failed", lambda: self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1)) assert_raises_rpc_error(-4, "Fee estimation failed", lambda: self.nodes[0].fundrawtransaction(self.nodes[0].createrawtransaction([], {self.nodes[0].getnewaddress(): 1}))) - assert_raises_rpc_error(-4, "Fee estimation failed", lambda: self.nodes[0].sendfrom("", self.nodes[0].getnewaddress(), 1)) assert_raises_rpc_error(-6, "Fee estimation failed", lambda: self.nodes[0].sendmany("", {self.nodes[0].getnewaddress(): 1})) if __name__ == '__main__': diff --git a/test/functional/wallet_labels.py b/test/functional/wallet_labels.py index f5d7830fca..8d961fb34a 100755 --- a/test/functional/wallet_labels.py +++ b/test/functional/wallet_labels.py @@ -85,7 +85,8 @@ class WalletLabelsTest(BitcoinTestFramework): # we want to reset so that the "" label has what's expected. # otherwise we're off by exactly the fee amount as that's mined # and matures in the next 100 blocks - node.sendfrom("", common_address, fee) + if accounts_api: + node.sendfrom("", common_address, fee) amount_to_send = 1.0 # Create labels and make sure subsequent label API calls @@ -125,7 +126,7 @@ class WalletLabelsTest(BitcoinTestFramework): if accounts_api: node.sendfrom(label.name, to_label.receive_address, amount_to_send) else: - node.sendfrom(label.name, to_label.addresses[0], amount_to_send) + node.sendtoaddress(to_label.addresses[0], amount_to_send) node.generate(1) for label in labels: if accounts_api: @@ -166,7 +167,8 @@ class WalletLabelsTest(BitcoinTestFramework): label.add_address(multisig_address) label.purpose[multisig_address] = "send" label.verify(node) - node.sendfrom("", multisig_address, 50) + if accounts_api: + node.sendfrom("", multisig_address, 50) node.generate(101) if accounts_api: for label in labels: diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py index a0fbc4a754..fa5a2154a4 100755 --- a/test/functional/wallet_multiwallet.py +++ b/test/functional/wallet_multiwallet.py @@ -211,6 +211,10 @@ class MultiWalletTest(BitcoinTestFramework): # Fail to load if wallet file is a symlink assert_raises_rpc_error(-4, "Wallet file verification failed: Invalid -wallet path 'w8_symlink'", self.nodes[0].loadwallet, 'w8_symlink') + # Fail to load if a directory is specified that doesn't contain a wallet + os.mkdir(wallet_dir('empty_wallet_dir')) + assert_raises_rpc_error(-18, "Directory empty_wallet_dir does not contain a wallet.dat file", self.nodes[0].loadwallet, 'empty_wallet_dir') + self.log.info("Test dynamic wallet creation.") # Fail to create a wallet if it already exists. @@ -234,5 +238,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/lint-all.sh b/test/lint/lint-all.sh index c9d4ec7199..7c4f96cb3b 100755 --- a/test/lint/lint-all.sh +++ b/test/lint/lint-all.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Copyright (c) 2017 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying diff --git a/test/lint/lint-filenames.sh b/test/lint/lint-filenames.sh index 61e978fe79..5391e43d91 100755 --- a/test/lint/lint-filenames.sh +++ b/test/lint/lint-filenames.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Copyright (c) 2018 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying @@ -10,7 +10,10 @@ export LC_ALL=C EXIT_CODE=0 -OUTPUT=$(git ls-files -- "*.cpp" "*.h" "*.py" "*.sh" | grep -vE '^[a-z0-9_./-]+$' | grep -vE 'src/(secp256k1|univalue)/') +OUTPUT=$(git ls-files --full-name -- "*.[cC][pP][pP]" "*.[hH]" "*.[pP][yY]" "*.[sS][hH]" | \ + grep -vE '^[a-z0-9_./-]+$' | \ + grep -vE '^src/(secp256k1|univalue)/') + if [[ ${OUTPUT} != "" ]]; then echo "Use only lowercase alphanumerics (a-z0-9), underscores (_), hyphens (-) and dots (.)" echo "in source code filenames:" diff --git a/test/lint/lint-include-guards.sh b/test/lint/lint-include-guards.sh index ea791b0176..464969794b 100755 --- a/test/lint/lint-include-guards.sh +++ b/test/lint/lint-include-guards.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Copyright (c) 2018 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying diff --git a/test/lint/lint-includes.sh b/test/lint/lint-includes.sh index 03350576a7..40d28ed3e0 100755 --- a/test/lint/lint-includes.sh +++ b/test/lint/lint-includes.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Copyright (c) 2018 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying @@ -53,7 +53,6 @@ EXPECTED_BOOST_INCLUDES=( boost/algorithm/string/predicate.hpp boost/algorithm/string/replace.hpp boost/algorithm/string/split.hpp - boost/assign/std/vector.hpp boost/bind.hpp boost/chrono/chrono.hpp boost/date_time/posix_time/posix_time.hpp diff --git a/test/lint/lint-locale-dependence.sh b/test/lint/lint-locale-dependence.sh index 98814ce4b9..cbe1143bd0 100755 --- a/test/lint/lint-locale-dependence.sh +++ b/test/lint/lint-locale-dependence.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash export LC_ALL=C KNOWN_VIOLATIONS=( diff --git a/test/lint/lint-logs.sh b/test/lint/lint-logs.sh index 89e7fb8764..1afd4cfc1a 100755 --- a/test/lint/lint-logs.sh +++ b/test/lint/lint-logs.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Copyright (c) 2018 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying diff --git a/test/lint/lint-python-shebang.sh b/test/lint/lint-python-shebang.sh index 031487a91a..4ff87f0bf7 100755 --- a/test/lint/lint-python-shebang.sh +++ b/test/lint/lint-python-shebang.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Shebang must use python3 (not python or python2) export LC_ALL=C diff --git a/test/lint/lint-python-utf8-encoding.sh b/test/lint/lint-python-utf8-encoding.sh index d0ff38ea0a..14183a5ccf 100755 --- a/test/lint/lint-python-utf8-encoding.sh +++ b/test/lint/lint-python-utf8-encoding.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Copyright (c) 2018 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying diff --git a/test/lint/lint-shell-locale.sh b/test/lint/lint-shell-locale.sh index d78bac2d47..242b27c763 100755 --- a/test/lint/lint-shell-locale.sh +++ b/test/lint/lint-shell-locale.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Copyright (c) 2018 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying diff --git a/test/lint/lint-shell.sh b/test/lint/lint-shell.sh index bf56db90c6..e2ccdb5165 100755 --- a/test/lint/lint-shell.sh +++ b/test/lint/lint-shell.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Copyright (c) 2018 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying diff --git a/test/lint/lint-tests.sh b/test/lint/lint-tests.sh index 9d83547df4..35d11023eb 100755 --- a/test/lint/lint-tests.sh +++ b/test/lint/lint-tests.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Copyright (c) 2018 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying diff --git a/test/lint/lint-whitespace.sh b/test/lint/lint-whitespace.sh index 128aa10da5..beb7ec42f4 100755 --- a/test/lint/lint-whitespace.sh +++ b/test/lint/lint-whitespace.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Copyright (c) 2017 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying diff --git a/test/util/data/bitcoin-util-test.json b/test/util/data/bitcoin-util-test.json index a115aa30d9..f2213f4f2e 100644 --- a/test/util/data/bitcoin-util-test.json +++ b/test/util/data/bitcoin-util-test.json @@ -27,6 +27,12 @@ "description": "Creates a blank transaction when nothing is piped into bitcoin-tx (output in json)" }, { "exec": "./bitcoin-tx", + "args": ["-create", "nversion=1foo"], + "return_code": 1, + "error_txt": "error: Invalid TX version requested", + "description": "Tests the check for invalid nversion value" + }, + { "exec": "./bitcoin-tx", "args": ["-", "delin=1"], "input": "tx394b54bb.hex", "output_cmp": "tt-delin1-out.hex", @@ -46,6 +52,13 @@ "description": "Attempts to delete an input with a bad index from a transaction. Expected to fail." }, { "exec": "./bitcoin-tx", + "args": ["-", "delin=1foo"], + "input": "tx394b54bb.hex", + "return_code": 1, + "error_txt": "error: Invalid TX input index", + "description": "Tests the check for an invalid input index with delin" + }, + { "exec": "./bitcoin-tx", "args": ["-", "delout=1"], "input": "tx394b54bb.hex", "output_cmp": "tt-delout1-out.hex", @@ -65,6 +78,13 @@ "description": "Attempts to delete an output with a bad index from a transaction. Expected to fail." }, { "exec": "./bitcoin-tx", + "args": ["-", "delout=1foo"], + "input": "tx394b54bb.hex", + "return_code": 1, + "error_txt": "error: Invalid TX output index", + "description": "Tests the check for an invalid output index with delout" + }, + { "exec": "./bitcoin-tx", "args": ["-", "locktime=317000"], "input": "tx394b54bb.hex", "output_cmp": "tt-locktime317000-out.hex", @@ -77,6 +97,29 @@ "description": "Adds an nlocktime to a transaction (output in json)" }, { "exec": "./bitcoin-tx", + "args": ["-create", "locktime=317000foo"], + "return_code": 1, + "error_txt": "error: Invalid TX locktime requested", + "description": "Tests the check for invalid locktime value" + }, + { "exec": "./bitcoin-tx", + "args": + ["-create", + "in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0", + "replaceable=0foo"], + "return_code": 1, + "error_txt": "error: Invalid TX input index", + "description": "Tests the check for an invalid input index with replaceable" + }, + { "exec": "./bitcoin-tx", + "args": + ["-create", + "in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0x"], + "return_code": 1, + "error_txt": "error: invalid TX input vout", + "description": "Tests the check for an invalid vout value when adding an input" + }, + { "exec": "./bitcoin-tx", "args": ["-create", "outaddr=1"], @@ -227,6 +270,18 @@ }, { "exec": "./bitcoin-tx", "args": + ["-create", + "in=4d49a71ec9da436f71ec4ee231d04f292a29cd316f598bb7068feccabdc59485:0", + "set=privatekeys:[\"5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreAnchuDf\"]", + "set=prevtxs:[{\"txid\":\"4d49a71ec9da436f71ec4ee231d04f292a29cd316f598bb7068feccabdc59485\",\"vout\":\"0foo\",\"scriptPubKey\":\"76a91491b24bf9f5288532960ac687abb035127b1d28a588ac\"}]", + "sign=ALL", + "outaddr=0.001:193P6LtvS4nCnkDvM9uXn1gsSRqh4aDAz7"], + "return_code": 1, + "error_txt": "error: prevtxs internal object typecheck fail", + "description": "Tests the check for invalid vout index in prevtxs for sign" + }, + { "exec": "./bitcoin-tx", + "args": ["-create", "outpubkey=0:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397", "nversion=1"], "output_cmp": "txcreateoutpubkey1.hex", "description": "Creates a new transaction with a single pay-to-pubkey output" |