diff options
118 files changed, 2619 insertions, 775 deletions
diff --git a/.travis.yml b/.travis.yml index 6570e6571b..4087a854b4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -58,9 +58,11 @@ script: - depends/$HOST/native/bin/ccache --max-size=$CCACHE_SIZE - test -n "$USE_SHELL" && eval '"$USE_SHELL" -c "./autogen.sh"' || ./autogen.sh - mkdir build && cd build - - ../configure $BITCOIN_CONFIG_ALL $BITCOIN_CONFIG || ( cat config.log && false) + - ../configure --cache-file=config.cache $BITCOIN_CONFIG_ALL $BITCOIN_CONFIG || ( cat config.log && false) + - make distdir VERSION=$HOST + - cd bitcoin-$HOST + - ./configure --cache-file=../config.cache $BITCOIN_CONFIG_ALL $BITCOIN_CONFIG || ( cat config.log && false) - make $MAKEJOBS $GOAL || ( echo "Build failure. Verbose build follows." && make $GOAL V=1 ; false ) - - make distdir - export LD_LIBRARY_PATH=$TRAVIS_BUILD_DIR/depends/$HOST/lib - if [ "$RUN_TESTS" = "true" ]; then make $MAKEJOBS check VERBOSE=1; fi - if [ "$RUN_TESTS" = "true" ]; then qa/pull-tester/rpc-tests.py --coverage; fi diff --git a/depends/config.guess b/depends/config.guess index c4bd827a7b..bbd48b60e8 100755 --- a/depends/config.guess +++ b/depends/config.guess @@ -1,8 +1,8 @@ #! /bin/sh # Attempt to guess a canonical system name. -# Copyright 1992-2016 Free Software Foundation, Inc. +# Copyright 1992-2017 Free Software Foundation, Inc. -timestamp='2016-05-15' +timestamp='2017-01-01' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -50,7 +50,7 @@ version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. -Copyright 1992-2016 Free Software Foundation, Inc. +Copyright 1992-2017 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." @@ -1000,6 +1000,9 @@ EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; } ;; + mips64el:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; openrisc*:Linux:*:*) echo or1k-unknown-linux-${LIBC} exit ;; @@ -1032,6 +1035,9 @@ EOF ppcle:Linux:*:*) echo powerpcle-unknown-linux-${LIBC} exit ;; + riscv32:Linux:*:* | riscv64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; s390:Linux:*:* | s390x:Linux:*:*) echo ${UNAME_MACHINE}-ibm-linux-${LIBC} exit ;; diff --git a/depends/config.site.in b/depends/config.site.in index 27e3aedd8e..3d7c9fd43c 100644 --- a/depends/config.site.in +++ b/depends/config.site.in @@ -63,7 +63,6 @@ LDFLAGS="-L$depends_prefix/lib $LDFLAGS" CC="@CC@" CXX="@CXX@" OBJC="${CC}" -OBJCXX="${CXX}" CCACHE=$depends_prefix/native/bin/ccache PYTHONPATH=$depends_prefix/native/lib/python/dist-packages:$PYTHONPATH diff --git a/depends/config.sub b/depends/config.sub index 6d86a1e2f7..7e792b4ae1 100755 --- a/depends/config.sub +++ b/depends/config.sub @@ -1,8 +1,8 @@ #! /bin/sh # Configuration validation subroutine script. -# Copyright 1992-2016 Free Software Foundation, Inc. +# Copyright 1992-2017 Free Software Foundation, Inc. -timestamp='2016-05-10' +timestamp='2017-01-01' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -67,7 +67,7 @@ Report bugs and patches to <config-patches@gnu.org>." version="\ GNU config.sub ($timestamp) -Copyright 1992-2016 Free Software Foundation, Inc. +Copyright 1992-2017 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." @@ -117,7 +117,7 @@ case $maybe_os in nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \ - kopensolaris*-gnu* | \ + kopensolaris*-gnu* | cloudabi*-eabi* | \ storm-chaos* | os2-emx* | rtmk-nova*) os=-$maybe_os basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` @@ -301,6 +301,7 @@ case $basic_machine in | open8 | or1k | or1knd | or32 \ | pdp10 | pdp11 | pj | pjl \ | powerpc | powerpc64 | powerpc64le | powerpcle \ + | pru \ | pyramid \ | riscv32 | riscv64 \ | rl78 | rx \ @@ -428,6 +429,7 @@ case $basic_machine in | orion-* \ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ + | pru-* \ | pyramid-* \ | riscv32-* | riscv64-* \ | rl78-* | romp-* | rs6000-* | rx-* \ @@ -643,6 +645,14 @@ case $basic_machine in basic_machine=m68k-bull os=-sysv3 ;; + e500v[12]) + basic_machine=powerpc-unknown + os=$os"spe" + ;; + e500v[12]-*) + basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + os=$os"spe" + ;; ebmon29k) basic_machine=a29k-amd os=-ebmon @@ -1022,7 +1032,7 @@ case $basic_machine in ppc-* | ppcbe-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` ;; - ppcle | powerpclittle | ppc-le | powerpc-little) + ppcle | powerpclittle) basic_machine=powerpcle-unknown ;; ppcle-* | powerpclittle-*) @@ -1032,7 +1042,7 @@ case $basic_machine in ;; ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` ;; - ppc64le | powerpc64little | ppc64-le | powerpc64-little) + ppc64le | powerpc64little) basic_machine=powerpc64le-unknown ;; ppc64le-* | powerpc64little-*) @@ -1387,9 +1397,9 @@ case $os in | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ - | -chorusos* | -chorusrdb* | -cegcc* \ + | -chorusos* | -chorusrdb* | -cegcc* | -glidix* \ | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ - | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ + | -midipix* | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ | -linux-newlib* | -linux-musl* | -linux-uclibc* \ | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ @@ -1399,7 +1409,7 @@ case $os in | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* \ - | -onefs* | -tirtos* | -phoenix*) + | -onefs* | -tirtos* | -phoenix* | -fuchsia* | -redox*) # Remember, each alternative MUST END IN *, to match a version number. ;; -qnx*) @@ -1628,6 +1638,9 @@ case $basic_machine in sparc-* | *-sun) os=-sunos4.1.1 ;; + pru-*) + os=-elf + ;; *-be) os=-beos ;; diff --git a/depends/packages/boost.mk b/depends/packages/boost.mk index 4173c0d168..57d96e4821 100644 --- a/depends/packages/boost.mk +++ b/depends/packages/boost.mk @@ -1,8 +1,8 @@ package=boost -$(package)_version=1_61_0 -$(package)_download_path=https://sourceforge.net/projects/boost/files/boost/1.61.0 +$(package)_version=1_63_0 +$(package)_download_path=https://sourceforge.net/projects/boost/files/boost/1.63.0 $(package)_file_name=$(package)_$($(package)_version).tar.bz2 -$(package)_sha256_hash=a547bd06c2fd9a71ba1d169d9cf0339da7ebf4753849a8f7d6fdb8feee99b640 +$(package)_sha256_hash=beae2529f759f6b3bf3f4969a19c2e9d6f0c503edcb2de4a61d1428519fcb3b0 define $(package)_set_vars $(package)_config_opts_release=variant=release diff --git a/depends/packages/dbus.mk b/depends/packages/dbus.mk index 8ac9ab742b..90ddcb923f 100644 --- a/depends/packages/dbus.mk +++ b/depends/packages/dbus.mk @@ -1,8 +1,8 @@ package=dbus -$(package)_version=1.8.6 +$(package)_version=1.10.14 $(package)_download_path=http://dbus.freedesktop.org/releases/dbus $(package)_file_name=$(package)-$($(package)_version).tar.gz -$(package)_sha256_hash=eded83ca007b719f32761e60fd8b9ffd0f5796a4caf455b01b5a5ef740ebd23f +$(package)_sha256_hash=23238f70353e38ce5ca183ebc9525c0d97ac00ef640ad29cf794782af6e6a083 $(package)_dependencies=expat define $(package)_set_vars diff --git a/depends/packages/freetype.mk b/depends/packages/freetype.mk index 7cea28ff0b..76b025c463 100644 --- a/depends/packages/freetype.mk +++ b/depends/packages/freetype.mk @@ -1,8 +1,8 @@ package=freetype -$(package)_version=2.6.3 +$(package)_version=2.7.1 $(package)_download_path=http://download.savannah.gnu.org/releases/$(package) $(package)_file_name=$(package)-$($(package)_version).tar.bz2 -$(package)_sha256_hash=371e707aa522acf5b15ce93f11183c725b8ed1ee8546d7b3af549863045863a2 +$(package)_sha256_hash=3a3bb2c4e15ffb433f2032f50a5b5a92558206822e22bfe8cbe339af4aa82f88 define $(package)_set_vars $(package)_config_opts=--without-zlib --without-png --disable-static diff --git a/depends/packages/libevent.mk b/depends/packages/libevent.mk index 4b02b2eff9..70f345e71d 100644 --- a/depends/packages/libevent.mk +++ b/depends/packages/libevent.mk @@ -1,13 +1,11 @@ package=libevent -$(package)_version=2.0.22 -$(package)_download_path=https://github.com/libevent/libevent/releases/download/release-2.0.22-stable -$(package)_file_name=$(package)-$($(package)_version)-stable.tar.gz -$(package)_sha256_hash=71c2c49f0adadacfdbe6332a372c38cf9c8b7895bb73dabeaa53cdcc1d4e1fa3 -$(package)_patches=reuseaddr.patch libevent-2-fixes.patch +$(package)_version=2.1.7 +$(package)_download_path=https://github.com/libevent/libevent/archive/ +$(package)_file_name=release-$($(package)_version)-rc.tar.gz +$(package)_sha256_hash=548362d202e22fe24d4c3fad38287b4f6d683e6c21503341373b89785fa6f991 define $(package)_preprocess_cmds - patch -p1 < $($(package)_patch_dir)/reuseaddr.patch && \ - patch -p1 < $($(package)_patch_dir)/libevent-2-fixes.patch + ./autogen.sh endef define $(package)_set_vars diff --git a/depends/packages/native_ccache.mk b/depends/packages/native_ccache.mk index a3a58604e5..4ed61a49e9 100644 --- a/depends/packages/native_ccache.mk +++ b/depends/packages/native_ccache.mk @@ -1,8 +1,8 @@ package=native_ccache -$(package)_version=3.3.1 +$(package)_version=3.3.3 $(package)_download_path=https://samba.org/ftp/ccache $(package)_file_name=ccache-$($(package)_version).tar.bz2 -$(package)_sha256_hash=cb6e4bafbb19ba0a2ec43386b123a5f92a20e1e3384c071d5d13e0cb3c84bf73 +$(package)_sha256_hash=2985bc5e32ebe38d2958d508eb54ddcad39eed909489c0c2988035214597ca54 define $(package)_set_vars $(package)_config_opts= diff --git a/depends/packages/native_ds_store.mk b/depends/packages/native_ds_store.mk index 8e902af1b6..49f5829ac1 100644 --- a/depends/packages/native_ds_store.mk +++ b/depends/packages/native_ds_store.mk @@ -1,9 +1,9 @@ package=native_ds_store -$(package)_version=c80c23706eae +$(package)_version=1.1.0 $(package)_download_path=https://bitbucket.org/al45tair/ds_store/get -$(package)_download_file=$($(package)_version).tar.bz2 +$(package)_download_file=v$($(package)_version).tar.bz2 $(package)_file_name=$(package)-$($(package)_version).tar.bz2 -$(package)_sha256_hash=ce1aa412211610c63d567bbe3e06213006a2d5ba5d76d89399c151b5472cb0da +$(package)_sha256_hash=921596764d71d1bbd3297a90ef6d286f718794d667e4f81d91d14053525d64c1 $(package)_install_libdir=$(build_prefix)/lib/python/dist-packages $(package)_dependencies=native_biplist diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk index bdde15aa17..383ef2dae6 100644 --- a/depends/packages/qt.mk +++ b/depends/packages/qt.mk @@ -1,30 +1,30 @@ PACKAGE=qt -$(package)_version=5.6.1 -$(package)_download_path=http://download.qt.io/official_releases/qt/5.6/$($(package)_version)/submodules +$(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)_file_name=qtbase-$($(package)_suffix) -$(package)_sha256_hash=0ac67cf8d66d52b995f96c31c4b48117a1afb3db99eaa93e20ccd8f7f55f7fde +$(package)_sha256_hash=95f83e532d23b3ddbde7973f380ecae1bac13230340557276f75f2e37984e410 $(package)_dependencies=openssl $(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 configure-xcoderun.patch mingw-uuidof.patch pidlist_absolute.patch fix-xcb-include-order.patch fix_qt_pkgconfig.patch +$(package)_patches=mac-qmake.conf mingw-uuidof.patch pidlist_absolute.patch fix-xcb-include-order.patch fix_qt_pkgconfig.patch $(package)_qttranslations_file_name=qttranslations-$($(package)_suffix) -$(package)_qttranslations_sha256_hash=dcc1534d247babca1840cb6d0a000671801a341ea352d0535474f86adadaf028 +$(package)_qttranslations_sha256_hash=3a15aebd523c6d89fb97b2d3df866c94149653a26d27a00aac9b6d3020bc5a1d $(package)_qttools_file_name=qttools-$($(package)_suffix) -$(package)_qttools_sha256_hash=e0f845de28c31230dfa428f0190ccb3b91d1fc02481b1f064698ae4ef8376aa1 +$(package)_qttools_sha256_hash=22d67de915cb8cd93e16fdd38fa006224ad9170bd217c2be1e53045a8dd02f0f $(package)_extra_sources = $($(package)_qttranslations_file_name) $(package)_extra_sources += $($(package)_qttools_file_name) define $(package)_set_vars $(package)_config_opts_release = -release -$(package)_config_opts_debug = -debug +$(package)_config_opts_debug = -debug $(package)_config_opts += -bindir $(build_prefix)/bin -$(package)_config_opts += -c++11 +$(package)_config_opts += -c++std c++11 $(package)_config_opts += -confirm-license $(package)_config_opts += -dbus-runtime $(package)_config_opts += -hostprefix $(build_prefix) @@ -46,7 +46,6 @@ $(package)_config_opts += -no-linuxfb $(package)_config_opts += -no-libudev $(package)_config_opts += -no-mitshm $(package)_config_opts += -no-mtdev -$(package)_config_opts += -no-nis $(package)_config_opts += -no-pulseaudio $(package)_config_opts += -no-openvg $(package)_config_opts += -no-reduce-relocations @@ -125,6 +124,7 @@ 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|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 && \ @@ -134,7 +134,6 @@ 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)/configure-xcoderun.patch && \ 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 && \ diff --git a/depends/patches/libevent/libevent-2-fixes.patch b/depends/patches/libevent/libevent-2-fixes.patch deleted file mode 100644 index 79fec8a488..0000000000 --- a/depends/patches/libevent/libevent-2-fixes.patch +++ /dev/null @@ -1,18 +0,0 @@ ---- a/util-internal.h 2013-11-01 12:18:57.000000000 -0600 -+++ b/util-internal.h 2015-07-20 20:19:43.199560900 -0500 -@@ -299,8 +299,13 @@ HANDLE evutil_load_windows_system_librar - - #if defined(__STDC__) && defined(__STDC_VERSION__) - #if (__STDC_VERSION__ >= 199901L) --#define EV_SIZE_FMT "%zu" --#define EV_SSIZE_FMT "%zd" -+ #if defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__) -+ #define EV_SIZE_FMT "%Iu" -+ #define EV_SSIZE_FMT "%Id" -+ #else -+ #define EV_SIZE_FMT "%zu" -+ #define EV_SSIZE_FMT "%zd" -+ #endif - #define EV_SIZE_ARG(x) (x) - #define EV_SSIZE_ARG(x) (x) - #endif diff --git a/depends/patches/libevent/reuseaddr.patch b/depends/patches/libevent/reuseaddr.patch deleted file mode 100644 index 58695c11f5..0000000000 --- a/depends/patches/libevent/reuseaddr.patch +++ /dev/null @@ -1,21 +0,0 @@ ---- old/evutil.c 2015-08-28 19:26:23.488765923 -0400 -+++ new/evutil.c 2015-08-28 19:27:41.392767019 -0400 -@@ -321,15 +321,16 @@ - int - evutil_make_listen_socket_reuseable(evutil_socket_t sock) - { --#ifndef WIN32 - int one = 1; -+#ifndef WIN32 - /* REUSEADDR on Unix means, "don't hang on to this address after the - * listener is closed." On Windows, though, it means "don't keep other - * processes from binding to this address while we're using it. */ - return setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*) &one, - (ev_socklen_t)sizeof(one)); - #else -- return 0; -+ return setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*) &one, -+ (ev_socklen_t)sizeof(one)); - #endif - } - diff --git a/depends/patches/qt/configure-xcoderun.patch b/depends/patches/qt/configure-xcoderun.patch deleted file mode 100644 index 08286d8420..0000000000 --- a/depends/patches/qt/configure-xcoderun.patch +++ /dev/null @@ -1,25 +0,0 @@ ---- old/qtbase/configure -+++ new/qtbase/configure -@@ -543,7 +543,7 @@ if [ "$BUILD_ON_MAC" = "yes" ]; then - exit 2 - fi - -- if ! /usr/bin/xcrun -find xcrun >/dev/null 2>&1; then -+ if ! /usr/bin/xcrun -find xcodebuild >/dev/null 2>&1; then - echo >&2 - echo " Xcode not set up properly. You may need to confirm the license" >&2 - echo " agreement by running /usr/bin/xcodebuild without arguments." >&2 -diff --git a/mkspecs/features/mac/default_pre.prf b/mkspecs/features/mac/default_pre.prf -index 0cc8cd6..5df99d1 100644 ---- old/qtbase/mkspecs/features/mac/default_pre.prf -+++ new/qtbase/mkspecs/features/mac/default_pre.prf -@@ -12,7 +12,7 @@ isEmpty(QMAKE_XCODE_DEVELOPER_PATH) { - error("Xcode is not installed in $${QMAKE_XCODE_DEVELOPER_PATH}. Please use xcode-select to choose Xcode installation path.") - - # Make sure Xcode is set up properly -- isEmpty($$list($$system("/usr/bin/xcrun -find xcrun 2>/dev/null"))): \ -+ isEmpty($$list($$system("/usr/bin/xcrun -find xcodebuild 2>/dev/null"))): \ - error("Xcode not set up properly. You may need to confirm the license agreement by running /usr/bin/xcodebuild.") - } - --- diff --git a/depends/patches/qt/fix-xcb-include-order.patch b/depends/patches/qt/fix-xcb-include-order.patch index c7dbebedce..ec2bc17d9b 100644 --- a/depends/patches/qt/fix-xcb-include-order.patch +++ b/depends/patches/qt/fix-xcb-include-order.patch @@ -1,6 +1,6 @@ ---- old/qtbase/src/plugins/platforms/xcb/xcb_qpa_lib.pro 2015-03-17 02:06:42.705930685 +0000 -+++ new/qtbase/src/plugins/platforms/xcb/xcb_qpa_lib.pro 2015-03-17 02:08:41.281926351 +0000 -@@ -74,8 +74,6 @@ +--- 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 @@ -9,18 +9,18 @@ CONFIG += qpa/genericunixfontdatabase -@@ -87,7 +85,8 @@ +@@ -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$$OUT_PWD/xcb-static -lxcb-static + 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 2015-03-17 02:07:04.641929383 +0000 -+++ new/qtbase/src/plugins/platforms/xcb/xcb-static/xcb-static.pro 2015-03-17 02:10:15.485922059 +0000 +--- 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 @@ -31,8 +31,8 @@ QMAKE_CXXFLAGS += $$QMAKE_CFLAGS_XCB QMAKE_CFLAGS += $$QMAKE_CFLAGS_XCB ---- old/qtbase/src/plugins/platforms/xcb/xcb-plugin.pro 2015-07-24 16:02:59.530038830 -0400 -+++ new/qtbase/src/plugins/platforms/xcb/xcb-plugin.pro 2015-07-24 16:01:22.106037459 -0400 +--- 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 diff --git a/depends/patches/qt/fix_qt_pkgconfig.patch b/depends/patches/qt/fix_qt_pkgconfig.patch index 3772db4f8b..34302a9f2d 100644 --- a/depends/patches/qt/fix_qt_pkgconfig.patch +++ b/depends/patches/qt/fix_qt_pkgconfig.patch @@ -1,6 +1,6 @@ ---- old/qtbase/mkspecs/features/qt_module.prf 2016-03-17 02:06:42.705930685 +0000 -+++ new/qtbase/mkspecs/features/qt_module.prf 2016-03-17 02:06:42.705930685 +0000 -@@ -244,7 +244,7 @@ +--- old/qtbase/mkspecs/features/qt_module.prf ++++ new/qtbase/mkspecs/features/qt_module.prf +@@ -245,7 +245,7 @@ load(qt_targets) # this builds on top of qt_common diff --git a/depends/patches/qt/mac-qmake.conf b/depends/patches/qt/mac-qmake.conf index a6d0070cca..402e0555b0 100644 --- a/depends/patches/qt/mac-qmake.conf +++ b/depends/patches/qt/mac-qmake.conf @@ -11,10 +11,10 @@ QMAKE_XCODE_VERSION=4.3 QMAKE_XCODE_DEVELOPER_PATH=/Developer QMAKE_MACOSX_DEPLOYMENT_TARGET = $${MAC_MIN_VERSION} QMAKE_MAC_SDK=macosx -QMAKE_MAC_SDK.macosx.path = $${MAC_SDK_PATH} +QMAKE_MAC_SDK.macosx.Path = $${MAC_SDK_PATH} QMAKE_MAC_SDK.macosx.platform_name = macosx -QMAKE_MAC_SDK.macosx.version = $${MAC_SDK_VERSION} -QMAKE_MAC_SDK.macosx.platform_path = /phony +QMAKE_MAC_SDK.macosx.SDKVersion = $${MAC_SDK_VERSION} +QMAKE_MAC_SDK.macosx.PlatformPath = /phony QMAKE_CFLAGS += -target $${MAC_TARGET} QMAKE_OBJECTIVE_CFLAGS += $$QMAKE_CFLAGS QMAKE_CXXFLAGS += $$QMAKE_CFLAGS diff --git a/depends/patches/qt/mingw-uuidof.patch b/depends/patches/qt/mingw-uuidof.patch index 975366e612..fb21923c8c 100644 --- a/depends/patches/qt/mingw-uuidof.patch +++ b/depends/patches/qt/mingw-uuidof.patch @@ -1,6 +1,6 @@ ---- old/qtbase/src/plugins/platforms/windows/qwindowscontext.cpp 2015-06-20 17:40:20.956781548 -0400 -+++ new/qtbase/src/plugins/platforms/windows/qwindowscontext.cpp 2015-06-20 17:29:32.052772416 -0400 -@@ -69,7 +69,7 @@ +--- 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> @@ -9,8 +9,8 @@ # include <comdef.h> #endif -@@ -762,7 +762,7 @@ - HWND_MESSAGE, NULL, (HINSTANCE)GetModuleHandle(0), NULL); +@@ -814,7 +814,7 @@ + HWND_MESSAGE, NULL, static_cast<HINSTANCE>(GetModuleHandle(0)), NULL); } -#ifndef Q_OS_WINCE @@ -18,16 +18,16 @@ // 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). -@@ -781,7 +781,7 @@ - return QStringLiteral("IDispatch error #") + QString::number(wCode); - return QStringLiteral("Unknown error 0x0") + QString::number(comError.Error(), 16); +@@ -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. -@@ -846,12 +846,12 @@ +@@ -901,12 +901,12 @@ default: break; } diff --git a/depends/patches/qt/pidlist_absolute.patch b/depends/patches/qt/pidlist_absolute.patch index 0b49c050dc..c792824179 100644 --- a/depends/patches/qt/pidlist_absolute.patch +++ b/depends/patches/qt/pidlist_absolute.patch @@ -1,7 +1,7 @@ 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 2015-06-29 22:04:40.000000000 +0200 -+++ new/qtbase/src/plugins/platforms/windows/qwindowscontext.h 2015-11-01 12:55:59.751234846 +0100 -@@ -124,10 +124,18 @@ +--- 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 **); @@ -21,9 +21,9 @@ diff -dur old/qtbase/src/plugins/platforms/windows/qwindowscontext.h new/qtbase/ 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 2015-06-29 22:04:40.000000000 +0200 -+++ new/qtbase/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp 2015-11-01 13:41:09.503149772 +0100 -@@ -1008,7 +1008,11 @@ +--- 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; } diff --git a/doc/developer-notes.md b/doc/developer-notes.md index ba03579e86..f8c34e060f 100644 --- a/doc/developer-notes.md +++ b/doc/developer-notes.md @@ -11,9 +11,13 @@ gradually. - Braces on new lines for namespaces, classes, functions, methods. - Braces on the same line for everything else. - 4 space indentation (no tabs) for every block except namespaces. - - No indentation for public/protected/private or for namespaces. + - No indentation for `public`/`protected`/`private` or for `namespace`. - No extra spaces inside parenthesis; don't do ( this ) - - No space after function names; one space after if, for and while. + - No space after function names; one space after `if`, `for` and `while`. + - If an `if` only has a single-statement then-clause, it can appear + on the same line as the if, without braces. In every other case, + braces are required, and the then and else clauses must appear + correctly indented on a new line. - `++i` is preferred over `i++`. Block style example: @@ -22,14 +26,18 @@ namespace foo { class Class { - bool Function(char* psz, int n) + bool Function(const std::string& s, int n) { // Comment summarising what this section of code does for (int i = 0; i < n; ++i) { // When something fails, return early - if (!Something()) - return false; + if (!Something()) return false; ... + if (SomethingElse()) { + DoMore(); + } else { + DoLess(); + } } // Success return is usually at the end diff --git a/doc/man/bitcoin-qt.1 b/doc/man/bitcoin-qt.1 index 24b529dac1..2129a151e2 100644 --- a/doc/man/bitcoin-qt.1 +++ b/doc/man/bitcoin-qt.1 @@ -75,11 +75,13 @@ Specify pid file (default: bitcoind.pid) .HP \fB\-prune=\fR<n> .IP -Reduce storage requirements by pruning (deleting) old blocks. This mode -is incompatible with \fB\-txindex\fR and \fB\-rescan\fR. Warning: Reverting -this setting requires re\-downloading the entire blockchain. -(default: 0 = disable pruning blocks, >550 = target size in MiB -to use for block files) +Reduce storage requirements by enabling pruning (deleting) of old blocks. +This allows the pruneblockchain RPC to be called to delete specific blocks, +and enables automatic pruning of old blocks if a target size in MiB is +provided. This mode is incompatible with \fB\-txindex\fR and \fB\-rescan\fR. +Warning: Reverting this setting requires re\-downloading the entire blockchain. +(default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >550 = +automatically prune block files to stay under the specified target size in MiB) .HP \fB\-reindex\-chainstate\fR .IP diff --git a/doc/man/bitcoind.1 b/doc/man/bitcoind.1 index b99657a5fa..47539d8131 100644 --- a/doc/man/bitcoind.1 +++ b/doc/man/bitcoind.1 @@ -80,11 +80,13 @@ Specify pid file (default: bitcoind.pid) .HP \fB\-prune=\fR<n> .IP -Reduce storage requirements by pruning (deleting) old blocks. This mode -is incompatible with \fB\-txindex\fR and \fB\-rescan\fR. Warning: Reverting -this setting requires re\-downloading the entire blockchain. -(default: 0 = disable pruning blocks, >550 = target size in MiB -to use for block files) +Reduce storage requirements by enabling pruning (deleting) of old blocks. +This allows the pruneblockchain RPC to be called to delete specific blocks, +and enables automatic pruning of old blocks if a target size in MiB is +provided. This mode is incompatible with \fB\-txindex\fR and \fB\-rescan\fR. +Warning: Reverting this setting requires re\-downloading the entire blockchain. +(default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >550 = +automatically prune block files to stay under the specified target size in MiB) .HP \fB\-reindex\-chainstate\fR .IP diff --git a/doc/release-notes.md b/doc/release-notes.md index 7d0d689684..bbda55854f 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -19,24 +19,16 @@ To receive security and update notifications, please subscribe to: Compatibility ============== -Microsoft ended support for Windows XP on [April 8th, 2014](https://www.microsoft.com/en-us/WindowsForBusiness/end-of-xp-support), -an OS initially released in 2001. This means that not even critical security -updates will be released anymore. Without security updates, using a bitcoin -wallet on a XP machine is irresponsible at least. +Bitcoin Core is extensively tested on multiple operating systems using +the Linux kernel, macOS 10.8+, and Windows Vista and later. -In addition to that, with 0.12.x there have been varied reports of Bitcoin Core -randomly crashing on Windows XP. It is [not clear](https://github.com/bitcoin/bitcoin/issues/7681#issuecomment-217439891) -what the source of these crashes is, but it is likely that upstream -libraries such as Qt are no longer being tested on XP. +Microsoft ended support for Windows XP on [April 8th, 2014](https://www.microsoft.com/en-us/WindowsForBusiness/end-of-xp-support). +No attempt is made to prevent installing or running the software on Windows XP, you +can still do so at your own risk but be aware that there are known instabilities. +Please do not report issues about Windows XP to the issue tracker. -We do not have time nor resources to provide support for an OS that is -end-of-life. From 0.13.0 on, Windows XP is no longer supported. Users are -suggested to upgrade to a newer version of Windows, or install an alternative OS -that is supported. - -No attempt is made to prevent installing or running the software on Windows XP, -you can still do so at your own risk, but do not expect it to work: do not -report issues about Windows XP to the issue tracker. +Bitcoin Core should also work on most other Unix-like systems but is not +frequently tested on them. Notable changes =============== @@ -48,8 +40,19 @@ Low-level RPC changes an optional third arg, which was always ignored. Make sure to never pass more than two arguments. +Fee Estimation Changes +---------------------- + +- Since 0.13.2 fee estimation for a confirmation target of 1 block has been + disabled. This is only a minor behavior change as there was often insufficient + data for this target anyway. `estimatefee 1` will now always return -1 and + `estimatesmartfee 1` will start searching at a target of 2. + +- The default target for fee estimation is changed to 6 blocks in both the GUI + (previously 25) and for RPC calls (previously 2). + Removal of Priority Estimation ------------------------------- +------------------------------- - Estimation of "priority" needed for a transaction to be included within a target number of blocks has been removed. The rpc calls are deprecated and will either @@ -58,9 +61,11 @@ Removal of Priority Estimation converted to the new format which is not readable by prior versions of the software. -- The concept of "priority" transactions is planned to be removed in the next - major version. To prepare for this, the default for the rate limit of priority - transactions (`-limitfreerelay`) has been set to `0` kB/minute. +- The concept of "priority" (coin age) transactions is planned to be removed in + the next major version. To prepare for this, the default for the rate limit of + priority transactions (`-limitfreerelay`) has been set to `0` kB/minute. This + is not to be confused with the `prioritisetransaction` RPC which will remain + supported for adding fee deltas to transactions. P2P connection management -------------------------- @@ -71,6 +76,27 @@ P2P connection management - New connections to manually added peers are much faster. +Introduction of assumed-valid blocks +------------------------------------- + +- A significant portion of the initial block download time is spent verifying + scripts/signatures. Although the verification must pass to ensure the security + of the system, no other result from this verification is needed: If the node + knew the history of a given block were valid it could skip checking scripts + for its ancestors. + +- A new configuration option 'assumevalid' is provided to express this knowledge + to the software. Unlike the 'checkpoints' in the past this setting does not + force the use of a particular chain: chains that are consistent with it are + processed quicker, but other chains are still accepted if they'd otherwise + be chosen as best. Also unlike 'checkpoints' the user can configure which + block history is assumed true, this means that even outdated software can + sync more quickly if the setting is updated by the user. + +- Because the validity of a chain history is a simple objective fact it is much + easier to review this setting. As a result the software ships with a default + value adjusted to match the current chain shortly before release. The use + of this default value can be disabled by setting -assumevalid=0 0.14.0 Change log ================= diff --git a/doc/release-process.md b/doc/release-process.md index 61f05b0771..399ed25c91 100644 --- a/doc/release-process.md +++ b/doc/release-process.md @@ -13,6 +13,11 @@ Before every minor and major release: * Update version in sources (see below) * Write release notes (see below) * Update `src/chainparams.cpp` nMinimumChainWork with information from the getblockchaininfo rpc. +* Update `src/chainparams.cpp` defaultAssumeValid with information from the getblockhash rpc. + - The selected value must not be orphaned so it may be useful to set the value two blocks back from the tip. + - Testnet should be set some tens of thousands back from the tip due to reorgs there. + - This update should be reviewed with a reindex-chainstate with assumevalid=0 to catch any defect + that causes rejection of blocks in the past history. Before every major release: diff --git a/qa/rpc-tests/assumevalid.py b/qa/rpc-tests/assumevalid.py new file mode 100755 index 0000000000..e4bc22951b --- /dev/null +++ b/qa/rpc-tests/assumevalid.py @@ -0,0 +1,191 @@ +#!/usr/bin/env python3 +# Copyright (c) 2014-2016 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +''' +assumevalid.py + +Test logic for skipping signature validation on blocks which we've assumed +valid (https://github.com/bitcoin/bitcoin/pull/9484) + +We build a chain that includes and invalid signature for one of the +transactions: + + 0: genesis block + 1: block 1 with coinbase transaction output. + 2-101: bury that block with 100 blocks so the coinbase transaction + output can be spent + 102: a block containing a transaction spending the coinbase + transaction output. The transaction has an invalid signature. + 103-2202: bury the bad block with just over two weeks' worth of blocks + (2100 blocks) + +Start three nodes: + + - node0 has no -assumevalid parameter. Try to sync to block 2202. It will + reject block 102 and only sync as far as block 101 + - node1 has -assumevalid set to the hash of block 102. Try to sync to + block 2202. node1 will sync all the way to block 2202. + - node2 has -assumevalid set to the hash of block 102. Try to sync to + block 200. node2 will reject block 102 since it's assumed valid, but it + isn't buried by at least two weeks' work. +''' + +from test_framework.mininode import * +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * +from test_framework.blocktools import create_block, create_coinbase +from test_framework.key import CECKey +from test_framework.script import * + +class BaseNode(SingleNodeConnCB): + def __init__(self): + SingleNodeConnCB.__init__(self) + self.last_inv = None + self.last_headers = None + self.last_block = None + self.last_getdata = None + self.block_announced = False + self.last_getheaders = None + self.disconnected = False + self.last_blockhash_announced = None + + def on_close(self, conn): + self.disconnected = True + + def wait_for_disconnect(self, timeout=60): + test_function = lambda: self.disconnected + assert(wait_until(test_function, timeout=timeout)) + return + + def send_header_for_blocks(self, new_blocks): + headers_message = msg_headers() + headers_message.headers = [ CBlockHeader(b) for b in new_blocks ] + self.send_message(headers_message) + +class SendHeadersTest(BitcoinTestFramework): + def __init__(self): + super().__init__() + self.setup_clean_chain = True + self.num_nodes = 3 + + def setup_network(self): + # Start node0. We don't start the other nodes yet since + # we need to pre-mine a block with an invalid transaction + # signature so we can pass in the block hash as assumevalid. + self.nodes = [] + self.nodes.append(start_node(0, self.options.tmpdir, ["-debug"])) + + def run_test(self): + + # Connect to node0 + node0 = BaseNode() + connections = [] + connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node0)) + node0.add_connection(connections[0]) + + NetworkThread().start() # Start up network handling in another thread + node0.wait_for_verack() + + # Build the blockchain + self.tip = int(self.nodes[0].getbestblockhash(), 16) + self.block_time = self.nodes[0].getblock(self.nodes[0].getbestblockhash())['time'] + 1 + + self.blocks = [] + + # Get a pubkey for the coinbase TXO + coinbase_key = CECKey() + coinbase_key.set_secretbytes(b"horsebattery") + coinbase_pubkey = coinbase_key.get_pubkey() + + # Create the first block with a coinbase output to our key + height = 1 + block = create_block(self.tip, create_coinbase(height, coinbase_pubkey), self.block_time) + self.blocks.append(block) + self.block_time += 1 + block.solve() + # Save the coinbase for later + self.block1 = block + self.tip = block.sha256 + height += 1 + + # Bury the block 100 deep so the coinbase output is spendable + for i in range(100): + block = create_block(self.tip, create_coinbase(height), self.block_time) + block.solve() + self.blocks.append(block) + self.tip = block.sha256 + self.block_time += 1 + height += 1 + + # Create a transaction spending the coinbase output with an invalid (null) signature + tx = CTransaction() + tx.vin.append(CTxIn(COutPoint(self.block1.vtx[0].sha256, 0), scriptSig=b"")) + tx.vout.append(CTxOut(49*100000000, CScript([OP_TRUE]))) + tx.calc_sha256() + + block102 = create_block(self.tip, create_coinbase(height), self.block_time) + self.block_time += 1 + block102.vtx.extend([tx]) + block102.hashMerkleRoot = block102.calc_merkle_root() + block102.rehash() + block102.solve() + self.blocks.append(block102) + self.tip = block102.sha256 + self.block_time += 1 + height += 1 + + # Bury the assumed valid block 2100 deep + for i in range(2100): + block = create_block(self.tip, create_coinbase(height), self.block_time) + block.nVersion = 4 + block.solve() + self.blocks.append(block) + self.tip = block.sha256 + self.block_time += 1 + height += 1 + + # Start node1 and node2 with assumevalid so they accept a block with a bad signature. + self.nodes.append(start_node(1, self.options.tmpdir, + ["-debug", "-assumevalid=" + hex(block102.sha256)])) + node1 = BaseNode() # connects to node1 + connections.append(NodeConn('127.0.0.1', p2p_port(1), self.nodes[1], node1)) + node1.add_connection(connections[1]) + node1.wait_for_verack() + + self.nodes.append(start_node(2, self.options.tmpdir, + ["-debug", "-assumevalid=" + hex(block102.sha256)])) + node2 = BaseNode() # connects to node2 + connections.append(NodeConn('127.0.0.1', p2p_port(2), self.nodes[2], node2)) + node2.add_connection(connections[2]) + node2.wait_for_verack() + + # send header lists to all three nodes + node0.send_header_for_blocks(self.blocks[0:2000]) + node0.send_header_for_blocks(self.blocks[2000:]) + node1.send_header_for_blocks(self.blocks[0:2000]) + node1.send_header_for_blocks(self.blocks[2000:]) + node2.send_header_for_blocks(self.blocks[0:200]) + + # Send 102 blocks to node0. Block 102 will be rejected. + for i in range(101): + node0.send_message(msg_block(self.blocks[i])) + node0.sync_with_ping() # make sure the most recent block is synced + node0.send_message(msg_block(self.blocks[101])) + assert_equal(self.nodes[0].getblock(self.nodes[0].getbestblockhash())['height'], 101) + + # Send 3102 blocks to node1. All blocks will be accepted. + for i in range(2202): + node1.send_message(msg_block(self.blocks[i])) + node1.sync_with_ping() # make sure the most recent block is synced + assert_equal(self.nodes[1].getblock(self.nodes[1].getbestblockhash())['height'], 2202) + + # Send 102 blocks to node2. Block 102 will be rejected. + for i in range(101): + node2.send_message(msg_block(self.blocks[i])) + node2.sync_with_ping() # make sure the most recent block is synced + node2.send_message(msg_block(self.blocks[101])) + assert_equal(self.nodes[2].getblock(self.nodes[2].getbestblockhash())['height'], 101) + +if __name__ == '__main__': + SendHeadersTest().main() diff --git a/qa/rpc-tests/fundrawtransaction.py b/qa/rpc-tests/fundrawtransaction.py index 82e148c55a..b97e9aecdf 100755 --- a/qa/rpc-tests/fundrawtransaction.py +++ b/qa/rpc-tests/fundrawtransaction.py @@ -660,5 +660,75 @@ class RawTransactionsTest(BitcoinTestFramework): assert_fee_amount(result2['fee'], count_bytes(result2['hex']), 2 * result_fee_rate) assert_fee_amount(result3['fee'], count_bytes(result3['hex']), 10 * result_fee_rate) + ###################################### + # Test subtractFeeFromOutputs option # + ###################################### + + # Make sure there is exactly one input so coin selection can't skew the result + assert_equal(len(self.nodes[3].listunspent(1)), 1) + + inputs = [] + outputs = {self.nodes[2].getnewaddress(): 1} + rawtx = self.nodes[3].createrawtransaction(inputs, outputs) + + result = [self.nodes[3].fundrawtransaction(rawtx), # uses min_relay_tx_fee (set by settxfee) + self.nodes[3].fundrawtransaction(rawtx, {"subtractFeeFromOutputs": []}), # empty subtraction list + self.nodes[3].fundrawtransaction(rawtx, {"subtractFeeFromOutputs": [0]}), # uses min_relay_tx_fee (set by settxfee) + self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 2*min_relay_tx_fee}), + self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 2*min_relay_tx_fee, "subtractFeeFromOutputs": [0]})] + + dec_tx = [self.nodes[3].decoderawtransaction(tx['hex']) for tx in result] + output = [d['vout'][1 - r['changepos']]['value'] for d, r in zip(dec_tx, result)] + change = [d['vout'][r['changepos']]['value'] for d, r in zip(dec_tx, result)] + + assert_equal(result[0]['fee'], result[1]['fee'], result[2]['fee']) + assert_equal(result[3]['fee'], result[4]['fee']) + assert_equal(change[0], change[1]) + assert_equal(output[0], output[1]) + assert_equal(output[0], output[2] + result[2]['fee']) + assert_equal(change[0] + result[0]['fee'], change[2]) + assert_equal(output[3], output[4] + result[4]['fee']) + assert_equal(change[3] + result[3]['fee'], change[4]) + + inputs = [] + outputs = {self.nodes[2].getnewaddress(): value for value in (1.0, 1.1, 1.2, 1.3)} + keys = list(outputs.keys()) + rawtx = self.nodes[3].createrawtransaction(inputs, outputs) + + result = [self.nodes[3].fundrawtransaction(rawtx), + # split the fee between outputs 0, 2, and 3, but not output 1 + self.nodes[3].fundrawtransaction(rawtx, {"subtractFeeFromOutputs": [0, 2, 3]})] + + dec_tx = [self.nodes[3].decoderawtransaction(result[0]['hex']), + self.nodes[3].decoderawtransaction(result[1]['hex'])] + + # Nested list of non-change output amounts for each transaction + output = [[out['value'] for i, out in enumerate(d['vout']) if i != r['changepos']] + for d, r in zip(dec_tx, result)] + + # List of differences in output amounts between normal and subtractFee transactions + share = [o0 - o1 for o0, o1 in zip(output[0], output[1])] + + # output 1 is the same in both transactions + assert_equal(share[1], 0) + + # the other 3 outputs are smaller as a result of subtractFeeFromOutputs + assert_greater_than(share[0], 0) + assert_greater_than(share[2], 0) + assert_greater_than(share[3], 0) + + # outputs 2 and 3 take the same share of the fee + assert_equal(share[2], share[3]) + + # output 0 takes at least as much share of the fee, and no more than 2 satoshis more, than outputs 2 and 3 + assert_greater_than_or_equal(share[0], share[2]) + assert_greater_than_or_equal(share[2] + Decimal(2e-8), share[0]) + + # the fee is the same in both transactions + assert_equal(result[0]['fee'], result[1]['fee']) + + # the total subtracted from the outputs is equal to the fee + assert_equal(share[0] + share[2] + share[3], result[0]['fee']) + if __name__ == '__main__': RawTransactionsTest().main() diff --git a/qa/rpc-tests/p2p-compactblocks.py b/qa/rpc-tests/p2p-compactblocks.py index fc1f16c6d2..9151ecf5de 100755 --- a/qa/rpc-tests/p2p-compactblocks.py +++ b/qa/rpc-tests/p2p-compactblocks.py @@ -310,6 +310,9 @@ class CompactBlocksTest(BitcoinTestFramework): tip = int(node.getbestblockhash(), 16) assert(test_node.wait_for_block_announcement(tip)) + # Make sure we will receive a fast-announce compact block + self.request_cb_announcements(test_node, node, version) + # Now mine a block, and look at the resulting compact block. test_node.clear_block_announcement() block_hash = int(node.generate(1)[0], 16) @@ -319,27 +322,36 @@ class CompactBlocksTest(BitcoinTestFramework): [tx.calc_sha256() for tx in block.vtx] block.rehash() - # Don't care which type of announcement came back for this test; just - # request the compact block if we didn't get one yet. + # Wait until the block was announced (via compact blocks) wait_until(test_node.received_block_announcement, timeout=30) assert(test_node.received_block_announcement()) + # Now fetch and check the compact block + header_and_shortids = None + with mininode_lock: + assert(test_node.last_cmpctblock is not None) + # Convert the on-the-wire representation to absolute indexes + header_and_shortids = HeaderAndShortIDs(test_node.last_cmpctblock.header_and_shortids) + self.check_compactblock_construction_from_block(version, header_and_shortids, block_hash, block) + + # Now fetch the compact block using a normal non-announce getdata with mininode_lock: - if test_node.last_cmpctblock is None: - test_node.clear_block_announcement() - inv = CInv(4, block_hash) # 4 == "CompactBlock" - test_node.send_message(msg_getdata([inv])) + test_node.clear_block_announcement() + inv = CInv(4, block_hash) # 4 == "CompactBlock" + test_node.send_message(msg_getdata([inv])) wait_until(test_node.received_block_announcement, timeout=30) assert(test_node.received_block_announcement()) - # Now we should have the compactblock + # Now fetch and check the compact block header_and_shortids = None with mininode_lock: assert(test_node.last_cmpctblock is not None) # Convert the on-the-wire representation to absolute indexes header_and_shortids = HeaderAndShortIDs(test_node.last_cmpctblock.header_and_shortids) + self.check_compactblock_construction_from_block(version, header_and_shortids, block_hash, block) + def check_compactblock_construction_from_block(self, version, header_and_shortids, block_hash, block): # Check that we got the right block! header_and_shortids.header.calc_sha256() assert_equal(header_and_shortids.header.sha256, block_hash) diff --git a/qa/rpc-tests/preciousblock.py b/qa/rpc-tests/preciousblock.py index f43160e19a..a806294648 100755 --- a/qa/rpc-tests/preciousblock.py +++ b/qa/rpc-tests/preciousblock.py @@ -102,7 +102,7 @@ class PreciousTest(BitcoinTestFramework): assert_equal(self.nodes[2].getblockcount(), 6) hashL = self.nodes[2].getbestblockhash() print("Connect nodes and check no reorg occurs") - node_sync_via_rpc(self.nodes[0:3]) + node_sync_via_rpc(self.nodes[1:3]) connect_nodes_bi(self.nodes,1,2) connect_nodes_bi(self.nodes,0,2) assert_equal(self.nodes[0].getbestblockhash(), hashH) diff --git a/qa/rpc-tests/pruning.py b/qa/rpc-tests/pruning.py index 78b8938e4a..05e72e6078 100755 --- a/qa/rpc-tests/pruning.py +++ b/qa/rpc-tests/pruning.py @@ -16,6 +16,8 @@ from test_framework.util import * import time import os +MIN_BLOCKS_TO_KEEP = 288 + def calc_usage(blockdir): return sum(os.path.getsize(blockdir+f) for f in os.listdir(blockdir) if os.path.isfile(blockdir+f)) / (1024. * 1024.) @@ -25,7 +27,7 @@ class PruneTest(BitcoinTestFramework): def __init__(self): super().__init__() self.setup_clean_chain = True - self.num_nodes = 3 + self.num_nodes = 6 # Cache for utxos, as the listunspent may take a long time later in the test self.utxo_cache_0 = [] @@ -43,10 +45,22 @@ class PruneTest(BitcoinTestFramework): self.nodes.append(start_node(2, self.options.tmpdir, ["-debug","-maxreceivebuffer=20000","-prune=550"], timewait=900)) self.prunedir = self.options.tmpdir+"/node2/regtest/blocks/" + # Create nodes 3 and 4 to test manual pruning (they will be re-started with manual pruning later) + self.nodes.append(start_node(3, self.options.tmpdir, ["-debug=0","-maxreceivebuffer=20000","-blockmaxsize=999000"], timewait=900)) + self.nodes.append(start_node(4, self.options.tmpdir, ["-debug=0","-maxreceivebuffer=20000","-blockmaxsize=999000"], timewait=900)) + + # Create nodes 5 to test wallet in prune mode, but do not connect + self.nodes.append(start_node(5, self.options.tmpdir, ["-debug=0", "-prune=550"])) + + # Determine default relay fee + self.relayfee = self.nodes[0].getnetworkinfo()["relayfee"] + connect_nodes(self.nodes[0], 1) connect_nodes(self.nodes[1], 2) connect_nodes(self.nodes[2], 0) - sync_blocks(self.nodes[0:3]) + connect_nodes(self.nodes[0], 3) + connect_nodes(self.nodes[0], 4) + sync_blocks(self.nodes[0:5]) def create_big_chain(self): # Start by creating some coinbases we can spend later @@ -57,7 +71,7 @@ class PruneTest(BitcoinTestFramework): for i in range(645): mine_large_block(self.nodes[0], self.utxo_cache_0) - sync_blocks(self.nodes[0:3]) + sync_blocks(self.nodes[0:5]) def test_height_min(self): if not os.path.isfile(self.prunedir+"blk00000.dat"): @@ -212,6 +226,118 @@ class PruneTest(BitcoinTestFramework): # Verify we can now have the data for a block previously pruned assert(self.nodes[2].getblock(self.forkhash)["height"] == self.forkheight) + def manual_test(self, node_number, use_timestamp): + # at this point, node has 995 blocks and has not yet run in prune mode + node = self.nodes[node_number] = start_node(node_number, self.options.tmpdir, ["-debug=0"], timewait=900) + assert_equal(node.getblockcount(), 995) + assert_raises_message(JSONRPCException, "not in prune mode", node.pruneblockchain, 500) + stop_node(node, node_number) + + # now re-start in manual pruning mode + node = self.nodes[node_number] = start_node(node_number, self.options.tmpdir, ["-debug=0","-prune=1"], timewait=900) + assert_equal(node.getblockcount(), 995) + + def height(index): + if use_timestamp: + return node.getblockheader(node.getblockhash(index))["time"] + else: + return index + + def prune(index, expected_ret=None): + ret = node.pruneblockchain(height(index)) + # Check the return value. When use_timestamp is True, just check + # that the return value is less than or equal to the expected + # value, because when more than one block is generated per second, + # a timestamp will not be granular enough to uniquely identify an + # individual block. + if expected_ret is None: + expected_ret = index + if use_timestamp: + assert_greater_than(ret, 0) + assert_greater_than(expected_ret + 1, ret) + else: + assert_equal(ret, expected_ret) + + def has_block(index): + return os.path.isfile(self.options.tmpdir + "/node{}/regtest/blocks/blk{:05}.dat".format(node_number, index)) + + # should not prune because chain tip of node 3 (995) < PruneAfterHeight (1000) + assert_raises_message(JSONRPCException, "Blockchain is too short for pruning", node.pruneblockchain, height(500)) + + # mine 6 blocks so we are at height 1001 (i.e., above PruneAfterHeight) + node.generate(6) + + # negative and zero inputs should raise an exception + try: + node.pruneblockchain(-10) + raise AssertionError("pruneblockchain(-10) should have failed.") + except: + pass + + try: + node.pruneblockchain(0) + raise AssertionError("pruneblockchain(0) should have failed.") + except: + pass + + # height=100 too low to prune first block file so this is a no-op + prune(100) + if not has_block(0): + raise AssertionError("blk00000.dat is missing when should still be there") + + # height=500 should prune first file + prune(500) + if has_block(0): + raise AssertionError("blk00000.dat is still there, should be pruned by now") + if not has_block(1): + raise AssertionError("blk00001.dat is missing when should still be there") + + # height=650 should prune second file + prune(650) + if has_block(1): + raise AssertionError("blk00001.dat is still there, should be pruned by now") + + # height=1000 should not prune anything more, because tip-288 is in blk00002.dat. + prune(1000, 1001 - MIN_BLOCKS_TO_KEEP) + if not has_block(2): + raise AssertionError("blk00002.dat is still there, should be pruned by now") + + # advance the tip so blk00002.dat and blk00003.dat can be pruned (the last 288 blocks should now be in blk00004.dat) + node.generate(288) + prune(1000) + if has_block(2): + raise AssertionError("blk00002.dat is still there, should be pruned by now") + if has_block(3): + raise AssertionError("blk00003.dat is still there, should be pruned by now") + + # stop node, start back up with auto-prune at 550MB, make sure still runs + stop_node(node, node_number) + self.nodes[node_number] = start_node(node_number, self.options.tmpdir, ["-debug=0","-prune=550"], timewait=900) + + print("Success") + + def wallet_test(self): + # check that the pruning node's wallet is still in good shape + print("Stop and start pruning node to trigger wallet rescan") + try: + stop_node(self.nodes[2], 2) + start_node(2, self.options.tmpdir, ["-debug=1","-prune=550"]) + print("Success") + except Exception as detail: + raise AssertionError("Wallet test: unable to re-start the pruning node") + + # check that wallet loads loads successfully when restarting a pruned node after IBD. + # this was reported to fail in #7494. + print ("Syncing node 5 to test wallet") + connect_nodes(self.nodes[0], 5) + nds = [self.nodes[0], self.nodes[5]] + sync_blocks(nds) + try: + stop_node(self.nodes[5],5) #stop and start to trigger rescan + start_node(5, self.options.tmpdir, ["-debug=1","-prune=550"]) + print ("Success") + except Exception as detail: + raise AssertionError("Wallet test: unable to re-start node5") def run_test(self): print("Warning! This test requires 4GB of disk space and takes over 30 mins (up to 2 hours)") @@ -226,6 +352,10 @@ class PruneTest(BitcoinTestFramework): # Start by mining a simple chain that all nodes have # N0=N1=N2 **...*(995) + # stop manual-pruning node with 995 blocks + stop_node(self.nodes[3],3) + stop_node(self.nodes[4],4) + print("Check that we haven't started pruning yet because we're below PruneAfterHeight") self.test_height_min() # Extend this chain past the PruneAfterHeight @@ -308,6 +438,15 @@ class PruneTest(BitcoinTestFramework): # # N1 doesn't change because 1033 on main chain (*) is invalid + print("Test manual pruning with block indices") + self.manual_test(3, use_timestamp=False) + + print("Test manual pruning with timestamps") + self.manual_test(4, use_timestamp=True) + + print("Test wallet re-scan") + self.wallet_test() + print("Done") if __name__ == '__main__': diff --git a/qa/rpc-tests/test_framework/util.py b/qa/rpc-tests/test_framework/util.py index c29033595d..aca82c8b6f 100644 --- a/qa/rpc-tests/test_framework/util.py +++ b/qa/rpc-tests/test_framework/util.py @@ -524,14 +524,18 @@ def assert_fee_amount(fee, tx_size, fee_per_kB): if fee > (tx_size + 2) * fee_per_kB / 1000: raise AssertionError("Fee of %s BTC too high! (Should be %s BTC)"%(str(fee), str(target_fee))) -def assert_equal(thing1, thing2): - if thing1 != thing2: - raise AssertionError("%s != %s"%(str(thing1),str(thing2))) +def assert_equal(thing1, thing2, *args): + if thing1 != thing2 or any(thing1 != arg for arg in args): + raise AssertionError("not(%s)" % " == ".join(str(arg) for arg in (thing1, thing2) + args)) def assert_greater_than(thing1, thing2): if thing1 <= thing2: raise AssertionError("%s <= %s"%(str(thing1),str(thing2))) +def assert_greater_than_or_equal(thing1, thing2): + if thing1 < thing2: + raise AssertionError("%s < %s"%(str(thing1),str(thing2))) + def assert_raises(exc, fun, *args, **kwds): assert_raises_message(exc, None, fun, *args, **kwds) diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 7ae42e83b1..edc3c4b292 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -355,7 +355,7 @@ endif RES_IMAGES = -RES_MOVIES = $(wildcard qt/res/movies/spinner-*.png) +RES_MOVIES = $(wildcard $(srcdir)/qt/res/movies/spinner-*.png) BITCOIN_RC = qt/res/bitcoin-qt-res.rc diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 27b456240e..4d44b35bb6 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -13,32 +13,54 @@ EXTRA_DIST += \ test/bctest.py \ test/bitcoin-util-test.py \ test/data/bitcoin-util-test.json \ + test/data/blanktxv1.hex \ test/data/blanktxv1.json \ + test/data/blanktxv2.hex \ test/data/blanktxv2.json \ + test/data/tt-delin1-out.hex \ test/data/tt-delin1-out.json \ + test/data/tt-delout1-out.hex \ test/data/tt-delout1-out.json \ + test/data/tt-locktime317000-out.hex \ test/data/tt-locktime317000-out.json \ + test/data/tx394b54bb.hex \ + test/data/txcreate1.hex \ test/data/txcreate1.json \ + test/data/txcreate2.hex \ test/data/txcreate2.json \ + test/data/txcreatedata1.hex \ test/data/txcreatedata1.json \ + test/data/txcreatedata2.hex \ test/data/txcreatedata2.json \ + test/data/txcreatedata_seq0.hex \ test/data/txcreatedata_seq0.json \ + test/data/txcreatedata_seq1.hex \ test/data/txcreatedata_seq1.json \ - test/data/txcreatesignv1.json \ - test/data/blanktxv1.hex \ - test/data/blanktxv2.hex \ - test/data/tt-delin1-out.hex \ - test/data/tt-delout1-out.hex \ - test/data/tt-locktime317000-out.hex \ - test/data/tx394b54bb.hex \ - test/data/txcreate1.hex \ - test/data/txcreate2.hex \ - test/data/txcreatedata1.hex \ - test/data/txcreatedata2.hex \ + test/data/txcreatemultisig1.hex \ + test/data/txcreatemultisig1.json \ + test/data/txcreatemultisig2.hex \ + test/data/txcreatemultisig2.json \ + test/data/txcreatemultisig3.hex \ + test/data/txcreatemultisig3.json \ + test/data/txcreatemultisig4.hex \ + test/data/txcreatemultisig4.json \ + test/data/txcreateoutpubkey1.hex \ + test/data/txcreateoutpubkey1.json \ + test/data/txcreateoutpubkey2.hex \ + test/data/txcreateoutpubkey2.json \ + test/data/txcreateoutpubkey3.hex \ + test/data/txcreateoutpubkey3.json \ + test/data/txcreatescript1.hex \ + test/data/txcreatescript1.json \ + test/data/txcreatescript2.hex \ + test/data/txcreatescript2.json \ + test/data/txcreatescript3.hex \ + test/data/txcreatescript3.json \ + test/data/txcreatescript4.hex \ + test/data/txcreatescript4.json \ test/data/txcreatesignv1.hex \ - test/data/txcreatesignv2.hex \ - test/data/txcreatedata_seq0.hex \ - test/data/txcreatedata_seq1.hex + test/data/txcreatesignv1.json \ + test/data/txcreatesignv2.hex JSON_TEST_FILES = \ test/data/script_tests.json \ diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index 24a53b65a0..3c3646523a 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -78,8 +78,16 @@ static int AppInitRawTx(int argc, char* argv[]) strUsage += HelpMessageOpt("locktime=N", _("Set TX lock time to N")); strUsage += HelpMessageOpt("nversion=N", _("Set TX version to N")); strUsage += HelpMessageOpt("outaddr=VALUE:ADDRESS", _("Add address-based output to TX")); + strUsage += HelpMessageOpt("outpubkey=VALUE:PUBKEY[:FLAGS]", _("Add pay-to-pubkey output to TX") + ". " + + _("Optionally add the \"W\" flag to produce a pay-to-witness-pubkey-hash output") + ". " + + _("Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash.")); strUsage += HelpMessageOpt("outdata=[VALUE:]DATA", _("Add data-based output to TX")); - strUsage += HelpMessageOpt("outscript=VALUE:SCRIPT", _("Add raw script output to TX")); + strUsage += HelpMessageOpt("outscript=VALUE:SCRIPT[:FLAGS]", _("Add raw script output to TX") + ". " + + _("Optionally add the \"W\" flag to produce a pay-to-witness-script-hash output") + ". " + + _("Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash.")); + strUsage += HelpMessageOpt("outmultisig=VALUE:REQUIRED:PUBKEYS:PUBKEY1:PUBKEY2:....[:FLAGS]", _("Add Pay To n-of-m Multi-sig output to TX. n = REQUIRED, m = PUBKEYS") + ". " + + _("Optionally add the \"W\" flag to produce a pay-to-witness-script-hash output") + ". " + + _("Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash.")); strUsage += HelpMessageOpt("sign=SIGHASH-FLAGS", _("Add zero or more signatures to transaction") + ". " + _("This command requires JSON registers:") + _("prevtxs=JSON object") + ", " + @@ -168,6 +176,14 @@ static void RegisterLoad(const std::string& strInput) RegisterSetJson(key, valStr); } +static CAmount ExtractAndValidateValue(const std::string& strValue) +{ + CAmount value; + if (!ParseMoney(strValue, value)) + throw std::runtime_error("invalid TX output value"); + return value; +} + static void MutateTxVersion(CMutableTransaction& tx, const std::string& cmdVal) { int64_t newVersion = atoi64(cmdVal); @@ -222,25 +238,18 @@ static void MutateTxAddInput(CMutableTransaction& tx, const std::string& strInpu static void MutateTxAddOutAddr(CMutableTransaction& tx, const std::string& strInput) { - // separate VALUE:ADDRESS in string - size_t pos = strInput.find(':'); - if ((pos == std::string::npos) || - (pos == 0) || - (pos == (strInput.size() - 1))) - throw std::runtime_error("TX output missing separator"); + // Separate into VALUE:ADDRESS + std::vector<std::string> vStrInputParts; + boost::split(vStrInputParts, strInput, boost::is_any_of(":")); - // extract and validate VALUE - std::string strValue = strInput.substr(0, pos); - CAmount value; - if (!ParseMoney(strValue, value)) - throw std::runtime_error("invalid TX output value"); + // Extract and validate VALUE + CAmount value = ExtractAndValidateValue(vStrInputParts[0]); // extract and validate ADDRESS - std::string strAddr = strInput.substr(pos + 1, std::string::npos); + std::string strAddr = vStrInputParts[1]; CBitcoinAddress addr(strAddr); if (!addr.IsValid()) throw std::runtime_error("invalid TX output address"); - // build standard output script via GetScriptForDestination() CScript scriptPubKey = GetScriptForDestination(addr.Get()); @@ -249,6 +258,114 @@ static void MutateTxAddOutAddr(CMutableTransaction& tx, const std::string& strIn tx.vout.push_back(txout); } +static void MutateTxAddOutPubKey(CMutableTransaction& tx, const std::string& strInput) +{ + // Separate into VALUE:PUBKEY[:FLAGS] + std::vector<std::string> vStrInputParts; + boost::split(vStrInputParts, strInput, boost::is_any_of(":")); + + // Extract and validate VALUE + CAmount value = ExtractAndValidateValue(vStrInputParts[0]); + + // Extract and validate PUBKEY + CPubKey pubkey(ParseHex(vStrInputParts[1])); + if (!pubkey.IsFullyValid()) + throw std::runtime_error("invalid TX output pubkey"); + CScript scriptPubKey = GetScriptForRawPubKey(pubkey); + CBitcoinAddress addr(scriptPubKey); + + // Extract and validate FLAGS + bool bSegWit = false; + bool bScriptHash = false; + if (vStrInputParts.size() == 3) { + std::string flags = vStrInputParts[2]; + bSegWit = (flags.find("W") != std::string::npos); + bScriptHash = (flags.find("S") != std::string::npos); + } + + if (bSegWit) { + // Call GetScriptForWitness() to build a P2WSH scriptPubKey + scriptPubKey = GetScriptForWitness(scriptPubKey); + } + if (bScriptHash) { + // Get the address for the redeem script, then call + // GetScriptForDestination() to construct a P2SH scriptPubKey. + CBitcoinAddress redeemScriptAddr(scriptPubKey); + scriptPubKey = GetScriptForDestination(redeemScriptAddr.Get()); + } + + // construct TxOut, append to transaction output list + CTxOut txout(value, scriptPubKey); + tx.vout.push_back(txout); +} + +static void MutateTxAddOutMultiSig(CMutableTransaction& tx, const std::string& strInput) +{ + // Separate into VALUE:REQUIRED:NUMKEYS:PUBKEY1:PUBKEY2:....[:FLAGS] + std::vector<std::string> vStrInputParts; + boost::split(vStrInputParts, strInput, boost::is_any_of(":")); + + // Check that there are enough parameters + if (vStrInputParts.size()<3) + throw std::runtime_error("Not enough multisig parameters"); + + // Extract and validate VALUE + CAmount value = ExtractAndValidateValue(vStrInputParts[0]); + + // Extract REQUIRED + uint32_t required = stoul(vStrInputParts[1]); + + // Extract NUMKEYS + uint32_t numkeys = stoul(vStrInputParts[2]); + + // Validate there are the correct number of pubkeys + if (vStrInputParts.size() < numkeys + 3) + throw std::runtime_error("incorrect number of multisig pubkeys"); + + if (required < 1 || required > 20 || numkeys < 1 || numkeys > 20 || numkeys < required) + throw std::runtime_error("multisig parameter mismatch. Required " \ + + std::to_string(required) + " of " + std::to_string(numkeys) + "signatures."); + + // extract and validate PUBKEYs + std::vector<CPubKey> pubkeys; + for(int pos = 1; pos <= int(numkeys); pos++) { + CPubKey pubkey(ParseHex(vStrInputParts[pos + 2])); + if (!pubkey.IsFullyValid()) + throw std::runtime_error("invalid TX output pubkey"); + pubkeys.push_back(pubkey); + } + + // Extract FLAGS + bool bSegWit = false; + bool bScriptHash = false; + if (vStrInputParts.size() == numkeys + 4) { + std::string flags = vStrInputParts.back(); + bSegWit = (flags.find("W") != std::string::npos); + bScriptHash = (flags.find("S") != std::string::npos); + } + else if (vStrInputParts.size() > numkeys + 4) { + // Validate that there were no more parameters passed + throw std::runtime_error("Too many parameters"); + } + + CScript scriptPubKey = GetScriptForMultisig(required, pubkeys); + + if (bSegWit) { + // Call GetScriptForWitness() to build a P2WSH scriptPubKey + scriptPubKey = GetScriptForWitness(scriptPubKey); + } + if (bScriptHash) { + // Get the address for the redeem script, then call + // GetScriptForDestination() to construct a P2SH scriptPubKey. + CBitcoinAddress addr(scriptPubKey); + scriptPubKey = GetScriptForDestination(addr.Get()); + } + + // construct TxOut, append to transaction output list + CTxOut txout(value, scriptPubKey); + tx.vout.push_back(txout); +} + static void MutateTxAddOutData(CMutableTransaction& tx, const std::string& strInput) { CAmount value = 0; @@ -260,10 +377,8 @@ static void MutateTxAddOutData(CMutableTransaction& tx, const std::string& strIn throw std::runtime_error("TX output value not specified"); if (pos != std::string::npos) { - // extract and validate VALUE - std::string strValue = strInput.substr(0, pos); - if (!ParseMoney(strValue, value)) - throw std::runtime_error("invalid TX output value"); + // Extract and validate VALUE + value = ExtractAndValidateValue(strInput.substr(0, pos)); } // extract and validate DATA @@ -280,21 +395,35 @@ static void MutateTxAddOutData(CMutableTransaction& tx, const std::string& strIn static void MutateTxAddOutScript(CMutableTransaction& tx, const std::string& strInput) { - // separate VALUE:SCRIPT in string - size_t pos = strInput.find(':'); - if ((pos == std::string::npos) || - (pos == 0)) + // separate VALUE:SCRIPT[:FLAGS] + std::vector<std::string> vStrInputParts; + boost::split(vStrInputParts, strInput, boost::is_any_of(":")); + if (vStrInputParts.size() < 2) throw std::runtime_error("TX output missing separator"); - // extract and validate VALUE - std::string strValue = strInput.substr(0, pos); - CAmount value; - if (!ParseMoney(strValue, value)) - throw std::runtime_error("invalid TX output value"); + // Extract and validate VALUE + CAmount value = ExtractAndValidateValue(vStrInputParts[0]); // extract and validate script - std::string strScript = strInput.substr(pos + 1, std::string::npos); - CScript scriptPubKey = ParseScript(strScript); // throws on err + std::string strScript = vStrInputParts[1]; + CScript scriptPubKey = ParseScript(strScript); + + // Extract FLAGS + bool bSegWit = false; + bool bScriptHash = false; + if (vStrInputParts.size() == 3) { + std::string flags = vStrInputParts.back(); + bSegWit = (flags.find("W") != std::string::npos); + bScriptHash = (flags.find("S") != std::string::npos); + } + + if (bSegWit) { + scriptPubKey = GetScriptForWitness(scriptPubKey); + } + if (bScriptHash) { + CBitcoinAddress addr(scriptPubKey); + scriptPubKey = GetScriptForDestination(addr.Get()); + } // construct TxOut, append to transaction output list CTxOut txout(value, scriptPubKey); @@ -538,10 +667,14 @@ static void MutateTx(CMutableTransaction& tx, const std::string& command, MutateTxDelOutput(tx, commandVal); else if (command == "outaddr") MutateTxAddOutAddr(tx, commandVal); - else if (command == "outdata") - MutateTxAddOutData(tx, commandVal); + else if (command == "outpubkey") + MutateTxAddOutPubKey(tx, commandVal); + else if (command == "outmultisig") + MutateTxAddOutMultiSig(tx, commandVal); else if (command == "outscript") MutateTxAddOutScript(tx, commandVal); + else if (command == "outdata") + MutateTxAddOutData(tx, commandVal); else if (command == "sign") { if (!ecc) { ecc.reset(new Secp256k1Init()); } diff --git a/src/chain.cpp b/src/chain.cpp index 3cd7b33920..0f4d422b9f 100644 --- a/src/chain.cpp +++ b/src/chain.cpp @@ -61,10 +61,10 @@ const CBlockIndex *CChain::FindFork(const CBlockIndex *pindex) const { return pindex; } -CBlockIndex* CChain::FindLatestBefore(int64_t nTime) const +CBlockIndex* CChain::FindEarliestAtLeast(int64_t nTime) const { std::vector<CBlockIndex*>::const_iterator lower = std::lower_bound(vChain.begin(), vChain.end(), nTime, - [](CBlockIndex* pBlock, const int64_t& time) -> bool { return pBlock->GetBlockTime() < time; }); + [](CBlockIndex* pBlock, const int64_t& time) -> bool { return pBlock->GetBlockTimeMax() < time; }); return (lower == vChain.end() ? NULL : *lower); } diff --git a/src/chain.h b/src/chain.h index 989a71958c..acb29b667b 100644 --- a/src/chain.h +++ b/src/chain.h @@ -202,6 +202,9 @@ public: //! (memory only) Sequential id assigned to distinguish order in which blocks are received. int32_t nSequenceId; + //! (memory only) Maximum nTime in the chain upto and including this block. + unsigned int nTimeMax; + void SetNull() { phashBlock = NULL; @@ -216,6 +219,7 @@ public: nChainTx = 0; nStatus = 0; nSequenceId = 0; + nTimeMax = 0; nVersion = 0; hashMerkleRoot = uint256(); @@ -281,6 +285,11 @@ public: return (int64_t)nTime; } + int64_t GetBlockTimeMax() const + { + return (int64_t)nTimeMax; + } + enum { nMedianTimeSpan=11 }; int64_t GetMedianTimePast() const @@ -461,8 +470,8 @@ public: /** Find the last common block between this chain and a block index entry. */ const CBlockIndex *FindFork(const CBlockIndex *pindex) const; - /** Find the most recent block with timestamp lower than the given. */ - CBlockIndex* FindLatestBefore(int64_t nTime) const; + /** Find the earliest block with timestamp equal or greater than the given. */ + CBlockIndex* FindEarliestAtLeast(int64_t nTime) const; }; #endif // BITCOIN_CHAIN_H diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 1e294da9f4..d99f800f0a 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -99,6 +99,9 @@ public: // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000002cb971dd56d1c583c20f90"); + // By default assume that the signatures in ancestors of this block are valid. + consensus.defaultAssumeValid = uint256S("0x0000000000000000030abc968e1bd635736e880b946085c93152969b9a81a6e2"); //447235 + /** * The message start string is designed to be unlikely to occur in normal data. * The characters are rarely used upper ASCII, not valid as UTF-8, and produce @@ -151,11 +154,15 @@ public: (225430, uint256S("0x00000000000001c108384350f74090433e7fcf79a606b8e797f065b130575932")) (250000, uint256S("0x000000000000003887df1f29024b06fc2200b55f8af8f35453d7be294df2d214")) (279000, uint256S("0x0000000000000001ae8c72a0b0c301f67e3afca10e819efa9041e458e9bd7e40")) - (295000, uint256S("0x00000000000000004d9b4ef50f0f9d686fd69db2e03af35a100370c64632a983")), - 1397080064, // * UNIX timestamp of last checkpoint block - 36544669, // * total number of transactions between genesis and last checkpoint + (295000, uint256S("0x00000000000000004d9b4ef50f0f9d686fd69db2e03af35a100370c64632a983")) + }; + + chainTxData = ChainTxData{ + // Data as of block 00000000000000000166d612d5595e2b1cd88d71d695fc580af64d8da8658c23 (height 446482). + 1483472411, // * UNIX timestamp of last known number of transactions + 184495391, // * total number of transactions between genesis and that timestamp // (the tx=... number in the SetBestChain debug.log lines) - 60000.0 // * estimated number of transactions per day after checkpoint + 3.2 // * estimated number of transactions per second after that timestamp }; } }; @@ -197,6 +204,9 @@ public: // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000000000198b4def2baa9338d6"); + // By default assume that the signatures in ancestors of this block are valid. + consensus.defaultAssumeValid = uint256S("0x000000000871ee6842d3648317ccc8a435eb8cc3c2429aee94faff9ba26b05a0"); //1043841 + pchMessageStart[0] = 0x0b; pchMessageStart[1] = 0x11; pchMessageStart[2] = 0x09; @@ -234,9 +244,13 @@ public: checkpointData = (CCheckpointData) { boost::assign::map_list_of ( 546, uint256S("000000002a936ca763904c3c35fce2f3556c559c0214345d31b1bcebf76acb70")), - 1337966069, - 1488, - 300 + }; + + chainTxData = ChainTxData{ + // Data as of block 00000000c2872f8f8a8935c8e3c5862be9038c97d4de2cf37ed496991166928a (height 1063660) + 1483546230, + 12834668, + 0.15 }; } @@ -275,6 +289,9 @@ public: // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x00"); + // By default assume that the signatures in ancestors of this block are valid. + consensus.defaultAssumeValid = uint256S("0x00"); + pchMessageStart[0] = 0xfa; pchMessageStart[1] = 0xbf; pchMessageStart[2] = 0xb5; @@ -297,11 +314,15 @@ public: checkpointData = (CCheckpointData){ boost::assign::map_list_of - ( 0, uint256S("0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206")), + ( 0, uint256S("0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206")) + }; + + chainTxData = ChainTxData{ 0, 0, 0 }; + base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,111); base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,196); base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1,239); diff --git a/src/chainparams.h b/src/chainparams.h index 381bac12f9..db524e8f8e 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -28,9 +28,12 @@ typedef std::map<int, uint256> MapCheckpoints; struct CCheckpointData { MapCheckpoints mapCheckpoints; - int64_t nTimeLastCheckpoint; - int64_t nTransactionsLastCheckpoint; - double fTransactionsPerDay; +}; + +struct ChainTxData { + int64_t nTime; + int64_t nTxCount; + double dTxRate; }; /** @@ -73,6 +76,7 @@ public: const std::vector<unsigned char>& Base58Prefix(Base58Type type) const { return base58Prefixes[type]; } const std::vector<SeedSpec6>& FixedSeeds() const { return vFixedSeeds; } const CCheckpointData& Checkpoints() const { return checkpointData; } + const ChainTxData& TxData() const { return chainTxData; } protected: CChainParams() {} @@ -90,6 +94,7 @@ protected: bool fRequireStandard; bool fMineBlocksOnDemand; CCheckpointData checkpointData; + ChainTxData chainTxData; }; /** diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp index 9a20747049..13b5876530 100644 --- a/src/checkpoints.cpp +++ b/src/checkpoints.cpp @@ -15,46 +15,6 @@ namespace Checkpoints { - /** - * How many times slower we expect checking transactions after the last - * checkpoint to be (from checking signatures, which is skipped up to the - * last checkpoint). This number is a compromise, as it can't be accurate - * for every system. When reindexing from a fast disk with a slow CPU, it - * can be up to 20, while when downloading from a slow network with a - * fast multicore CPU, it won't be much higher than 1. - */ - static const double SIGCHECK_VERIFICATION_FACTOR = 5.0; - - //! Guess how far we are in the verification process at the given block index - double GuessVerificationProgress(const CCheckpointData& data, CBlockIndex *pindex, bool fSigchecks) { - if (pindex==NULL) - return 0.0; - - int64_t nNow = time(NULL); - - double fSigcheckVerificationFactor = fSigchecks ? SIGCHECK_VERIFICATION_FACTOR : 1.0; - double fWorkBefore = 0.0; // Amount of work done before pindex - double fWorkAfter = 0.0; // Amount of work left after pindex (estimated) - // Work is defined as: 1.0 per transaction before the last checkpoint, and - // fSigcheckVerificationFactor per transaction after. - - if (pindex->nChainTx <= data.nTransactionsLastCheckpoint) { - double nCheapBefore = pindex->nChainTx; - double nCheapAfter = data.nTransactionsLastCheckpoint - pindex->nChainTx; - double nExpensiveAfter = (nNow - data.nTimeLastCheckpoint)/86400.0*data.fTransactionsPerDay; - fWorkBefore = nCheapBefore; - fWorkAfter = nCheapAfter + nExpensiveAfter*fSigcheckVerificationFactor; - } else { - double nCheapBefore = data.nTransactionsLastCheckpoint; - double nExpensiveBefore = pindex->nChainTx - data.nTransactionsLastCheckpoint; - double nExpensiveAfter = (nNow - pindex->GetBlockTime())/86400.0*data.fTransactionsPerDay; - fWorkBefore = nCheapBefore + nExpensiveBefore*fSigcheckVerificationFactor; - fWorkAfter = nExpensiveAfter*fSigcheckVerificationFactor; - } - - return fWorkBefore / (fWorkBefore + fWorkAfter); - } - CBlockIndex* GetLastCheckpoint(const CCheckpointData& data) { const MapCheckpoints& checkpoints = data.mapCheckpoints; diff --git a/src/checkpoints.h b/src/checkpoints.h index f826de2319..7449d1063b 100644 --- a/src/checkpoints.h +++ b/src/checkpoints.h @@ -22,8 +22,6 @@ namespace Checkpoints //! Returns last CBlockIndex* in mapBlockIndex that is a checkpoint CBlockIndex* GetLastCheckpoint(const CCheckpointData& data); -double GuessVerificationProgress(const CCheckpointData& data, CBlockIndex* pindex, bool fSigchecks = true); - } //namespace Checkpoints #endif // BITCOIN_CHECKPOINTS_H diff --git a/src/consensus/params.h b/src/consensus/params.h index edf445e1c7..3f98938f7e 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -62,6 +62,7 @@ struct Params { int64_t nPowTargetTimespan; int64_t DifficultyAdjustmentInterval() const { return nPowTargetTimespan / nPowTargetSpacing; } uint256 nMinimumChainWork; + uint256 defaultAssumeValid; }; } // namespace Consensus diff --git a/src/init.cpp b/src/init.cpp index 992ce8ebdc..14c0a36f52 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -329,8 +329,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-blocknotify=<cmd>", _("Execute command when the best block changes (%s in cmd is replaced by block hash)")); if (showDebug) strUsage += HelpMessageOpt("-blocksonly", strprintf(_("Whether to operate in a blocks only mode (default: %u)"), DEFAULT_BLOCKSONLY)); - strUsage += HelpMessageOpt("-checkblocks=<n>", strprintf(_("How many blocks to check at startup (default: %u, 0 = all)"), DEFAULT_CHECKBLOCKS)); - strUsage += HelpMessageOpt("-checklevel=<n>", strprintf(_("How thorough the block verification of -checkblocks is (0-4, default: %u)"), DEFAULT_CHECKLEVEL)); + strUsage +=HelpMessageOpt("-assumevalid=<hex>", strprintf(_("If this block is in the chain assume that it and its ancestors are valid and potentially skip their script verification (0 to verify all, default: %s, testnet: %s)"), Params(CBaseChainParams::MAIN).GetConsensus().defaultAssumeValid.GetHex(), Params(CBaseChainParams::TESTNET).GetConsensus().defaultAssumeValid.GetHex())); strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), BITCOIN_CONF_FILENAME)); if (mode == HMM_BITCOIND) { @@ -351,9 +350,9 @@ std::string HelpMessage(HelpMessageMode mode) #ifndef WIN32 strUsage += HelpMessageOpt("-pid=<file>", strprintf(_("Specify pid file (default: %s)"), BITCOIN_PID_FILENAME)); #endif - strUsage += HelpMessageOpt("-prune=<n>", strprintf(_("Reduce storage requirements by pruning (deleting) old blocks. This mode is incompatible with -txindex and -rescan. " + strUsage += HelpMessageOpt("-prune=<n>", strprintf(_("Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks, and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -txindex and -rescan. " "Warning: Reverting this setting requires re-downloading the entire blockchain. " - "(default: 0 = disable pruning blocks, >%u = target size in MiB to use for block files)"), MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024)); + "(default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >%u = automatically prune block files to stay under the specified target size in MiB)"), MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024)); strUsage += HelpMessageOpt("-reindex-chainstate", _("Rebuild chain state from the currently indexed blocks")); strUsage += HelpMessageOpt("-reindex", _("Rebuild chain state and block index from the blk*.dat files on disk")); #ifndef WIN32 @@ -420,6 +419,8 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-uacomment=<cmt>", _("Append comment to the user agent string")); if (showDebug) { + strUsage += HelpMessageOpt("-checkblocks=<n>", strprintf(_("How many blocks to check at startup (default: %u, 0 = all)"), DEFAULT_CHECKBLOCKS)); + strUsage += HelpMessageOpt("-checklevel=<n>", strprintf(_("How thorough the block verification of -checkblocks is (0-4, default: %u)"), DEFAULT_CHECKLEVEL)); strUsage += HelpMessageOpt("-checkblockindex", strprintf("Do a full consistency check for mapBlockIndex, setBlockIndexCandidates, chainActive and mapBlocksUnlinked occasionally. Also sets -checkmempool (default: %u)", Params(CBaseChainParams::MAIN).DefaultConsistencyChecks())); strUsage += HelpMessageOpt("-checkmempool=<n>", strprintf("Run checks every <n> transactions (default: %u)", Params(CBaseChainParams::MAIN).DefaultConsistencyChecks())); strUsage += HelpMessageOpt("-checkpoints", strprintf("Disable expensive verification for known chain history (default: %u)", DEFAULT_CHECKPOINTS_ENABLED)); @@ -467,8 +468,11 @@ std::string HelpMessage(HelpMessageMode mode) AppendParamsHelpMessages(strUsage, showDebug); strUsage += HelpMessageGroup(_("Node relay options:")); - if (showDebug) + if (showDebug) { strUsage += HelpMessageOpt("-acceptnonstdtxn", strprintf("Relay and mine \"non-standard\" transactions (%sdefault: %u)", "testnet/regtest only; ", !Params(CBaseChainParams::TESTNET).RequireStandard())); + strUsage += HelpMessageOpt("-incrementalrelayfee=<amt>", strprintf("Fee rate (in %s/kB) used to define cost of relay, used for mempool limiting and BIP 125 replacement. (default: %s)", CURRENCY_UNIT, FormatMoney(DEFAULT_INCREMENTAL_RELAY_FEE))); + strUsage += HelpMessageOpt("-dustrelayfee=<amt>", strprintf("Fee rate (in %s/kB) used to defined dust, the value of an output such that it will cost about 1/3 of its value in fees at this fee rate to spend it. (default: %s)", CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE))); + } strUsage += HelpMessageOpt("-bytespersigop", strprintf(_("Equivalent bytes per sigop in transactions for relay and mining (default: %u)"), DEFAULT_BYTES_PER_SIGOP)); strUsage += HelpMessageOpt("-datacarrier", strprintf(_("Relay and mine data carrier transactions (default: %u)"), DEFAULT_ACCEPT_DATACARRIER)); strUsage += HelpMessageOpt("-datacarriersize", strprintf(_("Maximum size of data in data carrier transactions we relay and mine (default: %u)"), MAX_OP_RETURN_RELAY)); @@ -478,6 +482,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-blockmaxweight=<n>", strprintf(_("Set maximum BIP141 block weight (default: %d)"), DEFAULT_BLOCK_MAX_WEIGHT)); strUsage += HelpMessageOpt("-blockmaxsize=<n>", strprintf(_("Set maximum block size in bytes (default: %d)"), DEFAULT_BLOCK_MAX_SIZE)); strUsage += HelpMessageOpt("-blockprioritysize=<n>", strprintf(_("Set maximum size of high-priority/low-fee transactions in bytes (default: %d)"), DEFAULT_BLOCK_PRIORITY_SIZE)); + strUsage += HelpMessageOpt("-blockmintxfee=<amt>", strprintf(_("Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s)"), CURRENCY_UNIT, FormatMoney(DEFAULT_BLOCK_MIN_TX_FEE))); if (showDebug) strUsage += HelpMessageOpt("-blockversion=<n>", "Override block version to test forking scenarios"); @@ -920,11 +925,26 @@ bool AppInitParameterInteraction() fCheckBlockIndex = GetBoolArg("-checkblockindex", chainparams.DefaultConsistencyChecks()); fCheckpointsEnabled = GetBoolArg("-checkpoints", DEFAULT_CHECKPOINTS_ENABLED); + hashAssumeValid = uint256S(GetArg("-assumevalid", chainparams.GetConsensus().defaultAssumeValid.GetHex())); + if (!hashAssumeValid.IsNull()) + LogPrintf("Assuming ancestors of block %s have valid signatures.\n", hashAssumeValid.GetHex()); + else + LogPrintf("Validating signatures for all blocks.\n"); + // mempool limits int64_t nMempoolSizeMax = GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000; int64_t nMempoolSizeMin = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000 * 40; if (nMempoolSizeMax < 0 || nMempoolSizeMax < nMempoolSizeMin) return InitError(strprintf(_("-maxmempool must be at least %d MB"), std::ceil(nMempoolSizeMin / 1000000.0))); + // incremental relay fee sets the minimimum feerate increase necessary for BIP 125 replacement in the mempool + // and the amount the mempool min fee increases above the feerate of txs evicted due to mempool limiting. + if (IsArgSet("-incrementalrelayfee")) + { + CAmount n = 0; + if (!ParseMoney(GetArg("-incrementalrelayfee", ""), n)) + return InitError(AmountErrMsg("incrementalrelayfee", GetArg("-incrementalrelayfee", ""))); + incrementalRelayFee = CFeeRate(n); + } // -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency nScriptCheckThreads = GetArg("-par", DEFAULT_SCRIPTCHECK_THREADS); @@ -936,12 +956,16 @@ bool AppInitParameterInteraction() nScriptCheckThreads = MAX_SCRIPTCHECK_THREADS; // block pruning; get the amount of disk space (in MiB) to allot for block & undo files - int64_t nSignedPruneTarget = GetArg("-prune", 0) * 1024 * 1024; - if (nSignedPruneTarget < 0) { + int64_t nPruneArg = GetArg("-prune", 0); + if (nPruneArg < 0) { return InitError(_("Prune cannot be configured with a negative value.")); } - nPruneTarget = (uint64_t) nSignedPruneTarget; - if (nPruneTarget) { + nPruneTarget = (uint64_t) nPruneArg * 1024 * 1024; + if (nPruneArg == 1) { // manual pruning: -prune=1 + LogPrintf("Block pruning enabled. Use RPC call pruneblockchain(height) to manually prune block and undo files.\n"); + nPruneTarget = std::numeric_limits<uint64_t>::max(); + fPruneMode = true; + } else if (nPruneTarget) { if (nPruneTarget < MIN_DISK_SPACE_FOR_BLOCK_FILES) { return InitError(strprintf(_("Prune configured below the minimum of %d MiB. Please use a higher number."), MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024)); } @@ -971,6 +995,29 @@ bool AppInitParameterInteraction() return InitError(AmountErrMsg("minrelaytxfee", GetArg("-minrelaytxfee", ""))); // High fee check is done afterward in CWallet::ParameterInteraction() ::minRelayTxFee = CFeeRate(n); + } else if (incrementalRelayFee > ::minRelayTxFee) { + // Allow only setting incrementalRelayFee to control both + ::minRelayTxFee = incrementalRelayFee; + LogPrintf("Increasing minrelaytxfee to %s to match incrementalrelayfee\n",::minRelayTxFee.ToString()); + } + + // Sanity check argument for min fee for including tx in block + // TODO: Harmonize which arguments need sanity checking and where that happens + if (IsArgSet("-blockmintxfee")) + { + CAmount n = 0; + if (!ParseMoney(GetArg("-blockmintxfee", ""), n)) + return InitError(AmountErrMsg("blockmintxfee", GetArg("-blockmintxfee", ""))); + } + + // Feerate used to define dust. Shouldn't be changed lightly as old + // implementations may inadvertently create non-standard transactions + if (IsArgSet("-dustrelayfee")) + { + CAmount n = 0; + if (!ParseMoney(GetArg("-dustrelayfee", ""), n) || 0 == n) + return InitError(AmountErrMsg("dustrelayfee", GetArg("-dustrelayfee", ""))); + dustRelayFee = CFeeRate(n); } fRequireStandard = !GetBoolArg("-acceptnonstdtxn", !chainparams.RequireStandard()); diff --git a/src/miner.cpp b/src/miner.cpp index 856e9edc14..acded94168 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -95,12 +95,18 @@ BlockAssembler::BlockAssembler(const CChainParams& _chainparams) nBlockMaxWeight = nBlockMaxSize * WITNESS_SCALE_FACTOR; } } + if (IsArgSet("-blockmintxfee")) { + CAmount n = 0; + ParseMoney(GetArg("-blockmintxfee", ""), n); + blockMinFeeRate = CFeeRate(n); + } else { + blockMinFeeRate = CFeeRate(DEFAULT_BLOCK_MIN_TX_FEE); + } // Limit weight to between 4K and MAX_BLOCK_WEIGHT-4K for sanity: nBlockMaxWeight = std::max((unsigned int)4000, std::min((unsigned int)(MAX_BLOCK_WEIGHT-4000), nBlockMaxWeight)); // Limit size to between 1K and MAX_BLOCK_SERIALIZED_SIZE-1K for sanity: nBlockMaxSize = std::max((unsigned int)1000, std::min((unsigned int)(MAX_BLOCK_SERIALIZED_SIZE-1000), nBlockMaxSize)); - // Whether we need to account for byte usage (in addition to weight usage) fNeedSizeAccounting = (nBlockMaxSize < MAX_BLOCK_SERIALIZED_SIZE-1000); } @@ -460,7 +466,7 @@ void BlockAssembler::addPackageTxs() packageSigOpsCost = modit->nSigOpCostWithAncestors; } - if (packageFees < ::minRelayTxFee.GetFee(packageSize)) { + if (packageFees < blockMinFeeRate.GetFee(packageSize)) { // Everything else we might consider has a lower fee rate return; } diff --git a/src/miner.h b/src/miner.h index 3dd9bf4cab..3ba92b16b8 100644 --- a/src/miner.h +++ b/src/miner.h @@ -143,6 +143,7 @@ private: bool fIncludeWitness; unsigned int nBlockMaxWeight, nBlockMaxSize; bool fNeedSizeAccounting; + CFeeRate blockMinFeeRate; // Information on the current status of the block uint64_t nBlockWeight; diff --git a/src/net.cpp b/src/net.cpp index 37e7dfed4c..b275bdd809 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -437,11 +437,6 @@ void CNode::CloseSocketDisconnect() LogPrint("net", "disconnecting peer=%d\n", id); CloseSocket(hSocket); } - - // in case this fails, we'll empty the recv buffer when the CNode is deleted - TRY_LOCK(cs_vRecvMsg, lockRecv); - if (lockRecv) - vRecvMsg.clear(); } void CConnman::ClearBanned() @@ -650,16 +645,18 @@ void CNode::copyStats(CNodeStats &stats) } #undef X -// requires LOCK(cs_vRecvMsg) bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes, bool& complete) { complete = false; + int64_t nTimeMicros = GetTimeMicros(); + nLastRecv = nTimeMicros / 1000000; + nRecvBytes += nBytes; while (nBytes > 0) { // get current incomplete message, or create a new one if (vRecvMsg.empty() || vRecvMsg.back().complete()) - vRecvMsg.push_back(CNetMessage(Params().MessageStart(), SER_NETWORK, nRecvVersion)); + vRecvMsg.push_back(CNetMessage(Params().MessageStart(), SER_NETWORK, INIT_PROTO_VERSION)); CNetMessage& msg = vRecvMsg.back(); @@ -691,7 +688,7 @@ bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes, bool& complete assert(i != mapRecvBytesPerMsgCmd.end()); i->second += msg.hdr.nMessageSize + CMessageHeader::HEADER_SIZE; - msg.nTime = GetTimeMicros(); + msg.nTime = nTimeMicros; complete = true; } } @@ -764,7 +761,7 @@ const uint256& CNetMessage::GetMessageHash() const // requires LOCK(cs_vSend) -size_t SocketSendData(CNode *pnode) +size_t CConnman::SocketSendData(CNode *pnode) { auto it = pnode->vSendMsg.begin(); size_t nSentSize = 0; @@ -781,6 +778,7 @@ size_t SocketSendData(CNode *pnode) if (pnode->nSendOffset == data.size()) { pnode->nSendOffset = 0; pnode->nSendSize -= data.size(); + pnode->fPauseSend = pnode->nSendSize > nSendBufferMaxSize; it++; } else { // could not send full message; stop sending more @@ -1052,8 +1050,7 @@ void CConnman::ThreadSocketHandler() std::vector<CNode*> vNodesCopy = vNodes; BOOST_FOREACH(CNode* pnode, vNodesCopy) { - if (pnode->fDisconnect || - (pnode->GetRefCount() <= 0 && pnode->vRecvMsg.empty() && pnode->nSendSize == 0)) + if (pnode->fDisconnect) { // remove from vNodes vNodes.erase(remove(vNodes.begin(), vNodes.end(), pnode), vNodes.end()); @@ -1083,13 +1080,9 @@ void CConnman::ThreadSocketHandler() TRY_LOCK(pnode->cs_vSend, lockSend); if (lockSend) { - TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); - if (lockRecv) - { TRY_LOCK(pnode->cs_inventory, lockInv); if (lockInv) fDelete = true; - } } } if (fDelete) @@ -1149,15 +1142,10 @@ void CConnman::ThreadSocketHandler() // write buffer in this case before receiving more. This avoids // needlessly queueing received data, if the remote peer is not themselves // receiving data. This means properly utilizing TCP flow control signalling. - // * Otherwise, if there is no (complete) message in the receive buffer, - // or there is space left in the buffer, select() for receiving data. - // * (if neither of the above applies, there is certainly one message - // in the receiver buffer ready to be processed). - // Together, that means that at least one of the following is always possible, - // so we don't deadlock: - // * We send some data. - // * We wait for data to be received (and disconnect after timeout). - // * We process a message in the buffer (message handler thread). + // * Otherwise, if there is space left in the receive buffer, select() for + // receiving data. + // * Hand off all complete messages to the processor, to be handled without + // blocking here. { TRY_LOCK(pnode->cs_vSend, lockSend); if (lockSend) { @@ -1168,10 +1156,7 @@ void CConnman::ThreadSocketHandler() } } { - TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); - if (lockRecv && ( - pnode->vRecvMsg.empty() || !pnode->vRecvMsg.front().complete() || - pnode->GetTotalRecvSize() <= GetReceiveFloodSize())) + if (!pnode->fPauseRecv) FD_SET(pnode->hSocket, &fdsetRecv); } } @@ -1230,8 +1215,6 @@ void CConnman::ThreadSocketHandler() continue; if (FD_ISSET(pnode->hSocket, &fdsetRecv) || FD_ISSET(pnode->hSocket, &fdsetError)) { - TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); - if (lockRecv) { { // typical socket buffer is 8K-64K @@ -1242,11 +1225,23 @@ void CConnman::ThreadSocketHandler() bool notify = false; if (!pnode->ReceiveMsgBytes(pchBuf, nBytes, notify)) pnode->CloseSocketDisconnect(); - if(notify) - condMsgProc.notify_one(); - pnode->nLastRecv = GetTime(); - pnode->nRecvBytes += nBytes; RecordBytesRecv(nBytes); + if (notify) { + size_t nSizeAdded = 0; + auto it(pnode->vRecvMsg.begin()); + for (; it != pnode->vRecvMsg.end(); ++it) { + if (!it->complete()) + break; + nSizeAdded += it->vRecv.size() + CMessageHeader::HEADER_SIZE; + } + { + LOCK(pnode->cs_vProcessMsg); + pnode->vProcessMsg.splice(pnode->vProcessMsg.end(), pnode->vRecvMsg, pnode->vRecvMsg.begin(), it); + pnode->nProcessQueueSize += nSizeAdded; + pnode->fPauseRecv = pnode->nProcessQueueSize > nReceiveFloodSize; + } + WakeMessageHandler(); + } } else if (nBytes == 0) { @@ -1280,8 +1275,9 @@ void CConnman::ThreadSocketHandler() TRY_LOCK(pnode->cs_vSend, lockSend); if (lockSend) { size_t nBytes = SocketSendData(pnode); - if (nBytes) + if (nBytes) { RecordBytesSent(nBytes); + } } } @@ -1321,8 +1317,14 @@ void CConnman::ThreadSocketHandler() } } - - +void CConnman::WakeMessageHandler() +{ + { + std::lock_guard<std::mutex> lock(mutexMsgProc); + fMsgProcWake = true; + } + condMsgProc.notify_one(); +} @@ -1858,7 +1860,7 @@ void CConnman::ThreadMessageHandler() } } - bool fSleep = true; + bool fMoreWork = false; BOOST_FOREACH(CNode* pnode, vNodesCopy) { @@ -1866,22 +1868,8 @@ void CConnman::ThreadMessageHandler() continue; // Receive messages - { - TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); - if (lockRecv) - { - if (!GetNodeSignals().ProcessMessages(pnode, *this, flagInterruptMsgProc)) - pnode->CloseSocketDisconnect(); - - if (pnode->nSendSize < GetSendBufferSize()) - { - if (!pnode->vRecvGetData.empty() || (!pnode->vRecvMsg.empty() && pnode->vRecvMsg[0].complete())) - { - fSleep = false; - } - } - } - } + bool fMoreNodeWork = GetNodeSignals().ProcessMessages(pnode, *this, flagInterruptMsgProc); + fMoreWork |= (fMoreNodeWork && !pnode->fPauseSend); if (flagInterruptMsgProc) return; @@ -1901,10 +1889,11 @@ void CConnman::ThreadMessageHandler() pnode->Release(); } - if (fSleep) { - std::unique_lock<std::mutex> lock(mutexMsgProc); - condMsgProc.wait_until(lock, std::chrono::steady_clock::now() + std::chrono::milliseconds(100)); + std::unique_lock<std::mutex> lock(mutexMsgProc); + if (!fMoreWork) { + condMsgProc.wait_until(lock, std::chrono::steady_clock::now() + std::chrono::milliseconds(100), [this] { return fMsgProcWake; }); } + fMsgProcWake = false; } } @@ -2121,7 +2110,7 @@ bool CConnman::Start(CScheduler& scheduler, std::string& strNodeError, Options c nMaxFeeler = connOptions.nMaxFeeler; nSendBufferMaxSize = connOptions.nSendBufferMaxSize; - nReceiveFloodSize = connOptions.nSendBufferMaxSize; + nReceiveFloodSize = connOptions.nReceiveFloodSize; nMaxOutboundLimit = connOptions.nMaxOutboundLimit; nMaxOutboundTimeframe = connOptions.nMaxOutboundTimeframe; @@ -2182,6 +2171,11 @@ bool CConnman::Start(CScheduler& scheduler, std::string& strNodeError, Options c interruptNet.reset(); flagInterruptMsgProc = false; + { + std::unique_lock<std::mutex> lock(mutexMsgProc); + fMsgProcWake = false; + } + // Send and receive from sockets, accept connections threadSocketHandler = std::thread(&TraceThread<std::function<void()> >, "net", std::function<void()>(std::bind(&CConnman::ThreadSocketHandler, this))); @@ -2613,6 +2607,9 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn minFeeFilter = 0; lastSentFeeFilter = 0; nextSendTimeFeeFilter = 0; + fPauseRecv = false; + fPauseSend = false; + nProcessQueueSize = 0; BOOST_FOREACH(const std::string &msg, getAllNetMessageTypes()) mapRecvBytesPerMsgCmd[msg] = 0; @@ -2692,6 +2689,8 @@ void CConnman::PushMessage(CNode* pnode, CSerializedNetMsg&& msg) pnode->mapSendBytesPerMsgCmd[msg.command] += nTotalSize; pnode->nSendSize += nTotalSize; + if (pnode->nSendSize > nSendBufferMaxSize) + pnode->fPauseSend = true; pnode->vSendMsg.push_back(std::move(serializedHeader)); if (nMessageSize) pnode->vSendMsg.push_back(std::move(msg.data)); @@ -327,6 +327,9 @@ public: /** Get a unique deterministic randomizer. */ CSipHasher GetDeterministicRandomizer(uint64_t id); + unsigned int GetReceiveFloodSize() const; + + void WakeMessageHandler(); private: struct ListenSocket { SOCKET socket; @@ -358,6 +361,7 @@ private: NodeId GetNewNodeId(); + size_t SocketSendData(CNode *pnode); //!check is the banlist has unwritten changes bool BannedSetIsDirty(); //!set the "dirty" flag for the banlist @@ -368,8 +372,6 @@ private: void DumpData(); void DumpBanlist(); - unsigned int GetReceiveFloodSize() const; - // Network stats void RecordBytesRecv(uint64_t bytes); void RecordBytesSent(uint64_t bytes); @@ -428,6 +430,9 @@ private: /** SipHasher seeds for deterministic randomness */ const uint64_t nSeed0, nSeed1; + /** flag for waking the message processor. */ + bool fMsgProcWake; + std::condition_variable condMsgProc; std::mutex mutexMsgProc; std::atomic<bool> flagInterruptMsgProc; @@ -445,7 +450,6 @@ void Discover(boost::thread_group& threadGroup); void MapPort(bool fUseUPnP); unsigned short GetListenPort(); bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false); -size_t SocketSendData(CNode *pnode); struct CombinerAll { @@ -610,11 +614,13 @@ public: std::deque<std::vector<unsigned char>> vSendMsg; CCriticalSection cs_vSend; + CCriticalSection cs_vProcessMsg; + std::list<CNetMessage> vProcessMsg; + size_t nProcessQueueSize; + std::deque<CInv> vRecvGetData; - std::deque<CNetMessage> vRecvMsg; - CCriticalSection cs_vRecvMsg; uint64_t nRecvBytes; - int nRecvVersion; + std::atomic<int> nRecvVersion; int64_t nLastSend; int64_t nLastRecv; @@ -650,6 +656,8 @@ public: const NodeId id; const uint64_t nKeyedNetGroup; + std::atomic_bool fPauseRecv; + std::atomic_bool fPauseSend; protected: mapMsgCmdSize mapSendBytesPerMsgCmd; @@ -723,6 +731,7 @@ private: const ServiceFlags nLocalServices; const int nMyStartingHeight; int nSendVersion; + std::list<CNetMessage> vRecvMsg; // Used only by SocketHandler thread public: NodeId GetId() const { @@ -743,24 +752,15 @@ public: return nRefCount; } - // requires LOCK(cs_vRecvMsg) - unsigned int GetTotalRecvSize() - { - unsigned int total = 0; - BOOST_FOREACH(const CNetMessage &msg, vRecvMsg) - total += msg.vRecv.size() + 24; - return total; - } - - // requires LOCK(cs_vRecvMsg) bool ReceiveMsgBytes(const char *pch, unsigned int nBytes, bool& complete); - // requires LOCK(cs_vRecvMsg) void SetRecvVersion(int nVersionIn) { nRecvVersion = nVersionIn; - BOOST_FOREACH(CNetMessage &msg, vRecvMsg) - msg.SetVersion(nVersionIn); + } + int GetRecvVersion() + { + return nRecvVersion; } void SetSendVersion(int nVersionIn) { diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 3a956e89e7..03568283fb 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -101,7 +101,7 @@ namespace { /** Blocks that are in flight, and that are in the queue to be downloaded. Protected by cs_main. */ struct QueuedBlock { uint256 hash; - CBlockIndex* pindex; //!< Optional. + const CBlockIndex* pindex; //!< Optional. bool fValidatedHeaders; //!< Whether this block has validated headers at the time of request. std::unique_ptr<PartiallyDownloadedBlock> partialBlock; //!< Optional, used for CMPCTBLOCK downloads }; @@ -156,13 +156,13 @@ struct CNodeState { //! List of asynchronously-determined block rejections to notify this peer about. std::vector<CBlockReject> rejects; //! The best known block we know this peer has announced. - CBlockIndex *pindexBestKnownBlock; + const CBlockIndex *pindexBestKnownBlock; //! The hash of the last unknown block this peer has announced. uint256 hashLastUnknownBlock; //! The last full block we both have. - CBlockIndex *pindexLastCommonBlock; + const CBlockIndex *pindexLastCommonBlock; //! The best header we have sent our peer. - CBlockIndex *pindexBestHeaderSent; + const CBlockIndex *pindexBestHeaderSent; //! Length of current-streak of unconnecting headers announcements int nUnconnectingHeaders; //! Whether we've started headers synchronization with this peer. @@ -331,7 +331,7 @@ bool MarkBlockAsReceived(const uint256& hash) { // Requires cs_main. // returns false, still setting pit, if the block was already in flight from the same peer // pit will only be valid as long as the same cs_main lock is being held -bool MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const Consensus::Params& consensusParams, CBlockIndex *pindex = NULL, list<QueuedBlock>::iterator **pit = NULL) { +bool MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const Consensus::Params& consensusParams, const CBlockIndex *pindex = NULL, list<QueuedBlock>::iterator **pit = NULL) { CNodeState *state = State(nodeid); assert(state != NULL); @@ -395,33 +395,38 @@ void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) { } } -void MaybeSetPeerAsAnnouncingHeaderAndIDs(const CNodeState* nodestate, CNode* pfrom, CConnman& connman) { - if (!nodestate->fSupportsDesiredCmpctVersion) { +void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman& connman) { + AssertLockHeld(cs_main); + CNodeState* nodestate = State(nodeid); + if (!nodestate || !nodestate->fSupportsDesiredCmpctVersion) { // Never ask from peers who can't provide witnesses. return; } if (nodestate->fProvidesHeaderAndIDs) { for (std::list<NodeId>::iterator it = lNodesAnnouncingHeaderAndIDs.begin(); it != lNodesAnnouncingHeaderAndIDs.end(); it++) { - if (*it == pfrom->GetId()) { + if (*it == nodeid) { lNodesAnnouncingHeaderAndIDs.erase(it); - lNodesAnnouncingHeaderAndIDs.push_back(pfrom->GetId()); + lNodesAnnouncingHeaderAndIDs.push_back(nodeid); return; } } - bool fAnnounceUsingCMPCTBLOCK = false; - uint64_t nCMPCTBLOCKVersion = (pfrom->GetLocalServices() & NODE_WITNESS) ? 2 : 1; - if (lNodesAnnouncingHeaderAndIDs.size() >= 3) { - // As per BIP152, we only get 3 of our peers to announce - // blocks using compact encodings. - connman.ForNode(lNodesAnnouncingHeaderAndIDs.front(), [&connman, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion](CNode* pnodeStop){ - connman.PushMessage(pnodeStop, CNetMsgMaker(pnodeStop->GetSendVersion()).Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion)); - return true; - }); - lNodesAnnouncingHeaderAndIDs.pop_front(); - } - fAnnounceUsingCMPCTBLOCK = true; - connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion)); - lNodesAnnouncingHeaderAndIDs.push_back(pfrom->GetId()); + connman.ForNode(nodeid, [&connman](CNode* pfrom){ + bool fAnnounceUsingCMPCTBLOCK = false; + uint64_t nCMPCTBLOCKVersion = (pfrom->GetLocalServices() & NODE_WITNESS) ? 2 : 1; + if (lNodesAnnouncingHeaderAndIDs.size() >= 3) { + // As per BIP152, we only get 3 of our peers to announce + // blocks using compact encodings. + connman.ForNode(lNodesAnnouncingHeaderAndIDs.front(), [&connman, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion](CNode* pnodeStop){ + connman.PushMessage(pnodeStop, CNetMsgMaker(pnodeStop->GetSendVersion()).Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion)); + return true; + }); + lNodesAnnouncingHeaderAndIDs.pop_front(); + } + fAnnounceUsingCMPCTBLOCK = true; + connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion)); + lNodesAnnouncingHeaderAndIDs.push_back(pfrom->GetId()); + return true; + }); } } @@ -432,7 +437,7 @@ bool CanDirectFetch(const Consensus::Params &consensusParams) } // Requires cs_main -bool PeerHasHeader(CNodeState *state, CBlockIndex *pindex) +bool PeerHasHeader(CNodeState *state, const CBlockIndex *pindex) { if (state->pindexBestKnownBlock && pindex == state->pindexBestKnownBlock->GetAncestor(pindex->nHeight)) return true; @@ -443,7 +448,7 @@ bool PeerHasHeader(CNodeState *state, CBlockIndex *pindex) /** Find the last common ancestor two blocks have. * Both pa and pb must be non-NULL. */ -CBlockIndex* LastCommonAncestor(CBlockIndex* pa, CBlockIndex* pb) { +const CBlockIndex* LastCommonAncestor(const CBlockIndex* pa, const CBlockIndex* pb) { if (pa->nHeight > pb->nHeight) { pa = pa->GetAncestor(pb->nHeight); } else if (pb->nHeight > pa->nHeight) { @@ -462,7 +467,7 @@ CBlockIndex* LastCommonAncestor(CBlockIndex* pa, CBlockIndex* pb) { /** Update pindexLastCommonBlock and add not-in-flight missing successors to vBlocks, until it has * at most count entries. */ -void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<CBlockIndex*>& vBlocks, NodeId& nodeStaller, const Consensus::Params& consensusParams) { +void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<const CBlockIndex*>& vBlocks, NodeId& nodeStaller, const Consensus::Params& consensusParams) { if (count == 0) return; @@ -490,8 +495,8 @@ void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<CBl if (state->pindexLastCommonBlock == state->pindexBestKnownBlock) return; - std::vector<CBlockIndex*> vToFetch; - CBlockIndex *pindexWalk = state->pindexLastCommonBlock; + std::vector<const CBlockIndex*> vToFetch; + const CBlockIndex *pindexWalk = state->pindexLastCommonBlock; // Never fetch further than the best block we know the peer has, or more than BLOCK_DOWNLOAD_WINDOW + 1 beyond the last // linked block we have in common with this peer. The +1 is so we can detect stalling, namely if we would be able to // download that next block if the window were 1 larger. @@ -514,7 +519,7 @@ void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<CBl // are not yet downloaded and not in flight to vBlocks. In the mean time, update // pindexLastCommonBlock as long as all ancestors are already downloaded, or if it's // already part of our chain (and therefore don't need it even if pruned). - BOOST_FOREACH(CBlockIndex* pindex, vToFetch) { + BOOST_FOREACH(const CBlockIndex* pindex, vToFetch) { if (!pindex->IsValid(BLOCK_VALID_TREE)) { // We consider the chain that this peer is on invalid. return; @@ -647,7 +652,7 @@ void EraseOrphansFor(NodeId peer) nErased += EraseOrphanTx(maybeErase->second.tx->GetHash()); } } - if (nErased > 0) LogPrint("mempool", "Erased %d orphan tx from peer %d\n", nErased, peer); + if (nErased > 0) LogPrint("mempool", "Erased %d orphan tx from peer=%d\n", nErased, peer); } @@ -752,6 +757,51 @@ void PeerLogicValidation::SyncTransaction(const CTransaction& tx, const CBlockIn } } +static CCriticalSection cs_most_recent_block; +static std::shared_ptr<const CBlock> most_recent_block; +static std::shared_ptr<const CBlockHeaderAndShortTxIDs> most_recent_compact_block; +static uint256 most_recent_block_hash; + +void PeerLogicValidation::NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& pblock) { + std::shared_ptr<const CBlockHeaderAndShortTxIDs> pcmpctblock = std::make_shared<const CBlockHeaderAndShortTxIDs> (*pblock, true); + CNetMsgMaker msgMaker(PROTOCOL_VERSION); + + LOCK(cs_main); + + static int nHighestFastAnnounce = 0; + if (pindex->nHeight <= nHighestFastAnnounce) + return; + nHighestFastAnnounce = pindex->nHeight; + + bool fWitnessEnabled = IsWitnessEnabled(pindex->pprev, Params().GetConsensus()); + uint256 hashBlock(pblock->GetHash()); + + { + LOCK(cs_most_recent_block); + most_recent_block_hash = hashBlock; + most_recent_block = pblock; + most_recent_compact_block = pcmpctblock; + } + + connman->ForEachNode([this, &pcmpctblock, pindex, &msgMaker, fWitnessEnabled, &hashBlock](CNode* pnode) { + // TODO: Avoid the repeated-serialization here + if (pnode->nVersion < INVALID_CB_NO_BAN_VERSION || pnode->fDisconnect) + return; + ProcessBlockAvailability(pnode->GetId()); + CNodeState &state = *State(pnode->GetId()); + // If the peer has, or we announced to them the previous block already, + // but we don't think they have this one, go ahead and announce it + if (state.fPreferHeaderAndIDs && (!fWitnessEnabled || state.fWantsCmpctWitness) && + !PeerHasHeader(&state, pindex) && PeerHasHeader(&state, pindex->pprev)) { + + LogPrint("net", "%s sending header-and-ids %s to peer=%d\n", "PeerLogicValidation::NewPoWValidBlock", + hashBlock.ToString(), pnode->id); + connman->PushMessage(pnode, msgMaker.Make(NetMsgType::CMPCTBLOCK, *pcmpctblock)); + state.pindexBestHeaderSent = pindex; + } + }); +} + void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) { const int nNewHeight = pindexNew->nHeight; connman->SetBestHeight(nNewHeight); @@ -777,6 +827,7 @@ void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CB } } }); + connman->WakeMessageHandler(); } nTimeBestReceived = GetTime(); @@ -798,6 +849,11 @@ void PeerLogicValidation::BlockChecked(const CBlock& block, const CValidationSta Misbehaving(it->second.first, nDoS); } } + else if (state.IsValid() && !IsInitialBlockDownload() && mapBlocksInFlight.count(hash) == mapBlocksInFlight.size()) { + if (it != mapBlockSource.end()) { + MaybeSetPeerAsAnnouncingHeaderAndIDs(it->second.first, *connman); + } + } if (it != mapBlockSource.end()) mapBlockSource.erase(it); } @@ -889,14 +945,13 @@ static void RelayAddress(const CAddress& addr, bool fReachable, CConnman& connma void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParams, CConnman& connman, std::atomic<bool>& interruptMsgProc) { std::deque<CInv>::iterator it = pfrom->vRecvGetData.begin(); - unsigned int nMaxSendBufferSize = connman.GetSendBufferSize(); vector<CInv> vNotFound; CNetMsgMaker msgMaker(pfrom->GetSendVersion()); LOCK(cs_main); while (it != pfrom->vRecvGetData.end()) { // Don't bother if send buffer is too full to respond anyway - if (pfrom->nSendSize >= nMaxSendBufferSize) + if (pfrom->fPauseSend) break; const CInv &inv = *it; @@ -912,6 +967,21 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam BlockMap::iterator mi = mapBlockIndex.find(inv.hash); if (mi != mapBlockIndex.end()) { + if (mi->second->nChainTx && !mi->second->IsValid(BLOCK_VALID_SCRIPTS) && + mi->second->IsValid(BLOCK_VALID_TREE)) { + // If we have the block and all of its parents, but have not yet validated it, + // we might be in the middle of connecting it (ie in the unlock of cs_main + // before ActivateBestChain but after AcceptBlock). + // In this case, we need to run ActivateBestChain prior to checking the relay + // conditions below. + std::shared_ptr<const CBlock> a_recent_block; + { + LOCK(cs_most_recent_block); + a_recent_block = most_recent_block; + } + CValidationState dummy; + ActivateBestChain(dummy, Params(), a_recent_block); + } if (chainActive.Contains(mi->second)) { send = true; } else { @@ -1049,7 +1119,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam } } -uint32_t GetFetchFlags(CNode* pfrom, CBlockIndex* pprev, const Consensus::Params& chainparams) { +uint32_t GetFetchFlags(CNode* pfrom, const CBlockIndex* pprev, const Consensus::Params& chainparams) { uint32_t nFetchFlags = 0; if ((pfrom->GetLocalServices() & NODE_WITNESS) && State(pfrom->GetId())->fHaveWitness) { nFetchFlags |= MSG_WITNESS_FLAG; @@ -1057,10 +1127,25 @@ uint32_t GetFetchFlags(CNode* pfrom, CBlockIndex* pprev, const Consensus::Params return nFetchFlags; } +inline void static SendBlockTransactions(const CBlock& block, const BlockTransactionsRequest& req, CNode* pfrom, CConnman& connman) { + BlockTransactions resp(req); + for (size_t i = 0; i < req.indexes.size(); i++) { + if (req.indexes[i] >= block.vtx.size()) { + LOCK(cs_main); + Misbehaving(pfrom->GetId(), 100); + LogPrintf("Peer %d sent us a getblocktxn with out-of-bounds tx indices", pfrom->id); + return; + } + resp.txn[i] = block.vtx[req.indexes[i]]; + } + LOCK(cs_main); + CNetMsgMaker msgMaker(pfrom->GetSendVersion()); + int nSendFlags = State(pfrom->GetId())->fWantsCmpctWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS; + connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCKTXN, resp)); +} + bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, CConnman& connman, std::atomic<bool>& interruptMsgProc) { - unsigned int nMaxSendBufferSize = connman.GetSendBufferSize(); - LogPrint("net", "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->id); if (IsArgSet("-dropmessagestest") && GetRand(GetArg("-dropmessagestest", 0)) == 0) { @@ -1413,11 +1498,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Track requests for our stuff GetMainSignals().Inventory(inv.hash); - - if (pfrom->nSendSize > (nMaxSendBufferSize * 2)) { - Misbehaving(pfrom->GetId(), 50); - return error("send buffer size() = %u", pfrom->nSendSize); - } } if (!vToFetch.empty()) @@ -1453,10 +1533,27 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, uint256 hashStop; vRecv >> locator >> hashStop; + // We might have announced the currently-being-connected tip using a + // compact block, which resulted in the peer sending a getblocks + // request, which we would otherwise respond to without the new block. + // To avoid this situation we simply verify that we are on our best + // known chain now. This is super overkill, but we handle it better + // for getheaders requests, and there are no known nodes which support + // compact blocks but still use getblocks to request blocks. + { + std::shared_ptr<const CBlock> a_recent_block; + { + LOCK(cs_most_recent_block); + a_recent_block = most_recent_block; + } + CValidationState dummy; + ActivateBestChain(dummy, Params(), a_recent_block); + } + LOCK(cs_main); // Find the last block the caller has in the main chain - CBlockIndex* pindex = FindForkInGlobalIndex(chainActive, locator); + const CBlockIndex* pindex = FindForkInGlobalIndex(chainActive, locator); // Send the rest of the chain if (pindex) @@ -1496,6 +1593,18 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, BlockTransactionsRequest req; vRecv >> req; + std::shared_ptr<const CBlock> recent_block; + { + LOCK(cs_most_recent_block); + if (most_recent_block_hash == req.blockhash) + recent_block = most_recent_block; + // Unlock cs_most_recent_block to avoid cs_main lock inversion + } + if (recent_block) { + SendBlockTransactions(*recent_block, req, pfrom, connman); + return true; + } + LOCK(cs_main); BlockMap::iterator it = mapBlockIndex.find(req.blockhash); @@ -1525,17 +1634,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, bool ret = ReadBlockFromDisk(block, it->second, chainparams.GetConsensus()); assert(ret); - BlockTransactions resp(req); - for (size_t i = 0; i < req.indexes.size(); i++) { - if (req.indexes[i] >= block.vtx.size()) { - Misbehaving(pfrom->GetId(), 100); - LogPrintf("Peer %d sent us a getblocktxn with out-of-bounds tx indices", pfrom->id); - return true; - } - resp.txn[i] = block.vtx[req.indexes[i]]; - } - int nSendFlags = State(pfrom->GetId())->fWantsCmpctWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS; - connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCKTXN, resp)); + SendBlockTransactions(block, req, pfrom, connman); } @@ -1552,7 +1651,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } CNodeState *nodestate = State(pfrom->GetId()); - CBlockIndex* pindex = NULL; + const CBlockIndex* pindex = NULL; if (locator.IsNull()) { // If locator is null, return the hashStop block @@ -1583,6 +1682,14 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // if our peer has chainActive.Tip() (and thus we are sending an empty // headers message). In both cases it's safe to update // pindexBestHeaderSent to be our tip. + // + // It is important that we simply reset the BestHeaderSent value here, + // and not max(BestHeaderSent, newHeaderSent). We might have announced + // the currently-being-connected tip using a compact block, which + // resulted in the peer sending a headers request, which we respond to + // without the new block. By resetting the BestHeaderSent, we ensure we + // will re-announce the new block via headers (or compact blocks again) + // in the SendMessages logic. nodestate->pindexBestHeaderSent = pindex ? pindex : chainActive.Tip(); connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::HEADERS, vHeaders)); } @@ -1715,6 +1822,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, LogPrint("mempool", "mapOrphan overflow, removed %u tx\n", nEvicted); } else { LogPrint("mempool", "not keeping orphan with rejected parents %s\n",tx.GetHash().ToString()); + // We will continue to reject this tx since it has rejected + // parents so avoid re-requesting it from other peers. + recentRejects->insert(tx.GetHash()); } } else { if (!tx.HasWitness() && !state.CorruptionPossible()) { @@ -1775,7 +1885,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } } - CBlockIndex *pindex = NULL; + const CBlockIndex *pindex = NULL; CValidationState state; if (!ProcessNewBlockHeaders({cmpctblock.header}, state, chainparams, &pindex)) { int nDoS; @@ -1873,12 +1983,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, return true; } - if (!fAlreadyInFlight && mapBlocksInFlight.size() == 1 && pindex->pprev->IsValid(BLOCK_VALID_CHAIN)) { - // We seem to be rather well-synced, so it appears pfrom was the first to provide us - // with this block! Let's get them to announce using compact blocks in the future. - MaybeSetPeerAsAnnouncingHeaderAndIDs(nodestate, pfrom, connman); - } - BlockTransactionsRequest req; for (size_t i = 0; i < cmpctblock.BlockTxCount(); i++) { if (!partialBlock.IsTxAvailable(i)) @@ -2051,7 +2155,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, return true; } - CBlockIndex *pindexLast = NULL; + const CBlockIndex *pindexLast = NULL; { LOCK(cs_main); CNodeState *nodestate = State(pfrom->GetId()); @@ -2128,8 +2232,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // If this set of headers is valid and ends in a block with at least as // much work as our tip, download as much as possible. if (fCanDirectFetch && pindexLast->IsValid(BLOCK_VALID_TREE) && chainActive.Tip()->nChainWork <= pindexLast->nChainWork) { - vector<CBlockIndex *> vToFetch; - CBlockIndex *pindexWalk = pindexLast; + vector<const CBlockIndex *> vToFetch; + const CBlockIndex *pindexWalk = pindexLast; // Calculate all the blocks we'd need to switch to pindexLast, up to a limit. while (pindexWalk && !chainActive.Contains(pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) { if (!(pindexWalk->nStatus & BLOCK_HAVE_DATA) && @@ -2151,7 +2255,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } else { vector<CInv> vGetData; // Download as much as possible, from earliest to latest. - BOOST_REVERSE_FOREACH(CBlockIndex *pindex, vToFetch) { + BOOST_REVERSE_FOREACH(const CBlockIndex *pindex, vToFetch) { if (nodestate->nBlocksInFlight >= MAX_BLOCKS_IN_TRANSIT_PER_PEER) { // Can't download any more from this peer break; @@ -2168,9 +2272,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } if (vGetData.size() > 0) { if (nodestate->fSupportsDesiredCmpctVersion && vGetData.size() == 1 && mapBlocksInFlight.size() == 1 && pindexLast->pprev->IsValid(BLOCK_VALID_CHAIN)) { - // We seem to be rather well-synced, so it appears pfrom was the first to provide us - // with this block! Let's get them to announce using compact blocks in the future. - MaybeSetPeerAsAnnouncingHeaderAndIDs(nodestate, pfrom, connman); // In any case, we want to download using a compact block, not a regular one vGetData[0] = CInv(MSG_CMPCT_BLOCK, vGetData[0].hash); } @@ -2447,14 +2548,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, return true; } -// requires LOCK(cs_vRecvMsg) bool ProcessMessages(CNode* pfrom, CConnman& connman, std::atomic<bool>& interruptMsgProc) { const CChainParams& chainparams = Params(); - unsigned int nMaxSendBufferSize = connman.GetSendBufferSize(); - //if (fDebug) - // LogPrintf("%s(%u messages)\n", __func__, pfrom->vRecvMsg.size()); - // // Message format // (4) message start @@ -2463,40 +2559,40 @@ bool ProcessMessages(CNode* pfrom, CConnman& connman, std::atomic<bool>& interru // (4) checksum // (x) data // - bool fOk = true; + bool fMoreWork = false; if (!pfrom->vRecvGetData.empty()) ProcessGetData(pfrom, chainparams.GetConsensus(), connman, interruptMsgProc); + if (pfrom->fDisconnect) + return false; + // this maintains the order of responses - if (!pfrom->vRecvGetData.empty()) return fOk; + if (!pfrom->vRecvGetData.empty()) return true; - std::deque<CNetMessage>::iterator it = pfrom->vRecvMsg.begin(); - while (!pfrom->fDisconnect && it != pfrom->vRecvMsg.end()) { // Don't bother if send buffer is too full to respond anyway - if (pfrom->nSendSize >= nMaxSendBufferSize) - break; - - // get next message - CNetMessage& msg = *it; - - //if (fDebug) - // LogPrintf("%s(message %u msgsz, %u bytes, complete:%s)\n", __func__, - // msg.hdr.nMessageSize, msg.vRecv.size(), - // msg.complete() ? "Y" : "N"); - - // end, if an incomplete message is found - if (!msg.complete()) - break; - - // at this point, any failure means we can delete the current message - it++; + if (pfrom->fPauseSend) + return false; + std::list<CNetMessage> msgs; + { + LOCK(pfrom->cs_vProcessMsg); + if (pfrom->vProcessMsg.empty()) + return false; + // Just take one message + msgs.splice(msgs.begin(), pfrom->vProcessMsg, pfrom->vProcessMsg.begin()); + pfrom->nProcessQueueSize -= msgs.front().vRecv.size() + CMessageHeader::HEADER_SIZE; + pfrom->fPauseRecv = pfrom->nProcessQueueSize > connman.GetReceiveFloodSize(); + fMoreWork = !pfrom->vProcessMsg.empty(); + } + CNetMessage& msg(msgs.front()); + + msg.SetVersion(pfrom->GetRecvVersion()); // Scan for message start if (memcmp(msg.hdr.pchMessageStart, chainparams.MessageStart(), CMessageHeader::MESSAGE_START_SIZE) != 0) { LogPrintf("PROCESSMESSAGE: INVALID MESSAGESTART %s peer=%d\n", SanitizeString(msg.hdr.GetCommand()), pfrom->id); - fOk = false; - break; + pfrom->fDisconnect = true; + return false; } // Read header @@ -2504,7 +2600,7 @@ bool ProcessMessages(CNode* pfrom, CConnman& connman, std::atomic<bool>& interru if (!hdr.IsValid(chainparams.MessageStart())) { LogPrintf("PROCESSMESSAGE: ERRORS IN HEADER %s peer=%d\n", SanitizeString(hdr.GetCommand()), pfrom->id); - continue; + return fMoreWork; } string strCommand = hdr.GetCommand(); @@ -2520,7 +2616,7 @@ bool ProcessMessages(CNode* pfrom, CConnman& connman, std::atomic<bool>& interru SanitizeString(strCommand), nMessageSize, HexStr(hash.begin(), hash.begin()+CMessageHeader::CHECKSUM_SIZE), HexStr(hdr.pchChecksum, hdr.pchChecksum+CMessageHeader::CHECKSUM_SIZE)); - continue; + return fMoreWork; } // Process message @@ -2529,7 +2625,9 @@ bool ProcessMessages(CNode* pfrom, CConnman& connman, std::atomic<bool>& interru { fRet = ProcessMessage(pfrom, strCommand, vRecv, msg.nTime, chainparams, connman, interruptMsgProc); if (interruptMsgProc) - return true; + return false; + if (!pfrom->vRecvGetData.empty()) + fMoreWork = true; } catch (const std::ios_base::failure& e) { @@ -2563,14 +2661,7 @@ bool ProcessMessages(CNode* pfrom, CConnman& connman, std::atomic<bool>& interru if (!fRet) LogPrintf("%s(%s, %u bytes) FAILED peer=%d\n", __func__, SanitizeString(strCommand), nMessageSize, pfrom->id); - break; - } - - // In case the connection got shut down, its receive buffer was wiped - if (!pfrom->fDisconnect) - pfrom->vRecvMsg.erase(pfrom->vRecvMsg.begin(), it); - - return fOk; + return fMoreWork; } class CompareInvMempoolOrder @@ -2742,7 +2833,7 @@ bool SendMessages(CNode* pto, CConnman& connman, std::atomic<bool>& interruptMsg bool fRevertToInv = ((!state.fPreferHeaders && (!state.fPreferHeaderAndIDs || pto->vBlockHashesToAnnounce.size() > 1)) || pto->vBlockHashesToAnnounce.size() > MAX_BLOCKS_TO_ANNOUNCE); - CBlockIndex *pBestIndex = NULL; // last header queued for delivery + const CBlockIndex *pBestIndex = NULL; // last header queued for delivery ProcessBlockAvailability(pto->id); // ensure pindexBestKnownBlock is up-to-date if (!fRevertToInv) { @@ -2753,7 +2844,7 @@ bool SendMessages(CNode* pto, CConnman& connman, std::atomic<bool>& interruptMsg BOOST_FOREACH(const uint256 &hash, pto->vBlockHashesToAnnounce) { BlockMap::iterator mi = mapBlockIndex.find(hash); assert(mi != mapBlockIndex.end()); - CBlockIndex *pindex = mi->second; + const CBlockIndex *pindex = mi->second; if (chainActive[pindex->nHeight] != pindex) { // Bail out if we reorged away from this block fRevertToInv = true; @@ -2797,15 +2888,31 @@ bool SendMessages(CNode* pto, CConnman& connman, std::atomic<bool>& interruptMsg if (vHeaders.size() == 1 && state.fPreferHeaderAndIDs) { // We only send up to 1 block as header-and-ids, as otherwise // probably means we're doing an initial-ish-sync or they're slow - LogPrint("net", "%s sending header-and-ids %s to peer %d\n", __func__, + LogPrint("net", "%s sending header-and-ids %s to peer=%d\n", __func__, vHeaders.front().GetHash().ToString(), pto->id); - //TODO: Shouldn't need to reload block from disk, but requires refactor - CBlock block; - bool ret = ReadBlockFromDisk(block, pBestIndex, consensusParams); - assert(ret); - CBlockHeaderAndShortTxIDs cmpctblock(block, state.fWantsCmpctWitness); + int nSendFlags = state.fWantsCmpctWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS; - connman.PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock)); + + bool fGotBlockFromCache = false; + { + LOCK(cs_most_recent_block); + if (most_recent_block_hash == pBestIndex->GetBlockHash()) { + if (state.fWantsCmpctWitness) + connman.PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *most_recent_compact_block)); + else { + CBlockHeaderAndShortTxIDs cmpctblock(*most_recent_block, state.fWantsCmpctWitness); + connman.PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock)); + } + fGotBlockFromCache = true; + } + } + if (!fGotBlockFromCache) { + CBlock block; + bool ret = ReadBlockFromDisk(block, pBestIndex, consensusParams); + assert(ret); + CBlockHeaderAndShortTxIDs cmpctblock(block, state.fWantsCmpctWitness); + connman.PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock)); + } state.pindexBestHeaderSent = pBestIndex; } else if (state.fPreferHeaders) { if (vHeaders.size() > 1) { @@ -2830,7 +2937,7 @@ bool SendMessages(CNode* pto, CConnman& connman, std::atomic<bool>& interruptMsg const uint256 &hashToAnnounce = pto->vBlockHashesToAnnounce.back(); BlockMap::iterator mi = mapBlockIndex.find(hashToAnnounce); assert(mi != mapBlockIndex.end()); - CBlockIndex *pindex = mi->second; + const CBlockIndex *pindex = mi->second; // Warn if we're announcing a block that is not on the main chain. // This should be very rare and could be optimized out. @@ -3015,10 +3122,10 @@ bool SendMessages(CNode* pto, CConnman& connman, std::atomic<bool>& interruptMsg // vector<CInv> vGetData; if (!pto->fClient && (fFetch || !IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) { - vector<CBlockIndex*> vToDownload; + vector<const CBlockIndex*> vToDownload; NodeId staller = -1; FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller, consensusParams); - BOOST_FOREACH(CBlockIndex *pindex, vToDownload) { + BOOST_FOREACH(const CBlockIndex *pindex, vToDownload) { uint32_t nFetchFlags = GetFetchFlags(pto, pindex->pprev, consensusParams); vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash())); MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), consensusParams, pindex); diff --git a/src/net_processing.h b/src/net_processing.h index 230d805bd4..eaa0305136 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -24,6 +24,7 @@ public: virtual void SyncTransaction(const CTransaction& tx, const CBlockIndex* pindex, int nPosInBlock); virtual void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload); virtual void BlockChecked(const CBlock& block, const CValidationState& state); + virtual void NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& pblock); }; struct CNodeStateStats { @@ -46,6 +47,7 @@ bool ProcessMessages(CNode* pfrom, CConnman& connman, std::atomic<bool>& interru * @param[in] pto The node which we are sending messages to. * @param[in] connman The connection manager for that node. * @param[in] interrupt Interrupt condition for processing threads + * @return True if there is more work to be done */ bool SendMessages(CNode* pto, CConnman& connman, std::atomic<bool>& interrupt); diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index d318a0997c..ec398f6627 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -105,7 +105,7 @@ bool IsStandardTx(const CTransaction& tx, std::string& reason, const bool witnes else if ((whichType == TX_MULTISIG) && (!fIsBareMultisigStd)) { reason = "bare-multisig"; return false; - } else if (txout.IsDust(::minRelayTxFee)) { + } else if (txout.IsDust(dustRelayFee)) { reason = "dust"; return false; } @@ -206,6 +206,8 @@ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) return true; } +CFeeRate incrementalRelayFee = CFeeRate(DEFAULT_INCREMENTAL_RELAY_FEE); +CFeeRate dustRelayFee = CFeeRate(DUST_RELAY_TX_FEE); unsigned int nBytesPerSigOp = DEFAULT_BYTES_PER_SIGOP; int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost) diff --git a/src/policy/policy.h b/src/policy/policy.h index 764ee27806..9b1323ac26 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -20,6 +20,8 @@ static const unsigned int DEFAULT_BLOCK_MAX_SIZE = 750000; static const unsigned int DEFAULT_BLOCK_PRIORITY_SIZE = 0; /** Default for -blockmaxweight, which controls the range of block weights the mining code will create **/ static const unsigned int DEFAULT_BLOCK_MAX_WEIGHT = 3000000; +/** Default for -blockmintxfee, which sets the minimum feerate for a transaction in blocks created by mining code **/ +static const unsigned int DEFAULT_BLOCK_MIN_TX_FEE = 1000; /** The maximum weight for transactions we're willing to relay/mine */ static const unsigned int MAX_STANDARD_TX_WEIGHT = 400000; /** Maximum number of signature check operations in an IsStandard() P2SH script */ @@ -28,6 +30,8 @@ static const unsigned int MAX_P2SH_SIGOPS = 15; static const unsigned int MAX_STANDARD_TX_SIGOPS_COST = MAX_BLOCK_SIGOPS_COST/5; /** Default for -maxmempool, maximum megabytes of mempool memory usage */ static const unsigned int DEFAULT_MAX_MEMPOOL_SIZE = 300; +/** Default for -incrementalrelayfee, which sets the minimum feerate increase for mempool limiting or BIP 125 replacement **/ +static const unsigned int DEFAULT_INCREMENTAL_RELAY_FEE = 1000; /** Default for -bytespersigop */ static const unsigned int DEFAULT_BYTES_PER_SIGOP = 20; /** The maximum number of witness stack items in a standard P2WSH script */ @@ -36,6 +40,12 @@ static const unsigned int MAX_STANDARD_P2WSH_STACK_ITEMS = 100; static const unsigned int MAX_STANDARD_P2WSH_STACK_ITEM_SIZE = 80; /** The maximum size of a standard witnessScript */ static const unsigned int MAX_STANDARD_P2WSH_SCRIPT_SIZE = 3600; +/** Min feerate for defining dust. Historically this has been the same as the + * minRelayTxFee, however changing the dust limit changes which transactions are + * standard and should be done with care and ideally rarely. It makes sense to + * only increase the dust limit after prior releases were already not creating + * outputs below the new threshold */ +static const unsigned int DUST_RELAY_TX_FEE = 1000; /** * Standard script verification flags that standard transactions will comply * with. However scripts violating these flags may still be present in valid @@ -83,6 +93,8 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) */ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs); +extern CFeeRate incrementalRelayFee; +extern CFeeRate dustRelayFee; extern unsigned int nBytesPerSigOp; /** Compute the virtual transaction size (weight reinterpreted as bytes). */ diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 9d5ff31fb4..a3cb376bcf 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -817,7 +817,7 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVer } else { - QString timeBehindText = GUIUtil::formateNiceTimeOffset(secs); + QString timeBehindText = GUIUtil::formatNiceTimeOffset(secs); progressBarLabel->setVisible(true); progressBar->setFormat(tr("%1 behind").arg(timeBehindText)); diff --git a/src/qt/bitcoinstrings.cpp b/src/qt/bitcoinstrings.cpp index 348e7378ab..64bc7dac03 100644 --- a/src/qt/bitcoinstrings.cpp +++ b/src/qt/bitcoinstrings.cpp @@ -132,14 +132,21 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "Randomize credentials for every proxy connection. This enables Tor stream " "isolation (default: %u)"), QT_TRANSLATE_NOOP("bitcoin-core", "" -"Reduce storage requirements by pruning (deleting) old blocks. This mode is " -"incompatible with -txindex and -rescan. Warning: Reverting this setting " -"requires re-downloading the entire blockchain. (default: 0 = disable pruning " -"blocks, >%u = target size in MiB to use for block files)"), +"Reduce storage requirements by enabling pruning (deleting) of old blocks. " +"This allows the pruneblockchain RPC to be called to delete specific blocks, " +"and enables automatic pruning of old blocks if a target size in MiB is " +"provided. This mode is incompatible with -txindex and -rescan. Warning: " +"Reverting this setting requires re-downloading the entire blockchain. " +"(default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >%u " +"= automatically prune block files to stay under the specified target size in " +"MiB)"), QT_TRANSLATE_NOOP("bitcoin-core", "" "Rescans are not possible in pruned mode. You will need to use -reindex which " "will download the whole blockchain again."), QT_TRANSLATE_NOOP("bitcoin-core", "" +"Set lowest fee rate (in %s/kB) for transactions to be included in block " +"creation. (default: %s)"), +QT_TRANSLATE_NOOP("bitcoin-core", "" "Set maximum size of high-priority/low-fee transactions in bytes (default: %d)"), QT_TRANSLATE_NOOP("bitcoin-core", "" "Set the number of script verification threads (%u to %d, 0 = auto, <0 = " diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 20a04dc8a8..bb10e49422 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -130,7 +130,7 @@ double ClientModel::getVerificationProgress(const CBlockIndex *tipIn) const LOCK(cs_main); tip = chainActive.Tip(); } - return Checkpoints::GuessVerificationProgress(Params().Checkpoints(), tip); + return GuessVerificationProgress(Params().TxData(), tip); } void ClientModel::updateTimer() diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index a0c2813477..f8aba70d92 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -15,7 +15,8 @@ #include "wallet/coincontrol.h" #include "init.h" -#include "validation.h" // For minRelayTxFee +#include "policy/policy.h" +#include "validation.h" // For mempool #include "wallet/wallet.h" #include <boost/assign/list_of.hpp> // for 'map_list_of()' @@ -432,7 +433,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) { CTxOut txout(amount, (CScript)std::vector<unsigned char>(24, 0)); txDummy.vout.push_back(txout); - if (txout.IsDust(::minRelayTxFee)) + if (txout.IsDust(dustRelayFee)) fDust = true; } } @@ -545,10 +546,10 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) if (nChange > 0 && nChange < MIN_CHANGE) { CTxOut txout(nChange, (CScript)std::vector<unsigned char>(24, 0)); - if (txout.IsDust(::minRelayTxFee)) + if (txout.IsDust(dustRelayFee)) { if (CoinControlDialog::fSubtractFeeFromAmount) // dust-change will be raised until no dust - nChange = txout.GetDustThreshold(::minRelayTxFee); + nChange = txout.GetDustThreshold(dustRelayFee); else { nPayFee += nChange; diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 4463cfdaef..ea80a1f549 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -11,7 +11,7 @@ #include "primitives/transaction.h" #include "init.h" -#include "validation.h" // For minRelayTxFee +#include "policy/policy.h" #include "protocol.h" #include "script/script.h" #include "script/standard.h" @@ -257,7 +257,7 @@ bool isDust(const QString& address, const CAmount& amount) CTxDestination dest = CBitcoinAddress(address.toStdString()).Get(); CScript script = GetScriptForDestination(dest); CTxOut txOut(amount, script); - return txOut.IsDust(::minRelayTxFee); + return txOut.IsDust(dustRelayFee); } QString HtmlEscape(const QString& str, bool fMultiLine) @@ -951,7 +951,7 @@ QString formatTimeOffset(int64_t nTimeOffset) return QString(QObject::tr("%1 s")).arg(QString::number((int)nTimeOffset, 10)); } -QString formateNiceTimeOffset(qint64 secs) +QString formatNiceTimeOffset(qint64 secs) { // Represent time from last generated block in human readable text QString timeBehindText; diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index b2c9338b56..5765b2ec4a 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -200,7 +200,7 @@ namespace GUIUtil /* Format a CNodeCombinedStats.nTimeOffset into a user-readable string. */ QString formatTimeOffset(int64_t nTimeOffset); - QString formateNiceTimeOffset(qint64 secs); + QString formatNiceTimeOffset(qint64 secs); class ClickableLabel : public QLabel { diff --git a/src/qt/locale/bitcoin_cs.ts b/src/qt/locale/bitcoin_cs.ts index e0f4a18994..3b5d7947f6 100644 --- a/src/qt/locale/bitcoin_cs.ts +++ b/src/qt/locale/bitcoin_cs.ts @@ -3706,10 +3706,6 @@ <translation>Použít samostatnou SOCKS5 proxy ke spojení s protějšky přes skryté služby v Toru (výchozí: %s)</translation> </message> <message> - <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</source> - <translation>Uživatelské jméno a zahašované heslo pro JSON-RPC spojení. Pole <userpw> má formát: <UŽIVATELSKÉ_JMÉNO>:<SŮL>$<HAŠ>. Pomocný pythonní skript je přiložen v share/rpcuser. Tuto volbu lze použít i vícekrát</translation> - </message> - <message> <source>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</source> <translation>Upozornění: Síť těží neznámé verze bloků! Je možné, že jsou v platnosti neznámá pravidla</translation> </message> diff --git a/src/qt/locale/bitcoin_da.ts b/src/qt/locale/bitcoin_da.ts index 9fc52efe57..0b49b5aefc 100644 --- a/src/qt/locale/bitcoin_da.ts +++ b/src/qt/locale/bitcoin_da.ts @@ -2205,6 +2205,14 @@ <translation>Advarsel: Ukendt byttepengeadresse</translation> </message> <message> + <source>Confirm custom change address</source> + <translation>Bekræft tilpasset byttepengeadresse</translation> + </message> + <message> + <source>The address you selected for change is not part of this wallet. Any or all funds in your wallet may be sent to this address. Are you sure?</source> + <translation>Den adresse, du har valgt til byttepenge, er ikke en del af denne tegnebog. Nogle af eller alle penge i din tegnebog kan blive sendt til denne adresse. Er du sikker?</translation> + </message> + <message> <source>(no label)</source> <translation>(ingen mærkat)</translation> </message> @@ -3126,6 +3134,10 @@ <translation>Brug UPnP for at konfigurere den lyttende port (standard: 1 under lytning og ingen -proxy)</translation> </message> <message> + <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. The client then connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> pair of arguments. This option can be specified multiple times</source> + <translation>Brugernavn og hashet adgangskode for JSON-RPC-forbindelser. Feltet <userpw> er i formatet: <BRUGERNAVN>:<SALT>$<HASH>. Et kanonisk Python-skript er inkluderet i share/rpcuser. Klienten forbinder så normalt ved hjælp af argumentparret rpcuser=<BRUGERNAVN>/rpcpassword=<ADGANGSKODE>. Dette tilvalg kan angives flere gange</translation> + </message> + <message> <source>Wallet will not create transactions that violate mempool chain limits (default: %u)</source> <translation>Tegnebogen vil ikke oprette transaktioner, som overtræder begrænsningen for hukommelsespuljekæden (standard: %u)</translation> </message> @@ -3706,10 +3718,6 @@ <translation>Brug separat SOCS5-proxy for at nå knuder via skjulte Tor-tjenester (standard: %s)</translation> </message> <message> - <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</source> - <translation>Brugernavn og hashet adgangskode for JSON-RPC-forbindelser. Feltet <userpw> er i formatet: <BRUGERNAVN>:<SALT>$<HASH>. Et kanonisk Python-skript inkluderes i share/rpcuser. Dette tilvalg kan angives flere gange</translation> - </message> - <message> <source>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</source> <translation>Advarsel: Ukendte blokversioner bliver minet! Det er muligt, at ukendte regler er i brug</translation> </message> diff --git a/src/qt/locale/bitcoin_de.ts b/src/qt/locale/bitcoin_de.ts index 25d3f45163..1dfd77cf61 100644 --- a/src/qt/locale/bitcoin_de.ts +++ b/src/qt/locale/bitcoin_de.ts @@ -2590,6 +2590,10 @@ <translation>Gesamte Transaktionsgröße</translation> </message> <message> + <source>Output index</source> + <translation>Ausgabeindex</translation> + </message> + <message> <source>Merchant</source> <translation>Händler</translation> </message> @@ -3662,10 +3666,6 @@ <translation>Separaten SOCKS5-Proxy verwenden, um Gegenstellen über versteckte Tor-Dienste zu erreichen (Standard: %s)</translation> </message> <message> - <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</source> - <translation>Benutzername und gehashtes Passwort für JSON-RPC Verbindungen. Das Feld <userpw> kommt im Format: <USERNAME>:<SALT>$<HASH>. Ein entsprechendes Pythonskript ist in share/rpcuser inbegriffen. Diese Option kann mehrere Male spezifiziert werden</translation> - </message> - <message> <source>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</source> <translation>Warnung: Unbekannte Blockversion wird durch Mining erzeugt! Es ist möglich, dass unbekannte Regeln in Kraft sind.</translation> </message> diff --git a/src/qt/locale/bitcoin_en.ts b/src/qt/locale/bitcoin_en.ts index 02a6888998..60dcc9f701 100644 --- a/src/qt/locale/bitcoin_en.ts +++ b/src/qt/locale/bitcoin_en.ts @@ -778,7 +778,7 @@ <translation type="unfinished">Confirmed</translation> </message> <message> - <location filename="../coincontroldialog.cpp" line="+54"/> + <location filename="../coincontroldialog.cpp" line="+55"/> <source>Copy address</source> <translation type="unfinished"></translation> </message> @@ -3862,7 +3862,7 @@ <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+304"/> + <location filename="../bitcoinstrings.cpp" line="+311"/> <source>Options:</source> <translation>Options:</translation> </message> @@ -3887,7 +3887,7 @@ <translation>Accept command line and JSON-RPC commands</translation> </message> <message> - <location line="-207"/> + <location line="-214"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect/-noconnect)</source> <translation type="unfinished"></translation> </message> @@ -3917,17 +3917,12 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+9"/> - <source>Reduce storage requirements by pruning (deleting) old blocks. This mode is incompatible with -txindex and -rescan. Warning: Reverting this setting requires re-downloading the entire blockchain. (default: 0 = disable pruning blocks, >%u = target size in MiB to use for block files)</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+5"/> + <location line="+18"/> <source>Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again.</source> <translation type="unfinished"></translation> </message> <message> - <location line="+129"/> + <location line="+132"/> <source>Error: A fatal internal error occurred, see debug.log for details</source> <translation type="unfinished"></translation> </message> @@ -3952,7 +3947,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="-346"/> + <location line="-353"/> <source>Bitcoin Core</source> <translation type="unfinished">Bitcoin Core</translation> </message> @@ -4022,7 +4017,17 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+24"/> + <location line="+14"/> + <source>Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks, and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -txindex and -rescan. Warning: Reverting this setting requires re-downloading the entire blockchain. (default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >%u = automatically prune block files to stay under the specified target size in MiB)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+12"/> + <source>Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+5"/> <source>Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)</source> <translation type="unfinished"></translation> </message> @@ -4397,7 +4402,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="-344"/> + <location line="-351"/> <source>Allow JSON-RPC connections from specified source. Valid for <ip> are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). This option can be specified multiple times</source> <translation type="unfinished"></translation> </message> @@ -4457,7 +4462,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+11"/> + <location line="+18"/> <source>Set maximum size of high-priority/low-fee transactions in bytes (default: %d)</source> <translation type="unfinished"></translation> </message> @@ -4662,12 +4667,12 @@ <translation>Password for JSON-RPC connections</translation> </message> <message> - <location line="-228"/> + <location line="-235"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation>Execute command when the best block changes (%s in cmd is replaced by block hash)</translation> </message> <message> - <location line="+156"/> + <location line="+163"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation>Allow DNS lookups for -addnode, -seednode and -connect</translation> </message> @@ -4677,7 +4682,7 @@ <translation>Loading addresses...</translation> </message> <message> - <location line="-277"/> + <location line="-284"/> <source>(1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data)</source> <translation type="unfinished"></translation> </message> @@ -4732,7 +4737,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+19"/> + <location line="+26"/> <source>Sets the serialization of raw transaction or block hex returned in non-verbose mode, non-segwit(0) or segwit(1) (default: %d)</source> <translation type="unfinished"></translation> </message> diff --git a/src/qt/locale/bitcoin_en_GB.ts b/src/qt/locale/bitcoin_en_GB.ts index e2944030c7..1676f67979 100644 --- a/src/qt/locale/bitcoin_en_GB.ts +++ b/src/qt/locale/bitcoin_en_GB.ts @@ -2514,10 +2514,6 @@ <translation>Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: %s)</translation> </message> <message> - <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</source> - <translation>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</translation> - </message> - <message> <source>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</source> <translation>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</translation> </message> diff --git a/src/qt/locale/bitcoin_es.ts b/src/qt/locale/bitcoin_es.ts index 435ea110cf..5ba44a954c 100644 --- a/src/qt/locale/bitcoin_es.ts +++ b/src/qt/locale/bitcoin_es.ts @@ -1360,7 +1360,15 @@ <source>Node/Service</source> <translation>Nodo/Servicio</translation> </message> - </context> + <message> + <source>NodeId</source> + <translation>ID de nodo</translation> + </message> + <message> + <source>Ping</source> + <translation>Sonido</translation> + </message> +</context> <context> <name>QObject</name> <message> @@ -1399,14 +1407,54 @@ <source>%1 ms</source> <translation>%1 ms</translation> </message> + <message numerus="yes"> + <source>%n second(s)</source> + <translation><numerusform>%n segundo</numerusform><numerusform>%n segundos</numerusform></translation> + </message> + <message numerus="yes"> + <source>%n minute(s)</source> + <translation><numerusform>%n minuto</numerusform><numerusform>%n minutos</numerusform></translation> + </message> + <message numerus="yes"> + <source>%n hour(s)</source> + <translation><numerusform>%n hora</numerusform><numerusform>%n horas</numerusform></translation> + </message> + <message numerus="yes"> + <source>%n day(s)</source> + <translation><numerusform>%n dia</numerusform><numerusform>%n dias</numerusform></translation> + </message> + <message numerus="yes"> + <source>%n week(s)</source> + <translation><numerusform>%n semana</numerusform><numerusform>%n semanas</numerusform></translation> + </message> <message> <source>%1 and %2</source> <translation>%1 y %2</translation> </message> - </context> + <message numerus="yes"> + <source>%n year(s)</source> + <translation><numerusform>%n año</numerusform><numerusform>%n años</numerusform></translation> + </message> + <message> + <source>%1 didn't yet exit safely...</source> + <translation>%1 no se ha cerrado de forma segura todavía...</translation> + </message> +</context> <context> <name>QObject::QObject</name> - </context> + <message> + <source>Error: Specified data directory "%1" does not exist.</source> + <translation>Error: El directorio de datos «%1» especificado no existe.</translation> + </message> + <message> + <source>Error: Cannot parse configuration file: %1. Only use key=value syntax.</source> + <translation>Error: No se puede analizar el archivo de configuración: %1. Utilice únicamente la sintaxis clave=valor.</translation> + </message> + <message> + <source>Error: %1</source> + <translation>Error: %1</translation> + </message> +</context> <context> <name>QRImageWidget</name> <message> @@ -1585,6 +1633,10 @@ <translation>Espera de Ping</translation> </message> <message> + <source>Min Ping</source> + <translation>Sonido Mínimo</translation> + </message> + <message> <source>Time Offset</source> <translation>Desplazamiento de tiempo</translation> </message> @@ -1645,6 +1697,14 @@ <translation>1 &año</translation> </message> <message> + <source>&Disconnect</source> + <translation>&Desconectar</translation> + </message> + <message> + <source>Ban for</source> + <translation>Prohibir para</translation> + </message> + <message> <source>Welcome to the %1 RPC console.</source> <translation>Bienvenido a la consola RPC %1.</translation> </message> @@ -3547,10 +3607,6 @@ <translation>Usar distintos proxys SOCKS5 para comunicarse vía Tor de forma anónima (Por defecto: %s)</translation> </message> <message> - <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</source> - <translation>Nombre de usuario y hash de la contraseña para las conexiones JSON-RPC. El campo <userpw> tiene el formato: <USERNAME>:<SALT>$<HASH>. Se incluye un script python convencional en share/rpcuser. Esta opción puede ser especificada multiples veces</translation> - </message> - <message> <source>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</source> <translation>Advertencia: Se están minando versiones de bloques desconocidas! Es posible que normas desconocidas estén activas</translation> </message> diff --git a/src/qt/locale/bitcoin_es_ES.ts b/src/qt/locale/bitcoin_es_ES.ts index 5ac57a5c25..c971d664d8 100644 --- a/src/qt/locale/bitcoin_es_ES.ts +++ b/src/qt/locale/bitcoin_es_ES.ts @@ -3483,10 +3483,6 @@ <translation>Usar distintos proxys SOCKS5 para comunicarse vía Tor de forma anónima (Por defecto: %s)</translation> </message> <message> - <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</source> - <translation>Nombre de usuario y hash de la contraseña para las conexiones JSON-RPC. El campo <userpw> tiene el formato: <USERNAME>:<SALT>$<HASH>. Se incluye un script python convencional en share/rpcuser. Esta opción puede ser especificada multiples veces</translation> - </message> - <message> <source>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</source> <translation>Advertencia: Se están minando versiones de bloques desconocidas! Es posible que normas desconocidas estén activas</translation> </message> diff --git a/src/qt/locale/bitcoin_fr.ts b/src/qt/locale/bitcoin_fr.ts index bdcfed9584..eef88b43a3 100644 --- a/src/qt/locale/bitcoin_fr.ts +++ b/src/qt/locale/bitcoin_fr.ts @@ -3706,10 +3706,6 @@ <translation>Utiliser un serveur mandataire SOCKS5 séparé pour atteindre les pairs par les services cachés de Tor (par défaut : %s)</translation> </message> <message> - <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</source> - <translation>Nom d'utilisateur et mot de passe haché pour les connexions JSON-RPC. Le champ <userpw> vient au format : <USERNAME>:<SALT>$<HASH>. Un script python canonique est inclus dans share/rpcuser. Cette option peut être spécifiée plusieurs fois.</translation> - </message> - <message> <source>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</source> <translation>Avertissement : des versions de blocs inconnues sont minées ! Il est possible que des règles inconnues soient en vigeur</translation> </message> diff --git a/src/qt/locale/bitcoin_it.ts b/src/qt/locale/bitcoin_it.ts index 265120b19f..76cfdfafa5 100644 --- a/src/qt/locale/bitcoin_it.ts +++ b/src/qt/locale/bitcoin_it.ts @@ -2583,10 +2583,6 @@ Per specificare più URL separarli con una barra verticale "|".</translation> <translation>Usa un proxy SOCKS5 a parte per raggiungere i peer attraverso gli hidden services di Tor (predefinito: %s)</translation> </message> <message> - <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</source> - <translation>Username e hash password per connessioni JSON-RPC. Il campo <userpw> utilizza il formato: <USERNAME>:<SALT>$<HASH>. Uno script python standard è incluso in share/rpcuser. Questa opzione può essere specificata più volte</translation> - </message> - <message> <source>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</source> <translation>Attenzione: si stanno minando versioni sconocsiute di blocchi! E' possibile che siano attive regole sconosciute</translation> </message> diff --git a/src/qt/locale/bitcoin_ja.ts b/src/qt/locale/bitcoin_ja.ts index 548fa26cc1..3b56a76855 100644 --- a/src/qt/locale/bitcoin_ja.ts +++ b/src/qt/locale/bitcoin_ja.ts @@ -3707,10 +3707,6 @@ <translation>Tor 秘匿サービスを通し、別々の SOCKS5 プロキシを用いることでピアに到達する (初期値: %s)</translation> </message> <message> - <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</source> - <translation>JSON-RPC接続時のユーザ名とハッシュ化されたパスワード。<userpw> フィールドのフォーマットは <USERNAME>:<SALT>$<HASH>。標準的な Python スクリプトが share/rpcuser 内に含まれています。このオプションは複数回指定できます。</translation> - </message> - <message> <source>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</source> <translation>警告: 未知のバージョンのブロックが採掘されました。未知のルールが導入された可能性があります</translation> </message> diff --git a/src/qt/locale/bitcoin_ko_KR.ts b/src/qt/locale/bitcoin_ko_KR.ts index 4198b7ea56..a80c6ccc45 100644 --- a/src/qt/locale/bitcoin_ko_KR.ts +++ b/src/qt/locale/bitcoin_ko_KR.ts @@ -2898,10 +2898,6 @@ <translation>Tor 서비스를 이용하여 피어에게 연결하기 위해 분리된 SOCKS5 프록시를 사용 (기본값: %s)</translation> </message> <message> - <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</source> - <translation>JSON-RPC 연결시 사용자 이름과 해시화된 암호문. <userpw> 필드는 <USERNAME>:<SALT>$<HASH> 포멧으로 구성되어 있습니다. 전형적 파이썬 스크립트에선 share/rpcuser가 포함되어 있습니다. 이 옵션은 여러번 지정할 수 있습니다.</translation> - </message> - <message> <source>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</source> <translation>경고: 알려지지 않은 버전의 블록이 채굴되었습니다. 알려지지 않은 규칙이 적용되었을 가능성이 있습니다.</translation> </message> diff --git a/src/qt/locale/bitcoin_nb.ts b/src/qt/locale/bitcoin_nb.ts index 9749db2b1f..f98cdd32d2 100644 --- a/src/qt/locale/bitcoin_nb.ts +++ b/src/qt/locale/bitcoin_nb.ts @@ -2202,10 +2202,6 @@ <translation>Bruk separate SOCKS5 proxyer for å nå noder via Tor skjulte tjenester (standardverdi: %s)</translation> </message> <message> - <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</source> - <translation>Brukernavn og hashet passord for JSON-RPC tilkoblinger. Feltet <userpw> kommer i formatet: <USERNAME>:<SALT>$<HASH>. Et Python-skript er inkludert i share/rpcuser. Dette alternativet kan angis flere ganger</translation> - </message> - <message> <source>(default: %s)</source> <translation>(standardverdi: %s)</translation> </message> diff --git a/src/qt/locale/bitcoin_nl.ts b/src/qt/locale/bitcoin_nl.ts index 3f5b903336..8a09bed2ce 100644 --- a/src/qt/locale/bitcoin_nl.ts +++ b/src/qt/locale/bitcoin_nl.ts @@ -3482,10 +3482,6 @@ <translation>Gebruik een aparte SOCKS5 proxy om verborgen diensten van Tor te bereiken (standaard: %s)</translation> </message> <message> - <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</source> - <translation>Gebruikersnaam en gehasht wachtwoord voor JSON-RPC-verbindingen. De velden <userpw> is in het formaat: <GEBRUIKERSNAAM>:<SALT>$<HASH>. Een kanoniek Pythonscript is inbegrepen in de share/rpcuser. Deze optie kan meerdere keren worden meegegeven</translation> - </message> - <message> <source>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</source> <translation>Waarschuwing: Onbekende blok versies worden gemined! Er zijn mogelijk onbekende regels in werking getreden</translation> </message> diff --git a/src/qt/locale/bitcoin_pt_BR.ts b/src/qt/locale/bitcoin_pt_BR.ts index 3589cdbc1b..4d49399c11 100644 --- a/src/qt/locale/bitcoin_pt_BR.ts +++ b/src/qt/locale/bitcoin_pt_BR.ts @@ -2197,6 +2197,10 @@ <translation>Aviso: Endereço de troco inválido</translation> </message> <message> + <source>Confirm custom change address</source> + <translation>Confirmar endereço de troco personalizado</translation> + </message> + <message> <source>(no label)</source> <translation>(sem rótuo)</translation> </message> @@ -3694,10 +3698,6 @@ <translation>Use um proxy SOCKS5 separado para alcançar participantes da rede via serviços ocultos Tor (padrão: %s)</translation> </message> <message> - <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</source> - <translation>Nome de usuário e senha hash para conexões JSON-RPC. O campo <userpw> vem com o formato: <USERNAME>:<SALT>$<HASH>. Um script python canônico é incluído em share/rpcuser. Essa opção pode ser especificada múltiplas vezes.</translation> - </message> - <message> <source>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</source> <translation>Aviso: Versões de bloco desconhecidas sendo mineradas! É possível que regras estranhas estejam ativas</translation> </message> diff --git a/src/qt/locale/bitcoin_pt_PT.ts b/src/qt/locale/bitcoin_pt_PT.ts index a2a52efd27..7bfb3e710d 100644 --- a/src/qt/locale/bitcoin_pt_PT.ts +++ b/src/qt/locale/bitcoin_pt_PT.ts @@ -43,35 +43,159 @@ </message> <message> <source>Choose the address to send coins to</source> - <translation>Escolhe qual o endereço para o qual enviar moedas</translation> + <translation>Escolha o endereço para enviar as moedas</translation> </message> <message> <source>Choose the address to receive coins with</source> - <translation>Escolhe qual o endereço para receber moedas</translation> + <translation>Escolha o endereço para receber as moedas</translation> </message> - </context> + <message> + <source>C&hoose</source> + <translation>Escol&her</translation> + </message> + <message> + <source>Sending addresses</source> + <translation>A enviar endereços</translation> + </message> + <message> + <source>Receiving addresses</source> + <translation>A receber endereços</translation> + </message> + <message> + <source>&Copy Address</source> + <translation>&Copiar Endereço</translation> + </message> + <message> + <source>Copy &Label</source> + <translation>Copiar &Etiqueta</translation> + </message> + <message> + <source>&Edit</source> + <translation>&Editar</translation> + </message> + <message> + <source>Export Address List</source> + <translation>Exportar Lista de Endereços</translation> + </message> + <message> + <source>Comma separated file (*.csv)</source> + <translation>Ficheiro separado por vírgulas (*.csv)</translation> + </message> + <message> + <source>Exporting Failed</source> + <translation>Exportação Falhou</translation> + </message> + <message> + <source>There was an error trying to save the address list to %1. Please try again.</source> + <translation>Ocorreu um erro ao tentar guardar a lista de endereços para %1. Por favor, tente novamente.</translation> + </message> +</context> <context> <name>AddressTableModel</name> - </context> + <message> + <source>Label</source> + <translation>Etiqueta</translation> + </message> + <message> + <source>Address</source> + <translation>Endereço</translation> + </message> + <message> + <source>(no label)</source> + <translation>(sem etiqueta)</translation> + </message> +</context> <context> <name>AskPassphraseDialog</name> <message> <source>Passphrase Dialog</source> - <translation>Janela da frase de palavra-passe</translation> + <translation>Janela da Frase de Segurança</translation> </message> <message> <source>Enter passphrase</source> - <translation>Insira a frase de palavra-passe</translation> + <translation>Insira a frase de segurança</translation> </message> <message> <source>New passphrase</source> - <translation>Nova frase de palavra-passe</translation> + <translation>Nova frase de frase de segurança</translation> </message> <message> <source>Repeat new passphrase</source> - <translation>Repita a nova frase de palavra-passe</translation> + <translation>Repita a nova frase de frase de segurança</translation> </message> - </context> + <message> + <source>Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>ten or more random characters</b>, or <b>eight or more words</b>.</source> + <translation>Insira a nova frase de segurança para a carteira. <br/> Por favor, utilize uma frase de segurança de <b>10 ou mais carateres aleatórios,</b> ou <b>oito ou mais palavras</b>.</translation> + </message> + <message> + <source>Encrypt wallet</source> + <translation>Encriptar carteira</translation> + </message> + <message> + <source>This operation needs your wallet passphrase to unlock the wallet.</source> + <translation>Esta operação precisa da sua frase de segurança da carteira para desbloquear a mesma.</translation> + </message> + <message> + <source>Unlock wallet</source> + <translation>Desbloquear carteira</translation> + </message> + <message> + <source>This operation needs your wallet passphrase to decrypt the wallet.</source> + <translation>Esta operação precisa da sua frase de segurança da carteira para desencriptar a mesma.</translation> + </message> + <message> + <source>Decrypt wallet</source> + <translation>Desencriptar carteira</translation> + </message> + <message> + <source>Change passphrase</source> + <translation>Alterar frase de segurança</translation> + </message> + <message> + <source>Enter the old passphrase and new passphrase to the wallet.</source> + <translation>Insira a frase de segurança antiga e a nova frase de segurança para a carteira.</translation> + </message> + <message> + <source>Confirm wallet encryption</source> + <translation>Confirmar encriptação da carteira</translation> + </message> + <message> + <source>Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR BITCOINS</b>!</source> + <translation>Aviso: se encriptar a sua carteira e perder a sua frase de segurnça, <b>PERDERÁ TODOS OS SEUS BITCOINS</b>!</translation> + </message> + <message> + <source>Are you sure you wish to encrypt your wallet?</source> + <translation>Tem a certeza que deseja encriptar a sua carteira?</translation> + </message> + <message> + <source>Wallet encrypted</source> + <translation>Carteira encriptada</translation> + </message> + <message> + <source>Wallet encryption failed</source> + <translation>Encriptação da carteira falhou</translation> + </message> + <message> + <source>The supplied passphrases do not match.</source> + <translation>As frases de segurança fornecidas não coincidem.</translation> + </message> + <message> + <source>Wallet unlock failed</source> + <translation>Desbloqueio da carteira falhou</translation> + </message> + <message> + <source>Wallet decryption failed</source> + <translation>Desencriptação da carteira falhou</translation> + </message> + <message> + <source>Wallet passphrase was successfully changed.</source> + <translation>A frase de segurança da carteira foi alterada com sucesso.</translation> + </message> + <message> + <source>Warning: The Caps Lock key is on!</source> + <translation>Aviso: a tecla Caps Lock está ligada!</translation> + </message> +</context> <context> <name>BanTableModel</name> <message> @@ -95,7 +219,7 @@ </message> <message> <source>&Overview</source> - <translation>&Sinopse</translation> + <translation>&Resumo</translation> </message> <message> <source>Node</source> @@ -103,7 +227,7 @@ </message> <message> <source>Show general overview of wallet</source> - <translation>Mostrar sinopse geral da carteira</translation> + <translation>Mostrar resumo geral da carteira</translation> </message> <message> <source>&Transactions</source> @@ -123,19 +247,19 @@ </message> <message> <source>&About %1</source> - <translation>&Acerca de %1</translation> + <translation>&Sobre o %1</translation> </message> <message> <source>Show information about %1</source> - <translation>Mostrar informação sobre %1</translation> + <translation>Mostrar informação sobre o %1</translation> </message> <message> <source>About &Qt</source> - <translation>Sobre &Qt</translation> + <translation>Sobre o &Qt</translation> </message> <message> <source>Show information about Qt</source> - <translation>Mostrar informação sobre Qt</translation> + <translation>Mostrar informação sobre o Qt</translation> </message> <message> <source>&Options...</source> @@ -151,11 +275,11 @@ </message> <message> <source>&Backup Wallet...</source> - <translation>&Guardar Carteira...</translation> + <translation>Efetuar &Cópia de Segurança da Carteira...</translation> </message> <message> <source>&Change Passphrase...</source> - <translation>Alterar &Frase de Palavra-passe...</translation> + <translation>Alterar &Frase de Segurança...</translation> </message> <message> <source>&Sending addresses...</source> @@ -170,12 +294,24 @@ <translation>Abrir &URI...</translation> </message> <message> + <source>Click to disable network activity.</source> + <translation>Clique para desativar a atividade de rede.</translation> + </message> + <message> + <source>Network activity disabled.</source> + <translation>Atividade de rede desativada.</translation> + </message> + <message> + <source>Click to enable network activity again.</source> + <translation>Clique para ativar novamente a atividade de rede.</translation> + </message> + <message> <source>Reindexing blocks on disk...</source> <translation>A reindexar os blocos no disco...</translation> </message> <message> <source>Send coins to a Bitcoin address</source> - <translation>Enviar moedas para um endereço bitcoin</translation> + <translation>Enviar moedas para um endereço Bitcoin</translation> </message> <message> <source>Backup wallet to another location</source> @@ -183,11 +319,11 @@ </message> <message> <source>Change the passphrase used for wallet encryption</source> - <translation>Alterar a frase de palavra-passe utilizada na encriptação da carteira</translation> + <translation>Alterar a frase de segurança utilizada na encriptação da carteira</translation> </message> <message> <source>&Debug window</source> - <translation>Janela de &depuração</translation> + <translation>Janela de &Depuração</translation> </message> <message> <source>Open debugging and diagnostic console</source> @@ -227,7 +363,7 @@ </message> <message> <source>Sign messages with your Bitcoin addresses to prove you own them</source> - <translation>Assine mensagens com os seus endereços Bitcoin para provar que os controla</translation> + <translation>Assine as mensagens com os seus endereços Bitcoin para provar que é o proprietário dos mesmos</translation> </message> <message> <source>Verify messages to ensure they were signed with specified Bitcoin addresses</source> @@ -318,6 +454,10 @@ <translation>Atualizado</translation> </message> <message> + <source>%1 client</source> + <translation>Cliente %1</translation> + </message> + <message> <source>Catching up...</source> <translation>Recuperando o atraso...</translation> </message> @@ -360,6 +500,14 @@ <translation>Transação recebida</translation> </message> <message> + <source>HD key generation is <b>enabled</b></source> + <translation>Criação de chave HD está <b>ativada</b></translation> + </message> + <message> + <source>HD key generation is <b>disabled</b></source> + <translation>Criação de chave HD está <b>desativada</b></translation> + </message> + <message> <source>Wallet is <b>encrypted</b> and currently <b>unlocked</b></source> <translation>A carteira está <b>encriptada</b> e atualmente <b>desbloqueada</b></translation> </message> @@ -438,7 +586,75 @@ <source>Confirmed</source> <translation>Confirmada</translation> </message> - </context> + <message> + <source>Copy address</source> + <translation>Copiar endereço</translation> + </message> + <message> + <source>Copy label</source> + <translation>Copiar etiqueta</translation> + </message> + <message> + <source>Copy amount</source> + <translation>Copiar valor</translation> + </message> + <message> + <source>Copy transaction ID</source> + <translation>Copiar Id. da transação</translation> + </message> + <message> + <source>Lock unspent</source> + <translation>Bloquear não gasto</translation> + </message> + <message> + <source>Unlock unspent</source> + <translation>Desbloquear não gasto</translation> + </message> + <message> + <source>Copy quantity</source> + <translation>Copiar quantidade</translation> + </message> + <message> + <source>Copy fee</source> + <translation>Copiar taxa</translation> + </message> + <message> + <source>Copy after fee</source> + <translation>Copiar depois da taxa</translation> + </message> + <message> + <source>Copy bytes</source> + <translation>Copiar bytes</translation> + </message> + <message> + <source>Copy change</source> + <translation>Copiar troco</translation> + </message> + <message> + <source>(%1 locked)</source> + <translation>(%1 bloqueado)</translation> + </message> + <message> + <source>yes</source> + <translation>sim</translation> + </message> + <message> + <source>no</source> + <translation>não</translation> + </message> + <message> + <source>(no label)</source> + <translation>(sem etiqueta)</translation> + </message> + <message> + <source>change from %1 (%2)</source> + <translation>troco de %1 (%2)</translation> + </message> + <message> + <source>(change)</source> + <translation>(troco)</translation> + </message> +</context> <context> <name>EditAddressDialog</name> <message> @@ -461,12 +677,20 @@ <source>&Address</source> <translation>E&ndereço</translation> </message> - </context> + <message> + <source>Could not unlock wallet.</source> + <translation>Não foi possível desbloquear a carteira.</translation> + </message> + <message> + <source>New key generation failed.</source> + <translation>A criação da nova chave falhou.</translation> + </message> +</context> <context> <name>FreespaceChecker</name> <message> <source>A new data directory will be created.</source> - <translation>Irá ser criada uma nova diretoria de dados.</translation> + <translation>Será criada uma nova diretoria de dados.</translation> </message> <message> <source>name</source> @@ -478,7 +702,7 @@ </message> <message> <source>Path already exists, and is not a directory.</source> - <translation>Caminho já existe, e não é uma pasta.</translation> + <translation>O caminho já existe, e este não é uma pasta.</translation> </message> <message> <source>Cannot create data directory here.</source> @@ -497,7 +721,7 @@ </message> <message> <source>About %1</source> - <translation>Sobre %1</translation> + <translation>Sobre o %1</translation> </message> <message> <source>Command-line options</source> @@ -517,11 +741,11 @@ </message> <message> <source>Choose data directory on startup (default: %u)</source> - <translation>Escolha a diretoria dos dados no arranque (predefinição: %u)</translation> + <translation>Escolher a pasta de dados no arranque (predefinição: %u)</translation> </message> <message> <source>Set language, for example "de_DE" (default: system locale)</source> - <translation>Definir linguagem, por exemplo "pt_PT" (por defeito: linguagem do sistema)</translation> + <translation>Definir idioma, por exemplo "pt_PT" (predefinição: idioma do sistema)</translation> </message> <message> <source>Start minimized</source> @@ -529,13 +753,17 @@ </message> <message> <source>Set SSL root certificates for payment request (default: -system-)</source> - <translation>Configurar certificados SSL root para pedido de pagamento (default: -system-)</translation> + <translation>Definir certificados de raiz SSL para pedidos de pagamento (predefinição: -system-)</translation> </message> <message> <source>Show splash screen on startup (default: %u)</source> <translation>Mostrar o ecrã de abertura no arranque (predefinição: %u)</translation> </message> - </context> + <message> + <source>Reset all settings changed in the GUI</source> + <translation>Redefinir todas as definições alteradas na GUI</translation> + </message> +</context> <context> <name>Intro</name> <message> @@ -544,19 +772,19 @@ </message> <message> <source>Welcome to %1.</source> - <translation>Bem-vindo a %1.</translation> + <translation>Bem-vindo ao %1.</translation> </message> <message> <source>Use the default data directory</source> - <translation>Utilizar a diretoria de dados predefinida</translation> + <translation>Utilizar a pasta de dados predefinida</translation> </message> <message> <source>Use a custom data directory:</source> - <translation>Utilizar uma diretoria de dados personalizada:</translation> + <translation>Utilizar uma pasta de dados personalizada:</translation> </message> <message> <source>Error: Specified data directory "%1" cannot be created.</source> - <translation>Erro: não pode ser criada a diretoria de dados especificada como "%1.</translation> + <translation>Erro: não pode ser criada a pasta de dados especificada como "%1.</translation> </message> <message> <source>Error</source> @@ -578,14 +806,38 @@ <translation>Formulário</translation> </message> <message> + <source>Number of blocks left</source> + <translation>Número de blocos restantes</translation> + </message> + <message> + <source>Unknown...</source> + <translation>Desconhecido...</translation> + </message> + <message> <source>Last block time</source> <translation>Data do último bloco</translation> </message> <message> + <source>Progress</source> + <translation>Progresso</translation> + </message> + <message> + <source>calculating...</source> + <translation>a calcular...</translation> + </message> + <message> + <source>Estimated time left until synced</source> + <translation>tempo restante estimado até à sincronização</translation> + </message> + <message> <source>Hide</source> - <translation>Esconder</translation> + <translation>Ocultar</translation> </message> - </context> + <message> + <source>Unknown. Syncing Headers (%1)...</source> + <translation>Desconhecido. Sincronização de Cabeçalhos (%1)...</translation> + </message> +</context> <context> <name>OpenURIDialog</name> <message> @@ -602,9 +854,13 @@ </message> <message> <source>Select payment request file</source> - <translation>Seleccione o ficheiro de pedido de pagamento</translation> + <translation>Selecione o ficheiro de pedido de pagamento</translation> </message> - </context> + <message> + <source>Select payment request file to open</source> + <translation>Selecione o ficheiro de pedido de pagamento para abrir</translation> + </message> +</context> <context> <name>OptionsDialog</name> <message> @@ -617,7 +873,7 @@ </message> <message> <source>Automatically start %1 after logging in to the system.</source> - <translation>Começar o %1 automaticamente ao iniciar a sessão no sistema.</translation> + <translation>Iniciar automaticamente o %1 depois de iniciar a sessão no sistema.</translation> </message> <message> <source>&Start %1 on system login</source> @@ -645,7 +901,7 @@ </message> <message> <source>IP address of the proxy (e.g. IPv4: 127.0.0.1 / IPv6: ::1)</source> - <translation>Endereço IP do proxy (p.ex. IPv4: 127.0.0.1 / IPv6: ::1)</translation> + <translation>Endereço de IP do proxy (exemplo, IPv4: 127.0.0.1 / IPv6: ::1)</translation> </message> <message> <source>Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Exit in the menu.</source> @@ -686,15 +942,15 @@ </message> <message> <source>Expert</source> - <translation> Especialistas </translation> + <translation> Técnicos</translation> </message> <message> <source>Enable coin &control features</source> - <translation>Ativar as funcionalidades de controlo de moedas</translation> + <translation>Ativar as funcionalidades de &controlo de moedas</translation> </message> <message> <source>If you disable the spending of unconfirmed change, the change from a transaction cannot be used until that transaction has at least one confirmation. This also affects how your balance is computed.</source> - <translation>No caso de desativar o gasto de troco não confirmado, o troco de uma transação não poderá ser utilizado até que essa transação tenha pelo menos uma confirmação. Isto também afeta o cálculo do seu saldo.</translation> + <translation>Se desativar o gasto de troco não confirmado, o troco de uma transação não pode ser utilizado até que essa transação tenha pelo menos uma confirmação. Isto também afeta o cálculo do seu saldo.</translation> </message> <message> <source>&Spend unconfirmed change</source> @@ -761,6 +1017,10 @@ <translation>&Janela</translation> </message> <message> + <source>&Hide the icon from the system tray.</source> + <translation>&Ocultar o ícone da bandeja do sistema.</translation> + </message> + <message> <source>Hide tray icon</source> <translation>Ocultar ícone da bandeja</translation> </message> @@ -826,7 +1086,7 @@ </message> <message> <source>This change would require a client restart.</source> - <translation>Esta alteração requer um reinício do cliente.</translation> + <translation>Esta alteração obrigará a um reinício do cliente.</translation> </message> <message> <source>The supplied proxy address is invalid.</source> @@ -910,6 +1170,34 @@ </context> <context> <name>PaymentServer</name> + <message> + <source>Payment request error</source> + <translation>Erro do pedido de pagamento</translation> + </message> + <message> + <source>Payment request rejected</source> + <translation>Pedido de pagamento rejeitado</translation> + </message> + <message> + <source>Payment request expired.</source> + <translation>Pedido de pagamento expirado.</translation> + </message> + <message> + <source>Payment request is not initialized.</source> + <translation>O pedido de pagamento não foi inicializado.</translation> + </message> + <message> + <source>Invalid payment request.</source> + <translation>Pedido de pagamento inválido.</translation> + </message> + <message> + <source>Refund from %1</source> + <translation>Reembolso de %1</translation> + </message> + <message> + <source>Error communicating with %1: %2</source> + <translation>Erro ao comunicar com %1: %2</translation> + </message> </context> <context> <name>PeerTableModel</name> @@ -1312,7 +1600,15 @@ <source>Remove</source> <translation>Remover</translation> </message> - </context> + <message> + <source>Copy label</source> + <translation>Copiar etiqueta</translation> + </message> + <message> + <source>Copy amount</source> + <translation>Copiar valor</translation> + </message> +</context> <context> <name>ReceiveRequestDialog</name> <message> @@ -1332,12 +1628,28 @@ <translation>&Salvar Imagem...</translation> </message> <message> + <source>Address</source> + <translation>Endereço</translation> + </message> + <message> <source>Amount</source> <translation>Valor</translation> </message> + <message> + <source>Label</source> + <translation>Etiqueta</translation> + </message> </context> <context> <name>RecentRequestsTableModel</name> + <message> + <source>Label</source> + <translation>Etiqueta</translation> + </message> + <message> + <source>(no label)</source> + <translation>(sem etiqueta)</translation> + </message> </context> <context> <name>SendCoinsDialog</name> @@ -1387,7 +1699,7 @@ </message> <message> <source>If this is activated, but the change address is empty or invalid, change will be sent to a newly generated address.</source> - <translation>Se isto estiver ativo, mas o endereço de troco estiver vazio ou for inválido, o troco irá ser enviado para um novo endereço.</translation> + <translation>Se isto estiver ativo, mas o endereço de troco estiver vazio ou for inválido, o troco será enviado para um novo endereço gerado.</translation> </message> <message> <source>Custom change address</source> @@ -1395,7 +1707,7 @@ </message> <message> <source>Transaction Fee:</source> - <translation>Custo da Transação:</translation> + <translation>Taxa da transação:</translation> </message> <message> <source>Choose...</source> @@ -1403,7 +1715,7 @@ </message> <message> <source>collapse fee-settings</source> - <translation>fechar definições-de custos</translation> + <translation>ocultar definições de taxa</translation> </message> <message> <source>per kilobyte</source> @@ -1411,7 +1723,7 @@ </message> <message> <source>If the custom fee is set to 1000 satoshis and the transaction is only 250 bytes, then "per kilobyte" only pays 250 satoshis in fee, while "total at least" pays 1000 satoshis. For transactions bigger than a kilobyte both pay by kilobyte.</source> - <translation>Se a taxa fixa for 1000 satoshis e a transação for somente 250 bytes, pagará somente 250 satoshis "por kilobyte" em custos se trasacionar "pelo menos" 1000 satoshis. Transações superiores a um kilobyte são cobradas por kilobyte.</translation> + <translation>Se a taxa personalizada estiver definida para 1.000 satoshis e a transação é de apenas 250 bytes, então paga apenas 250 satoshis "por kilobyte" na taxa, enquanto em "total pelo menos" paga 1.000 satoshis. Para transações superiores a um kilobyte ambos pagam por kilobyte.</translation> </message> <message> <source>Hide</source> @@ -1439,7 +1751,7 @@ </message> <message> <source>(Smart fee not initialized yet. This usually takes a few blocks...)</source> - <translation>(Taxa inteligente ainda não foi acionada. Normalmente demora alguns blocos...)</translation> + <translation>(A taxa inteligente ainda não foi inicializada. Isto normalmente demora alguns blocos...)</translation> </message> <message> <source>normal</source> @@ -1481,7 +1793,63 @@ <source>S&end</source> <translation>E&nviar</translation> </message> - </context> + <message> + <source>Copy quantity</source> + <translation>Copiar quantidade</translation> + </message> + <message> + <source>Copy amount</source> + <translation>Copiar valor</translation> + </message> + <message> + <source>Copy fee</source> + <translation>Copiar taxa</translation> + </message> + <message> + <source>Copy after fee</source> + <translation>Copiar depois da taxa</translation> + </message> + <message> + <source>Copy bytes</source> + <translation>Copiar bytes</translation> + </message> + <message> + <source>Copy change</source> + <translation>Copiar troco</translation> + </message> + <message> + <source>added as transaction fee</source> + <translation>adicionado como taxa de transação</translation> + </message> + <message> + <source>The total exceeds your balance when the %1 transaction fee is included.</source> + <translation>O total excede o seu saldo quando a taxa de transação %1 está incluída.</translation> + </message> + <message> + <source>A fee higher than %1 is considered an absurdly high fee.</source> + <translation>Uma taxa superior a %1 é considerada uma taxa altamente absurda.</translation> + </message> + <message> + <source>Payment request expired.</source> + <translation>Pedido de pagamento expirado.</translation> + </message> + <message> + <source>Pay only the required fee of %1</source> + <translation>Pague apenas a taxa obrigatória de %1</translation> + </message> + <message> + <source>Warning: Unknown change address</source> + <translation>Aviso: endereço de troco desconhecido</translation> + </message> + <message> + <source>Confirm custom change address</source> + <translation>Confirmar endereço de troco personalizado</translation> + </message> + <message> + <source>(no label)</source> + <translation>(sem etiqueta)</translation> + </message> +</context> <context> <name>SendCoinsEntry</name> <message> @@ -1530,7 +1898,7 @@ </message> <message> <source>S&ubtract fee from amount</source> - <translation>S&ubtrair taxa ao montante</translation> + <translation>S&ubtrair a taxa ao montante</translation> </message> <message> <source>Message:</source> @@ -1679,6 +2047,10 @@ <context> <name>TransactionDesc</name> <message> + <source>Transaction fee</source> + <translation>Taxa de transação</translation> + </message> + <message> <source>Amount</source> <translation>Valor</translation> </message> @@ -1692,9 +2064,49 @@ </context> <context> <name>TransactionTableModel</name> + <message> + <source>Label</source> + <translation>Etiqueta</translation> + </message> + <message> + <source>(no label)</source> + <translation>(sem etiqueta)</translation> + </message> </context> <context> <name>TransactionView</name> + <message> + <source>Copy address</source> + <translation>Copiar endereço</translation> + </message> + <message> + <source>Copy label</source> + <translation>Copiar etiqueta</translation> + </message> + <message> + <source>Copy amount</source> + <translation>Copiar valor</translation> + </message> + <message> + <source>Copy transaction ID</source> + <translation>Copiar Id. da transação</translation> + </message> + <message> + <source>Comma separated file (*.csv)</source> + <translation>Ficheiro separado por vírgulas (*.csv)</translation> + </message> + <message> + <source>Label</source> + <translation>Etiqueta</translation> + </message> + <message> + <source>Address</source> + <translation>Endereço</translation> + </message> + <message> + <source>Exporting Failed</source> + <translation>Exportação Falhou</translation> + </message> </context> <context> <name>UnitDisplayStatusBarControl</name> @@ -1764,7 +2176,7 @@ </message> <message> <source>Fee (in %s/kB) to add to transactions you send (default: %s)</source> - <translation>Taxa (em %s/kB) a adicionar às transacções que envia (padrão: %s)</translation> + <translation>Taxa (em %s/kB) para adicionar às transações que envia (predefinição: %s)</translation> </message> <message> <source>Pruning blockstore...</source> @@ -1800,7 +2212,7 @@ </message> <message> <source>Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)</source> - <translation>Executar comando quando uma das transações na carteira mudar (no comando, %s é substituído pelo ID da Transação)</translation> + <translation>Executar o comando quando uma transação da carteira muda (no comando, %s é substituído pela Id. da Transação)</translation> </message> <message> <source>Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)</source> @@ -2040,7 +2452,7 @@ </message> <message> <source>Fees (in %s/kB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)</source> - <translation>Taxas (em %s/kB) abaixo deste valor são consideradas nulas para propagação, mineração e criação de transacções (padrão: %s)</translation> + <translation>Taxas (em %s/kB) inferiores a este valor são consideradas nulas para propagação, mineração e criação de transações (predefinição: %s)</translation> </message> <message> <source>If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)</source> @@ -2220,7 +2632,7 @@ </message> <message> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> - <translation>Executar comando quando o melhor bloco mudar (no comando, %s é substituído pela hash do bloco)</translation> + <translation>Executar o comando quando o melhor bloco muda (no comando, %s é substituído pela hash do bloco)</translation> </message> <message> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> @@ -2287,10 +2699,6 @@ <translation>Use um proxy SOCKS5 separado para alcançar pares via serviços ocultos do Tor (padrão: %s)</translation> </message> <message> - <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</source> - <translation>Username e hash da password para ligações JSON-RPC. O campo <userpw> está no formato: <USERNAME>:<SALT>$<HASH>. Um script python está incluido em share/rpcuser. Esta opção pode ser especificada múltiplas vezes.</translation> - </message> - <message> <source>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</source> <translation>Atenção: Versões desconhecidas de blocos estão a ser mineradas! É possível que regras desconhecias estão a ser efetuadas</translation> </message> @@ -2372,7 +2780,7 @@ </message> <message> <source>Spend unconfirmed change when sending transactions (default: %u)</source> - <translation>Gastar troco não confirmado ao enviar transacções (padrão: %u)</translation> + <translation>Gastar o troco não confirmado quando enviar transações (predefinição: %u)</translation> </message> <message> <source>Threshold for disconnecting misbehaving peers (default: %u)</source> diff --git a/src/qt/locale/bitcoin_ru.ts b/src/qt/locale/bitcoin_ru.ts index aa30270415..11fa2f7fea 100644 --- a/src/qt/locale/bitcoin_ru.ts +++ b/src/qt/locale/bitcoin_ru.ts @@ -543,7 +543,11 @@ <source>Wallet is <b>encrypted</b> and currently <b>locked</b></source> <translation>Бумажник <b>зашифрован</b> и в настоящее время <b>заблокирован</b></translation> </message> - </context> + <message> + <source>A fatal error occurred. Bitcoin can no longer continue safely and will quit.</source> + <translation>Произошла неисправимая ошибка. Bitcoin не может безопасно продолжать работу и будет закрыт.</translation> + </message> +</context> <context> <name>CoinControlDialog</name> <message> @@ -1431,10 +1435,26 @@ <source>%n year(s)</source> <translation><numerusform>%n год</numerusform><numerusform>%n года</numerusform><numerusform>%n лет</numerusform><numerusform>%n лет</numerusform></translation> </message> - </context> + <message> + <source>%1 didn't yet exit safely...</source> + <translation>%1 ещё не завершился безопасно...</translation> + </message> +</context> <context> <name>QObject::QObject</name> - </context> + <message> + <source>Error: Specified data directory "%1" does not exist.</source> + <translation>Ошибка: указанный каталог "%1" не существует.</translation> + </message> + <message> + <source>Error: Cannot parse configuration file: %1. Only use key=value syntax.</source> + <translation>Ошибка: не удалось разобрать конфигурационный файл: %1. Используйте синтаксис вида ключ=значение.</translation> + </message> + <message> + <source>Error: %1</source> + <translation>Ошибка: %1</translation> + </message> +</context> <context> <name>QRImageWidget</name> <message> @@ -2185,6 +2205,14 @@ <translation>Внимание: неизвестный адрес для сдачи</translation> </message> <message> + <source>Confirm custom change address</source> + <translation>Подтвердите свой адрес для сдачи</translation> + </message> + <message> + <source>The address you selected for change is not part of this wallet. Any or all funds in your wallet may be sent to this address. Are you sure?</source> + <translation>Выбранный вами адрес для сдачи не принадлежит этому кошельку. Часть или все средства могут быть отправлены на этот адрес. Вы уверены?</translation> + </message> + <message> <source>(no label)</source> <translation>(нет метки)</translation> </message> @@ -2445,6 +2473,10 @@ </context> <context> <name>TransactionDesc</name> + <message numerus="yes"> + <source>Open for %n more block(s)</source> + <translation><numerusform>Открыто для ещё %n блока</numerusform><numerusform>Открыто для ещё %n блоков</numerusform><numerusform>Открыто для ещё %n блоков</numerusform><numerusform>Открыто для ещё %n блоков</numerusform></translation> + </message> <message> <source>Open until %1</source> <translation>Открыто до %1</translation> @@ -2533,6 +2565,10 @@ <source>Credit</source> <translation>Кредит</translation> </message> + <message numerus="yes"> + <source>matures in %n more block(s)</source> + <translation><numerusform>будет доступно через %n блок</numerusform><numerusform>будет доступно через %n блока</numerusform><numerusform>будет доступно через %n блоков</numerusform><numerusform>будет доступно через %n блоков</numerusform></translation> + </message> <message> <source>not accepted</source> <translation>не принято</translation> @@ -2635,6 +2671,10 @@ <source>Label</source> <translation>Метка</translation> </message> + <message numerus="yes"> + <source>Open for %n more block(s)</source> + <translation><numerusform>Открыто для ещё %n блока</numerusform><numerusform>Открыто для ещё %n блоков</numerusform><numerusform>Открыто для ещё %n блоков</numerusform><numerusform>Открыто для ещё %n блоков</numerusform></translation> + </message> <message> <source>Open until %1</source> <translation>Открыто до %1</translation> @@ -3078,6 +3118,10 @@ <translation>База данных блоков содержит блок, который появляется из будущего. Это может из-за некорректно установленных даты и времени на вашем компьютере. Остается только перестроивать базу блоков, если вы уверены, что дата и время корректны.</translation> </message> <message> + <source>This is a pre-release test build - use at your own risk - do not use for mining or merchant applications</source> + <translation>Это пре-релизная тестовая сборка - используйте на свой страх и риск - не используйте для добычи или торговых приложений</translation> + </message> + <message> <source>Unable to rewind the database to a pre-fork state. You will need to redownload the blockchain</source> <translation>Невозможно отмотать базу данных до пред-форкового состояния. Вам будет необходимо перекачать цепочку блоков.</translation> </message> @@ -3086,6 +3130,22 @@ <translation>Использовать UPnP для проброса порта (по умолчанию: 1, если используется прослушивание и нет -proxy)</translation> </message> <message> + <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. The client then connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> pair of arguments. This option can be specified multiple times</source> + <translation>Имя пользователя и хэш пароля для JSON-RPC соединений. Поле <userpw> использует формат: <USERNAME>:<SALT>$<HASH>. Каноничный пример скрипта на питоне находится в share/rpcuser. Эта опция может быть указана несколько раз</translation> + </message> + <message> + <source>Wallet will not create transactions that violate mempool chain limits (default: %u)</source> + <translation>Бумажник не будет создавать транзакции, которые нарушают лимиты цепочки пула в памяти (по умолчанию: %u)</translation> + </message> + <message> + <source>Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.</source> + <translation>Внимание: похоже, в сети нет полного согласия! Некоторые майнеры, возможно, испытывают проблемы.</translation> + </message> + <message> + <source>Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.</source> + <translation>Внимание: мы не полностью согласны с подключенными участниками! Вам или другим участникам, возможно, следует обновиться.</translation> + </message> + <message> <source>You need to rebuild the database using -reindex-chainstate to change -txindex</source> <translation>Вам необходимо пересобрать базы данных с помощью -reindex-chainstate, чтобы изменить -txindex</translation> </message> @@ -3118,6 +3178,10 @@ <translation>Не удаётся разрешить адрес в параметре -%s: '%s'</translation> </message> <message> + <source>Chain selection options:</source> + <translation>Параметры выбора цепочки:</translation> + </message> + <message> <source>Change index out of range</source> <translation>Изменение индекса вне диапазона</translation> </message> @@ -3314,6 +3378,10 @@ <translation>Использовать UPnP для проброса порта (по умолчанию: %u)</translation> </message> <message> + <source>Use the test chain</source> + <translation>Использовать тестовую цепочку</translation> + </message> + <message> <source>User Agent comment (%s) contains unsafe characters.</source> <translation>Комментарий пользователя (%s) содержит небезопасные символы.</translation> </message> @@ -3646,10 +3714,6 @@ <translation>Использовать отдельный прокси SOCKS5 для соединения с участниками через скрытые сервисы Tor (по умолчанию: %s)</translation> </message> <message> - <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</source> - <translation>Имя пользователя и хэш пароля для JSON-RPC соединений. Поле <userpw> использует формат: <USERNAME>:<SALT>$<HASH>. Каноничный пример скрипта на питоне включен в "share/rpcuser". Эта опция может быть указана несколько раз</translation> - </message> - <message> <source>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</source> <translation>Внимание: Получена неизвестная версия блока! Возможно неизвестные правила вступили в силу.</translation> </message> diff --git a/src/qt/locale/bitcoin_sk.ts b/src/qt/locale/bitcoin_sk.ts index 6c5828c4f8..15a32b2050 100644 --- a/src/qt/locale/bitcoin_sk.ts +++ b/src/qt/locale/bitcoin_sk.ts @@ -2295,10 +2295,6 @@ <translation>Použiť samostatný SOCKS5 proxy server na dosiahnutie počítačov cez skryté služby Tor (predvolené: %s)</translation> </message> <message> - <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</source> - <translation>Užívateľské hash meno a heslo pre JSON-RPC pripojenia. Pole <userpw> je vo formáte <USERNAME>:<SALT>$<HASH>. Kanonický python skript je zahrnutý v share/rpcuser. Toto nastavenie môže byť špecifikované viac krát</translation> - </message> - <message> <source>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</source> <translation>Varovanie: Neznáma verzia blokov sa doluje! Je možné, že neznáme pravidlá majú efekt</translation> </message> diff --git a/src/qt/locale/bitcoin_sv.ts b/src/qt/locale/bitcoin_sv.ts index cc6b47e4d6..4d45d20cbf 100644 --- a/src/qt/locale/bitcoin_sv.ts +++ b/src/qt/locale/bitcoin_sv.ts @@ -2647,10 +2647,6 @@ Var vänlig och försök igen.</translation> <translation>Använd separat SOCKS5 proxy för att nå kollegor via dolda tjänster i Tor (förvalt: -%s)</translation> </message> <message> - <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</source> - <translation>Användarnamn och hashat lösenord för JSON-RPC-anslutningar. Fältet <userpw> kommer i formatet: <USERNAME>:<SALT>$<HASH>. Ett kanoniskt pythonskript finns inkluderat i share/rpcuser. Detta alternativ kan anges flera gånger</translation> - </message> - <message> <source>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</source> <translation>Varning: Okända blockversioner bryts! Det är möjligt att okända regler används</translation> </message> diff --git a/src/qt/locale/bitcoin_tr.ts b/src/qt/locale/bitcoin_tr.ts index b210d01990..d1249d09be 100644 --- a/src/qt/locale/bitcoin_tr.ts +++ b/src/qt/locale/bitcoin_tr.ts @@ -2578,10 +2578,6 @@ <translation>Eşlere gizli Tor servisleri ile ulaşmak için ayrı SOCKS5 vekil sunucusu kullan (varsayılan: %s)</translation> </message> <message> - <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</source> - <translation>JSON-RPC bağlantıları için kullanıcı ismi ve karmalanmış parola. <userpw> alanı şu biçimdedir: <USERNAME>:<SALT>$<HASH>. Kanonik bir Python betiği share/rpcuser klasöründe bulunabilir. Bu seçenek birden çok kez belirtilebilir.</translation> - </message> - <message> <source>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</source> <translation>İkaz: bilinmeyen blok sürümü oluşturulmaya çalışılıyor. Bilinmeyen kuralların işlemesi mümkündür.</translation> </message> diff --git a/src/qt/locale/bitcoin_uk.ts b/src/qt/locale/bitcoin_uk.ts index e423977eba..9c46c18224 100644 --- a/src/qt/locale/bitcoin_uk.ts +++ b/src/qt/locale/bitcoin_uk.ts @@ -2158,10 +2158,6 @@ <translation>Використовувати окремий SOCKS5-проксі для з'єднання з учасниками через приховані сервіси Tor (типово: %s)</translation> </message> <message> - <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</source> - <translation>Логін та хешований пароль для зв'язків JSON-RPC. Поле <userpw> має формат: <USERNAME>:<SALT>$<HASH>. Класичний Python script додано до share/rpcuser. Цей параметр може бути застосований декілька разів.</translation> - </message> - <message> <source>(default: %s)</source> <translation>(типово: %s)</translation> </message> diff --git a/src/qt/locale/bitcoin_zh_CN.ts b/src/qt/locale/bitcoin_zh_CN.ts index 41ec0574d5..8267a4f0e3 100644 --- a/src/qt/locale/bitcoin_zh_CN.ts +++ b/src/qt/locale/bitcoin_zh_CN.ts @@ -3506,10 +3506,6 @@ <translation>通过Tor隐藏服务连接节点时 使用不同的SOCKS5代理 (默认: %s)</translation> </message> <message> - <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</source> - <translation>JSON-RPC 连接要使用的用户名和散列密码。<userpw> 的格式是:<用户名>:<盐>$<散列值>。在 share/rpcuser 目录下有一个示范的 python 脚本。这个选项可以被多次指定。</translation> - </message> - <message> <source>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</source> <translation>警告: 未知的区块版本被挖掘!未知规则可能已生效</translation> </message> diff --git a/src/qt/locale/bitcoin_zh_TW.ts b/src/qt/locale/bitcoin_zh_TW.ts index 44d596013b..bdcf966297 100644 --- a/src/qt/locale/bitcoin_zh_TW.ts +++ b/src/qt/locale/bitcoin_zh_TW.ts @@ -3703,10 +3703,6 @@ <translation>使用另外的 SOCK5 代理伺服器,來透過 Tor 隱藏服務跟其他節點聯絡(預設值: %s)</translation> </message> <message> - <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</source> - <translation>JSON-RPC 連線要用的使用者名稱和雜湊密碼。<userpw> 的格式是:<使用者名稱>:<調味值>$<雜湊值>。在 share/rpcuser 目錄下有一個示範的 python 程式。這個選項可以給很多次。</translation> - </message> - <message> <source>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</source> <translation>警告: 有礦工正在開採不明版本的區塊!這表示有不明的交易規則正在作用中</translation> </message> diff --git a/src/qt/modaloverlay.cpp b/src/qt/modaloverlay.cpp index 3e8282e63d..89fb159956 100644 --- a/src/qt/modaloverlay.cpp +++ b/src/qt/modaloverlay.cpp @@ -105,7 +105,7 @@ void ModalOverlay::tipUpdate(int count, const QDateTime& blockDate, double nVeri ui->progressIncreasePerH->setText(QString::number(progressPerHour*100, 'f', 2)+"%"); // show expected remaining time - ui->expectedTimeLeft->setText(GUIUtil::formateNiceTimeOffset(remainingMSecs/1000.0)); + ui->expectedTimeLeft->setText(GUIUtil::formatNiceTimeOffset(remainingMSecs/1000.0)); static const int MAX_SAMPLES = 5000; if (blockProcessTime.count() > MAX_SAMPLES) diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp index 6b2871424d..688e8123af 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -10,7 +10,7 @@ #include "base58.h" #include "chainparams.h" -#include "validation.h" // For minRelayTxFee +#include "policy/policy.h" #include "ui_interface.h" #include "util.h" #include "wallet/wallet.h" @@ -582,7 +582,7 @@ bool PaymentServer::processPaymentRequest(const PaymentRequestPlus& request, Sen // Extract and check amounts CTxOut txOut(sendingTo.second, sendingTo.first); - if (txOut.IsDust(::minRelayTxFee)) { + if (txOut.IsDust(dustRelayFee)) { Q_EMIT message(tr("Payment request error"), tr("Requested payment amount of %1 is too small (considered dust).") .arg(BitcoinUnits::formatWithUnit(optionsModel->getDisplayUnit(), sendingTo.second)), CClientUIInterface::MSG_ERROR); diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 0b42c1d625..368654bfa6 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -336,8 +336,8 @@ std::string EntryDescriptionString() " \"modifiedfee\" : n, (numeric) transaction fee with fee deltas used for mining priority\n" " \"time\" : n, (numeric) local time transaction entered pool in seconds since 1 Jan 1970 GMT\n" " \"height\" : n, (numeric) block height when transaction entered pool\n" - " \"startingpriority\" : n, (numeric) priority when transaction entered pool\n" - " \"currentpriority\" : n, (numeric) transaction priority now\n" + " \"startingpriority\" : n, (numeric) DEPRECATED. Priority when transaction entered pool\n" + " \"currentpriority\" : n, (numeric) DEPRECATED. Transaction priority now\n" " \"descendantcount\" : n, (numeric) number of in-mempool descendant transactions (including this one)\n" " \"descendantsize\" : n, (numeric) virtual transaction size of in-mempool descendants (including this one)\n" " \"descendantfees\" : n, (numeric) modified fees (see above) of in-mempool descendants (including this one)\n" @@ -651,9 +651,9 @@ UniValue getblockheader(const JSONRPCRequest& request) " \"nonce\" : n, (numeric) The nonce\n" " \"bits\" : \"1d00ffff\", (string) The bits\n" " \"difficulty\" : x.xxx, (numeric) The difficulty\n" + " \"chainwork\" : \"0000...1f3\" (string) Expected number of hashes required to produce the current chain (in hex)\n" " \"previousblockhash\" : \"hash\", (string) The hash of the previous block\n" " \"nextblockhash\" : \"hash\", (string) The hash of the next block\n" - " \"chainwork\" : \"0000...1f3\" (string) Expected number of hashes required to produce the current chain (in hex)\n" "}\n" "\nResult (for verbose=false):\n" "\"data\" (string) A string that is serialized, hex-encoded data for block 'hash'.\n" @@ -814,6 +814,53 @@ static bool GetUTXOStats(CCoinsView *view, CCoinsStats &stats) return true; } +UniValue pruneblockchain(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() != 1) + throw runtime_error( + "pruneblockchain\n" + "\nArguments:\n" + "1. \"height\" (numeric, required) The block height to prune up to. May be set to a discrete height, or to a unix timestamp to prune based on block time.\n" + "\nResult:\n" + "n (numeric) Height of the last block pruned.\n" + "\nExamples:\n" + + HelpExampleCli("pruneblockchain", "1000") + + HelpExampleRpc("pruneblockchain", "1000")); + + if (!fPruneMode) + throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Cannot prune blocks because node is not in prune mode."); + + LOCK(cs_main); + + int heightParam = request.params[0].get_int(); + if (heightParam < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative block height."); + + // Height value more than a billion is too high to be a block height, and + // too low to be a block time (corresponds to timestamp from Sep 2001). + if (heightParam > 1000000000) { + CBlockIndex* pindex = chainActive.FindEarliestAtLeast(heightParam); + if (!pindex) { + throw JSONRPCError(RPC_INTERNAL_ERROR, "Could not find block with at least the specified timestamp."); + } + heightParam = pindex->nHeight; + } + + unsigned int height = (unsigned int) heightParam; + unsigned int chainHeight = (unsigned int) chainActive.Height(); + if (chainHeight < Params().PruneAfterHeight()) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Blockchain is too short for pruning."); + else if (height > chainHeight) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Blockchain is shorter than the attempted prune height."); + else if (height > chainHeight - MIN_BLOCKS_TO_KEEP) { + LogPrint("rpc", "Attempt to prune blocks close to the tip. Retaining the minimum number of blocks."); + height = chainHeight - MIN_BLOCKS_TO_KEEP; + } + + PruneBlockFilesManual(height); + return uint64_t(height); +} + UniValue gettxoutsetinfo(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 0) @@ -1073,7 +1120,7 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) obj.push_back(Pair("bestblockhash", chainActive.Tip()->GetBlockHash().GetHex())); obj.push_back(Pair("difficulty", (double)GetDifficulty())); obj.push_back(Pair("mediantime", (int64_t)chainActive.Tip()->GetMedianTimePast())); - obj.push_back(Pair("verificationprogress", Checkpoints::GuessVerificationProgress(Params().Checkpoints(), chainActive.Tip()))); + obj.push_back(Pair("verificationprogress", GuessVerificationProgress(Params().TxData(), chainActive.Tip()))); obj.push_back(Pair("chainwork", chainActive.Tip()->nChainWork.GetHex())); obj.push_back(Pair("pruned", fPruneMode)); @@ -1384,6 +1431,7 @@ static const CRPCCommand commands[] = { "blockchain", "getrawmempool", &getrawmempool, true, {"verbose"} }, { "blockchain", "gettxout", &gettxout, true, {"txid","n","include_mempool"} }, { "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, true, {} }, + { "blockchain", "pruneblockchain", &pruneblockchain, true, {"height"} }, { "blockchain", "verifychain", &verifychain, true, {"checklevel","nblocks"} }, { "blockchain", "preciousblock", &preciousblock, true, {"blockhash"} }, diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 422d005f0c..5d3c458455 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -103,6 +103,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "importmulti", 1, "options" }, { "verifychain", 0, "checklevel" }, { "verifychain", 1, "nblocks" }, + { "pruneblockchain", 0, "height" }, { "keypoolrefill", 0, "newsize" }, { "getrawmempool", 0, "verbose" }, { "estimatefee", 0, "nblocks" }, diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index acbeb189e0..27b9963a10 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -100,13 +100,14 @@ UniValue getpeerinfo(const JSONRPCRequest& request) " \"inflight\": [\n" " n, (numeric) The heights of blocks we're currently asking from this peer\n" " ...\n" - " ]\n" + " ],\n" + " \"whitelisted\": true|false, (boolean) Whether the peer is whitelisted\n" " \"bytessent_per_msg\": {\n" - " \"addr\": n, (numeric) The total bytes sent aggregated by message type\n" + " \"addr\": n, (numeric) The total bytes sent aggregated by message type\n" " ...\n" - " }\n" + " },\n" " \"bytesrecv_per_msg\": {\n" - " \"addr\": n, (numeric) The total bytes received aggregated by message type\n" + " \"addr\": n, (numeric) The total bytes received aggregated by message type\n" " ...\n" " }\n" " }\n" @@ -411,6 +412,7 @@ UniValue getnetworkinfo(const JSONRPCRequest& request) " \"limited\": true|false, (boolean) is the network limited using -onlynet?\n" " \"reachable\": true|false, (boolean) is the network reachable?\n" " \"proxy\": \"host:port\" (string) the proxy that is used for this network, or empty if none\n" + " \"proxy_randomize_credentials\": true|false, (string) Whether randomized credentials are used\n" " }\n" " ,...\n" " ],\n" @@ -423,7 +425,7 @@ UniValue getnetworkinfo(const JSONRPCRequest& request) " }\n" " ,...\n" " ]\n" - " \"warnings\": \"...\" (string) any network warnings (such as alert messages) \n" + " \"warnings\": \"...\" (string) any network warnings\n" "}\n" "\nExamples:\n" + HelpExampleCli("getnetworkinfo", "") diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 4996eb2a69..f328543323 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -128,9 +128,11 @@ UniValue getrawtransaction(const JSONRPCRequest& request) if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) throw runtime_error( "getrawtransaction \"txid\" ( verbose )\n" - "\nNOTE: By default this function only works sometimes. This is when the tx is in the mempool\n" - "or there is an unspent output in the utxo for this transaction. To make it always work,\n" - "you need to maintain a transaction index, using the -txindex command line option.\n" + + "\nNOTE: By default this function only works for mempool transactions. If the -txindex option is\n" + "enabled, it also works for blockchain transactions.\n" + "DEPRECATED: for now, it also works for transactions with unspent outputs.\n" + "\nReturn the raw transaction data.\n" "\nIf verbose is 'true', returns an Object with information about 'txid'.\n" "If verbose is 'false' or omitted, returns a string that is serialized, hex-encoded data for 'txid'.\n" @@ -218,7 +220,9 @@ UniValue getrawtransaction(const JSONRPCRequest& request) CTransactionRef tx; uint256 hashBlock; if (!GetTransaction(hash, tx, Params().GetConsensus(), hashBlock, true)) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction"); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string(fTxIndex ? "No such mempool or blockchain transaction" + : "No such mempool transaction. Use -txindex to enable blockchain transaction queries") + + ". Use gettransaction for wallet transactions."); string strHex = EncodeHexTx(*tx, RPCSerializationFlags()); diff --git a/src/test/data/bitcoin-util-test.json b/src/test/data/bitcoin-util-test.json index d001b2056f..a80ab51901 100644 --- a/src/test/data/bitcoin-util-test.json +++ b/src/test/data/bitcoin-util-test.json @@ -118,6 +118,46 @@ "description": "Parses a transation with no inputs and a single output script (output in json)" }, { "exec": "./bitcoin-tx", + "args": ["-create", "outscript=0:OP_DROP", "nversion=1"], + "output_cmp": "txcreatescript1.hex", + "description": "Create a new transaction with a single output script (OP_DROP)" + }, + { "exec": "./bitcoin-tx", + "args": ["-json", "-create", "outscript=0:OP_DROP", "nversion=1"], + "output_cmp": "txcreatescript1.json", + "description": "Create a new transaction with a single output script (OP_DROP) (output as json)" + }, + { "exec": "./bitcoin-tx", + "args": ["-create", "outscript=0:OP_DROP:S", "nversion=1"], + "output_cmp": "txcreatescript2.hex", + "description": "Create a new transaction with a single output script (OP_DROP) in a P2SH" + }, + { "exec": "./bitcoin-tx", + "args": ["-json", "-create", "outscript=0:OP_DROP:S", "nversion=1"], + "output_cmp": "txcreatescript2.json", + "description": "Create a new transaction with a single output script (OP_DROP) in a P2SH (output as json)" + }, + { "exec": "./bitcoin-tx", + "args": ["-create", "outscript=0:OP_DROP:W", "nversion=1"], + "output_cmp": "txcreatescript3.hex", + "description": "Create a new transaction with a single output script (OP_DROP) in a P2WSH" + }, + { "exec": "./bitcoin-tx", + "args": ["-json", "-create", "outscript=0:OP_DROP:W", "nversion=1"], + "output_cmp": "txcreatescript3.json", + "description": "Create a new transaction with a single output script (OP_DROP) in a P2WSH (output as json)" + }, + { "exec": "./bitcoin-tx", + "args": ["-create", "outscript=0:OP_DROP:WS", "nversion=1"], + "output_cmp": "txcreatescript4.hex", + "description": "Create a new transaction with a single output script (OP_DROP) in a P2WSH, wrapped in a P2SH" + }, + { "exec": "./bitcoin-tx", + "args": ["-json", "-create", "outscript=0:OP_DROP:WS", "nversion=1"], + "output_cmp": "txcreatescript4.json", + "description": "Create a new transaction with a single output script (OP_DROP) in a P2SH, wrapped in a P2SH (output as json)" + }, + { "exec": "./bitcoin-tx", "args": ["-create", "nversion=1", "in=4d49a71ec9da436f71ec4ee231d04f292a29cd316f598bb7068feccabdc59485:0", @@ -153,6 +193,42 @@ }, { "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" + }, + { "exec": "./bitcoin-tx", + "args": + ["-json", "-create", "outpubkey=0:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397", "nversion=1"], + "output_cmp": "txcreateoutpubkey1.json", + "description": "Creates a new transaction with a single pay-to-pubkey output (output as json)" + }, + { "exec": "./bitcoin-tx", + "args": + ["-create", "outpubkey=0:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:W", "nversion=1"], + "output_cmp": "txcreateoutpubkey2.hex", + "description": "Creates a new transaction with a single pay-to-witness-pubkey output" + }, + { "exec": "./bitcoin-tx", + "args": + ["-json", "-create", "outpubkey=0:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:W", "nversion=1"], + "output_cmp": "txcreateoutpubkey2.json", + "description": "Creates a new transaction with a single pay-to-witness-pubkey output (output as json)" + }, + { "exec": "./bitcoin-tx", + "args": + ["-create", "outpubkey=0:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:WS", "nversion=1"], + "output_cmp": "txcreateoutpubkey3.hex", + "description": "Creates a new transaction with a single pay-to-witness-pubkey, wrapped in P2SH output" + }, + { "exec": "./bitcoin-tx", + "args": + ["-json", "-create", "outpubkey=0:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:WS", "nversion=1"], + "output_cmp": "txcreateoutpubkey3.json", + "description": "Creates a new transaction with a single pay-to-pub-key output, wrapped in P2SH (output as json)" + }, + { "exec": "./bitcoin-tx", + "args": ["-create", "in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0", "outdata=4:badhexdata"], @@ -236,5 +312,45 @@ "in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0:1"], "output_cmp": "txcreatedata_seq1.json", "description": "Adds a new input with sequence number to a transaction (output in json)" + }, + { "exec": "./bitcoin-tx", + "args": ["-create", "outmultisig=1:2:3:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d:02df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb485", "nversion=1"], + "output_cmp": "txcreatemultisig1.hex", + "description": "Creates a new transaction with a single 2-of-3 multisig output" + }, + { "exec": "./bitcoin-tx", + "args": ["-json", "-create", "outmultisig=1:2:3:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d:02df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb485", "nversion=1"], + "output_cmp": "txcreatemultisig1.json", + "description": "Creates a new transaction with a single 2-of-3 multisig output (output in json)" + }, + { "exec": "./bitcoin-tx", + "args": ["-create", "outmultisig=1:2:3:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d:02df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb485:S", "nversion=1"], + "output_cmp": "txcreatemultisig2.hex", + "description": "Creates a new transaction with a single 2-of-3 multisig in a P2SH output" + }, + { "exec": "./bitcoin-tx", + "args": ["-json", "-create", "outmultisig=1:2:3:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d:02df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb485:S", "nversion=1"], + "output_cmp": "txcreatemultisig2.json", + "description": "Creates a new transaction with a single 2-of-3 multisig in a P2SH output (output in json)" + }, + { "exec": "./bitcoin-tx", + "args": ["-create", "outmultisig=1:2:3:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d:02df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb485:W", "nversion=1"], + "output_cmp": "txcreatemultisig3.hex", + "description": "Creates a new transaction with a single 2-of-3 multisig in a P2WSH output" + }, + { "exec": "./bitcoin-tx", + "args": ["-json", "-create", "outmultisig=1:2:3:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d:02df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb485:W", "nversion=1"], + "output_cmp": "txcreatemultisig3.json", + "description": "Creates a new transaction with a single 2-of-3 multisig in a P2WSH output (output in json)" + }, + { "exec": "./bitcoin-tx", + "args": ["-create", "outmultisig=1:2:3:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d:02df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb485:WS", "nversion=1"], + "output_cmp": "txcreatemultisig4.hex", + "description": "Creates a new transaction with a single 2-of-3 multisig in a P2WSH output, wrapped in P2SH" + }, + { "exec": "./bitcoin-tx", + "args": ["-json", "-create", "outmultisig=1:2:3:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d:02df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb485:WS", "nversion=1"], + "output_cmp": "txcreatemultisig4.json", + "description": "Creates a new transaction with a single 2-of-3 multisig in a P2WSH output, wrapped in P2SH (output in json)" } ] diff --git a/src/test/data/txcreatemultisig1.hex b/src/test/data/txcreatemultisig1.hex new file mode 100644 index 0000000000..9c00004d38 --- /dev/null +++ b/src/test/data/txcreatemultisig1.hex @@ -0,0 +1 @@ +01000000000100e1f5050000000069522102a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff39721021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d2102df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb48553ae00000000 diff --git a/src/test/data/txcreatemultisig1.json b/src/test/data/txcreatemultisig1.json new file mode 100644 index 0000000000..f6ce43c202 --- /dev/null +++ b/src/test/data/txcreatemultisig1.json @@ -0,0 +1,26 @@ +{ + "txid": "0d1d4edfc217d9db3ab6a9298f26a52eae3c52f55a6cb8ccbc14f7c727572894", + "hash": "0d1d4edfc217d9db3ab6a9298f26a52eae3c52f55a6cb8ccbc14f7c727572894", + "version": 1, + "locktime": 0, + "vin": [ + ], + "vout": [ + { + "value": 1.00, + "n": 0, + "scriptPubKey": { + "asm": "2 02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397 021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d 02df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb485 3 OP_CHECKMULTISIG", + "hex": "522102a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff39721021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d2102df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb48553ae", + "reqSigs": 2, + "type": "multisig", + "addresses": [ + "1FoG2386FG2tAJS9acMuiDsKy67aGg9MKz", + "1FXtz9KU8JNmQDyHdiEm5HDiALuP3zdHvV", + "14LuavcBbXZYJ6Tsz3cAUQj9SuQoL2xCQX" + ] + } + } + ], + "hex": "01000000000100e1f5050000000069522102a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff39721021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d2102df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb48553ae00000000" +} diff --git a/src/test/data/txcreatemultisig2.hex b/src/test/data/txcreatemultisig2.hex new file mode 100644 index 0000000000..07835c54d3 --- /dev/null +++ b/src/test/data/txcreatemultisig2.hex @@ -0,0 +1 @@ +01000000000100e1f5050000000017a9141c6fbaf46d64221e80cbae182c33ddf81b9294ac8700000000 diff --git a/src/test/data/txcreatemultisig2.json b/src/test/data/txcreatemultisig2.json new file mode 100644 index 0000000000..e09d22060f --- /dev/null +++ b/src/test/data/txcreatemultisig2.json @@ -0,0 +1,24 @@ +{ + "txid": "0d861f278a3b7bce7cb5a88d71e6e6a903336f95ad5a2c29b295b63835b6eee3", + "hash": "0d861f278a3b7bce7cb5a88d71e6e6a903336f95ad5a2c29b295b63835b6eee3", + "version": 1, + "locktime": 0, + "vin": [ + ], + "vout": [ + { + "value": 1.00, + "n": 0, + "scriptPubKey": { + "asm": "OP_HASH160 1c6fbaf46d64221e80cbae182c33ddf81b9294ac OP_EQUAL", + "hex": "a9141c6fbaf46d64221e80cbae182c33ddf81b9294ac87", + "reqSigs": 1, + "type": "scripthash", + "addresses": [ + "34HNh57oBCRKkxNyjTuWAJkTbuGh6jg2Ms" + ] + } + } + ], + "hex": "01000000000100e1f5050000000017a9141c6fbaf46d64221e80cbae182c33ddf81b9294ac8700000000" +} diff --git a/src/test/data/txcreatemultisig3.hex b/src/test/data/txcreatemultisig3.hex new file mode 100644 index 0000000000..8d34f28f87 --- /dev/null +++ b/src/test/data/txcreatemultisig3.hex @@ -0,0 +1 @@ +01000000000100e1f50500000000220020e15a86a23178f433d514dbbce042e87d72662b8b5edcacfd2e37ab7a2d135f0500000000 diff --git a/src/test/data/txcreatemultisig3.json b/src/test/data/txcreatemultisig3.json new file mode 100644 index 0000000000..88e32bd310 --- /dev/null +++ b/src/test/data/txcreatemultisig3.json @@ -0,0 +1,20 @@ +{ + "txid": "ccc552220b46a3b5140048b03395987ce4f0fa1ddf8c635bba1fa44e0f8c1d7f", + "hash": "ccc552220b46a3b5140048b03395987ce4f0fa1ddf8c635bba1fa44e0f8c1d7f", + "version": 1, + "locktime": 0, + "vin": [ + ], + "vout": [ + { + "value": 1.00, + "n": 0, + "scriptPubKey": { + "asm": "0 e15a86a23178f433d514dbbce042e87d72662b8b5edcacfd2e37ab7a2d135f05", + "hex": "0020e15a86a23178f433d514dbbce042e87d72662b8b5edcacfd2e37ab7a2d135f05", + "type": "witness_v0_scripthash" + } + } + ], + "hex": "01000000000100e1f50500000000220020e15a86a23178f433d514dbbce042e87d72662b8b5edcacfd2e37ab7a2d135f0500000000" +} diff --git a/src/test/data/txcreatemultisig4.hex b/src/test/data/txcreatemultisig4.hex new file mode 100644 index 0000000000..7da54366c7 --- /dev/null +++ b/src/test/data/txcreatemultisig4.hex @@ -0,0 +1 @@ +01000000000100e1f5050000000017a9146edf12858999f0dae74f9c692e6694ee3621b2ac8700000000 diff --git a/src/test/data/txcreatemultisig4.json b/src/test/data/txcreatemultisig4.json new file mode 100644 index 0000000000..fc69c7269c --- /dev/null +++ b/src/test/data/txcreatemultisig4.json @@ -0,0 +1,24 @@ +{ + "txid": "5e8b1cc73234e208d4b7ca9075f136b908c34101be7a048df4ba9ac758b61567", + "hash": "5e8b1cc73234e208d4b7ca9075f136b908c34101be7a048df4ba9ac758b61567", + "version": 1, + "locktime": 0, + "vin": [ + ], + "vout": [ + { + "value": 1.00, + "n": 0, + "scriptPubKey": { + "asm": "OP_HASH160 6edf12858999f0dae74f9c692e6694ee3621b2ac OP_EQUAL", + "hex": "a9146edf12858999f0dae74f9c692e6694ee3621b2ac87", + "reqSigs": 1, + "type": "scripthash", + "addresses": [ + "3BoFUz1StqcNcgUTZE5cC1eFhuYFzj3fGH" + ] + } + } + ], + "hex": "01000000000100e1f5050000000017a9146edf12858999f0dae74f9c692e6694ee3621b2ac8700000000" +} diff --git a/src/test/data/txcreateoutpubkey1.hex b/src/test/data/txcreateoutpubkey1.hex new file mode 100644 index 0000000000..4a08244b2f --- /dev/null +++ b/src/test/data/txcreateoutpubkey1.hex @@ -0,0 +1 @@ +0100000000010000000000000000232102a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397ac00000000 diff --git a/src/test/data/txcreateoutpubkey1.json b/src/test/data/txcreateoutpubkey1.json new file mode 100644 index 0000000000..6019fa2dcd --- /dev/null +++ b/src/test/data/txcreateoutpubkey1.json @@ -0,0 +1,24 @@ +{ + "txid": "f42b38ac12e3fafc96ba1a9ba70cbfe326744aef75df5fb9db5d6e2855ca415f", + "hash": "f42b38ac12e3fafc96ba1a9ba70cbfe326744aef75df5fb9db5d6e2855ca415f", + "version": 1, + "locktime": 0, + "vin": [ + ], + "vout": [ + { + "value": 0.00, + "n": 0, + "scriptPubKey": { + "asm": "02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397 OP_CHECKSIG", + "hex": "2102a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397ac", + "reqSigs": 1, + "type": "pubkey", + "addresses": [ + "1FoG2386FG2tAJS9acMuiDsKy67aGg9MKz" + ] + } + } + ], + "hex": "0100000000010000000000000000232102a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397ac00000000" +} diff --git a/src/test/data/txcreateoutpubkey2.hex b/src/test/data/txcreateoutpubkey2.hex new file mode 100644 index 0000000000..8283c722ab --- /dev/null +++ b/src/test/data/txcreateoutpubkey2.hex @@ -0,0 +1 @@ +0100000000010000000000000000160014a2516e770582864a6a56ed21a102044e388c62e300000000 diff --git a/src/test/data/txcreateoutpubkey2.json b/src/test/data/txcreateoutpubkey2.json new file mode 100644 index 0000000000..6fc3d57527 --- /dev/null +++ b/src/test/data/txcreateoutpubkey2.json @@ -0,0 +1,20 @@ +{ + "txid": "70f2a088cde460e677415fa1fb71895e90c231e6ed38ed203a35b6f848e9cc73", + "hash": "70f2a088cde460e677415fa1fb71895e90c231e6ed38ed203a35b6f848e9cc73", + "version": 1, + "locktime": 0, + "vin": [ + ], + "vout": [ + { + "value": 0.00, + "n": 0, + "scriptPubKey": { + "asm": "0 a2516e770582864a6a56ed21a102044e388c62e3", + "hex": "0014a2516e770582864a6a56ed21a102044e388c62e3", + "type": "witness_v0_keyhash" + } + } + ], + "hex": "0100000000010000000000000000160014a2516e770582864a6a56ed21a102044e388c62e300000000" +} diff --git a/src/test/data/txcreateoutpubkey3.hex b/src/test/data/txcreateoutpubkey3.hex new file mode 100644 index 0000000000..84adff4d89 --- /dev/null +++ b/src/test/data/txcreateoutpubkey3.hex @@ -0,0 +1 @@ +010000000001000000000000000017a914a5ab14c9804d0d8bf02f1aea4e82780733ad0a838700000000 diff --git a/src/test/data/txcreateoutpubkey3.json b/src/test/data/txcreateoutpubkey3.json new file mode 100644 index 0000000000..a1a25fc834 --- /dev/null +++ b/src/test/data/txcreateoutpubkey3.json @@ -0,0 +1,24 @@ +{ + "txid": "bfc7e898ee9f6a9652d7b8cca147e2da134502e2ada0f279ed634fc8cf833f8c", + "hash": "bfc7e898ee9f6a9652d7b8cca147e2da134502e2ada0f279ed634fc8cf833f8c", + "version": 1, + "locktime": 0, + "vin": [ + ], + "vout": [ + { + "value": 0.00, + "n": 0, + "scriptPubKey": { + "asm": "OP_HASH160 a5ab14c9804d0d8bf02f1aea4e82780733ad0a83 OP_EQUAL", + "hex": "a914a5ab14c9804d0d8bf02f1aea4e82780733ad0a8387", + "reqSigs": 1, + "type": "scripthash", + "addresses": [ + "3GnzN8FqgvYGYdhj8NW6UNxxVv3Uj1ApQn" + ] + } + } + ], + "hex": "010000000001000000000000000017a914a5ab14c9804d0d8bf02f1aea4e82780733ad0a838700000000" +} diff --git a/src/test/data/txcreatescript1.hex b/src/test/data/txcreatescript1.hex new file mode 100644 index 0000000000..0adce270fb --- /dev/null +++ b/src/test/data/txcreatescript1.hex @@ -0,0 +1 @@ +0100000000010000000000000000017500000000 diff --git a/src/test/data/txcreatescript1.json b/src/test/data/txcreatescript1.json new file mode 100644 index 0000000000..8ffecba411 --- /dev/null +++ b/src/test/data/txcreatescript1.json @@ -0,0 +1,20 @@ +{ + "txid": "f0851b68202f736b792649cfc960259c2374badcb644ab20cac726b5f72f61c9", + "hash": "f0851b68202f736b792649cfc960259c2374badcb644ab20cac726b5f72f61c9", + "version": 1, + "locktime": 0, + "vin": [ + ], + "vout": [ + { + "value": 0.00, + "n": 0, + "scriptPubKey": { + "asm": "OP_DROP", + "hex": "75", + "type": "nonstandard" + } + } + ], + "hex": "0100000000010000000000000000017500000000" +} diff --git a/src/test/data/txcreatescript2.hex b/src/test/data/txcreatescript2.hex new file mode 100644 index 0000000000..5afe8786e3 --- /dev/null +++ b/src/test/data/txcreatescript2.hex @@ -0,0 +1 @@ +010000000001000000000000000017a91471ed53322d470bb96657deb786b94f97dd46fb158700000000 diff --git a/src/test/data/txcreatescript2.json b/src/test/data/txcreatescript2.json new file mode 100644 index 0000000000..41eb69f1af --- /dev/null +++ b/src/test/data/txcreatescript2.json @@ -0,0 +1,24 @@ +{ + "txid": "6e07a7cc075e0703f32ee8c4e5373fe654bfbc315148fda364e1be286ff290d0", + "hash": "6e07a7cc075e0703f32ee8c4e5373fe654bfbc315148fda364e1be286ff290d0", + "version": 1, + "locktime": 0, + "vin": [ + ], + "vout": [ + { + "value": 0.00, + "n": 0, + "scriptPubKey": { + "asm": "OP_HASH160 71ed53322d470bb96657deb786b94f97dd46fb15 OP_EQUAL", + "hex": "a91471ed53322d470bb96657deb786b94f97dd46fb1587", + "reqSigs": 1, + "type": "scripthash", + "addresses": [ + "3C5QarEGh9feKbDJ3QbMf2YNjnMoiPDhNp" + ] + } + } + ], + "hex": "010000000001000000000000000017a91471ed53322d470bb96657deb786b94f97dd46fb158700000000" +} diff --git a/src/test/data/txcreatescript3.hex b/src/test/data/txcreatescript3.hex new file mode 100644 index 0000000000..8a2b973bf0 --- /dev/null +++ b/src/test/data/txcreatescript3.hex @@ -0,0 +1 @@ +01000000000100000000000000002200200bfe935e70c321c7ca3afc75ce0d0ca2f98b5422e008bb31c00c6d7f1f1c0ad600000000 diff --git a/src/test/data/txcreatescript3.json b/src/test/data/txcreatescript3.json new file mode 100644 index 0000000000..90e7e27f9f --- /dev/null +++ b/src/test/data/txcreatescript3.json @@ -0,0 +1,20 @@ +{ + "txid": "8a234037b088e987c877030efc83374a07441c321bf9dc6dd2f206bc26507df8", + "hash": "8a234037b088e987c877030efc83374a07441c321bf9dc6dd2f206bc26507df8", + "version": 1, + "locktime": 0, + "vin": [ + ], + "vout": [ + { + "value": 0.00, + "n": 0, + "scriptPubKey": { + "asm": "0 0bfe935e70c321c7ca3afc75ce0d0ca2f98b5422e008bb31c00c6d7f1f1c0ad6", + "hex": "00200bfe935e70c321c7ca3afc75ce0d0ca2f98b5422e008bb31c00c6d7f1f1c0ad6", + "type": "witness_v0_scripthash" + } + } + ], + "hex": "01000000000100000000000000002200200bfe935e70c321c7ca3afc75ce0d0ca2f98b5422e008bb31c00c6d7f1f1c0ad600000000" +} diff --git a/src/test/data/txcreatescript4.hex b/src/test/data/txcreatescript4.hex new file mode 100644 index 0000000000..b4cfe58f42 --- /dev/null +++ b/src/test/data/txcreatescript4.hex @@ -0,0 +1 @@ +010000000001000000000000000017a9146a2c482f4985f57e702f325816c90e3723ca81ae8700000000 diff --git a/src/test/data/txcreatescript4.json b/src/test/data/txcreatescript4.json new file mode 100644 index 0000000000..11783751a4 --- /dev/null +++ b/src/test/data/txcreatescript4.json @@ -0,0 +1,24 @@ +{ + "txid": "24225cf5e9391100d6b218134b9f03383ca4c880a1f634ac12990cf28b66adbc", + "hash": "24225cf5e9391100d6b218134b9f03383ca4c880a1f634ac12990cf28b66adbc", + "version": 1, + "locktime": 0, + "vin": [ + ], + "vout": [ + { + "value": 0.00, + "n": 0, + "scriptPubKey": { + "asm": "OP_HASH160 6a2c482f4985f57e702f325816c90e3723ca81ae OP_EQUAL", + "hex": "a9146a2c482f4985f57e702f325816c90e3723ca81ae87", + "reqSigs": 1, + "type": "scripthash", + "addresses": [ + "3BNQbeFeJJGMAyDxPwWPuqxPMrjsFLjk3f" + ] + } + } + ], + "hex": "010000000001000000000000000017a9146a2c482f4985f57e702f325816c90e3723ca81ae8700000000" +} diff --git a/src/test/skiplist_tests.cpp b/src/test/skiplist_tests.cpp index 5b4ef3fe7d..0b2fe0ef9d 100644 --- a/src/test/skiplist_tests.cpp +++ b/src/test/skiplist_tests.cpp @@ -100,4 +100,47 @@ BOOST_AUTO_TEST_CASE(getlocator_test) } } +BOOST_AUTO_TEST_CASE(findearliestatleast_test) +{ + std::vector<uint256> vHashMain(100000); + std::vector<CBlockIndex> vBlocksMain(100000); + for (unsigned int i=0; i<vBlocksMain.size(); i++) { + vHashMain[i] = ArithToUint256(i); // Set the hash equal to the height + vBlocksMain[i].nHeight = i; + vBlocksMain[i].pprev = i ? &vBlocksMain[i - 1] : NULL; + vBlocksMain[i].phashBlock = &vHashMain[i]; + vBlocksMain[i].BuildSkip(); + if (i < 10) { + vBlocksMain[i].nTime = i; + vBlocksMain[i].nTimeMax = i; + } else { + // randomly choose something in the range [MTP, MTP*2] + int64_t medianTimePast = vBlocksMain[i].GetMedianTimePast(); + int r = insecure_rand() % medianTimePast; + vBlocksMain[i].nTime = r + medianTimePast; + vBlocksMain[i].nTimeMax = std::max(vBlocksMain[i].nTime, vBlocksMain[i-1].nTimeMax); + } + } + // Check that we set nTimeMax up correctly. + unsigned int curTimeMax = 0; + for (unsigned int i=0; i<vBlocksMain.size(); ++i) { + curTimeMax = std::max(curTimeMax, vBlocksMain[i].nTime); + BOOST_CHECK(curTimeMax == vBlocksMain[i].nTimeMax); + } + + // Build a CChain for the main branch. + CChain chain; + chain.SetTip(&vBlocksMain.back()); + + // Verify that FindEarliestAtLeast is correct. + for (unsigned int i=0; i<10000; ++i) { + // Pick a random element in vBlocksMain. + int r = insecure_rand() % vBlocksMain.size(); + int64_t test_time = vBlocksMain[r].nTime; + CBlockIndex *ret = chain.FindEarliestAtLeast(test_time); + BOOST_CHECK(ret->nTimeMax >= test_time); + BOOST_CHECK((ret->pprev==NULL) || ret->pprev->nTimeMax < test_time); + BOOST_CHECK(vBlocksMain[r].GetAncestor(ret->nHeight) == ret); + } +} BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 8c9aaef02f..374423179c 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -690,7 +690,7 @@ BOOST_AUTO_TEST_CASE(test_IsStandard) BOOST_CHECK(IsStandardTx(t, reason)); // Check dust with default relay fee: - CAmount nDustThreshold = 182 * minRelayTxFee.GetFeePerK()/1000 * 3; + CAmount nDustThreshold = 182 * dustRelayFee.GetFeePerK()/1000 * 3; BOOST_CHECK_EQUAL(nDustThreshold, 546); // dust: t.vout[0].nValue = nDustThreshold - 1; @@ -701,14 +701,14 @@ BOOST_AUTO_TEST_CASE(test_IsStandard) // Check dust with odd relay fee to verify rounding: // nDustThreshold = 182 * 1234 / 1000 * 3 - minRelayTxFee = CFeeRate(1234); + dustRelayFee = CFeeRate(1234); // dust: t.vout[0].nValue = 672 - 1; BOOST_CHECK(!IsStandardTx(t, reason)); // not dust: t.vout[0].nValue = 672; BOOST_CHECK(IsStandardTx(t, reason)); - minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE); + dustRelayFee = CFeeRate(DUST_RELAY_TX_FEE); t.vout[0].scriptPubKey = CScript() << OP_1; BOOST_CHECK(!IsStandardTx(t, reason)); diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 4f4540a1fc..5b085f492d 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -359,7 +359,6 @@ CTxMemPool::CTxMemPool(const CFeeRate& _minReasonableRelayFee) : nCheckFrequency = 0; minerPolicyEstimator = new CBlockPolicyEstimator(_minReasonableRelayFee); - minReasonableRelayFee = _minReasonableRelayFee; } CTxMemPool::~CTxMemPool() @@ -581,8 +580,8 @@ void CTxMemPool::removeConflicts(const CTransaction &tx) const CTransaction &txConflict = *it->second; if (txConflict != tx) { - removeRecursive(txConflict); ClearPrioritisation(txConflict.GetHash()); + removeRecursive(txConflict); } } } @@ -1076,12 +1075,12 @@ CFeeRate CTxMemPool::GetMinFee(size_t sizelimit) const { rollingMinimumFeeRate = rollingMinimumFeeRate / pow(2.0, (time - lastRollingFeeUpdate) / halflife); lastRollingFeeUpdate = time; - if (rollingMinimumFeeRate < (double)minReasonableRelayFee.GetFeePerK() / 2) { + if (rollingMinimumFeeRate < (double)incrementalRelayFee.GetFeePerK() / 2) { rollingMinimumFeeRate = 0; return CFeeRate(0); } } - return std::max(CFeeRate(rollingMinimumFeeRate), minReasonableRelayFee); + return std::max(CFeeRate(rollingMinimumFeeRate), incrementalRelayFee); } void CTxMemPool::trackPackageRemoved(const CFeeRate& rate) { @@ -1105,7 +1104,7 @@ void CTxMemPool::TrimToSize(size_t sizelimit, std::vector<uint256>* pvNoSpendsRe // to have 0 fee). This way, we don't allow txn to enter mempool with feerate // equal to txn which were removed with no block in between. CFeeRate removed(it->GetModFeesWithDescendants(), it->GetSizeWithDescendants()); - removed += minReasonableRelayFee; + removed += incrementalRelayFee; trackPackageRemoved(removed); maxFeeRateRemoved = std::max(maxFeeRateRemoved, removed); diff --git a/src/txmempool.h b/src/txmempool.h index 6a00b540a5..ffb1c1309b 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -423,8 +423,6 @@ private: uint64_t totalTxSize; //!< sum of all mempool tx's virtual sizes. Differs from serialized tx size since witness data is discounted. Defined in BIP 141. uint64_t cachedInnerUsage; //!< sum of dynamic memory usage of all the map elements (NOT the maps themselves) - CFeeRate minReasonableRelayFee; - mutable int64_t lastRollingFeeUpdate; mutable bool blockSinceLastRollingFeeBump; mutable double rollingMinimumFeeRate; //!< minimum fee to get into the pool, decreases exponentially @@ -503,9 +501,6 @@ public: std::map<uint256, std::pair<double, CAmount> > mapDeltas; /** Create a new CTxMemPool. - * minReasonableRelayFee should be a feerate which is, roughly, somewhere - * around what it "costs" to relay a transaction around the network and - * below which we would reasonably say a transaction has 0-effective-fee. */ CTxMemPool(const CFeeRate& _minReasonableRelayFee); ~CTxMemPool(); @@ -588,7 +583,7 @@ public: /** The minimum fee to get into the mempool, which may itself not be enough * for larger-sized transactions. - * The minReasonableRelayFee constructor arg is used to bound the time it + * The incrementalRelayFee policy variable is used to bound the time it * takes the fee rate to go back down all the way to 0. When the feerate * would otherwise be half of this, it is set to 0 instead. */ diff --git a/src/validation.cpp b/src/validation.cpp index 37a4186e0a..2ad3dadeb6 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -78,6 +78,7 @@ uint64_t nPruneTarget = 0; int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE; bool fEnableReplacement = DEFAULT_ENABLE_REPLACEMENT; +uint256 hashAssumeValid; CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE); CAmount maxTxFee = DEFAULT_TRANSACTION_MAXFEE; @@ -185,7 +186,8 @@ enum FlushStateMode { }; // See definition for documentation -bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode); +bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode, int nManualPruneHeight=0); +void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight); bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime) { @@ -1388,11 +1390,10 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi // Only if ALL inputs pass do we perform expensive ECDSA signature checks. // Helps prevent CPU exhaustion attacks. - // Skip ECDSA signature verification when connecting blocks before the - // last block chain checkpoint. Assuming the checkpoints are valid this + // Skip script verification when connecting blocks under the + // assumedvalid block. Assuming the assumedvalid block is valid this // is safe because block merkle hashes are still computed and checked, - // and any change will be caught at the next checkpoint. Of course, if - // the checkpoint is for a chain that's invalid due to false scriptSigs + // Of course, if an assumed valid block is invalid due to false scriptSigs // this optimization would allow an invalid chain to be accepted. if (fScriptChecks) { for (unsigned int i = 0; i < tx.vin.size(); i++) { @@ -1720,11 +1721,28 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin } bool fScriptChecks = true; - if (fCheckpointsEnabled) { - CBlockIndex *pindexLastCheckpoint = Checkpoints::GetLastCheckpoint(chainparams.Checkpoints()); - if (pindexLastCheckpoint && pindexLastCheckpoint->GetAncestor(pindex->nHeight) == pindex) { - // This block is an ancestor of a checkpoint: disable script checks - fScriptChecks = false; + if (!hashAssumeValid.IsNull()) { + // We've been configured with the hash of a block which has been externally verified to have a valid history. + // A suitable default value is included with the software and updated from time to time. Because validity + // relative to a piece of software is an objective fact these defaults can be easily reviewed. + // This setting doesn't force the selection of any particular chain but makes validating some faster by + // effectively caching the result of part of the verification. + BlockMap::const_iterator it = mapBlockIndex.find(hashAssumeValid); + if (it != mapBlockIndex.end()) { + if (it->second->GetAncestor(pindex->nHeight) == pindex && + pindexBestHeader->GetAncestor(pindex->nHeight) == pindex && + pindexBestHeader->nChainWork >= UintToArith256(chainparams.GetConsensus().nMinimumChainWork)) { + // This block is a member of the assumed verified chain and an ancestor of the best header. + // The equivalent time check discourages hashpower from extorting the network via DOS attack + // into accepting an invalid block through telling users they must manually set assumevalid. + // Requiring a software change or burying the invalid block, regardless of the setting, makes + // it hard to hide the implication of the demand. This also avoids having release candidates + // that are hardly doing any signature verification at all in testing without having to + // artificially set the default assumed verified block further back. + // The test against nMinimumChainWork prevents the skipping when denied access to any chain at + // least as good as the expected chain. + fScriptChecks = (GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, chainparams.GetConsensus()) <= 60 * 60 * 24 * 7 * 2); + } } } @@ -1934,7 +1952,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin * if they're too large, if it's been a while since the last write, * or always and in all cases if we're in prune mode and are deleting files. */ -bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) { +bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode, int nManualPruneHeight) { int64_t nMempoolUsage = mempool.DynamicMemoryUsage(); const CChainParams& chainparams = Params(); LOCK2(cs_main, cs_LastBlockFile); @@ -1944,9 +1962,13 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) { std::set<int> setFilesToPrune; bool fFlushForPrune = false; try { - if (fPruneMode && fCheckForPruning && !fReindex) { - FindFilesToPrune(setFilesToPrune, chainparams.PruneAfterHeight()); - fCheckForPruning = false; + if (fPruneMode && (fCheckForPruning || nManualPruneHeight > 0) && !fReindex) { + if (nManualPruneHeight > 0) { + FindFilesToPruneManual(setFilesToPrune, nManualPruneHeight); + } else { + FindFilesToPrune(setFilesToPrune, chainparams.PruneAfterHeight()); + fCheckForPruning = false; + } if (!setFilesToPrune.empty()) { fFlushForPrune = true; if (!fHavePruned) { @@ -2101,7 +2123,7 @@ void static UpdateTip(CBlockIndex *pindexNew, const CChainParams& chainParams) { chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), chainActive.Tip()->nVersion, log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), (unsigned long)chainActive.Tip()->nChainTx, DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), - Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), chainActive.Tip()), pcoinsTip->DynamicMemoryUsage() * (1.0 / (1<<20)), pcoinsTip->GetCacheSize()); + GuessVerificationProgress(chainParams.TxData(), chainActive.Tip()), pcoinsTip->DynamicMemoryUsage() * (1.0 / (1<<20)), pcoinsTip->GetCacheSize()); if (!warningMessages.empty()) LogPrintf(" warning='%s'", boost::algorithm::join(warningMessages, ", ")); LogPrintf("\n"); @@ -2409,6 +2431,11 @@ static void NotifyHeaderTip() { * that is already loaded (to avoid loading it again from disk). */ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock) { + // Note that while we're often called here from ProcessNewBlock, this is + // far from a guarantee. Things in the P2P/RPC will often end up calling + // us in the middle of ProcessNewBlock - do not assume pblock is set + // sanely for performance or correctness! + CBlockIndex *pindexMostWork = NULL; CBlockIndex *pindexNewTip = NULL; do { @@ -2601,6 +2628,7 @@ CBlockIndex* AddToBlockIndex(const CBlockHeader& block) pindexNew->nHeight = pindexNew->pprev->nHeight + 1; pindexNew->BuildSkip(); } + pindexNew->nTimeMax = (pindexNew->pprev ? std::max(pindexNew->pprev->nTimeMax, pindexNew->nTime) : pindexNew->nTime); pindexNew->nChainWork = (pindexNew->pprev ? pindexNew->pprev->nChainWork : 0) + GetBlockProof(*pindexNew); pindexNew->RaiseValidity(BLOCK_VALID_TREE); if (pindexBestHeader == NULL || pindexBestHeader->nChainWork < pindexNew->nChainWork) @@ -3051,14 +3079,18 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state } // Exposed wrapper for AcceptBlockHeader -bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex) +bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex) { { LOCK(cs_main); for (const CBlockHeader& header : headers) { - if (!AcceptBlockHeader(header, state, chainparams, ppindex)) { + CBlockIndex *pindex = NULL; // Use a temp pindex instead of ppindex to avoid a const_cast + if (!AcceptBlockHeader(header, state, chainparams, &pindex)) { return false; } + if (ppindex) { + *ppindex = pindex; + } } } NotifyHeaderTip(); @@ -3066,8 +3098,10 @@ bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, CValidatio } /** Store block on disk. If dbp is non-NULL, the file is known to already reside on disk */ -static bool AcceptBlock(const CBlock& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const CDiskBlockPos* dbp, bool* fNewBlock) +static bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const CDiskBlockPos* dbp, bool* fNewBlock) { + const CBlock& block = *pblock; + if (fNewBlock) *fNewBlock = false; AssertLockHeld(cs_main); @@ -3113,6 +3147,11 @@ static bool AcceptBlock(const CBlock& block, CValidationState& state, const CCha return error("%s: %s", __func__, FormatStateMessage(state)); } + // Header is valid/has work, merkle tree and segwit merkle tree are good...RELAY NOW + // (but if it does not build on our best tip, let the SendMessages loop relay it) + if (!IsInitialBlockDownload() && chainActive.Tip() == pindex->pprev) + GetMainSignals().NewPoWValidBlock(pindex, pblock); + int nHeight = pindex->nHeight; // Write block to history file @@ -3147,7 +3186,7 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons CBlockIndex *pindex = NULL; if (fNewBlock) *fNewBlock = false; CValidationState state; - bool ret = AcceptBlock(*pblock, state, chainparams, &pindex, fForceProcessing, NULL, fNewBlock); + bool ret = AcceptBlock(pblock, state, chainparams, &pindex, fForceProcessing, NULL, fNewBlock); CheckBlockIndex(chainparams.GetConsensus()); if (!ret) { GetMainSignals().BlockChecked(*pblock, state); @@ -3247,6 +3286,35 @@ void UnlinkPrunedFiles(std::set<int>& setFilesToPrune) } } +/* Calculate the block/rev files to delete based on height specified by user with RPC command pruneblockchain */ +void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight) +{ + assert(fPruneMode && nManualPruneHeight > 0); + + LOCK2(cs_main, cs_LastBlockFile); + if (chainActive.Tip() == NULL) + return; + + // last block to prune is the lesser of (user-specified height, MIN_BLOCKS_TO_KEEP from the tip) + unsigned int nLastBlockWeCanPrune = min((unsigned)nManualPruneHeight, chainActive.Tip()->nHeight - MIN_BLOCKS_TO_KEEP); + int count=0; + for (int fileNumber = 0; fileNumber < nLastBlockFile; fileNumber++) { + if (vinfoBlockFile[fileNumber].nSize == 0 || vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune) + continue; + PruneOneBlockFile(fileNumber); + setFilesToPrune.insert(fileNumber); + count++; + } + LogPrintf("Prune (Manual): prune_height=%d removed %d blk/rev pairs\n", nLastBlockWeCanPrune, count); +} + +/* This function is called from the RPC code for pruneblockchain */ +void PruneBlockFilesManual(int nManualPruneHeight) +{ + CValidationState state; + FlushStateToDisk(state, FLUSH_STATE_NONE, nManualPruneHeight); +} + /* Calculate the block/rev files that should be deleted to remain under target*/ void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight) { @@ -3382,6 +3450,7 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams) { CBlockIndex* pindex = item.second; pindex->nChainWork = (pindex->pprev ? pindex->pprev->nChainWork : 0) + GetBlockProof(*pindex); + pindex->nTimeMax = (pindex->pprev ? std::max(pindex->pprev->nTimeMax, pindex->nTime) : pindex->nTime); // We can link the chain of blocks for which we've received transactions at some point. // Pruned nodes may have deleted the block. if (pindex->nTx > 0) { @@ -3466,7 +3535,7 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams) LogPrintf("%s: hashBestChain=%s height=%d date=%s progress=%f\n", __func__, chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), - Checkpoints::GuessVerificationProgress(chainparams.Checkpoints(), chainActive.Tip())); + GuessVerificationProgress(chainparams.TxData(), chainActive.Tip())); return true; } @@ -3774,7 +3843,8 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB dbp->nPos = nBlockPos; blkdat.SetLimit(nBlockPos + nSize); blkdat.SetPos(nBlockPos); - CBlock block; + std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>(); + CBlock& block = *pblock; blkdat >> block; nRewind = blkdat.GetPos(); @@ -3792,7 +3862,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB if (mapBlockIndex.count(hash) == 0 || (mapBlockIndex[hash]->nStatus & BLOCK_HAVE_DATA) == 0) { LOCK(cs_main); CValidationState state; - if (AcceptBlock(block, state, chainparams, NULL, true, dbp, NULL)) + if (AcceptBlock(pblock, state, chainparams, NULL, true, dbp, NULL)) nLoaded++; if (state.IsError()) break; @@ -3819,16 +3889,17 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB std::pair<std::multimap<uint256, CDiskBlockPos>::iterator, std::multimap<uint256, CDiskBlockPos>::iterator> range = mapBlocksUnknownParent.equal_range(head); while (range.first != range.second) { std::multimap<uint256, CDiskBlockPos>::iterator it = range.first; - if (ReadBlockFromDisk(block, it->second, chainparams.GetConsensus())) + std::shared_ptr<CBlock> pblockrecursive = std::make_shared<CBlock>(); + if (ReadBlockFromDisk(*pblockrecursive, it->second, chainparams.GetConsensus())) { - LogPrint("reindex", "%s: Processing out of order child %s of %s\n", __func__, block.GetHash().ToString(), + LogPrint("reindex", "%s: Processing out of order child %s of %s\n", __func__, pblockrecursive->GetHash().ToString(), head.ToString()); LOCK(cs_main); CValidationState dummy; - if (AcceptBlock(block, dummy, chainparams, NULL, true, &it->second, NULL)) + if (AcceptBlock(pblockrecursive, dummy, chainparams, NULL, true, &it->second, NULL)) { nLoaded++; - queue.push_back(block.GetHash()); + queue.push_back(pblockrecursive->GetHash()); } } range.first++; @@ -4164,6 +4235,24 @@ void DumpMempool(void) } } +//! Guess how far we are in the verification process at the given block index +double GuessVerificationProgress(const ChainTxData& data, CBlockIndex *pindex) { + if (pindex == NULL) + return 0.0; + + int64_t nNow = time(NULL); + + double fTxTotal; + + if (pindex->nChainTx <= data.nTxCount) { + fTxTotal = data.nTxCount + (nNow - data.nTime) * data.dTxRate; + } else { + fTxTotal = pindex->nChainTx + (nNow - pindex->GetBlockTime()) * data.dTxRate; + } + + return pindex->nChainTx / fTxTotal; +} + class CMainCleanup { public: diff --git a/src/validation.h b/src/validation.h index 631602a701..e57c636422 100644 --- a/src/validation.h +++ b/src/validation.h @@ -42,6 +42,7 @@ class CScriptCheck; class CTxMemPool; class CValidationInterface; class CValidationState; +struct ChainTxData; struct PrecomputedTransactionData; struct LockPoints; @@ -185,6 +186,9 @@ extern CAmount maxTxFee; extern int64_t nMaxTipAge; extern bool fEnableReplacement; +/** Block hash whose ancestors we will assume to have valid scripts without checking them. */ +extern uint256 hashAssumeValid; + /** Best header we've seen so far (used for getheaders queries' starting points). */ extern CBlockIndex *pindexBestHeader; @@ -245,7 +249,7 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons * @param[in] chainparams The params for the chain we want to connect to * @param[out] ppindex If set, the pointer will be set to point to the last new block index object for the given headers */ -bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex=NULL); +bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& block, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex=NULL); /** Check whether enough disk space is available for an incoming block */ bool CheckDiskSpace(uint64_t nAdditionalBytes = 0); @@ -281,6 +285,9 @@ bool GetTransaction(const uint256 &hash, CTransactionRef &tx, const Consensus::P bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock = std::shared_ptr<const CBlock>()); CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams); +/** Guess verification progress (as a fraction between 0.0=genesis and 1.0=current tip). */ +double GuessVerificationProgress(const ChainTxData& data, CBlockIndex* pindex); + /** * Prune block and undo files (blk???.dat and undo???.dat) so that the disk space used is less than a user-defined target. * The user sets the target (in MB) on the command line or in config file. This will be run on startup and whenever new @@ -309,6 +316,8 @@ CBlockIndex * InsertBlockIndex(uint256 hash); void FlushStateToDisk(); /** Prune block files and flush state to disk. */ void PruneAndFlush(); +/** Prune block files up to a given height */ +void PruneBlockFilesManual(int nPruneUpToHeight); /** (try to) add transaction to memory pool **/ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, bool fLimitFree, diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp index 215c342dea..d4121a28bc 100644 --- a/src/validationinterface.cpp +++ b/src/validationinterface.cpp @@ -22,6 +22,7 @@ void RegisterValidationInterface(CValidationInterface* pwalletIn) { g_signals.BlockChecked.connect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2)); g_signals.ScriptForMining.connect(boost::bind(&CValidationInterface::GetScriptForMining, pwalletIn, _1)); g_signals.BlockFound.connect(boost::bind(&CValidationInterface::ResetRequestCount, pwalletIn, _1)); + g_signals.NewPoWValidBlock.connect(boost::bind(&CValidationInterface::NewPoWValidBlock, pwalletIn, _1, _2)); } void UnregisterValidationInterface(CValidationInterface* pwalletIn) { @@ -34,6 +35,7 @@ void UnregisterValidationInterface(CValidationInterface* pwalletIn) { g_signals.UpdatedTransaction.disconnect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1)); g_signals.SyncTransaction.disconnect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2, _3)); g_signals.UpdatedBlockTip.disconnect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1, _2, _3)); + g_signals.NewPoWValidBlock.disconnect(boost::bind(&CValidationInterface::NewPoWValidBlock, pwalletIn, _1, _2)); } void UnregisterAllValidationInterfaces() { @@ -46,4 +48,5 @@ void UnregisterAllValidationInterfaces() { g_signals.UpdatedTransaction.disconnect_all_slots(); g_signals.SyncTransaction.disconnect_all_slots(); g_signals.UpdatedBlockTip.disconnect_all_slots(); + g_signals.NewPoWValidBlock.disconnect_all_slots(); } diff --git a/src/validationinterface.h b/src/validationinterface.h index 717026389c..594072719c 100644 --- a/src/validationinterface.h +++ b/src/validationinterface.h @@ -8,6 +8,7 @@ #include <boost/signals2/signal.hpp> #include <boost/shared_ptr.hpp> +#include <memory> class CBlock; class CBlockIndex; @@ -40,6 +41,7 @@ protected: virtual void BlockChecked(const CBlock&, const CValidationState&) {} virtual void GetScriptForMining(boost::shared_ptr<CReserveScript>&) {}; virtual void ResetRequestCount(const uint256 &hash) {}; + virtual void NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& block) {}; friend void ::RegisterValidationInterface(CValidationInterface*); friend void ::UnregisterValidationInterface(CValidationInterface*); friend void ::UnregisterAllValidationInterfaces(); @@ -66,6 +68,10 @@ struct CMainSignals { boost::signals2::signal<void (boost::shared_ptr<CReserveScript>&)> ScriptForMining; /** Notifies listeners that a block has been successfully mined */ boost::signals2::signal<void (const uint256 &)> BlockFound; + /** + * Notifies listeners that a block which builds directly on our current tip + * has been received and connected to the headers tree, though not validated yet */ + boost::signals2::signal<void (const CBlockIndex *, const std::shared_ptr<const CBlock>&)> NewPoWValidBlock; }; CMainSignals& GetMainSignals(); diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index a1912a78ec..7d4ed70ed9 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -1048,8 +1048,8 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) } } - if (fRescan && fRunScan && requests.size() && nLowestTimestamp <= chainActive.Tip()->GetBlockTime()) { - CBlockIndex* pindex = nLowestTimestamp > minimumTimestamp ? chainActive.FindLatestBefore(nLowestTimestamp) : chainActive.Genesis(); + if (fRescan && fRunScan && requests.size() && nLowestTimestamp <= chainActive.Tip()->GetBlockTimeMax()) { + CBlockIndex* pindex = nLowestTimestamp > minimumTimestamp ? chainActive.FindEarliestAtLeast(nLowestTimestamp) : chainActive.Genesis(); if (pindex) { pwalletMain->ScanForWalletTransactions(pindex, true); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 4dda480216..2428ef04e2 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -446,9 +446,9 @@ UniValue listaddressgroupings(const JSONRPCRequest& request) "[\n" " [\n" " [\n" - " \"address\", (string) The bitcoin address\n" + " \"address\", (string) The bitcoin address\n" " amount, (numeric) The amount in " + CURRENCY_UNIT + "\n" - " \"account\" (string, optional) The account (DEPRECATED)\n" + " \"account\" (string, optional) DEPRECATED. The account\n" " ]\n" " ,...\n" " ]\n" @@ -668,7 +668,7 @@ UniValue getbalance(const JSONRPCRequest& request) "\nArguments:\n" "1. \"account\" (string, optional) DEPRECATED. The selected account, or \"*\" for entire wallet. It may be the default account using \"\".\n" "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" - "3. include_watchonly (bool, optional, default=false) Also include balance in watchonly addresses (see 'importaddress')\n" + "3. include_watchonly (bool, optional, default=false) Also include balance in watch-only addresses (see 'importaddress')\n" "\nResult:\n" "amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this account.\n" "\nExamples:\n" @@ -1258,7 +1258,7 @@ UniValue listreceivedbyaddress(const JSONRPCRequest& request) "\nArguments:\n" "1. minconf (numeric, optional, default=1) The minimum number of confirmations before payments are included.\n" "2. include_empty (bool, optional, default=false) Whether to include addresses that haven't received any payments.\n" - "3. include_watchonly (bool, optional, default=false) Whether to include watchonly addresses (see 'importaddress').\n" + "3. include_watchonly (bool, optional, default=false) Whether to include watch-only addresses (see 'importaddress').\n" "\nResult:\n" "[\n" @@ -1268,7 +1268,11 @@ UniValue listreceivedbyaddress(const JSONRPCRequest& request) " \"account\" : \"accountname\", (string) DEPRECATED. The account of the receiving address. The default account is \"\".\n" " \"amount\" : x.xxx, (numeric) The total amount in " + CURRENCY_UNIT + " received by the address\n" " \"confirmations\" : n, (numeric) The number of confirmations of the most recent transaction included\n" - " \"label\" : \"label\" (string) A comment for the address/transaction, if any\n" + " \"label\" : \"label\", (string) A comment for the address/transaction, if any\n" + " \"txids\": [\n" + " n, (numeric) The ids of transactions received with the address \n" + " ...\n" + " ]\n" " }\n" " ,...\n" "]\n" @@ -1296,7 +1300,7 @@ UniValue listreceivedbyaccount(const JSONRPCRequest& request) "\nArguments:\n" "1. minconf (numeric, optional, default=1) The minimum number of confirmations before payments are included.\n" "2. include_empty (bool, optional, default=false) Whether to include accounts that haven't received any payments.\n" - "3. include_watchonly (bool, optional, default=false) Whether to include watchonly addresses (see 'importaddress').\n" + "3. include_watchonly (bool, optional, default=false) Whether to include watch-only addresses (see 'importaddress').\n" "\nResult:\n" "[\n" @@ -1433,7 +1437,7 @@ UniValue listtransactions(const JSONRPCRequest& request) "1. \"account\" (string, optional) DEPRECATED. The account name. Should be \"*\".\n" "2. count (numeric, optional, default=10) The number of transactions to return\n" "3. skip (numeric, optional, default=0) The number of transactions to skip\n" - "4. include_watchonly (bool, optional, default=false) Include transactions to watchonly addresses (see 'importaddress')\n" + "4. include_watchonly (bool, optional, default=false) Include transactions to watch-only addresses (see 'importaddress')\n" "\nResult:\n" "[\n" " {\n" @@ -1448,14 +1452,14 @@ UniValue listtransactions(const JSONRPCRequest& request) " \"amount\": x.xxx, (numeric) The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and for the\n" " 'move' category for moves outbound. It is positive for the 'receive' category,\n" " and for the 'move' category for inbound funds.\n" + " \"label\": \"label\", (string) A comment for the address/transaction, if any\n" " \"vout\": n, (numeric) the vout value\n" " \"fee\": x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n" " 'send' category of transactions.\n" - " \"abandoned\": xxx (bool) 'true' if the transaction has been abandoned (inputs are respendable).\n" " \"confirmations\": n, (numeric) The number of confirmations for the transaction. Available for 'send' and \n" " 'receive' category of transactions. Negative confirmations indicate the\n" " transaction conflicts with the block chain\n" - " \"trusted\": xxx (bool) Whether we consider the outputs of this unconfirmed transaction safe to spend.\n" + " \"trusted\": xxx, (bool) Whether we consider the outputs of this unconfirmed transaction safe to spend.\n" " \"blockhash\": \"hashvalue\", (string) The block hash containing the transaction. Available for 'send' and 'receive'\n" " category of transactions.\n" " \"blockindex\": n, (numeric) The index of the transaction in the block that includes it. Available for 'send' and 'receive'\n" @@ -1466,12 +1470,13 @@ UniValue listtransactions(const JSONRPCRequest& request) " \"timereceived\": xxx, (numeric) The time received in seconds since epoch (midnight Jan 1 1970 GMT). Available \n" " for 'send' and 'receive' category of transactions.\n" " \"comment\": \"...\", (string) If a comment is associated with the transaction.\n" - " \"label\": \"label\" (string) A comment for the address/transaction, if any\n" - " \"otheraccount\": \"accountname\", (string) For the 'move' category of transactions, the account the funds came \n" + " \"otheraccount\": \"accountname\", (string) DEPRECATED. For the 'move' category of transactions, the account the funds came \n" " from (for receiving funds, positive amounts), or went to (for sending funds,\n" " negative amounts).\n" - " \"bip125-replaceable\": \"yes|no|unknown\" (string) Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n" + " \"bip125-replaceable\": \"yes|no|unknown\", (string) Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n" " may be unknown for unconfirmed transactions not in the mempool\n" + " \"abandoned\": xxx (bool) 'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n" + " 'send' category of transactions.\n" " }\n" "]\n" @@ -1558,7 +1563,7 @@ UniValue listaccounts(const JSONRPCRequest& request) "\nDEPRECATED. Returns Object that has account names as keys, account balances as values.\n" "\nArguments:\n" "1. minconf (numeric, optional, default=1) Only include transactions with at least this many confirmations\n" - "2. include_watchonly (bool, optional, default=false) Include balances in watchonly addresses (see 'importaddress')\n" + "2. include_watchonly (bool, optional, default=false) Include balances in watch-only addresses (see 'importaddress')\n" "\nResult:\n" "{ (json object where keys are account names, and values are numeric balances\n" " \"account\": x.xxx, (numeric) The property name is the account name, and the value is the total balance for the account.\n" @@ -1638,7 +1643,7 @@ UniValue listsinceblock(const JSONRPCRequest& request) "\nArguments:\n" "1. \"blockhash\" (string, optional) The block hash to list transactions since\n" "2. target_confirmations: (numeric, optional) The confirmations required, must be 1 or more\n" - "3. include_watchonly: (bool, optional, default=false) Include transactions to watchonly addresses (see 'importaddress')" + "3. include_watchonly: (bool, optional, default=false) Include transactions to watch-only addresses (see 'importaddress')" "\nResult:\n" "{\n" " \"transactions\": [\n" @@ -1657,6 +1662,9 @@ UniValue listsinceblock(const JSONRPCRequest& request) " \"txid\": \"transactionid\", (string) The transaction id. Available for 'send' and 'receive' category of transactions.\n" " \"time\": xxx, (numeric) The transaction time in seconds since epoch (Jan 1 1970 GMT).\n" " \"timereceived\": xxx, (numeric) The time received in seconds since epoch (Jan 1 1970 GMT). Available for 'send' and 'receive' category of transactions.\n" + " \"bip125-replaceable\": \"yes|no|unknown\", (string) Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n" + " may be unknown for unconfirmed transactions not in the mempool\n" + " \"abandoned\": xxx, (bool) 'true' if the transaction has been abandoned (inputs are respendable). Only available for the 'send' category of transactions.\n" " \"comment\": \"...\", (string) If a comment is associated with the transaction.\n" " \"label\" : \"label\" (string) A comment for the address/transaction, if any\n" " \"to\": \"...\", (string) If a comment to is associated with the transaction.\n" @@ -1730,10 +1738,12 @@ UniValue gettransaction(const JSONRPCRequest& request) "\nGet detailed information about in-wallet transaction <txid>\n" "\nArguments:\n" "1. \"txid\" (string, required) The transaction id\n" - "2. \"include_watchonly\" (bool, optional, default=false) Whether to include watchonly addresses in balance calculation and details[]\n" + "2. \"include_watchonly\" (bool, optional, default=false) Whether to include watch-only addresses in balance calculation and details[]\n" "\nResult:\n" "{\n" " \"amount\" : x.xxx, (numeric) The transaction amount in " + CURRENCY_UNIT + "\n" + " \"fee\": x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n" + " 'send' category of transactions.\n" " \"confirmations\" : n, (numeric) The number of confirmations\n" " \"blockhash\" : \"hash\", (string) The block hash\n" " \"blockindex\" : xx, (numeric) The index of the transaction in the block that includes it\n" @@ -1741,16 +1751,20 @@ UniValue gettransaction(const JSONRPCRequest& request) " \"txid\" : \"transactionid\", (string) The transaction id.\n" " \"time\" : ttt, (numeric) The transaction time in seconds since epoch (1 Jan 1970 GMT)\n" " \"timereceived\" : ttt, (numeric) The time received in seconds since epoch (1 Jan 1970 GMT)\n" - " \"bip125-replaceable\": \"yes|no|unknown\" (string) Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n" + " \"bip125-replaceable\": \"yes|no|unknown\", (string) Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n" " may be unknown for unconfirmed transactions not in the mempool\n" " \"details\" : [\n" " {\n" - " \"account\" : \"accountname\", (string) DEPRECATED. The account name involved in the transaction, can be \"\" for the default account.\n" - " \"address\" : \"address\", (string) The bitcoin address involved in the transaction\n" + " \"account\" : \"accountname\", (string) DEPRECATED. The account name involved in the transaction, can be \"\" for the default account.\n" + " \"address\" : \"address\", (string) The bitcoin address involved in the transaction\n" " \"category\" : \"send|receive\", (string) The category, either 'send' or 'receive'\n" " \"amount\" : x.xxx, (numeric) The amount in " + CURRENCY_UNIT + "\n" " \"label\" : \"label\", (string) A comment for the address/transaction, if any\n" " \"vout\" : n, (numeric) the vout value\n" + " \"fee\": x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n" + " 'send' category of transactions.\n" + " \"abandoned\": xxx (bool) 'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n" + " 'send' category of transactions.\n" " }\n" " ,...\n" " ],\n" @@ -2291,7 +2305,7 @@ UniValue getwalletinfo(const JSONRPCRequest& request) " \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated\n" " \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n" " \"paytxfee\": x.xxxx, (numeric) the transaction fee configuration, set in " + CURRENCY_UNIT + "/kB\n" - " \"hdmasterkeyid\": \"<hash160>\", (string) the Hash160 of the HD master pubkey\n" + " \"hdmasterkeyid\": \"<hash160>\" (string) the Hash160 of the HD master pubkey\n" "}\n" "\nExamples:\n" + HelpExampleCli("getwalletinfo", "") @@ -2465,7 +2479,8 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) throw runtime_error( "fundrawtransaction \"hexstring\" ( options )\n" "\nAdd inputs to a transaction until it has enough in value to meet its out value.\n" - "This will not modify existing inputs, and will add one change output to the outputs.\n" + "This will not modify existing inputs, and will add at most one change output to the outputs.\n" + "No existing outputs will be modified unless \"subtractFeeFromOutputs\" is specified.\n" "Note that inputs which were signed may need to be resigned after completion since in/outputs have been added.\n" "The inputs added will not be signed, use signrawtransaction for that.\n" "Note that all existing inputs must have their previous output transaction be in the wallet.\n" @@ -2477,11 +2492,17 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) "1. \"hexstring\" (string, required) The hex string of the raw transaction\n" "2. options (object, optional)\n" " {\n" - " \"changeAddress\" (string, optional, default pool address) The bitcoin address to receive the change\n" - " \"changePosition\" (numeric, optional, default random) The index of the change output\n" - " \"includeWatching\" (boolean, optional, default false) Also select inputs which are watch only\n" - " \"lockUnspents\" (boolean, optional, default false) Lock selected unspent outputs\n" - " \"feeRate\" (numeric, optional, default not set: makes wallet determine the fee) Set a specific feerate (" + CURRENCY_UNIT + " per KB)\n" + " \"changeAddress\" (string, optional, default pool address) The bitcoin address to receive the change\n" + " \"changePosition\" (numeric, optional, default random) The index of the change output\n" + " \"includeWatching\" (boolean, optional, default false) Also select inputs which are watch only\n" + " \"lockUnspents\" (boolean, optional, default false) Lock selected unspent outputs\n" + " \"feeRate\" (numeric, optional, default not set: makes wallet determine the fee) Set a specific feerate (" + CURRENCY_UNIT + " per KB)\n" + " \"subtractFeeFromOutputs\" (array, optional) A json array of integers.\n" + " The fee will be equally deducted from the amount of each specified output.\n" + " The outputs are specified by their zero-based index, before any change output is added.\n" + " Those recipients will receive less bitcoins than you enter in their corresponding amount field.\n" + " If no outputs are specified here, the sender pays the fee.\n" + " [vout_index,...]\n" " }\n" " for backward compatibility: passing in a true instead of an object will result in {\"includeWatching\":true}\n" "\nResult:\n" @@ -2490,7 +2511,6 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) " \"fee\": n, (numeric) Fee in " + CURRENCY_UNIT + " the resulting transaction pays\n" " \"changepos\": n (numeric) The position of the added change output, or -1\n" "}\n" - "\"hex\" \n" "\nExamples:\n" "\nCreate a transaction with no inputs\n" + HelpExampleCli("createrawtransaction", "\"[]\" \"{\\\"myaddress\\\":0.01}\"") + @@ -2510,6 +2530,8 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) bool lockUnspents = false; CFeeRate feeRate = CFeeRate(0); bool overrideEstimatedFeerate = false; + UniValue subtractFeeFromOutputs; + set<int> setSubtractFeeFromOutputs; if (request.params.size() > 1) { if (request.params[1].type() == UniValue::VBOOL) { @@ -2528,6 +2550,7 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) {"includeWatching", UniValueType(UniValue::VBOOL)}, {"lockUnspents", UniValueType(UniValue::VBOOL)}, {"feeRate", UniValueType()}, // will be checked below + {"subtractFeeFromOutputs", UniValueType(UniValue::VARR)}, }, true, true); @@ -2554,6 +2577,9 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) feeRate = CFeeRate(AmountFromValue(options["feeRate"])); overrideEstimatedFeerate = true; } + + if (options.exists("subtractFeeFromOutputs")) + subtractFeeFromOutputs = options["subtractFeeFromOutputs"].get_array(); } } @@ -2568,10 +2594,21 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) if (changePosition != -1 && (changePosition < 0 || (unsigned int)changePosition > tx.vout.size())) throw JSONRPCError(RPC_INVALID_PARAMETER, "changePosition out of bounds"); + for (unsigned int idx = 0; idx < subtractFeeFromOutputs.size(); idx++) { + int pos = subtractFeeFromOutputs[idx].get_int(); + if (setSubtractFeeFromOutputs.count(pos)) + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, duplicated position: %d", pos)); + if (pos < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, negative position: %d", pos)); + if (pos >= int(tx.vout.size())) + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, position too large: %d", pos)); + setSubtractFeeFromOutputs.insert(pos); + } + CAmount nFeeOut; string strFailReason; - if(!pwalletMain->FundTransaction(tx, nFeeOut, overrideEstimatedFeerate, feeRate, changePosition, strFailReason, includeWatching, lockUnspents, changeAddress)) + if(!pwalletMain->FundTransaction(tx, nFeeOut, overrideEstimatedFeerate, feeRate, changePosition, strFailReason, includeWatching, lockUnspents, setSubtractFeeFromOutputs, changeAddress)) throw JSONRPCError(RPC_INTERNAL_ERROR, strFailReason); UniValue result(UniValue::VOBJ); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 2775f4def3..b4ea77a8f0 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1479,12 +1479,12 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) pindex = chainActive.Next(pindex); ShowProgress(_("Rescanning..."), 0); // show rescan progress in GUI as dialog or on splashscreen, if -rescan on startup - double dProgressStart = Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), pindex, false); - double dProgressTip = Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), chainActive.Tip(), false); + double dProgressStart = GuessVerificationProgress(chainParams.TxData(), pindex); + double dProgressTip = GuessVerificationProgress(chainParams.TxData(), chainActive.Tip()); while (pindex) { if (pindex->nHeight % 100 == 0 && dProgressTip - dProgressStart > 0.0) - ShowProgress(_("Rescanning..."), std::max(1, std::min(99, (int)((Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), pindex, false) - dProgressStart) / (dProgressTip - dProgressStart) * 100)))); + ShowProgress(_("Rescanning..."), std::max(1, std::min(99, (int)((GuessVerificationProgress(chainParams.TxData(), pindex) - dProgressStart) / (dProgressTip - dProgressStart) * 100)))); CBlock block; ReadBlockFromDisk(block, pindex, Params().GetConsensus()); @@ -1497,7 +1497,7 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) pindex = chainActive.Next(pindex); if (GetTime() >= nNow + 60) { nNow = GetTime(); - LogPrintf("Still rescanning. At block %d. Progress=%f\n", pindex->nHeight, Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), pindex)); + LogPrintf("Still rescanning. At block %d. Progress=%f\n", pindex->nHeight, GuessVerificationProgress(chainParams.TxData(), pindex)); } } ShowProgress(_("Rescanning..."), 100); // hide progress dialog in GUI @@ -2192,14 +2192,15 @@ bool CWallet::SelectCoins(const vector<COutput>& vAvailableCoins, const CAmount& return res; } -bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool overrideEstimatedFeeRate, const CFeeRate& specificFeeRate, int& nChangePosInOut, std::string& strFailReason, bool includeWatching, bool lockUnspents, const CTxDestination& destChange) +bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool overrideEstimatedFeeRate, const CFeeRate& specificFeeRate, int& nChangePosInOut, std::string& strFailReason, bool includeWatching, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, const CTxDestination& destChange) { vector<CRecipient> vecSend; // Turn the txout set into a CRecipient vector - BOOST_FOREACH(const CTxOut& txOut, tx.vout) + for (size_t idx = 0; idx < tx.vout.size(); idx++) { - CRecipient recipient = {txOut.scriptPubKey, txOut.nValue, false}; + const CTxOut& txOut = tx.vout[idx]; + CRecipient recipient = {txOut.scriptPubKey, txOut.nValue, setSubtractFeeFromOutputs.count(idx) == 1}; vecSend.push_back(recipient); } @@ -2221,6 +2222,10 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool ov if (nChangePosInOut != -1) tx.vout.insert(tx.vout.begin() + nChangePosInOut, wtx.tx->vout[nChangePosInOut]); + // Copy output sizes from new transaction; they may have had the fee subtracted from them + for (unsigned int idx = 0; idx < tx.vout.size(); idx++) + tx.vout[idx].nValue = wtx.tx->vout[idx].nValue; + // Add new txins (keeping original txin scriptSig/order) BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin) { @@ -2336,7 +2341,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt } } - if (txout.IsDust(::minRelayTxFee)) + if (txout.IsDust(dustRelayFee)) { if (recipient.fSubtractFeeFromAmount && nFeeRet > 0) { @@ -2414,16 +2419,16 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt // We do not move dust-change to fees, because the sender would end up paying more than requested. // This would be against the purpose of the all-inclusive feature. // So instead we raise the change and deduct from the recipient. - if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(::minRelayTxFee)) + if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(dustRelayFee)) { - CAmount nDust = newTxOut.GetDustThreshold(::minRelayTxFee) - newTxOut.nValue; + CAmount nDust = newTxOut.GetDustThreshold(dustRelayFee) - newTxOut.nValue; newTxOut.nValue += nDust; // raise change until no more dust for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient { if (vecSend[i].fSubtractFeeFromAmount) { txNew.vout[i].nValue -= nDust; - if (txNew.vout[i].IsDust(::minRelayTxFee)) + if (txNew.vout[i].IsDust(dustRelayFee)) { strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); return false; @@ -2435,7 +2440,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt // Never create dust outputs; if we would, just // add the dust to the fee. - if (newTxOut.IsDust(::minRelayTxFee)) + if (newTxOut.IsDust(dustRelayFee)) { nChangePosInOut = -1; nFeeRet += nChange; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index b9fa6bb24b..92ad098486 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -780,7 +780,7 @@ public: * Insert additional inputs into the transaction by * calling CreateTransaction(); */ - bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool overrideEstimatedFeeRate, const CFeeRate& specificFeeRate, int& nChangePosInOut, std::string& strFailReason, bool includeWatching, bool lockUnspents, const CTxDestination& destChange = CNoDestination()); + bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool overrideEstimatedFeeRate, const CFeeRate& specificFeeRate, int& nChangePosInOut, std::string& strFailReason, bool includeWatching, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, const CTxDestination& destChange = CNoDestination()); /** * Create a new transaction paying the recipients with a set of coins |