aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml21
-rw-r--r--README.md2
-rw-r--r--build-aux/m4/ax_boost_chrono.m43
-rw-r--r--build-aux/m4/ax_boost_program_options.m4108
-rw-r--r--build-aux/m4/ax_boost_unit_test_framework.m43
-rw-r--r--configure.ac26
-rw-r--r--contrib/README.md2
-rwxr-xr-xcontrib/devtools/security-check.py4
-rwxr-xr-xcontrib/devtools/symbol-check.py2
-rwxr-xr-xcontrib/devtools/test-security-check.py16
-rwxr-xr-xcontrib/gitian-build.py220
-rwxr-xr-xcontrib/gitian-build.sh391
-rw-r--r--contrib/gitian-descriptors/gitian-linux.yml17
-rw-r--r--contrib/gitian-descriptors/gitian-osx-signer.yml2
-rw-r--r--contrib/gitian-descriptors/gitian-osx.yml7
-rw-r--r--contrib/gitian-descriptors/gitian-win-signer.yml2
-rw-r--r--contrib/gitian-descriptors/gitian-win.yml10
-rw-r--r--contrib/zmq/zmq_sub.py2
-rw-r--r--contrib/zmq/zmq_sub3.4.py2
-rw-r--r--depends/Makefile2
-rw-r--r--depends/README.md2
-rw-r--r--depends/hosts/darwin.mk2
-rw-r--r--depends/packages/boost.mk2
-rw-r--r--depends/packages/packages.mk1
-rw-r--r--depends/packages/qt.mk8
-rw-r--r--depends/packages/zeromq.mk2
-rw-r--r--depends/patches/qt/fix_rcc_determinism.patch15
-rw-r--r--doc/build-osx.md2
-rw-r--r--doc/build-unix.md2
-rw-r--r--doc/release-notes-pr12257.md9
-rw-r--r--doc/release-notes.md10
-rw-r--r--doc/release-notes/release-notes-0.16.2.md116
-rw-r--r--doc/release-process.md8
-rw-r--r--share/qt/Info.plist.in2
-rw-r--r--src/Makefile.am5
-rw-r--r--src/Makefile.bench.include1
-rw-r--r--src/Makefile.qt.include2
-rw-r--r--src/Makefile.test.include1
-rw-r--r--src/addrman.cpp2
-rw-r--r--src/addrman.h4
-rw-r--r--src/bench/bech32.cpp37
-rw-r--r--src/bench/bench_bitcoin.cpp13
-rw-r--r--src/bench/coin_selection.cpp24
-rw-r--r--src/bench/prevector.cpp28
-rw-r--r--src/bitcoin-cli.cpp1
-rw-r--r--src/bitcoind.cpp6
-rw-r--r--src/bloom.cpp2
-rw-r--r--src/bloom.h4
-rw-r--r--src/chainparamsbase.cpp1
-rw-r--r--src/checkqueue.h4
-rw-r--r--src/clientversion.cpp4
-rw-r--r--src/coins.h4
-rw-r--r--src/compat/byteswap.h4
-rw-r--r--src/compat/glibc_compat.cpp45
-rw-r--r--src/consensus/tx_verify.h2
-rw-r--r--src/core_io.h5
-rw-r--r--src/core_read.cpp56
-rw-r--r--src/core_write.cpp23
-rw-r--r--src/httprpc.cpp4
-rw-r--r--src/httpserver.cpp3
-rw-r--r--src/httpserver.h2
-rw-r--r--src/init.cpp14
-rw-r--r--src/interfaces/handler.cpp2
-rw-r--r--src/interfaces/wallet.cpp6
-rw-r--r--src/interfaces/wallet.h4
-rw-r--r--src/keystore.h21
-rw-r--r--src/limitedmap.h2
-rw-r--r--src/net.cpp41
-rw-r--r--src/net.h13
-rw-r--r--src/net_processing.cpp236
-rw-r--r--src/net_processing.h30
-rw-r--r--src/netbase.cpp4
-rw-r--r--src/outputtype.cpp101
-rw-r--r--src/outputtype.h49
-rw-r--r--src/pubkey.cpp8
-rw-r--r--src/pubkey.h1
-rw-r--r--src/qt/bitcoin.cpp1
-rw-r--r--src/qt/bitcoingui.cpp6
-rw-r--r--src/qt/bitcoingui.h2
-rw-r--r--src/qt/bitcoinstrings.cpp253
-rw-r--r--src/qt/clientmodel.cpp2
-rw-r--r--src/qt/guiconstants.h2
-rw-r--r--src/qt/guiutil.cpp64
-rw-r--r--src/qt/guiutil.h19
-rw-r--r--src/qt/locale/bitcoin_en.ts1289
-rw-r--r--src/qt/macdockiconhandler.mm6
-rw-r--r--src/qt/macnotificationhandler.mm2
-rw-r--r--src/qt/modaloverlay.cpp2
-rw-r--r--src/qt/optionsdialog.h2
-rw-r--r--src/qt/optionsmodel.cpp2
-rw-r--r--src/qt/qvalidatedlineedit.h2
-rw-r--r--src/qt/receivecoinsdialog.cpp3
-rw-r--r--src/qt/trafficgraphwidget.cpp2
-rw-r--r--src/qt/transactiondesc.cpp10
-rw-r--r--src/qt/transactionrecord.cpp8
-rw-r--r--src/qt/transactionrecord.h4
-rw-r--r--src/qt/transactiontablemodel.cpp9
-rw-r--r--src/qt/walletmodel.cpp5
-rw-r--r--src/qt/walletmodel.h1
-rw-r--r--src/qt/walletview.cpp2
-rw-r--r--src/reverse_iterator.h10
-rw-r--r--src/rpc/blockchain.cpp289
-rw-r--r--src/rpc/client.cpp18
-rw-r--r--src/rpc/mining.cpp1
-rw-r--r--src/rpc/misc.cpp21
-rw-r--r--src/rpc/rawtransaction.cpp705
-rw-r--r--src/rpc/rawtransaction.h3
-rw-r--r--src/rpc/server.cpp3
-rw-r--r--src/rpc/server.h2
-rw-r--r--src/script/bitcoinconsensus.cpp2
-rw-r--r--src/script/interpreter.cpp6
-rw-r--r--src/script/sign.cpp213
-rw-r--r--src/script/sign.h586
-rw-r--r--src/script/standard.cpp2
-rw-r--r--src/serialize.h18
-rw-r--r--src/streams.h4
-rw-r--r--src/sync.h4
-rw-r--r--src/test/README.md12
-rw-r--r--src/test/data/key_io_invalid.json98
-rw-r--r--src/test/data/script_tests.json2
-rw-r--r--src/test/dbwrapper_tests.cpp26
-rw-r--r--src/test/denialofservice_tests.cpp2
-rw-r--r--src/test/limitedmap_tests.cpp4
-rw-r--r--src/test/mempool_tests.cpp2
-rw-r--r--src/test/miner_tests.cpp2
-rw-r--r--src/test/net_tests.cpp4
-rw-r--r--src/test/netbase_tests.cpp8
-rw-r--r--src/test/prevector_tests.cpp2
-rw-r--r--src/test/raii_event_tests.cpp8
-rw-r--r--src/test/script_tests.cpp144
-rw-r--r--src/test/serialize_tests.cpp4
-rw-r--r--src/test/streams_tests.cpp20
-rw-r--r--src/test/test_bitcoin.cpp39
-rw-r--r--src/test/test_bitcoin.h6
-rw-r--r--src/test/transaction_tests.cpp1
-rw-r--r--src/test/util_tests.cpp23
-rw-r--r--src/threadsafety.h2
-rw-r--r--src/timedata.h2
-rw-r--r--src/torcontrol.cpp5
-rw-r--r--src/txdb.cpp5
-rw-r--r--src/txdb.h2
-rw-r--r--src/txmempool.cpp11
-rw-r--r--src/txmempool.h8
-rw-r--r--src/util.cpp57
-rw-r--r--src/util.h29
-rw-r--r--src/utilmemory.h19
-rw-r--r--src/utilstrencodings.cpp10
-rw-r--r--src/utilstrencodings.h10
-rw-r--r--src/validation.cpp45
-rw-r--r--src/validation.h22
-rw-r--r--src/validationinterface.cpp10
-rw-r--r--src/validationinterface.h7
-rw-r--r--src/wallet/coincontrol.cpp23
-rw-r--r--src/wallet/coincontrol.h16
-rw-r--r--src/wallet/coinselection.cpp152
-rw-r--r--src/wallet/coinselection.h55
-rw-r--r--src/wallet/crypter.h6
-rw-r--r--src/wallet/init.cpp2
-rw-r--r--src/wallet/rpcdump.cpp59
-rw-r--r--src/wallet/rpcwallet.cpp638
-rw-r--r--src/wallet/rpcwallet.h3
-rw-r--r--src/wallet/test/coinselector_tests.cpp120
-rw-r--r--src/wallet/test/psbt_wallet_tests.cpp150
-rw-r--r--src/wallet/test/wallet_tests.cpp15
-rw-r--r--src/wallet/wallet.cpp483
-rw-r--r--src/wallet/wallet.h141
-rw-r--r--src/wallet/walletdb.cpp34
-rw-r--r--src/wallet/walletdb.h1
-rw-r--r--test/README.md6
-rw-r--r--test/functional/README.md1
-rw-r--r--test/functional/data/rpc_psbt.json98
-rwxr-xr-xtest/functional/feature_config_args.py10
-rwxr-xr-xtest/functional/interface_bitcoin_cli.py3
-rwxr-xr-xtest/functional/interface_zmq.py19
-rwxr-xr-xtest/functional/p2p_leak.py27
-rwxr-xr-xtest/functional/p2p_segwit.py41
-rwxr-xr-xtest/functional/rpc_createmultisig.py98
-rwxr-xr-xtest/functional/rpc_psbt.py190
-rwxr-xr-xtest/functional/rpc_scantxoutset.py48
-rwxr-xr-xtest/functional/rpc_users.py2
-rwxr-xr-xtest/functional/rpc_zmq.py5
-rwxr-xr-xtest/functional/test_framework/messages.py2
-rwxr-xr-xtest/functional/test_framework/mininode.py12
-rwxr-xr-xtest/functional/test_framework/test_framework.py17
-rwxr-xr-xtest/functional/test_framework/test_node.py3
-rwxr-xr-xtest/functional/test_runner.py6
-rwxr-xr-xtest/functional/wallet_abandonconflict.py12
-rwxr-xr-xtest/functional/wallet_basic.py9
-rwxr-xr-xtest/functional/wallet_disableprivatekeys.py32
-rwxr-xr-xtest/functional/wallet_groups.py67
-rwxr-xr-xtest/functional/wallet_importprunedfunds.py25
-rwxr-xr-xtest/lint/lint-circular-dependencies.sh84
-rwxr-xr-xtest/lint/lint-includes.sh3
193 files changed, 5574 insertions, 3222 deletions
diff --git a/.travis.yml b/.travis.yml
index ec78896f44..d8216bc00d 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -15,6 +15,7 @@ env:
global:
- MAKEJOBS=-j3
- RUN_TESTS=false
+ - DOCKER_NAME_TAG=ubuntu:18.04
- BOOST_TEST_RANDOM=1$TRAVIS_BUILD_ID
- CCACHE_SIZE=100M
- CCACHE_TEMPDIR=/tmp/.ccache-temp
@@ -34,25 +35,27 @@ env:
# 32-bit + dash
- HOST=i686-pc-linux-gnu PACKAGES="g++-multilib python3-zmq" DEP_OPTS="NO_QT=1" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-zmq --enable-glibc-back-compat --enable-reduce-exports LDFLAGS=-static-libstdc++" CONFIG_SHELL="/bin/dash"
# x86_64 Linux (uses qt5 dev package instead of depends Qt to speed up build and avoid timeout)
- - HOST=x86_64-unknown-linux-gnu PACKAGES="python3-zmq qtbase5-dev qttools5-dev-tools protobuf-compiler libdbus-1-dev libharfbuzz-dev libprotobuf-dev" DEP_OPTS="NO_QT=1 NO_UPNP=1 DEBUG=1 ALLOW_HOST_PACKAGES=1" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-zmq --with-gui=qt5 --enable-glibc-back-compat --enable-reduce-exports --enable-debug"
+ - HOST=x86_64-unknown-linux-gnu PACKAGES="python3-zmq qtbase5-dev qttools5-dev-tools protobuf-compiler libdbus-1-dev libharfbuzz-dev libprotobuf-dev" DEP_OPTS="NO_QT=1 NO_UPNP=1 DEBUG=1 ALLOW_HOST_PACKAGES=1" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-zmq --with-gui=qt5 --enable-glibc-back-compat --enable-reduce-exports --enable-debug CXXFLAGS=\"-g0 -O2\""
# x86_64 Linux (Qt5 & system libs)
- - HOST=x86_64-unknown-linux-gnu PACKAGES="python3-zmq qtbase5-dev qttools5-dev-tools libssl1.0-dev libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-program-options-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libzmq3-dev libprotobuf-dev protobuf-compiler libqrencode-dev" NO_DEPENDS=1 RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-zmq --with-incompatible-bdb --enable-glibc-back-compat --enable-reduce-exports --with-gui=qt5 CPPFLAGS=-DDEBUG_LOCKORDER"
+ - HOST=x86_64-unknown-linux-gnu PACKAGES="python3-zmq qtbase5-dev qttools5-dev-tools libssl1.0-dev libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libzmq3-dev libprotobuf-dev protobuf-compiler libqrencode-dev" NO_DEPENDS=1 RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-zmq --with-incompatible-bdb --enable-glibc-back-compat --enable-reduce-exports --with-gui=qt5 CPPFLAGS=-DDEBUG_LOCKORDER"
# x86_64 Linux, No wallet
- HOST=x86_64-unknown-linux-gnu PACKAGES="python3" DEP_OPTS="NO_WALLET=1" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports"
# Cross-Mac
- - HOST=x86_64-apple-darwin11 PACKAGES="cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev python3-setuptools-git" BITCOIN_CONFIG="--enable-gui --enable-reduce-exports --enable-werror" OSX_SDK=10.11 GOAL="all deploy"
+ - HOST=x86_64-apple-darwin14 PACKAGES="cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev python3-setuptools-git" BITCOIN_CONFIG="--enable-gui --enable-reduce-exports --enable-werror" OSX_SDK=10.11 GOAL="all deploy"
before_install:
- export PATH=$(echo $PATH | tr ':' "\n" | sed '/\/opt\/python/d' | tr "\n" ":" | sed "s|::|:|g")
install:
+ - travis_retry docker pull $DOCKER_NAME_TAG
- env | grep -E '^(CCACHE_|WINEDEBUG|BOOST_TEST_RANDOM|CONFIG_SHELL)' | tee /tmp/env
- if [[ $HOST = *-mingw32 ]]; then DOCKER_ADMIN="--cap-add SYS_ADMIN"; fi
- - DOCKER_ID=$(docker run $DOCKER_ADMIN -idt --mount type=bind,src=$TRAVIS_BUILD_DIR,dst=$TRAVIS_BUILD_DIR --mount type=bind,src=$CCACHE_DIR,dst=$CCACHE_DIR -w $TRAVIS_BUILD_DIR --env-file /tmp/env ubuntu:18.04)
+ - DOCKER_ID=$(docker run $DOCKER_ADMIN -idt --mount type=bind,src=$TRAVIS_BUILD_DIR,dst=$TRAVIS_BUILD_DIR --mount type=bind,src=$CCACHE_DIR,dst=$CCACHE_DIR -w $TRAVIS_BUILD_DIR --env-file /tmp/env $DOCKER_NAME_TAG)
- DOCKER_EXEC () { docker exec $DOCKER_ID bash -c "cd $PWD && $*"; }
- if [ -n "$DPKG_ADD_ARCH" ]; then DOCKER_EXEC dpkg --add-architecture "$DPKG_ADD_ARCH" ; fi
- travis_retry DOCKER_EXEC apt-get update
- travis_retry DOCKER_EXEC apt-get install --no-install-recommends --no-upgrade -qq $PACKAGES $DOCKER_PACKAGES
before_script:
+ - DOCKER_EXEC echo \> \$HOME/.bitcoin # Make sure default datadir does not exist and is never read by creating a dummy file
- mkdir -p depends/SDKs depends/sdk-sources
- if [ -n "$OSX_SDK" -a ! -f depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz ]; then curl --location --fail $SDK_URL/MacOSX${OSX_SDK}.sdk.tar.gz -o depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz; fi
- if [ -n "$OSX_SDK" -a -f depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz ]; then tar -C depends/SDKs -xf depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz; fi
@@ -80,15 +83,13 @@ after_script:
jobs:
include:
- stage: lint
+ env:
sudo: false
cache: false
- addons:
- apt:
- packages:
- - python3-pip
- - shellcheck
+ language: python
+ python: '3.6'
install:
- - travis_retry pip3 install flake8 --user
+ - travis_retry pip install flake8
before_script:
- git fetch --unshallow
script:
diff --git a/README.md b/README.md
index 4e830109c2..55b85da977 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,7 @@ out collectively by the network. Bitcoin Core is the name of open source
software which enables the use of this currency.
For more information, as well as an immediately useable, binary version of
-the Bitcoin Core software, see https://bitcoin.org/en/download, or read the
+the Bitcoin Core software, see https://bitcoincore.org/en/download/, or read the
[original whitepaper](https://bitcoincore.org/bitcoin.pdf).
License
diff --git a/build-aux/m4/ax_boost_chrono.m4 b/build-aux/m4/ax_boost_chrono.m4
index e9b0f2061c..6ea77b9b3e 100644
--- a/build-aux/m4/ax_boost_chrono.m4
+++ b/build-aux/m4/ax_boost_chrono.m4
@@ -81,7 +81,6 @@ AC_DEFUN([AX_BOOST_CHRONO],
LDFLAGS_SAVE=$LDFLAGS
if test "x$ax_boost_user_chrono_lib" = "x"; then
- ax_lib=
for libextension in `ls $BOOSTLIBDIR/libboost_chrono*.so* $BOOSTLIBDIR/libboost_chrono*.dylib* $BOOSTLIBDIR/libboost_chrono*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_chrono.*\)\.so.*$;\1;' -e 's;^lib\(boost_chrono.*\)\.dylib.*$;\1;' -e 's;^lib\(boost_chrono.*\)\.a.*$;\1;'` ; do
ax_lib=${libextension}
AC_CHECK_LIB($ax_lib, exit,
@@ -106,7 +105,7 @@ AC_DEFUN([AX_BOOST_CHRONO],
fi
if test "x$ax_lib" = "x"; then
- AC_MSG_ERROR(Could not find a version of the boost_chrono library!)
+ AC_MSG_ERROR(Could not find a version of the library!)
fi
if test "x$link_chrono" = "xno"; then
AC_MSG_ERROR(Could not link against $ax_lib !)
diff --git a/build-aux/m4/ax_boost_program_options.m4 b/build-aux/m4/ax_boost_program_options.m4
deleted file mode 100644
index 2bdb593716..0000000000
--- a/build-aux/m4/ax_boost_program_options.m4
+++ /dev/null
@@ -1,108 +0,0 @@
-# ============================================================================
-# http://www.gnu.org/software/autoconf-archive/ax_boost_program_options.html
-# ============================================================================
-#
-# SYNOPSIS
-#
-# AX_BOOST_PROGRAM_OPTIONS
-#
-# DESCRIPTION
-#
-# Test for program options library from the Boost C++ libraries. The macro
-# requires a preceding call to AX_BOOST_BASE. Further documentation is
-# available at <http://randspringer.de/boost/index.html>.
-#
-# This macro calls:
-#
-# AC_SUBST(BOOST_PROGRAM_OPTIONS_LIB)
-#
-# And sets:
-#
-# HAVE_BOOST_PROGRAM_OPTIONS
-#
-# LICENSE
-#
-# Copyright (c) 2009 Thomas Porschberg <thomas@randspringer.de>
-#
-# Copying and distribution of this file, with or without modification, are
-# permitted in any medium without royalty provided the copyright notice
-# and this notice are preserved. This file is offered as-is, without any
-# warranty.
-
-#serial 24
-
-AC_DEFUN([AX_BOOST_PROGRAM_OPTIONS],
-[
- AC_ARG_WITH([boost-program-options],
- AS_HELP_STRING([--with-boost-program-options@<:@=special-lib@:>@],
- [use the program options library from boost - it is possible to specify a certain library for the linker
- e.g. --with-boost-program-options=boost_program_options-gcc-mt-1_33_1 ]),
- [
- if test "$withval" = "no"; then
- want_boost="no"
- elif test "$withval" = "yes"; then
- want_boost="yes"
- ax_boost_user_program_options_lib=""
- else
- want_boost="yes"
- ax_boost_user_program_options_lib="$withval"
- fi
- ],
- [want_boost="yes"]
- )
-
- if test "x$want_boost" = "xyes"; then
- AC_REQUIRE([AC_PROG_CC])
- export want_boost
- CPPFLAGS_SAVED="$CPPFLAGS"
- CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
- export CPPFLAGS
- LDFLAGS_SAVED="$LDFLAGS"
- LDFLAGS="$LDFLAGS $BOOST_LDFLAGS"
- export LDFLAGS
- AC_CACHE_CHECK([whether the Boost::Program_Options library is available],
- ax_cv_boost_program_options,
- [AC_LANG_PUSH(C++)
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include <boost/program_options/errors.hpp>
- ]],
- [[boost::program_options::error err("Error message");
- return 0;]])],
- ax_cv_boost_program_options=yes, ax_cv_boost_program_options=no)
- AC_LANG_POP([C++])
- ])
- if test "$ax_cv_boost_program_options" = yes; then
- AC_DEFINE(HAVE_BOOST_PROGRAM_OPTIONS,,[define if the Boost::PROGRAM_OPTIONS library is available])
- BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'`
- if test "x$ax_boost_user_program_options_lib" = "x"; then
- for libextension in `ls $BOOSTLIBDIR/libboost_program_options*.so* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_program_options.*\)\.so.*$;\1;'` `ls $BOOSTLIBDIR/libboost_program_options*.dylib* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_program_options.*\)\.dylib.*$;\1;'` `ls $BOOSTLIBDIR/libboost_program_options*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_program_options.*\)\.a.*$;\1;'` ; do
- ax_lib=${libextension}
- AC_CHECK_LIB($ax_lib, exit,
- [BOOST_PROGRAM_OPTIONS_LIB="-l$ax_lib"; AC_SUBST(BOOST_PROGRAM_OPTIONS_LIB) link_program_options="yes"; break],
- [link_program_options="no"])
- done
- if test "x$link_program_options" != "xyes"; then
- for libextension in `ls $BOOSTLIBDIR/boost_program_options*.dll* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^\(boost_program_options.*\)\.dll.*$;\1;'` `ls $BOOSTLIBDIR/boost_program_options*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^\(boost_program_options.*\)\.a.*$;\1;'` ; do
- ax_lib=${libextension}
- AC_CHECK_LIB($ax_lib, exit,
- [BOOST_PROGRAM_OPTIONS_LIB="-l$ax_lib"; AC_SUBST(BOOST_PROGRAM_OPTIONS_LIB) link_program_options="yes"; break],
- [link_program_options="no"])
- done
- fi
- else
- for ax_lib in $ax_boost_user_program_options_lib boost_program_options-$ax_boost_user_program_options_lib; do
- AC_CHECK_LIB($ax_lib, main,
- [BOOST_PROGRAM_OPTIONS_LIB="-l$ax_lib"; AC_SUBST(BOOST_PROGRAM_OPTIONS_LIB) link_program_options="yes"; break],
- [link_program_options="no"])
- done
- fi
- if test "x$ax_lib" = "x"; then
- AC_MSG_ERROR(Could not find a version of the boost_program_options library!)
- fi
- if test "x$link_program_options" != "xyes"; then
- AC_MSG_ERROR([Could not link against [$ax_lib] !])
- fi
- fi
- CPPFLAGS="$CPPFLAGS_SAVED"
- LDFLAGS="$LDFLAGS_SAVED"
- fi
-])
diff --git a/build-aux/m4/ax_boost_unit_test_framework.m4 b/build-aux/m4/ax_boost_unit_test_framework.m4
index 0cdbe752cf..3d8e93e964 100644
--- a/build-aux/m4/ax_boost_unit_test_framework.m4
+++ b/build-aux/m4/ax_boost_unit_test_framework.m4
@@ -76,7 +76,6 @@ AC_DEFUN([AX_BOOST_UNIT_TEST_FRAMEWORK],
if test "x$ax_boost_user_unit_test_framework_lib" = "x"; then
saved_ldflags="${LDFLAGS}"
- ax_lib=
for monitor_library in `ls $BOOSTLIBDIR/libboost_unit_test_framework*.so* $BOOSTLIBDIR/libboost_unit_test_framework*.dylib* $BOOSTLIBDIR/libboost_unit_test_framework*.a* 2>/dev/null` ; do
if test -r $monitor_library ; then
libextension=`echo $monitor_library | sed 's,.*/,,' | sed -e 's;^lib\(boost_unit_test_framework.*\)\.so.*$;\1;' -e 's;^lib\(boost_unit_test_framework.*\)\.dylib.*$;\1;' -e 's;^lib\(boost_unit_test_framework.*\)\.a.*$;\1;'`
@@ -125,7 +124,7 @@ AC_DEFUN([AX_BOOST_UNIT_TEST_FRAMEWORK],
done
fi
if test "x$ax_lib" = "x"; then
- AC_MSG_ERROR(Could not find a version of the boost_unit_test_framework library!)
+ AC_MSG_ERROR(Could not find a version of the library!)
fi
if test "x$link_unit_test_framework" != "xyes"; then
AC_MSG_ERROR(Could not link against $ax_lib !)
diff --git a/configure.ac b/configure.ac
index 490b638ee1..5851413cf4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -541,10 +541,17 @@ case $host in
CPPFLAGS="$CPPFLAGS -DMAC_OSX"
OBJCXXFLAGS="$CXXFLAGS"
;;
+ *android*)
+ dnl make sure android stays above linux for hosts like *linux-android*
+ LEVELDB_TARGET_FLAGS="-DOS_ANDROID"
+ ;;
*linux*)
TARGET_OS=linux
LEVELDB_TARGET_FLAGS="-DOS_LINUX"
;;
+ *kfreebsd*)
+ LEVELDB_TARGET_FLAGS="-DOS_KFREEBSD"
+ ;;
*freebsd*)
LEVELDB_TARGET_FLAGS="-DOS_FREEBSD"
;;
@@ -554,10 +561,17 @@ case $host in
*netbsd*)
LEVELDB_TARGET_FLAGS="-DOS_NETBSD"
;;
+ *dragonfly*)
+ LEVELDB_TARGET_FLAGS="-DOS_DRAGONFLYBSD"
+ ;;
+ *solaris*)
+ LEVELDB_TARGET_FLAGS="-DOS_SOLARIS"
+ ;;
+ *hpux*)
+ LEVELDB_TARGET_FLAGS="-DOS_HPUX"
+ ;;
*)
- OTHER_OS=`echo ${host_os} | awk '{print toupper($0)}'`
- AC_MSG_WARN([Guessing LevelDB OS as OS_${OTHER_OS}, please check whether this is correct, if not add an entry to configure.ac.])
- LEVELDB_TARGET_FLAGS="-DOS_${OTHER_OS}"
+ AC_MSG_ERROR(Cannot build leveldb for $host. Please file a bug report.)
;;
esac
@@ -651,6 +665,8 @@ if test x$use_glibc_compat != xno; then
[ fdelt_type="long int"])
AC_MSG_RESULT($fdelt_type)
AC_DEFINE_UNQUOTED(FDELT_TYPE, $fdelt_type,[parameter and return value type for __fdelt_chk])
+ AX_CHECK_LINK_FLAG([[-Wl,--wrap=__divmoddi4]], [COMPAT_LDFLAGS="$COMPAT_LDFLAGS -Wl,--wrap=__divmoddi4"])
+ AX_CHECK_LINK_FLAG([[-Wl,--wrap=log2f]], [COMPAT_LDFLAGS="$COMPAT_LDFLAGS -Wl,--wrap=log2f"])
else
AC_SEARCH_LIBS([clock_gettime],[rt])
fi
@@ -877,7 +893,6 @@ if test x$want_boost = xno; then
fi
AX_BOOST_SYSTEM
AX_BOOST_FILESYSTEM
-AX_BOOST_PROGRAM_OPTIONS
AX_BOOST_THREAD
AX_BOOST_CHRONO
@@ -946,7 +961,7 @@ fi
if test x$use_boost = xyes; then
-BOOST_LIBS="$BOOST_LDFLAGS $BOOST_SYSTEM_LIB $BOOST_FILESYSTEM_LIB $BOOST_PROGRAM_OPTIONS_LIB $BOOST_THREAD_LIB $BOOST_CHRONO_LIB"
+BOOST_LIBS="$BOOST_LDFLAGS $BOOST_SYSTEM_LIB $BOOST_FILESYSTEM_LIB $BOOST_THREAD_LIB $BOOST_CHRONO_LIB"
dnl If boost (prior to 1.57) was built without c++11, it emulated scoped enums
@@ -1351,6 +1366,7 @@ AC_SUBST(DEBUG_CPPFLAGS)
AC_SUBST(WARN_CXXFLAGS)
AC_SUBST(NOWARN_CXXFLAGS)
AC_SUBST(DEBUG_CXXFLAGS)
+AC_SUBST(COMPAT_LDFLAGS)
AC_SUBST(ERROR_CXXFLAGS)
AC_SUBST(GPROF_CXXFLAGS)
AC_SUBST(GPROF_LDFLAGS)
diff --git a/contrib/README.md b/contrib/README.md
index a582a724f7..bcf9186fa1 100644
--- a/contrib/README.md
+++ b/contrib/README.md
@@ -37,7 +37,7 @@ Scripts and notes for Mac builds.
### [RPM](/contrib/rpm) ###
RPM spec file for building bitcoin-core on RPM based distributions.
-### [Gitian-build](/contrib/gitian-build.sh) ###
+### [Gitian-build](/contrib/gitian-build.py) ###
Script for running full Gitian builds.
Test and Verify Tools
diff --git a/contrib/devtools/security-check.py b/contrib/devtools/security-check.py
index c9516ef83f..391984a8f2 100755
--- a/contrib/devtools/security-check.py
+++ b/contrib/devtools/security-check.py
@@ -14,7 +14,7 @@ import os
READELF_CMD = os.getenv('READELF', '/usr/bin/readelf')
OBJDUMP_CMD = os.getenv('OBJDUMP', '/usr/bin/objdump')
-NONFATAL = {'HIGH_ENTROPY_VA'} # checks which are non-fatal for now but only generate a warning
+NONFATAL = {} # checks which are non-fatal for now but only generate a warning
def check_ELF_PIE(executable):
'''
@@ -97,7 +97,7 @@ def check_ELF_RELRO(executable):
raise IOError('Error opening file')
for line in stdout.splitlines():
tokens = line.split()
- if len(tokens)>1 and tokens[1] == '(BIND_NOW)' or (len(tokens)>2 and tokens[1] == '(FLAGS)' and 'BIND_NOW' in tokens[2]):
+ if len(tokens)>1 and tokens[1] == '(BIND_NOW)' or (len(tokens)>2 and tokens[1] == '(FLAGS)' and 'BIND_NOW' in tokens[2:]):
have_bindnow = True
return have_gnu_relro and have_bindnow
diff --git a/contrib/devtools/symbol-check.py b/contrib/devtools/symbol-check.py
index 3a67319eaa..6808e77da7 100755
--- a/contrib/devtools/symbol-check.py
+++ b/contrib/devtools/symbol-check.py
@@ -46,7 +46,7 @@ MAX_VERSIONS = {
# Ignore symbols that are exported as part of every executable
IGNORE_EXPORTS = {
-'_edata', '_end', '_init', '__bss_start', '_fini', '_IO_stdin_used'
+'_edata', '_end', '_init', '__bss_start', '_fini', '_IO_stdin_used', 'stdin', 'stdout', 'stderr'
}
READELF_CMD = os.getenv('READELF', '/usr/bin/readelf')
CPPFILT_CMD = os.getenv('CPPFILT', '/usr/bin/c++filt')
diff --git a/contrib/devtools/test-security-check.py b/contrib/devtools/test-security-check.py
index 9b6d6bf665..4cf51378c0 100755
--- a/contrib/devtools/test-security-check.py
+++ b/contrib/devtools/test-security-check.py
@@ -32,11 +32,11 @@ class TestSecurityChecks(unittest.TestCase):
cc = 'gcc'
write_testcode(source)
- self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-zexecstack','-fno-stack-protector','-Wl,-znorelro']),
+ self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-zexecstack','-fno-stack-protector','-Wl,-znorelro','-no-pie','-fno-PIE']),
(1, executable+': failed PIE NX RELRO Canary'))
- self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fno-stack-protector','-Wl,-znorelro']),
+ self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fno-stack-protector','-Wl,-znorelro','-no-pie','-fno-PIE']),
(1, executable+': failed PIE RELRO Canary'))
- self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fstack-protector-all','-Wl,-znorelro']),
+ self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fstack-protector-all','-Wl,-znorelro','-no-pie','-fno-PIE']),
(1, executable+': failed PIE RELRO'))
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fstack-protector-all','-Wl,-znorelro','-pie','-fPIE']),
(1, executable+': failed RELRO'))
@@ -49,9 +49,9 @@ class TestSecurityChecks(unittest.TestCase):
cc = 'i686-w64-mingw32-gcc'
write_testcode(source)
- self.assertEqual(call_security_check(cc, source, executable, []),
+ self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--no-nxcompat','-Wl,--no-dynamicbase']),
(1, executable+': failed DYNAMIC_BASE NX'))
- self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat']),
+ self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat','-Wl,--no-dynamicbase']),
(1, executable+': failed DYNAMIC_BASE'))
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat','-Wl,--dynamicbase']),
(0, ''))
@@ -61,9 +61,9 @@ class TestSecurityChecks(unittest.TestCase):
cc = 'x86_64-w64-mingw32-gcc'
write_testcode(source)
- self.assertEqual(call_security_check(cc, source, executable, []), (1, executable+': failed DYNAMIC_BASE NX\n'+executable+': warning HIGH_ENTROPY_VA'))
- self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat']), (1, executable+': failed DYNAMIC_BASE\n'+executable+': warning HIGH_ENTROPY_VA'))
- self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat','-Wl,--dynamicbase']), (0, executable+': warning HIGH_ENTROPY_VA'))
+ self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--no-nxcompat','-Wl,--no-dynamicbase','-Wl,--no-high-entropy-va']), (1, executable+': failed DYNAMIC_BASE HIGH_ENTROPY_VA NX'))
+ self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat','-Wl,--no-dynamicbase','-Wl,--no-high-entropy-va']), (1, executable+': failed DYNAMIC_BASE HIGH_ENTROPY_VA'))
+ self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat','-Wl,--dynamicbase','-Wl,--no-high-entropy-va']), (1, executable+': failed HIGH_ENTROPY_VA'))
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat','-Wl,--dynamicbase','-Wl,--high-entropy-va']), (0, ''))
if __name__ == '__main__':
diff --git a/contrib/gitian-build.py b/contrib/gitian-build.py
new file mode 100755
index 0000000000..5b8db1e73d
--- /dev/null
+++ b/contrib/gitian-build.py
@@ -0,0 +1,220 @@
+#!/usr/bin/env python3
+
+import argparse
+import os
+import subprocess
+import sys
+
+def setup():
+ global args, workdir
+ programs = ['ruby', 'git', 'apt-cacher-ng', 'make', 'wget']
+ if args.kvm:
+ programs += ['python-vm-builder', 'qemu-kvm', 'qemu-utils']
+ elif args.docker:
+ dockers = ['docker.io', 'docker-ce']
+ for i in dockers:
+ return_code = subprocess.call(['sudo', 'apt-get', 'install', '-qq', i])
+ if return_code == 0:
+ break
+ if return_code != 0:
+ print('Cannot find any way to install docker', file=sys.stderr)
+ exit(1)
+ else:
+ programs += ['lxc', 'debootstrap']
+ subprocess.check_call(['sudo', 'apt-get', 'install', '-qq'] + programs)
+ if not os.path.isdir('gitian.sigs'):
+ subprocess.check_call(['git', 'clone', 'https://github.com/bitcoin-core/gitian.sigs.git'])
+ if not os.path.isdir('bitcoin-detached-sigs'):
+ subprocess.check_call(['git', 'clone', 'https://github.com/bitcoin-core/bitcoin-detached-sigs.git'])
+ if not os.path.isdir('gitian-builder'):
+ subprocess.check_call(['git', 'clone', 'https://github.com/devrandom/gitian-builder.git'])
+ if not os.path.isdir('bitcoin'):
+ subprocess.check_call(['git', 'clone', 'https://github.com/bitcoin/bitcoin.git'])
+ os.chdir('gitian-builder')
+ make_image_prog = ['bin/make-base-vm', '--suite', 'bionic', '--arch', 'amd64']
+ if args.docker:
+ make_image_prog += ['--docker']
+ elif not args.kvm:
+ make_image_prog += ['--lxc']
+ subprocess.check_call(make_image_prog)
+ os.chdir(workdir)
+ if args.is_bionic and not args.kvm and not args.docker:
+ subprocess.check_call(['sudo', 'sed', '-i', 's/lxcbr0/br0/', '/etc/default/lxc-net'])
+ print('Reboot is required')
+ exit(0)
+
+def build():
+ global args, workdir
+
+ os.makedirs('bitcoin-binaries/' + args.version, exist_ok=True)
+ print('\nBuilding Dependencies\n')
+ os.chdir('gitian-builder')
+ os.makedirs('inputs', exist_ok=True)
+
+ subprocess.check_call(['wget', '-N', '-P', 'inputs', 'http://downloads.sourceforge.net/project/osslsigncode/osslsigncode/osslsigncode-1.7.1.tar.gz'])
+ subprocess.check_call(['wget', '-N', '-P', 'inputs', 'https://bitcoincore.org/cfields/osslsigncode-Backports-to-1.7.1.patch'])
+ subprocess.check_call(['make', '-C', '../bitcoin/depends', 'download', 'SOURCES_PATH=' + os.getcwd() + '/cache/common'])
+
+ if args.linux:
+ print('\nCompiling ' + args.version + ' Linux')
+ subprocess.check_call(['bin/gbuild', '-j', args.jobs, '-m', args.memory, '--commit', 'bitcoin='+args.commit, '--url', 'bitcoin='+args.url, '../bitcoin/contrib/gitian-descriptors/gitian-linux.yml'])
+ subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-linux', '--destination', '../gitian.sigs/', '../bitcoin/contrib/gitian-descriptors/gitian-linux.yml'])
+ subprocess.check_call('mv build/out/bitcoin-*.tar.gz build/out/src/bitcoin-*.tar.gz ../bitcoin-binaries/'+args.version, shell=True)
+
+ if args.windows:
+ print('\nCompiling ' + args.version + ' Windows')
+ subprocess.check_call(['bin/gbuild', '-j', args.jobs, '-m', args.memory, '--commit', 'bitcoin='+args.commit, '--url', 'bitcoin='+args.url, '../bitcoin/contrib/gitian-descriptors/gitian-win.yml'])
+ subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-win-unsigned', '--destination', '../gitian.sigs/', '../bitcoin/contrib/gitian-descriptors/gitian-win.yml'])
+ subprocess.check_call('mv build/out/bitcoin-*-win-unsigned.tar.gz inputs/bitcoin-win-unsigned.tar.gz', shell=True)
+ subprocess.check_call('mv build/out/bitcoin-*.zip build/out/bitcoin-*.exe ../bitcoin-binaries/'+args.version, shell=True)
+
+ if args.macos:
+ print('\nCompiling ' + args.version + ' MacOS')
+ subprocess.check_call(['bin/gbuild', '-j', args.jobs, '-m', args.memory, '--commit', 'bitcoin='+args.commit, '--url', 'bitcoin='+args.url, '../bitcoin/contrib/gitian-descriptors/gitian-osx.yml'])
+ subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-osx-unsigned', '--destination', '../gitian.sigs/', '../bitcoin/contrib/gitian-descriptors/gitian-osx.yml'])
+ subprocess.check_call('mv build/out/bitcoin-*-osx-unsigned.tar.gz inputs/bitcoin-osx-unsigned.tar.gz', shell=True)
+ subprocess.check_call('mv build/out/bitcoin-*.tar.gz build/out/bitcoin-*.dmg ../bitcoin-binaries/'+args.version, shell=True)
+
+ os.chdir(workdir)
+
+ if args.commit_files:
+ print('\nCommitting '+args.version+' Unsigned Sigs\n')
+ os.chdir('gitian.sigs')
+ subprocess.check_call(['git', 'add', args.version+'-linux/'+args.signer])
+ subprocess.check_call(['git', 'add', args.version+'-win-unsigned/'+args.signer])
+ subprocess.check_call(['git', 'add', args.version+'-osx-unsigned/'+args.signer])
+ subprocess.check_call(['git', 'commit', '-m', 'Add '+args.version+' unsigned sigs for '+args.signer])
+ os.chdir(workdir)
+
+def sign():
+ global args, workdir
+ os.chdir('gitian-builder')
+
+ if args.windows:
+ print('\nSigning ' + args.version + ' Windows')
+ subprocess.check_call(['bin/gbuild', '-i', '--commit', 'signature='+args.commit, '../bitcoin/contrib/gitian-descriptors/gitian-win-signer.yml'])
+ subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-win-signed', '--destination', '../gitian.sigs/', '../bitcoin/contrib/gitian-descriptors/gitian-win-signer.yml'])
+ subprocess.check_call('mv build/out/bitcoin-*win64-setup.exe ../bitcoin-binaries/'+args.version, shell=True)
+ subprocess.check_call('mv build/out/bitcoin-*win32-setup.exe ../bitcoin-binaries/'+args.version, shell=True)
+
+ if args.macos:
+ print('\nSigning ' + args.version + ' MacOS')
+ subprocess.check_call(['bin/gbuild', '-i', '--commit', 'signature='+args.commit, '../bitcoin/contrib/gitian-descriptors/gitian-osx-signer.yml'])
+ subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-osx-signed', '--destination', '../gitian.sigs/', '../bitcoin/contrib/gitian-descriptors/gitian-osx-signer.yml'])
+ subprocess.check_call('mv build/out/bitcoin-osx-signed.dmg ../bitcoin-binaries/'+args.version+'/bitcoin-'+args.version+'-osx.dmg', shell=True)
+
+ os.chdir(workdir)
+
+ if args.commit_files:
+ print('\nCommitting '+args.version+' Signed Sigs\n')
+ os.chdir('gitian.sigs')
+ subprocess.check_call(['git', 'add', args.version+'-win-signed/'+args.signer])
+ subprocess.check_call(['git', 'add', args.version+'-osx-signed/'+args.signer])
+ subprocess.check_call(['git', 'commit', '-a', '-m', 'Add '+args.version+' signed binary sigs for '+args.signer])
+ os.chdir(workdir)
+
+def verify():
+ global args, workdir
+ os.chdir('gitian-builder')
+
+ print('\nVerifying v'+args.version+' Linux\n')
+ subprocess.check_call(['bin/gverify', '-v', '-d', '../gitian.sigs/', '-r', args.version+'-linux', '../bitcoin/contrib/gitian-descriptors/gitian-linux.yml'])
+ print('\nVerifying v'+args.version+' Windows\n')
+ subprocess.check_call(['bin/gverify', '-v', '-d', '../gitian.sigs/', '-r', args.version+'-win-unsigned', '../bitcoin/contrib/gitian-descriptors/gitian-win.yml'])
+ print('\nVerifying v'+args.version+' MacOS\n')
+ subprocess.check_call(['bin/gverify', '-v', '-d', '../gitian.sigs/', '-r', args.version+'-osx-unsigned', '../bitcoin/contrib/gitian-descriptors/gitian-osx.yml'])
+ print('\nVerifying v'+args.version+' Signed Windows\n')
+ subprocess.check_call(['bin/gverify', '-v', '-d', '../gitian.sigs/', '-r', args.version+'-win-signed', '../bitcoin/contrib/gitian-descriptors/gitian-win-signer.yml'])
+ print('\nVerifying v'+args.version+' Signed MacOS\n')
+ subprocess.check_call(['bin/gverify', '-v', '-d', '../gitian.sigs/', '-r', args.version+'-osx-signed', '../bitcoin/contrib/gitian-descriptors/gitian-osx-signer.yml'])
+
+ os.chdir(workdir)
+
+def main():
+ global args, workdir
+
+ parser = argparse.ArgumentParser(usage='%(prog)s [options] signer version')
+ parser.add_argument('-c', '--commit', action='store_true', dest='commit', help='Indicate that the version argument is for a commit or branch')
+ parser.add_argument('-u', '--url', dest='url', default='https://github.com/bitcoin/bitcoin', help='Specify the URL of the repository. Default is %(default)s')
+ parser.add_argument('-v', '--verify', action='store_true', dest='verify', help='Verify the Gitian build')
+ parser.add_argument('-b', '--build', action='store_true', dest='build', help='Do a Gitian build')
+ parser.add_argument('-s', '--sign', action='store_true', dest='sign', help='Make signed binaries for Windows and MacOS')
+ parser.add_argument('-B', '--buildsign', action='store_true', dest='buildsign', help='Build both signed and unsigned binaries')
+ parser.add_argument('-o', '--os', dest='os', default='lwm', help='Specify which Operating Systems the build is for. Default is %(default)s. l for Linux, w for Windows, m for MacOS')
+ parser.add_argument('-j', '--jobs', dest='jobs', default='2', help='Number of processes to use. Default %(default)s')
+ parser.add_argument('-m', '--memory', dest='memory', default='2000', help='Memory to allocate in MiB. Default %(default)s')
+ parser.add_argument('-k', '--kvm', action='store_true', dest='kvm', help='Use KVM instead of LXC')
+ parser.add_argument('-d', '--docker', action='store_true', dest='docker', help='Use Docker instead of LXC')
+ parser.add_argument('-S', '--setup', action='store_true', dest='setup', help='Set up the Gitian building environment. Uses LXC. If you want to use KVM, use the --kvm option. Only works on Debian-based systems (Ubuntu, Debian)')
+ parser.add_argument('-D', '--detach-sign', action='store_true', dest='detach_sign', help='Create the assert file for detached signing. Will not commit anything.')
+ parser.add_argument('-n', '--no-commit', action='store_false', dest='commit_files', help='Do not commit anything to git')
+ parser.add_argument('signer', help='GPG signer to sign each build assert file')
+ parser.add_argument('version', help='Version number, commit, or branch to build. If building a commit or branch, the -c option must be specified')
+
+ args = parser.parse_args()
+ workdir = os.getcwd()
+
+ args.linux = 'l' in args.os
+ args.windows = 'w' in args.os
+ args.macos = 'm' in args.os
+
+ args.is_bionic = b'bionic' in subprocess.check_output(['lsb_release', '-cs'])
+
+ if args.buildsign:
+ args.build=True
+ args.sign=True
+
+ if args.kvm and args.docker:
+ raise Exception('Error: cannot have both kvm and docker')
+
+ args.sign_prog = 'true' if args.detach_sign else 'gpg --detach-sign'
+
+ # Set enviroment variable USE_LXC or USE_DOCKER, let gitian-builder know that we use lxc or docker
+ if args.docker:
+ os.environ['USE_DOCKER'] = '1'
+ elif not args.kvm:
+ os.environ['USE_LXC'] = '1'
+ if not 'GITIAN_HOST_IP' in os.environ.keys():
+ os.environ['GITIAN_HOST_IP'] = '10.0.3.1'
+ if not 'LXC_GUEST_IP' in os.environ.keys():
+ os.environ['LXC_GUEST_IP'] = '10.0.3.5'
+
+ # Disable for MacOS if no SDK found
+ if args.macos and not os.path.isfile('gitian-builder/inputs/MacOSX10.11.sdk.tar.gz'):
+ print('Cannot build for MacOS, SDK does not exist. Will build for other OSes')
+ args.macos = False
+
+ script_name = os.path.basename(sys.argv[0])
+ # Signer and version shouldn't be empty
+ if args.signer == '':
+ print(script_name+': Missing signer.')
+ print('Try '+script_name+' --help for more information')
+ exit(1)
+ if args.version == '':
+ print(script_name+': Missing version.')
+ print('Try '+script_name+' --help for more information')
+ exit(1)
+
+ # Add leading 'v' for tags
+ args.commit = ('' if args.commit else 'v') + args.version
+ print(args.commit)
+
+ if args.setup:
+ setup()
+
+ os.chdir('bitcoin')
+ subprocess.check_call(['git', 'fetch'])
+ subprocess.check_call(['git', 'checkout', args.commit])
+ os.chdir(workdir)
+
+ if args.build:
+ build()
+
+ if args.sign:
+ sign()
+
+ if args.verify:
+ verify()
+
+if __name__ == '__main__':
+ main()
diff --git a/contrib/gitian-build.sh b/contrib/gitian-build.sh
deleted file mode 100755
index 05e7d6f4c6..0000000000
--- a/contrib/gitian-build.sh
+++ /dev/null
@@ -1,391 +0,0 @@
-#!/usr/bin/env bash
-# Copyright (c) 2016 The Bitcoin Core developers
-# Distributed under the MIT software license, see the accompanying
-# file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-export LC_ALL=C
-# What to do
-sign=false
-verify=false
-build=false
-
-# Systems to build
-linux=true
-windows=true
-osx=true
-
-# Other Basic variables
-SIGNER=
-VERSION=
-commit=false
-url=https://github.com/bitcoin/bitcoin
-proc=2
-mem=2000
-lxc=true
-osslTarUrl=http://downloads.sourceforge.net/project/osslsigncode/osslsigncode/osslsigncode-1.7.1.tar.gz
-osslPatchUrl=https://bitcoincore.org/cfields/osslsigncode-Backports-to-1.7.1.patch
-scriptName=$(basename -- "$0")
-signProg="gpg --detach-sign"
-commitFiles=true
-
-# Help Message
-read -d '' usage <<- EOF
-Usage: $scriptName [-c|u|v|b|s|B|o|h|j|m|] signer version
-
-Run this script from the directory containing the bitcoin, gitian-builder, gitian.sigs, and bitcoin-detached-sigs.
-
-Arguments:
-signer GPG signer to sign each build assert file
-version Version number, commit, or branch to build. If building a commit or branch, the -c option must be specified
-
-Options:
--c|--commit Indicate that the version argument is for a commit or branch
--u|--url Specify the URL of the repository. Default is https://github.com/bitcoin/bitcoin
--v|--verify Verify the Gitian build
--b|--build Do a Gitian build
--s|--sign Make signed binaries for Windows and Mac OSX
--B|--buildsign Build both signed and unsigned binaries
--o|--os Specify which Operating Systems the build is for. Default is lwx. l for linux, w for windows, x for osx
--j Number of processes to use. Default 2
--m Memory to allocate in MiB. Default 2000
---kvm Use KVM instead of LXC
---setup Set up the Gitian building environment. Uses LXC. If you want to use KVM, use the --kvm option. Only works on Debian-based systems (Ubuntu, Debian)
---detach-sign Create the assert file for detached signing. Will not commit anything.
---no-commit Do not commit anything to git
--h|--help Print this help message
-EOF
-
-# Get options and arguments
-while :; do
- case $1 in
- # Verify
- -v|--verify)
- verify=true
- ;;
- # Build
- -b|--build)
- build=true
- ;;
- # Sign binaries
- -s|--sign)
- sign=true
- ;;
- # Build then Sign
- -B|--buildsign)
- sign=true
- build=true
- ;;
- # PGP Signer
- -S|--signer)
- if [ -n "$2" ]
- then
- SIGNER="$2"
- shift
- else
- echo 'Error: "--signer" requires a non-empty argument.'
- exit 1
- fi
- ;;
- # Operating Systems
- -o|--os)
- if [ -n "$2" ]
- then
- linux=false
- windows=false
- osx=false
- if [[ "$2" = *"l"* ]]
- then
- linux=true
- fi
- if [[ "$2" = *"w"* ]]
- then
- windows=true
- fi
- if [[ "$2" = *"x"* ]]
- then
- osx=true
- fi
- shift
- else
- echo 'Error: "--os" requires an argument containing an l (for linux), w (for windows), or x (for Mac OSX)'
- exit 1
- fi
- ;;
- # Help message
- -h|--help)
- echo "$usage"
- exit 0
- ;;
- # Commit or branch
- -c|--commit)
- commit=true
- ;;
- # Number of Processes
- -j)
- if [ -n "$2" ]
- then
- proc=$2
- shift
- else
- echo 'Error: "-j" requires an argument'
- exit 1
- fi
- ;;
- # Memory to allocate
- -m)
- if [ -n "$2" ]
- then
- mem=$2
- shift
- else
- echo 'Error: "-m" requires an argument'
- exit 1
- fi
- ;;
- # URL
- -u)
- if [ -n "$2" ]
- then
- url=$2
- shift
- else
- echo 'Error: "-u" requires an argument'
- exit 1
- fi
- ;;
- # kvm
- --kvm)
- lxc=false
- ;;
- # Detach sign
- --detach-sign)
- signProg="true"
- commitFiles=false
- ;;
- # Commit files
- --no-commit)
- commitFiles=false
- ;;
- # Setup
- --setup)
- setup=true
- ;;
- *) # Default case: If no more options then break out of the loop.
- break
- esac
- shift
-done
-
-# Set up LXC
-if [[ $lxc = true ]]
-then
- export USE_LXC=1
-fi
-
-# Check for OSX SDK
-if [[ ! -e "gitian-builder/inputs/MacOSX10.11.sdk.tar.gz" && $osx == true ]]
-then
- echo "Cannot build for OSX, SDK does not exist. Will build for other OSes"
- osx=false
-fi
-
-# Get signer
-if [[ -n "$1" ]]
-then
- SIGNER="$1"
- shift
-fi
-
-# Get version
-if [[ -n "$1" ]]
-then
- VERSION=$1
- COMMIT=$VERSION
- shift
-fi
-
-# Check that a signer is specified
-if [[ "$SIGNER" == "" ]]
-then
- echo "$scriptName: Missing signer."
- echo "Try $scriptName --help for more information"
- exit 1
-fi
-
-# Check that a version is specified
-if [[ $VERSION == "" ]]
-then
- echo "$scriptName: Missing version."
- echo "Try $scriptName --help for more information"
- exit 1
-fi
-
-# Add a "v" if no -c
-if [[ $commit = false ]]
-then
- COMMIT="v${VERSION}"
-fi
-echo ${COMMIT}
-
-# Setup build environment
-if [[ $setup = true ]]
-then
- sudo apt-get install ruby apache2 git apt-cacher-ng python-vm-builder qemu-kvm qemu-utils
- git clone https://github.com/bitcoin-core/gitian.sigs.git
- git clone https://github.com/bitcoin-core/bitcoin-detached-sigs.git
- git clone https://github.com/devrandom/gitian-builder.git
- pushd ./gitian-builder
- if [[ -n "$USE_LXC" ]]
- then
- sudo apt-get install lxc
- bin/make-base-vm --suite trusty --arch amd64 --lxc
- else
- bin/make-base-vm --suite trusty --arch amd64
- fi
- popd
-fi
-
-# Set up build
-pushd ./bitcoin
-git fetch
-git checkout ${COMMIT}
-popd
-
-# Build
-if [[ $build = true ]]
-then
- # Make output folder
- mkdir -p ./bitcoin-binaries/${VERSION}
-
- # Build Dependencies
- echo ""
- echo "Building Dependencies"
- echo ""
- pushd ./gitian-builder
- mkdir -p inputs
- wget -N -P inputs $osslPatchUrl
- wget -N -P inputs $osslTarUrl
- make -C ../bitcoin/depends download SOURCES_PATH=`pwd`/cache/common
-
- # Linux
- if [[ $linux = true ]]
- then
- echo ""
- echo "Compiling ${VERSION} Linux"
- echo ""
- ./bin/gbuild -j ${proc} -m ${mem} --commit bitcoin=${COMMIT} --url bitcoin=${url} ../bitcoin/contrib/gitian-descriptors/gitian-linux.yml
- ./bin/gsign -p "$signProg" --signer "$SIGNER" --release ${VERSION}-linux --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-linux.yml
- mv build/out/bitcoin-*.tar.gz build/out/src/bitcoin-*.tar.gz ../bitcoin-binaries/${VERSION}
- fi
- # Windows
- if [[ $windows = true ]]
- then
- echo ""
- echo "Compiling ${VERSION} Windows"
- echo ""
- ./bin/gbuild -j ${proc} -m ${mem} --commit bitcoin=${COMMIT} --url bitcoin=${url} ../bitcoin/contrib/gitian-descriptors/gitian-win.yml
- ./bin/gsign -p "$signProg" --signer "$SIGNER" --release ${VERSION}-win-unsigned --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-win.yml
- mv build/out/bitcoin-*-win-unsigned.tar.gz inputs/bitcoin-win-unsigned.tar.gz
- mv build/out/bitcoin-*.zip build/out/bitcoin-*.exe ../bitcoin-binaries/${VERSION}
- fi
- # Mac OSX
- if [[ $osx = true ]]
- then
- echo ""
- echo "Compiling ${VERSION} Mac OSX"
- echo ""
- ./bin/gbuild -j ${proc} -m ${mem} --commit bitcoin=${COMMIT} --url bitcoin=${url} ../bitcoin/contrib/gitian-descriptors/gitian-osx.yml
- ./bin/gsign -p "$signProg" --signer "$SIGNER" --release ${VERSION}-osx-unsigned --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-osx.yml
- mv build/out/bitcoin-*-osx-unsigned.tar.gz inputs/bitcoin-osx-unsigned.tar.gz
- mv build/out/bitcoin-*.tar.gz build/out/bitcoin-*.dmg ../bitcoin-binaries/${VERSION}
- fi
- popd
-
- if [[ $commitFiles = true ]]
- then
- # Commit to gitian.sigs repo
- echo ""
- echo "Committing ${VERSION} Unsigned Sigs"
- echo ""
- pushd gitian.sigs
- git add ${VERSION}-linux/"${SIGNER}"
- git add ${VERSION}-win-unsigned/"${SIGNER}"
- git add ${VERSION}-osx-unsigned/"${SIGNER}"
- git commit -a -m "Add ${VERSION} unsigned sigs for ${SIGNER}"
- popd
- fi
-fi
-
-# Verify the build
-if [[ $verify = true ]]
-then
- # Linux
- pushd ./gitian-builder
- echo ""
- echo "Verifying v${VERSION} Linux"
- echo ""
- ./bin/gverify -v -d ../gitian.sigs/ -r ${VERSION}-linux ../bitcoin/contrib/gitian-descriptors/gitian-linux.yml
- # Windows
- echo ""
- echo "Verifying v${VERSION} Windows"
- echo ""
- ./bin/gverify -v -d ../gitian.sigs/ -r ${VERSION}-win-unsigned ../bitcoin/contrib/gitian-descriptors/gitian-win.yml
- # Mac OSX
- echo ""
- echo "Verifying v${VERSION} Mac OSX"
- echo ""
- ./bin/gverify -v -d ../gitian.sigs/ -r ${VERSION}-osx-unsigned ../bitcoin/contrib/gitian-descriptors/gitian-osx.yml
- # Signed Windows
- echo ""
- echo "Verifying v${VERSION} Signed Windows"
- echo ""
- ./bin/gverify -v -d ../gitian.sigs/ -r ${VERSION}-osx-signed ../bitcoin/contrib/gitian-descriptors/gitian-osx-signer.yml
- # Signed Mac OSX
- echo ""
- echo "Verifying v${VERSION} Signed Mac OSX"
- echo ""
- ./bin/gverify -v -d ../gitian.sigs/ -r ${VERSION}-osx-signed ../bitcoin/contrib/gitian-descriptors/gitian-osx-signer.yml
- popd
-fi
-
-# Sign binaries
-if [[ $sign = true ]]
-then
-
- pushd ./gitian-builder
- # Sign Windows
- if [[ $windows = true ]]
- then
- echo ""
- echo "Signing ${VERSION} Windows"
- echo ""
- ./bin/gbuild -i --commit signature=${COMMIT} ../bitcoin/contrib/gitian-descriptors/gitian-win-signer.yml
- ./bin/gsign -p "$signProg" --signer "$SIGNER" --release ${VERSION}-win-signed --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-win-signer.yml
- mv build/out/bitcoin-*win64-setup.exe ../bitcoin-binaries/${VERSION}
- mv build/out/bitcoin-*win32-setup.exe ../bitcoin-binaries/${VERSION}
- fi
- # Sign Mac OSX
- if [[ $osx = true ]]
- then
- echo ""
- echo "Signing ${VERSION} Mac OSX"
- echo ""
- ./bin/gbuild -i --commit signature=${COMMIT} ../bitcoin/contrib/gitian-descriptors/gitian-osx-signer.yml
- ./bin/gsign -p "$signProg" --signer "$SIGNER" --release ${VERSION}-osx-signed --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-osx-signer.yml
- mv build/out/bitcoin-osx-signed.dmg ../bitcoin-binaries/${VERSION}/bitcoin-${VERSION}-osx.dmg
- fi
- popd
-
- if [[ $commitFiles = true ]]
- then
- # Commit Sigs
- pushd gitian.sigs
- echo ""
- echo "Committing ${VERSION} Signed Sigs"
- echo ""
- git add ${VERSION}-win-signed/"${SIGNER}"
- git add ${VERSION}-osx-signed/"${SIGNER}"
- git commit -a -m "Add ${VERSION} signed binary sigs for ${SIGNER}"
- popd
- fi
-fi
diff --git a/contrib/gitian-descriptors/gitian-linux.yml b/contrib/gitian-descriptors/gitian-linux.yml
index f78eea73a3..4255fb0c9a 100644
--- a/contrib/gitian-descriptors/gitian-linux.yml
+++ b/contrib/gitian-descriptors/gitian-linux.yml
@@ -2,23 +2,23 @@
name: "bitcoin-linux-0.17"
enable_cache: true
suites:
-- "trusty"
+- "bionic"
architectures:
- "amd64"
packages:
- "curl"
- "g++-aarch64-linux-gnu"
-- "g++-4.8-aarch64-linux-gnu"
-- "gcc-4.8-aarch64-linux-gnu"
+- "g++-7-aarch64-linux-gnu"
+- "gcc-7-aarch64-linux-gnu"
- "binutils-aarch64-linux-gnu"
- "g++-arm-linux-gnueabihf"
-- "g++-4.8-arm-linux-gnueabihf"
-- "gcc-4.8-arm-linux-gnueabihf"
+- "g++-7-arm-linux-gnueabihf"
+- "gcc-7-arm-linux-gnueabihf"
- "binutils-arm-linux-gnueabihf"
-- "g++-4.8-multilib"
-- "gcc-4.8-multilib"
+- "g++-7-multilib"
+- "gcc-7-multilib"
- "binutils-gold"
-- "git-core"
+- "git"
- "pkg-config"
- "autoconf"
- "libtool"
@@ -43,6 +43,7 @@ script: |
HOST_LDFLAGS=-static-libstdc++
export QT_RCC_TEST=1
+ export QT_RCC_SOURCE_DATE_OVERRIDE=1
export GZIP="-9n"
export TAR_OPTIONS="--mtime="$REFERENCE_DATE\\\ $REFERENCE_TIME""
export TZ="UTC"
diff --git a/contrib/gitian-descriptors/gitian-osx-signer.yml b/contrib/gitian-descriptors/gitian-osx-signer.yml
index b5c863bb14..297a136fae 100644
--- a/contrib/gitian-descriptors/gitian-osx-signer.yml
+++ b/contrib/gitian-descriptors/gitian-osx-signer.yml
@@ -1,7 +1,7 @@
---
name: "bitcoin-dmg-signer"
suites:
-- "trusty"
+- "bionic"
architectures:
- "amd64"
packages:
diff --git a/contrib/gitian-descriptors/gitian-osx.yml b/contrib/gitian-descriptors/gitian-osx.yml
index 2cc62d4ce7..fb59cf671b 100644
--- a/contrib/gitian-descriptors/gitian-osx.yml
+++ b/contrib/gitian-descriptors/gitian-osx.yml
@@ -2,14 +2,14 @@
name: "bitcoin-osx-0.17"
enable_cache: true
suites:
-- "trusty"
+- "bionic"
architectures:
- "amd64"
packages:
- "ca-certificates"
- "curl"
- "g++"
-- "git-core"
+- "git"
- "pkg-config"
- "autoconf"
- "librsvg2-bin"
@@ -34,12 +34,13 @@ files:
- "MacOSX10.11.sdk.tar.gz"
script: |
WRAP_DIR=$HOME/wrapped
- HOSTS="x86_64-apple-darwin11"
+ HOSTS="x86_64-apple-darwin14"
CONFIGFLAGS="--enable-reduce-exports --disable-bench --disable-gui-tests GENISOIMAGE=$WRAP_DIR/genisoimage"
FAKETIME_HOST_PROGS=""
FAKETIME_PROGS="ar ranlib date dmg genisoimage"
export QT_RCC_TEST=1
+ export QT_RCC_SOURCE_DATE_OVERRIDE=1
export GZIP="-9n"
export TAR_OPTIONS="--mtime="$REFERENCE_DATE\\\ $REFERENCE_TIME""
export TZ="UTC"
diff --git a/contrib/gitian-descriptors/gitian-win-signer.yml b/contrib/gitian-descriptors/gitian-win-signer.yml
index 3c1e0214a0..2f3ec3e8ff 100644
--- a/contrib/gitian-descriptors/gitian-win-signer.yml
+++ b/contrib/gitian-descriptors/gitian-win-signer.yml
@@ -1,7 +1,7 @@
---
name: "bitcoin-win-signer"
suites:
-- "trusty"
+- "bionic"
architectures:
- "amd64"
packages:
diff --git a/contrib/gitian-descriptors/gitian-win.yml b/contrib/gitian-descriptors/gitian-win.yml
index f6eb1815f6..2d48170649 100644
--- a/contrib/gitian-descriptors/gitian-win.yml
+++ b/contrib/gitian-descriptors/gitian-win.yml
@@ -2,13 +2,13 @@
name: "bitcoin-win-0.17"
enable_cache: true
suites:
-- "trusty"
+- "bionic"
architectures:
- "amd64"
packages:
- "curl"
- "g++"
-- "git-core"
+- "git"
- "pkg-config"
- "autoconf"
- "libtool"
@@ -21,6 +21,7 @@ packages:
- "zip"
- "ca-certificates"
- "python"
+- "rename"
remotes:
- "url": "https://github.com/bitcoin/bitcoin.git"
"dir": "bitcoin"
@@ -29,12 +30,13 @@ script: |
WRAP_DIR=$HOME/wrapped
HOSTS="i686-w64-mingw32 x86_64-w64-mingw32"
CONFIGFLAGS="--enable-reduce-exports --disable-bench --disable-gui-tests"
- FAKETIME_HOST_PROGS="g++ ar ranlib nm windres strip objcopy"
+ FAKETIME_HOST_PROGS="ar ranlib nm windres strip objcopy"
FAKETIME_PROGS="date makensis zip"
HOST_CFLAGS="-O2 -g"
HOST_CXXFLAGS="-O2 -g"
export QT_RCC_TEST=1
+ export QT_RCC_SOURCE_DATE_OVERRIDE=1
export GZIP="-9n"
export TAR_OPTIONS="--mtime="$REFERENCE_DATE\\\ $REFERENCE_TIME""
export TZ="UTC"
@@ -84,7 +86,7 @@ script: |
done
for prog in gcc g++; do
echo '#!/usr/bin/env bash' > ${WRAP_DIR}/${i}-${prog}
- echo "REAL=\`which -a ${i}-${prog} | grep -v ${WRAP_DIR}/${i}-${prog} | head -1\`" >> ${WRAP_DIR}/${i}-${prog}
+ echo "REAL=\`which -a ${i}-${prog}-posix | grep -v ${WRAP_DIR}/${i}-${prog} | head -1\`" >> ${WRAP_DIR}/${i}-${prog}
echo 'export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${i}-${prog}
echo "export FAKETIME=\"$1\"" >> ${WRAP_DIR}/${i}-${prog}
echo "export COMPILER_PATH=${WRAP_DIR}/${i}" >> ${WRAP_DIR}/${i}-${prog}
diff --git a/contrib/zmq/zmq_sub.py b/contrib/zmq/zmq_sub.py
index 60768dc59a..fa9e669308 100644
--- a/contrib/zmq/zmq_sub.py
+++ b/contrib/zmq/zmq_sub.py
@@ -30,7 +30,7 @@ import signal
import struct
import sys
-if not (sys.version_info.major >= 3 and sys.version_info.minor >= 5):
+if (sys.version_info.major, sys.version_info.minor) < (3, 5):
print("This example only works with Python 3.5 and greater")
sys.exit(1)
diff --git a/contrib/zmq/zmq_sub3.4.py b/contrib/zmq/zmq_sub3.4.py
index 0df843c9a3..d05ecc2623 100644
--- a/contrib/zmq/zmq_sub3.4.py
+++ b/contrib/zmq/zmq_sub3.4.py
@@ -34,7 +34,7 @@ import signal
import struct
import sys
-if not (sys.version_info.major >= 3 and sys.version_info.minor >= 4):
+if (sys.version_info.major, sys.version_info.minor) < (3, 4):
print("This example only works with Python 3.4 and greater")
sys.exit(1)
diff --git a/depends/Makefile b/depends/Makefile
index c73ea0f730..3686aaf1f8 100644
--- a/depends/Makefile
+++ b/depends/Makefile
@@ -178,7 +178,7 @@ install: check-packages $(host_prefix)/share/config.site
download-one: check-sources $(all_sources)
download-osx:
- @$(MAKE) -s HOST=x86_64-apple-darwin11 download-one
+ @$(MAKE) -s HOST=x86_64-apple-darwin14 download-one
download-linux:
@$(MAKE) -s HOST=x86_64-unknown-linux-gnu download-one
download-win:
diff --git a/depends/README.md b/depends/README.md
index 226a1cc935..fd343f7010 100644
--- a/depends/README.md
+++ b/depends/README.md
@@ -22,7 +22,7 @@ Common `host-platform-triplets` for cross compilation are:
- `i686-w64-mingw32` for Win32
- `x86_64-w64-mingw32` for Win64
-- `x86_64-apple-darwin11` for macOS
+- `x86_64-apple-darwin14` for macOS
- `arm-linux-gnueabihf` for Linux ARM 32 bit
- `aarch64-linux-gnu` for Linux ARM 64 bit
- `riscv32-linux-gnu` for Linux RISC-V 32 bit
diff --git a/depends/hosts/darwin.mk b/depends/hosts/darwin.mk
index 4e58bec74e..a1c943d60b 100644
--- a/depends/hosts/darwin.mk
+++ b/depends/hosts/darwin.mk
@@ -1,4 +1,4 @@
-OSX_MIN_VERSION=10.8
+OSX_MIN_VERSION=10.10
OSX_SDK_VERSION=10.11
OSX_SDK=$(SDK_PATH)/MacOSX$(OSX_SDK_VERSION).sdk
LD64_VERSION=253.9
diff --git a/depends/packages/boost.mk b/depends/packages/boost.mk
index bf773ccd14..61806c7509 100644
--- a/depends/packages/boost.mk
+++ b/depends/packages/boost.mk
@@ -19,7 +19,7 @@ $(package)_toolset_$(host_os)=gcc
$(package)_archiver_$(host_os)=$($(package)_ar)
$(package)_toolset_darwin=darwin
$(package)_archiver_darwin=$($(package)_libtool)
-$(package)_config_libraries=chrono,filesystem,program_options,system,thread,test
+$(package)_config_libraries=chrono,filesystem,system,thread,test
$(package)_cxxflags=-std=c++11 -fvisibility=hidden
$(package)_cxxflags_linux=-fPIC
endef
diff --git a/depends/packages/packages.mk b/depends/packages/packages.mk
index 551c9fa70b..69acb19b8f 100644
--- a/depends/packages/packages.mk
+++ b/depends/packages/packages.mk
@@ -5,6 +5,7 @@ qt_packages = qrencode protobuf zlib
qt_x86_64_linux_packages:=qt expat dbus libxcb xcb_proto libXau xproto freetype fontconfig libX11 xextproto libXext xtrans
qt_i686_linux_packages:=$(qt_x86_64_linux_packages)
+qt_arm_linux_packages:=$(qt_x86_64_linux_packages)
qt_darwin_packages=qt
qt_mingw32_packages=qt
diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk
index b4d7756b56..7fdd7612c3 100644
--- a/depends/packages/qt.mk
+++ b/depends/packages/qt.mk
@@ -8,7 +8,7 @@ $(package)_dependencies=openssl zlib
$(package)_linux_dependencies=freetype fontconfig libxcb libX11 xproto libXext
$(package)_build_subdir=qtbase
$(package)_qt_libs=corelib network widgets gui plugins testlib
-$(package)_patches=fix_qt_pkgconfig.patch mac-qmake.conf fix_configure_mac.patch fix_no_printer.patch
+$(package)_patches=fix_qt_pkgconfig.patch mac-qmake.conf fix_configure_mac.patch fix_no_printer.patch fix_rcc_determinism.patch
$(package)_qttranslations_file_name=qttranslations-$($(package)_suffix)
$(package)_qttranslations_sha256_hash=9822084f8e2d2939ba39f4af4c0c2320e45d5996762a9423f833055607604ed8
@@ -89,11 +89,12 @@ $(package)_config_opts_linux += -system-freetype
$(package)_config_opts_linux += -no-feature-sessionmanager
$(package)_config_opts_linux += -fontconfig
$(package)_config_opts_linux += -no-opengl
-$(package)_config_opts_arm_linux = -platform linux-g++ -xplatform $(host)
+$(package)_config_opts_arm_linux += -platform linux-g++ -xplatform bitcoin-linux-g++
$(package)_config_opts_i686_linux = -xplatform linux-g++-32
$(package)_config_opts_x86_64_linux = -xplatform linux-g++-64
$(package)_config_opts_mingw32 = -no-opengl -xplatform win32-g++ -device-option CROSS_COMPILE="$(host)-"
$(package)_build_env = QT_RCC_TEST=1
+$(package)_build_env += QT_RCC_SOURCE_DATE_OVERRIDE=1
endef
define $(package)_fetch_cmds
@@ -128,9 +129,12 @@ 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 && \
+ cp -r qtbase/mkspecs/linux-arm-gnueabi-g++ qtbase/mkspecs/bitcoin-linux-g++ && \
+ sed -i.old "s/arm-linux-gnueabi-/$(host)-/g" qtbase/mkspecs/bitcoin-linux-g++/qmake.conf && \
patch -p1 -i $($(package)_patch_dir)/fix_qt_pkgconfig.patch &&\
patch -p1 -i $($(package)_patch_dir)/fix_configure_mac.patch &&\
patch -p1 -i $($(package)_patch_dir)/fix_no_printer.patch &&\
+ patch -p1 -i $($(package)_patch_dir)/fix_rcc_determinism.patch &&\
echo "!host_build: QMAKE_CFLAGS += $($(package)_cflags) $($(package)_cppflags)" >> qtbase/mkspecs/common/gcc-base.conf && \
echo "!host_build: QMAKE_CXXFLAGS += $($(package)_cxxflags) $($(package)_cppflags)" >> qtbase/mkspecs/common/gcc-base.conf && \
echo "!host_build: QMAKE_LFLAGS += $($(package)_ldflags)" >> qtbase/mkspecs/common/gcc-base.conf && \
diff --git a/depends/packages/zeromq.mk b/depends/packages/zeromq.mk
index 9412c181e6..d5fd1f39ab 100644
--- a/depends/packages/zeromq.mk
+++ b/depends/packages/zeromq.mk
@@ -6,7 +6,7 @@ $(package)_sha256_hash=8f1e2b2aade4dbfde98d82366d61baef2f62e812530160d2e6d0a5bb2
$(package)_patches=0001-fix-build-with-older-mingw64.patch 0002-disable-pthread_set_name_np.patch
define $(package)_set_vars
- $(package)_config_opts=--without-docs --disable-shared --without-libsodium --disable-curve --disable-curve-keygen --disable-perf
+ $(package)_config_opts=--without-docs --disable-shared --without-libsodium --disable-curve --disable-curve-keygen --disable-perf --disable-Werror
$(package)_config_opts_linux=--with-pic
$(package)_cxxflags=-std=c++11
endef
diff --git a/depends/patches/qt/fix_rcc_determinism.patch b/depends/patches/qt/fix_rcc_determinism.patch
new file mode 100644
index 0000000000..c1b07fe23a
--- /dev/null
+++ b/depends/patches/qt/fix_rcc_determinism.patch
@@ -0,0 +1,15 @@
+--- old/qtbase/src/tools/rcc/rcc.cpp
++++ new/qtbase/src/tools/rcc/rcc.cpp
+@@ -207,7 +207,11 @@ void RCCFileInfo::writeDataInfo(RCCResourceLibrary &lib)
+ if (lib.formatVersion() >= 2) {
+ // last modified time stamp
+ const QDateTime lastModified = m_fileInfo.lastModified();
+- lib.writeNumber8(quint64(lastModified.isValid() ? lastModified.toMSecsSinceEpoch() : 0));
++ quint64 lastmod = quint64(lastModified.isValid() ? lastModified.toMSecsSinceEpoch() : 0);
++ static const quint64 sourceDate = 1000 * qgetenv("QT_RCC_SOURCE_DATE_OVERRIDE").toULongLong();
++ if (sourceDate != 0)
++ lastmod = sourceDate;
++ lib.writeNumber8(lastmod);
+ if (text || pass1)
+ lib.writeChar('\n');
+ }
diff --git a/doc/build-osx.md b/doc/build-osx.md
index a07dbd1e1d..486a505b60 100644
--- a/doc/build-osx.md
+++ b/doc/build-osx.md
@@ -91,6 +91,6 @@ Other commands:
Notes
-----
-* Tested on OS X 10.8 Mountain Lion through macOS 10.13 High Sierra on 64-bit Intel processors only.
+* Tested on OS X 10.10 Yosemite through macOS 10.13 High Sierra on 64-bit Intel processors only.
* Building with downloaded Qt binaries is not officially supported. See the notes in [#7714](https://github.com/bitcoin/bitcoin/issues/7714)
diff --git a/doc/build-unix.md b/doc/build-unix.md
index e884c0ab67..d9208fed54 100644
--- a/doc/build-unix.md
+++ b/doc/build-unix.md
@@ -70,7 +70,7 @@ tuned to conserve memory with additional CXXFLAGS:
Build requirements:
- sudo apt-get install build-essential libtool autotools-dev automake pkg-config libssl-dev libevent-dev bsdmainutils python3 libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-program-options-dev libboost-test-dev libboost-thread-dev
+ sudo apt-get install build-essential libtool autotools-dev automake pkg-config libssl-dev libevent-dev bsdmainutils python3 libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-test-dev libboost-thread-dev
BerkeleyDB is required for the wallet.
diff --git a/doc/release-notes-pr12257.md b/doc/release-notes-pr12257.md
new file mode 100644
index 0000000000..293a00ad75
--- /dev/null
+++ b/doc/release-notes-pr12257.md
@@ -0,0 +1,9 @@
+Notable changes
+===============
+
+Coin selection
+--------------
+- A new `-avoidpartialspends` flag has been added (default=false). If enabled, the wallet will try to spend UTXO's that point at the same destination
+ together. This is a privacy increase, as there will no longer be cases where a wallet will inadvertently spend only parts of the coins sent to
+ the same address (note that if someone were to send coins to that address after it was used, those coins will still be included in future
+ coin selections).
diff --git a/doc/release-notes.md b/doc/release-notes.md
index 57f93abe03..ad0c42ab31 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -48,11 +48,14 @@ Compatibility
==============
Bitcoin Core is extensively tested on multiple operating systems using
-the Linux kernel, macOS 10.8+, and Windows 7 and newer (Windows XP is not supported).
+the Linux kernel, macOS 10.10+, and Windows 7 and newer (Windows XP is not supported).
Bitcoin Core should also work on most other Unix-like systems but is not
frequently tested on them.
+From 0.17.0 onwards macOS <10.10 is no longer supported. 0.17.0 is built using Qt 5.9.x, which doesn't
+support versions of macOS older than 10.10.
+
Notable changes
===============
@@ -131,6 +134,11 @@ Low-level RPC changes
- The log timestamp format is now ISO 8601 (e.g. "2018-02-28T12:34:56Z").
+- When running bitcoind with `-debug` but without `-daemon`, logging to stdout
+ is now the default behavior. Setting `-printtoconsole=1` no longer implicitly
+ disables logging to debug.log. Instead, logging to file can be explicitly disabled
+ by setting `-debuglogfile=0`.
+
Miner block size removed
------------------------
diff --git a/doc/release-notes/release-notes-0.16.2.md b/doc/release-notes/release-notes-0.16.2.md
new file mode 100644
index 0000000000..d549748d25
--- /dev/null
+++ b/doc/release-notes/release-notes-0.16.2.md
@@ -0,0 +1,116 @@
+Bitcoin Core version 0.16.2 is now available from:
+
+ <https://bitcoincore.org/bin/bitcoin-core-0.16.2/>
+
+This is a new minor version release, with various bugfixes
+as well as updated translations.
+
+Please report bugs using the issue tracker at GitHub:
+
+ <https://github.com/bitcoin/bitcoin/issues>
+
+To receive security and update notifications, please subscribe to:
+
+ <https://bitcoincore.org/en/list/announcements/join/>
+
+How to Upgrade
+==============
+
+If you are running an older version, shut it down. Wait until it has completely
+shut down (which might take a few minutes for older versions), then run the
+installer (on Windows) or just copy over `/Applications/Bitcoin-Qt` (on Mac)
+or `bitcoind`/`bitcoin-qt` (on Linux).
+
+The first time you run version 0.15.0 or newer, your chainstate database will be converted to a
+new format, which will take anywhere from a few minutes to half an hour,
+depending on the speed of your machine.
+
+Note that the block database format also changed in version 0.8.0 and there is no
+automatic upgrade code from before version 0.8 to version 0.15.0 or higher. Upgrading
+directly from 0.7.x and earlier without re-downloading the blockchain is not supported.
+However, as usual, old wallet versions are still supported.
+
+Downgrading warning
+-------------------
+
+Wallets created in 0.16 and later are not compatible with versions prior to 0.16
+and will not work if you try to use newly created wallets in older versions. Existing
+wallets that were created with older versions are not affected by this.
+
+Compatibility
+==============
+
+Bitcoin Core is extensively tested on multiple operating systems using
+the Linux kernel, macOS 10.8+, and Windows Vista and later. Windows XP is not supported.
+
+Bitcoin Core should also work on most other Unix-like systems but is not
+frequently tested on them.
+
+0.16.2 change log
+------------------
+
+### Wallet
+- #13622 `c04a4a5` Remove mapRequest tracking that just effects Qt display. (TheBlueMatt)
+- #12905 `cfc6f74` [rpcwallet] Clamp walletpassphrase value at 100M seconds (sdaftuar)
+- #13437 `ed82e71` wallet: Erase wtxOrderd wtx pointer on removeprunedfunds (MarcoFalke)
+
+### RPC and other APIs
+- #13451 `cbd2f70` rpc: expose CBlockIndex::nTx in getblock(header) (instagibbs)
+- #13507 `f7401c8` RPC: Fix parameter count check for importpubkey (kristapsk)
+- #13452 `6b9dc8c` rpc: have verifytxoutproof check the number of txns in proof structure (instagibbs)
+- #12837 `bf1f150` rpc: fix type mistmatch in `listreceivedbyaddress` (joemphilips)
+- #12743 `657dfc5` Fix csBestBlock/cvBlockChange waiting in rpc/mining (sipa)
+
+### GUI
+- #12432 `f78e7f6` [qt] send: Clear All also resets coin control options (Sjors)
+- #12617 `21dd512` gui: Show messages as text not html (laanwj)
+- #12793 `cf6feb7` qt: Avoid reseting on resetguisettigs=0 (MarcoFalke)
+
+### Build system
+- #13544 `9fd3e00` depends: Update Qt download url (fanquake)
+- #12573 `88d1a64` Fix compilation when compiler do not support `__builtin_clz*` (532479301)
+
+### Tests and QA
+- #13061 `170b309` Make tests pass after 2020 (bmwiedemann)
+- #13192 `79c4fff` [tests] Fixed intermittent failure in `p2p_sendheaders.py` (lmanners)
+- #13300 `d9c5630` qa: Initialize lockstack to prevent null pointer deref (MarcoFalke)
+- #13545 `e15e3a9` tests: Fix test case `streams_serializedata_xor` Remove Boost dependency. (practicalswift)
+- #13304 `cbdabef` qa: Fix `wallet_listreceivedby` race (MarcoFalke)
+
+### Miscellaneous
+- #12887 `2291774` Add newlines to end of log messages (jnewbery)
+- #12859 `18b0c69` Bugfix: Include <memory> for `std::unique_ptr` (luke-jr)
+- #13131 `ce8aa54` Add Windows shutdown handler (ken2812221)
+- #13652 `20461fc` rpc: Fix that CWallet::AbandonTransaction would leave the grandchildren, etc. active (Empact)
+
+Credits
+=======
+
+Thanks to everyone who directly contributed to this release:
+
+- 532479301
+- Ben Woosley
+- Bernhard M. Wiedemann
+- Chun Kuan Lee
+- Cory Fields
+- fanquake
+- Gregory Sanders
+- joemphilips
+- John Newbery
+- Kristaps Kaupe
+- lmanners
+- Luke Dashjr
+- MarcoFalke
+- Matt Corallo
+- Pieter Wuille
+- practicalswift
+- Sjors Provoost
+- Suhas Daftuar
+- Wladimir J. van der Laan
+
+And to those that reported security issues:
+
+- Braydon Fuller
+- Himanshu Mehta
+
+As well as everyone that helped translating on [Transifex](https://www.transifex.com/projects/p/bitcoin/).
diff --git a/doc/release-process.md b/doc/release-process.md
index 912b620794..3ba622ee6d 100644
--- a/doc/release-process.md
+++ b/doc/release-process.md
@@ -29,7 +29,7 @@ Before every major release:
### First time / New builders
-If you're using the automated script (found in [contrib/gitian-build.sh](/contrib/gitian-build.sh)), then at this point you should run it with the "--setup" command. Otherwise ignore this.
+If you're using the automated script (found in [contrib/gitian-build.py](/contrib/gitian-build.py)), then at this point you should run it with the "--setup" command. Otherwise ignore this.
Check out the source code in the following directory hierarchy.
@@ -50,7 +50,7 @@ and sort them into categories based on labels)
Generate list of authors:
- git log --format='%aN' "$*" | sort -ui | sed -e 's/^/- /'
+ git log --format='- %aN' v(current version, e.g. 0.16.0)..v(new version, e.g. 0.16.1) | sort -fiu
Tag version (or release candidate) in git
@@ -58,7 +58,7 @@ Tag version (or release candidate) in git
### Setup and perform Gitian builds
-If you're using the automated script (found in [contrib/gitian-build.sh](/contrib/gitian-build.sh)), then at this point you should run it with the "--build" command. Otherwise ignore this.
+If you're using the automated script (found in [contrib/gitian-build.py](/contrib/gitian-build.py)), then at this point you should run it with the "--build" command. Otherwise ignore this.
Setup Gitian descriptors:
@@ -157,7 +157,7 @@ Commit your signature to gitian.sigs:
git add ${VERSION}-linux/"${SIGNER}"
git add ${VERSION}-win-unsigned/"${SIGNER}"
git add ${VERSION}-osx-unsigned/"${SIGNER}"
- git commit -a
+ git commit -m "Add ${VERSION} unsigned sigs for ${SIGNER}"
git push # Assuming you can push to the gitian.sigs tree
popd
diff --git a/share/qt/Info.plist.in b/share/qt/Info.plist.in
index 5ca6d9d015..17b4ee47de 100644
--- a/share/qt/Info.plist.in
+++ b/share/qt/Info.plist.in
@@ -3,7 +3,7 @@
<plist version="0.9">
<dict>
<key>LSMinimumSystemVersion</key>
- <string>10.8.0</string>
+ <string>10.10.0</string>
<key>LSArchitecturePriority</key>
<array>
diff --git a/src/Makefile.am b/src/Makefile.am
index 458721293e..60ecf07a59 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -139,6 +139,7 @@ BITCOIN_CORE_H = \
netbase.h \
netmessagemaker.h \
noui.h \
+ outputtype.h \
policy/feerate.h \
policy/fees.h \
policy/policy.h \
@@ -178,6 +179,7 @@ BITCOIN_CORE_H = \
ui_interface.h \
undo.h \
util.h \
+ utilmemory.h \
utilmoneystr.h \
utiltime.h \
validation.h \
@@ -230,6 +232,7 @@ libbitcoin_server_a_SOURCES = \
net.cpp \
net_processing.cpp \
noui.cpp \
+ outputtype.cpp \
policy/fees.cpp \
policy/policy.cpp \
policy/rbf.cpp \
@@ -271,6 +274,7 @@ libbitcoin_wallet_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
libbitcoin_wallet_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_wallet_a_SOURCES = \
interfaces/wallet.cpp \
+ wallet/coincontrol.cpp \
wallet/crypter.cpp \
wallet/db.cpp \
wallet/feebumper.cpp \
@@ -418,6 +422,7 @@ libbitcoin_util_a_SOURCES = \
if GLIBC_BACK_COMPAT
libbitcoin_util_a_SOURCES += compat/glibc_compat.cpp
+AM_LDFLAGS += $(COMPAT_LDFLAGS)
endif
# cli: shared between bitcoin-cli and bitcoin-qt
diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include
index 7b32b72bd1..f5293585a0 100644
--- a/src/Makefile.bench.include
+++ b/src/Makefile.bench.include
@@ -26,6 +26,7 @@ bench_bench_bitcoin_SOURCES = \
bench/mempool_eviction.cpp \
bench/verify_script.cpp \
bench/base58.cpp \
+ bench/bech32.cpp \
bench/lockedpool.cpp \
bench/prevector.cpp
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index a84a11ac45..c7a1963135 100644
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -357,7 +357,7 @@ if ENABLE_WALLET
BITCOIN_QT_CPP += $(BITCOIN_QT_WALLET_CPP)
endif
-RES_IMAGES =
+RES_IMAGES =
RES_MOVIES = $(wildcard $(srcdir)/qt/res/movies/spinner-*.png)
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 0c1516f4d5..02f2063504 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -94,6 +94,7 @@ BITCOIN_TESTS =\
if ENABLE_WALLET
BITCOIN_TESTS += \
wallet/test/accounting_tests.cpp \
+ wallet/test/psbt_wallet_tests.cpp \
wallet/test/wallet_tests.cpp \
wallet/test/wallet_crypto_tests.cpp \
wallet/test/coinselector_tests.cpp
diff --git a/src/addrman.cpp b/src/addrman.cpp
index e811dd4bea..3337ec54db 100644
--- a/src/addrman.cpp
+++ b/src/addrman.cpp
@@ -356,7 +356,7 @@ CAddrInfo CAddrMan::Select_(bool newOnly)
// Use a 50% chance for choosing between tried and new table entries.
if (!newOnly &&
- (nTried > 0 && (nNew == 0 || RandomInt(2) == 0))) {
+ (nTried > 0 && (nNew == 0 || RandomInt(2) == 0))) {
// use a tried node
double fChanceFactor = 1.0;
while (1) {
diff --git a/src/addrman.h b/src/addrman.h
index a36f7ea100..dc8ef8375b 100644
--- a/src/addrman.h
+++ b/src/addrman.h
@@ -182,8 +182,8 @@ public:
//! the maximum number of tried addr collisions to store
#define ADDRMAN_SET_TRIED_COLLISION_SIZE 10
-/**
- * Stochastical (IP) address manager
+/**
+ * Stochastical (IP) address manager
*/
class CAddrMan
{
diff --git a/src/bench/bech32.cpp b/src/bench/bech32.cpp
new file mode 100644
index 0000000000..8b80e17391
--- /dev/null
+++ b/src/bench/bech32.cpp
@@ -0,0 +1,37 @@
+// Copyright (c) 2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <bench/bench.h>
+
+#include <validation.h>
+#include <bech32.h>
+#include <utilstrencodings.h>
+
+#include <vector>
+#include <string>
+
+
+static void Bech32Encode(benchmark::State& state)
+{
+ std::vector<uint8_t> v = ParseHex("c97f5a67ec381b760aeaf67573bc164845ff39a3bb26a1cee401ac67243b48db");
+ std::vector<unsigned char> tmp = {0};
+ tmp.reserve(1 + 32 * 8 / 5);
+ ConvertBits<8, 5, true>([&](unsigned char c) { tmp.push_back(c); }, v.begin(), v.end());
+ while (state.KeepRunning()) {
+ bech32::Encode("bc", tmp);
+ }
+}
+
+
+static void Bech32Decode(benchmark::State& state)
+{
+ std::string addr = "bc1qkallence7tjawwvy0dwt4twc62qjgaw8f4vlhyd006d99f09";
+ while (state.KeepRunning()) {
+ bech32::Decode(addr);
+ }
+}
+
+
+BENCHMARK(Bech32Encode, 800 * 1000);
+BENCHMARK(Bech32Decode, 800 * 1000);
diff --git a/src/bench/bench_bitcoin.cpp b/src/bench/bench_bitcoin.cpp
index f3302bfe5a..92befdd593 100644
--- a/src/bench/bench_bitcoin.cpp
+++ b/src/bench/bench_bitcoin.cpp
@@ -38,6 +38,14 @@ static void SetupBenchArgs()
gArgs.AddArg("-help", "", false, OptionsCategory::HIDDEN);
}
+static fs::path SetDataDir()
+{
+ fs::path ret = fs::temp_directory_path() / "bench_bitcoin" / fs::unique_path();
+ fs::create_directories(ret);
+ gArgs.ForceSetArg("-datadir", ret.string());
+ return ret;
+}
+
int main(int argc, char** argv)
{
SetupBenchArgs();
@@ -53,6 +61,9 @@ int main(int argc, char** argv)
return EXIT_SUCCESS;
}
+ // Set the datadir after parsing the bench options
+ const fs::path bench_datadir{SetDataDir()};
+
SHA256AutoDetect();
RandomInit();
ECC_Start();
@@ -80,6 +91,8 @@ int main(int argc, char** argv)
benchmark::BenchRunner::RunAll(*printer, evaluations, scaling_factor, regex_filter, is_list_only);
+ fs::remove_all(bench_datadir);
+
ECC_Stop();
return EXIT_SUCCESS;
diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp
index f3180809b5..7510d53c88 100644
--- a/src/bench/coin_selection.cpp
+++ b/src/bench/coin_selection.cpp
@@ -8,7 +8,7 @@
#include <set>
-static void addCoin(const CAmount& nValue, const CWallet& wallet, std::vector<COutput>& vCoins)
+static void addCoin(const CAmount& nValue, const CWallet& wallet, std::vector<OutputGroup>& groups)
{
int nInput = 0;
@@ -21,7 +21,7 @@ static void addCoin(const CAmount& nValue, const CWallet& wallet, std::vector<CO
int nAge = 6 * 24;
COutput output(wtx, nInput, nAge, true /* spendable */, true /* solvable */, true /* safe */);
- vCoins.push_back(output);
+ groups.emplace_back(output.GetInputCoin(), 0, false, 0, 0);
}
// Simple benchmark for wallet coin selection. Note that it maybe be necessary
@@ -37,11 +37,11 @@ static void CoinSelection(benchmark::State& state)
LOCK(wallet.cs_wallet);
// Add coins.
- std::vector<COutput> vCoins;
+ std::vector<OutputGroup> groups;
for (int i = 0; i < 1000; ++i) {
- addCoin(1000 * COIN, wallet, vCoins);
+ addCoin(1000 * COIN, wallet, groups);
}
- addCoin(3 * COIN, wallet, vCoins);
+ addCoin(3 * COIN, wallet, groups);
const CoinEligibilityFilter filter_standard(1, 6, 0);
const CoinSelectionParams coin_selection_params(true, 34, 148, CFeeRate(0), 0);
@@ -49,7 +49,7 @@ static void CoinSelection(benchmark::State& state)
std::set<CInputCoin> setCoinsRet;
CAmount nValueRet;
bool bnb_used;
- bool success = wallet.SelectCoinsMinConf(1003 * COIN, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used);
+ bool success = wallet.SelectCoinsMinConf(1003 * COIN, filter_standard, groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used);
assert(success);
assert(nValueRet == 1003 * COIN);
assert(setCoinsRet.size() == 2);
@@ -57,17 +57,21 @@ static void CoinSelection(benchmark::State& state)
}
typedef std::set<CInputCoin> CoinSet;
+static const CWallet testWallet("dummy", WalletDatabase::CreateDummy());
+std::vector<std::unique_ptr<CWalletTx>> wtxn;
// Copied from src/wallet/test/coinselector_tests.cpp
-static void add_coin(const CAmount& nValue, int nInput, std::vector<CInputCoin>& set)
+static void add_coin(const CAmount& nValue, int nInput, std::vector<OutputGroup>& set)
{
CMutableTransaction tx;
tx.vout.resize(nInput + 1);
tx.vout[nInput].nValue = nValue;
- set.emplace_back(MakeTransactionRef(tx), nInput);
+ std::unique_ptr<CWalletTx> wtx(new CWalletTx(&testWallet, MakeTransactionRef(std::move(tx))));
+ set.emplace_back(COutput(wtx.get(), nInput, 0, true, true, true).GetInputCoin(), 0, true, 0, 0);
+ wtxn.emplace_back(std::move(wtx));
}
// Copied from src/wallet/test/coinselector_tests.cpp
-static CAmount make_hard_case(int utxos, std::vector<CInputCoin>& utxo_pool)
+static CAmount make_hard_case(int utxos, std::vector<OutputGroup>& utxo_pool)
{
utxo_pool.clear();
CAmount target = 0;
@@ -82,7 +86,7 @@ static CAmount make_hard_case(int utxos, std::vector<CInputCoin>& utxo_pool)
static void BnBExhaustion(benchmark::State& state)
{
// Setup
- std::vector<CInputCoin> utxo_pool;
+ std::vector<OutputGroup> utxo_pool;
CoinSet selection;
CAmount value_ret = 0;
CAmount not_input_fees = 0;
diff --git a/src/bench/prevector.cpp b/src/bench/prevector.cpp
index 09c7020848..7986d0da79 100644
--- a/src/bench/prevector.cpp
+++ b/src/bench/prevector.cpp
@@ -4,12 +4,17 @@
#include <compat.h>
#include <prevector.h>
+#include <serialize.h>
+#include <streams.h>
#include <bench/bench.h>
struct nontrivial_t {
int x;
nontrivial_t() :x(-1) {}
+ ADD_SERIALIZE_METHODS
+ template <typename Stream, typename Operation>
+ inline void SerializationOp(Stream& s, Operation ser_action) {READWRITE(x);}
};
static_assert(!IS_TRIVIALLY_CONSTRUCTIBLE<nontrivial_t>::value,
"expected nontrivial_t to not be trivially constructible");
@@ -62,6 +67,28 @@ static void PrevectorResize(benchmark::State& state)
}
}
+template <typename T>
+static void PrevectorDeserialize(benchmark::State& state)
+{
+ CDataStream s0(SER_NETWORK, 0);
+ prevector<28, T> t0;
+ t0.resize(28);
+ for (auto x = 0; x < 900; ++x) {
+ s0 << t0;
+ }
+ t0.resize(100);
+ for (auto x = 0; x < 101; ++x) {
+ s0 << t0;
+ }
+ while (state.KeepRunning()) {
+ prevector<28, T> t1;
+ for (auto x = 0; x < 1000; ++x) {
+ s0 >> t1;
+ }
+ s0.Init(SER_NETWORK, 0);
+ }
+}
+
#define PREVECTOR_TEST(name, nontrivops, trivops) \
static void Prevector ## name ## Nontrivial(benchmark::State& state) { \
Prevector ## name<nontrivial_t>(state); \
@@ -75,3 +102,4 @@ static void PrevectorResize(benchmark::State& state)
PREVECTOR_TEST(Clear, 28300, 88600)
PREVECTOR_TEST(Destructor, 28800, 88900)
PREVECTOR_TEST(Resize, 28900, 90300)
+PREVECTOR_TEST(Deserialize, 6800, 52000)
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index b332b5e581..a77e3d49b8 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -35,6 +35,7 @@ static void SetupCliArgs()
const auto testnetBaseParams = CreateBaseChainParams(CBaseChainParams::TESTNET);
gArgs.AddArg("-?", "This help message", false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-version", "Print version and exit", false, OptionsCategory::OPTIONS);
gArgs.AddArg("-conf=<file>", strprintf("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME), false, OptionsCategory::OPTIONS);
gArgs.AddArg("-datadir=<dir>", "Specify data directory", false, OptionsCategory::OPTIONS);
gArgs.AddArg("-getinfo", "Get general information from the remote server. Note that unlike server-side RPC calls, the results of -getinfo is the result of multiple non-atomic requests. Some entries in the result may represent results from different states (e.g. wallet balance may be as of a different block from the chain state reported)", false, OptionsCategory::OPTIONS);
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index 494a925a79..5c711c0773 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -21,8 +21,6 @@
#include <utilstrencodings.h>
#include <walletinitinterface.h>
-#include <boost/thread.hpp>
-
#include <stdio.h>
/* Introduction text for doxygen: */
@@ -31,12 +29,14 @@
*
* \section intro_sec Introduction
*
- * This is the developer documentation of the reference client for an experimental new digital currency called Bitcoin (https://www.bitcoin.org/),
+ * This is the developer documentation of the reference client for an experimental new digital currency called Bitcoin,
* which enables instant payments to anyone, anywhere in the world. Bitcoin uses peer-to-peer technology to operate
* with no central authority: managing transactions and issuing money are carried out collectively by the network.
*
* The software is a community-driven open source project, released under the MIT license.
*
+ * See https://github.com/bitcoin/bitcoin and https://bitcoincore.org/ for further information about the project.
+ *
* \section Navigation
* Use the buttons <code>Namespaces</code>, <code>Classes</code> or <code>Files</code> at the top of the page to start navigating the code.
*/
diff --git a/src/bloom.cpp b/src/bloom.cpp
index f8e28edded..799e0eef00 100644
--- a/src/bloom.cpp
+++ b/src/bloom.cpp
@@ -148,7 +148,7 @@ bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx)
const CTxOut& txout = tx.vout[i];
// Match if the filter contains any arbitrary script data element in any scriptPubKey in tx
// If this matches, also add the specific output that was matched.
- // This means clients don't have to update the filter themselves when a new relevant tx
+ // This means clients don't have to update the filter themselves when a new relevant tx
// is discovered in order to find spending transactions, which avoids round-tripping and race conditions.
CScript::const_iterator pc = txout.scriptPubKey.begin();
std::vector<unsigned char> data;
diff --git a/src/bloom.h b/src/bloom.h
index 2f9d2109cd..71386f52fa 100644
--- a/src/bloom.h
+++ b/src/bloom.h
@@ -33,9 +33,9 @@ enum bloomflags
/**
* BloomFilter is a probabilistic filter which SPV clients provide
* so that we can filter the transactions we send them.
- *
+ *
* This allows for significantly more efficient transaction and block downloads.
- *
+ *
* Because bloom filters are probabilistic, a SPV node can increase the false-
* positive rate, making us send it transactions which aren't actually its,
* allowing clients to trade more bandwidth for more privacy by obfuscating which
diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp
index 787d8d8f6a..da9f3e3209 100644
--- a/src/chainparamsbase.cpp
+++ b/src/chainparamsbase.cpp
@@ -7,6 +7,7 @@
#include <tinyformat.h>
#include <util.h>
+#include <utilmemory.h>
#include <assert.h>
diff --git a/src/checkqueue.h b/src/checkqueue.h
index 7500bae60e..84360d0716 100644
--- a/src/checkqueue.h
+++ b/src/checkqueue.h
@@ -16,7 +16,7 @@
template <typename T>
class CCheckQueueControl;
-/**
+/**
* Queue for verifications that have to be performed.
* The verifications are represented by a type T, which must provide an
* operator(), returning a bool.
@@ -163,7 +163,7 @@ public:
};
-/**
+/**
* RAII-style controller object for a CCheckQueue that guarantees the passed
* queue is finished before continuing.
*/
diff --git a/src/clientversion.cpp b/src/clientversion.cpp
index 662fbb6e77..a3eb127ca8 100644
--- a/src/clientversion.cpp
+++ b/src/clientversion.cpp
@@ -81,8 +81,8 @@ std::string FormatFullVersion()
return CLIENT_BUILD;
}
-/**
- * Format the subversion field according to BIP 14 spec (https://github.com/bitcoin/bips/blob/master/bip-0014.mediawiki)
+/**
+ * Format the subversion field according to BIP 14 spec (https://github.com/bitcoin/bips/blob/master/bip-0014.mediawiki)
*/
std::string FormatSubVersion(const std::string& name, int nClientVersion, const std::vector<std::string>& comments)
{
diff --git a/src/coins.h b/src/coins.h
index a73f016a31..89685ce4b2 100644
--- a/src/coins.h
+++ b/src/coins.h
@@ -203,7 +203,7 @@ class CCoinsViewCache : public CCoinsViewBacked
protected:
/**
* Make mutable so that we can "fill the cache" even from Get-methods
- * declared as "const".
+ * declared as "const".
*/
mutable uint256 hashBlock;
mutable CCoinsMap cacheCoins;
@@ -280,7 +280,7 @@ public:
//! Calculate the size of the cache (in bytes)
size_t DynamicMemoryUsage() const;
- /**
+ /**
* Amount of bitcoins coming in to a transaction
* Note that lightweight clients may not know anything besides the hash of previous transactions,
* so may not be able to calculate this.
diff --git a/src/compat/byteswap.h b/src/compat/byteswap.h
index c331af2b5e..bfb80bd632 100644
--- a/src/compat/byteswap.h
+++ b/src/compat/byteswap.h
@@ -15,7 +15,7 @@
#include <byteswap.h>
#endif
-#if defined(__APPLE__)
+#if defined(MAC_OSX)
#if !defined(bswap_16)
@@ -61,6 +61,6 @@ inline uint64_t bswap_64(uint64_t x)
}
#endif // HAVE_DECL_BSWAP64 == 0
-#endif // defined(__APPLE__)
+#endif // defined(MAC_OSX)
#endif // BITCOIN_COMPAT_BYTESWAP_H
diff --git a/src/compat/glibc_compat.cpp b/src/compat/glibc_compat.cpp
index 55da5ef63f..b1cbe9f72a 100644
--- a/src/compat/glibc_compat.cpp
+++ b/src/compat/glibc_compat.cpp
@@ -7,6 +7,7 @@
#endif
#include <cstddef>
+#include <cstdint>
#if defined(HAVE_SYS_SELECT_H)
#include <sys/select.h>
@@ -27,3 +28,47 @@ extern "C" FDELT_TYPE __fdelt_warn(FDELT_TYPE a)
return a / __NFDBITS;
}
extern "C" FDELT_TYPE __fdelt_chk(FDELT_TYPE) __attribute__((weak, alias("__fdelt_warn")));
+
+#if defined(__i386__) || defined(__arm__)
+
+extern "C" int64_t __udivmoddi4(uint64_t u, uint64_t v, uint64_t* rp);
+
+extern "C" int64_t __wrap___divmoddi4(int64_t u, int64_t v, int64_t* rp)
+{
+ int32_t c1 = 0, c2 = 0;
+ int64_t uu = u, vv = v;
+ int64_t w;
+ int64_t r;
+
+ if (uu < 0) {
+ c1 = ~c1, c2 = ~c2, uu = -uu;
+ }
+ if (vv < 0) {
+ c1 = ~c1, vv = -vv;
+ }
+
+ w = __udivmoddi4(uu, vv, (uint64_t*)&r);
+ if (c1)
+ w = -w;
+ if (c2)
+ r = -r;
+
+ *rp = r;
+ return w;
+}
+#endif
+
+extern "C" float log2f_old(float x);
+#ifdef __i386__
+__asm(".symver log2f_old,log2f@GLIBC_2.1");
+#elif defined(__amd64__)
+__asm(".symver log2f_old,log2f@GLIBC_2.2.5");
+#elif defined(__arm__)
+__asm(".symver log2f_old,log2f@GLIBC_2.4");
+#elif defined(__aarch64__)
+__asm(".symver log2f_old,log2f@GLIBC_2.17");
+#endif
+extern "C" float __wrap_log2f(float x)
+{
+ return log2f_old(x);
+}
diff --git a/src/consensus/tx_verify.h b/src/consensus/tx_verify.h
index a4836ceda4..50db1775a3 100644
--- a/src/consensus/tx_verify.h
+++ b/src/consensus/tx_verify.h
@@ -41,7 +41,7 @@ unsigned int GetLegacySigOpCount(const CTransaction& tx);
/**
* Count ECDSA signature operations in pay-to-script-hash inputs.
- *
+ *
* @param[in] mapInputs Map of previous transactions that have outputs we're spending
* @return maximum number of sigops required to validate this transaction's inputs
* @see CTransaction::FetchInputs
diff --git a/src/core_io.h b/src/core_io.h
index 1d87d21d40..ebddcc2a04 100644
--- a/src/core_io.h
+++ b/src/core_io.h
@@ -14,6 +14,7 @@ class CBlock;
class CScript;
class CTransaction;
struct CMutableTransaction;
+struct PartiallySignedTransaction;
class uint256;
class UniValue;
@@ -24,12 +25,16 @@ bool DecodeHexTx(CMutableTransaction& tx, const std::string& hex_tx, bool try_no
bool DecodeHexBlk(CBlock&, const std::string& strHexBlk);
uint256 ParseHashStr(const std::string&, const std::string& strName);
std::vector<unsigned char> ParseHexUV(const UniValue& v, const std::string& strName);
+bool DecodePSBT(PartiallySignedTransaction& psbt, const std::string& base64_tx, std::string& error);
+int ParseSighashString(const UniValue& sighash);
// core_write.cpp
UniValue ValueFromAmount(const CAmount& amount);
std::string FormatScript(const CScript& script);
std::string EncodeHexTx(const CTransaction& tx, const int serializeFlags = 0);
+std::string SighashToStr(unsigned char sighash_type);
void ScriptPubKeyToUniv(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex);
+void ScriptToUniv(const CScript& script, UniValue& out, bool include_address);
void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, bool include_hex = true, int serialize_flags = 0);
#endif // BITCOIN_CORE_IO_H
diff --git a/src/core_read.cpp b/src/core_read.cpp
index 4d851610ef..4337c38282 100644
--- a/src/core_read.cpp
+++ b/src/core_read.cpp
@@ -7,6 +7,7 @@
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <script/script.h>
+#include <script/sign.h>
#include <serialize.h>
#include <streams.h>
#include <univalue.h>
@@ -15,10 +16,11 @@
#include <version.h>
#include <boost/algorithm/string/classification.hpp>
-#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/split.hpp>
+#include <algorithm>
+
CScript ParseScript(const std::string& s)
{
CScript result;
@@ -53,20 +55,20 @@ CScript ParseScript(const std::string& s)
{
// Empty string, ignore. (boost::split given '' will return one word)
}
- else if (all(*w, boost::algorithm::is_digit()) ||
- (boost::algorithm::starts_with(*w, "-") && all(std::string(w->begin()+1, w->end()), boost::algorithm::is_digit())))
+ else if (std::all_of(w->begin(), w->end(), ::IsDigit) ||
+ (w->front() == '-' && w->size() > 1 && std::all_of(w->begin()+1, w->end(), ::IsDigit)))
{
// Number
int64_t n = atoi64(*w);
result << n;
}
- else if (boost::algorithm::starts_with(*w, "0x") && (w->begin()+2 != w->end()) && IsHex(std::string(w->begin()+2, w->end())))
+ else if (w->substr(0,2) == "0x" && w->size() > 2 && IsHex(std::string(w->begin()+2, w->end())))
{
// Raw hex data, inserted NOT pushed onto stack:
std::vector<unsigned char> raw = ParseHex(std::string(w->begin()+2, w->end()));
result.insert(result.end(), raw.begin(), raw.end());
}
- else if (w->size() >= 2 && boost::algorithm::starts_with(*w, "'") && boost::algorithm::ends_with(*w, "'"))
+ else if (w->size() >= 2 && w->front() == '\'' && w->back() == '\'')
{
// Single-quoted string, pushed as data. NOTE: this is poor-man's
// parsing, spaces/tabs/newlines in single-quoted strings won't work.
@@ -104,7 +106,7 @@ static bool CheckTxScriptsSanity(const CMutableTransaction& tx)
return false;
}
}
-
+
return true;
}
@@ -139,7 +141,7 @@ bool DecodeHexTx(CMutableTransaction& tx, const std::string& hex_tx, bool try_no
// Fall through.
}
}
-
+
return false;
}
@@ -160,6 +162,23 @@ bool DecodeHexBlk(CBlock& block, const std::string& strHexBlk)
return true;
}
+bool DecodePSBT(PartiallySignedTransaction& psbt, const std::string& base64_tx, std::string& error)
+{
+ std::vector<unsigned char> tx_data = DecodeBase64(base64_tx.c_str());
+ CDataStream ss_data(tx_data, SER_NETWORK, PROTOCOL_VERSION);
+ try {
+ ss_data >> psbt;
+ if (!ss_data.empty()) {
+ error = "extra data after PSBT";
+ return false;
+ }
+ } catch (const std::exception& e) {
+ error = e.what();
+ return false;
+ }
+ return true;
+}
+
uint256 ParseHashStr(const std::string& strHex, const std::string& strName)
{
if (!IsHex(strHex)) // Note: IsHex("") is false
@@ -179,3 +198,26 @@ std::vector<unsigned char> ParseHexUV(const UniValue& v, const std::string& strN
throw std::runtime_error(strName + " must be hexadecimal string (not '" + strHex + "')");
return ParseHex(strHex);
}
+
+int ParseSighashString(const UniValue& sighash)
+{
+ int hash_type = SIGHASH_ALL;
+ if (!sighash.isNull()) {
+ static std::map<std::string, int> map_sighash_values = {
+ {std::string("ALL"), int(SIGHASH_ALL)},
+ {std::string("ALL|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_ANYONECANPAY)},
+ {std::string("NONE"), int(SIGHASH_NONE)},
+ {std::string("NONE|ANYONECANPAY"), int(SIGHASH_NONE|SIGHASH_ANYONECANPAY)},
+ {std::string("SINGLE"), int(SIGHASH_SINGLE)},
+ {std::string("SINGLE|ANYONECANPAY"), int(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY)},
+ };
+ std::string strHashType = sighash.get_str();
+ const auto& it = map_sighash_values.find(strHashType);
+ if (it != map_sighash_values.end()) {
+ hash_type = it->second;
+ } else {
+ throw std::runtime_error(strHashType + " is not a valid sighash parameter.");
+ }
+ }
+ return hash_type;
+}
diff --git a/src/core_write.cpp b/src/core_write.cpp
index ee6737201b..1272266235 100644
--- a/src/core_write.cpp
+++ b/src/core_write.cpp
@@ -70,6 +70,13 @@ const std::map<unsigned char, std::string> mapSigHashTypes = {
{static_cast<unsigned char>(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY), std::string("SINGLE|ANYONECANPAY")},
};
+std::string SighashToStr(unsigned char sighash_type)
+{
+ const auto& it = mapSigHashTypes.find(sighash_type);
+ if (it == mapSigHashTypes.end()) return "";
+ return it->second;
+}
+
/**
* Create the assembly string representation of a CScript object.
* @param[in] script CScript object to convert into the asm string representation.
@@ -128,6 +135,22 @@ std::string EncodeHexTx(const CTransaction& tx, const int serializeFlags)
return HexStr(ssTx.begin(), ssTx.end());
}
+void ScriptToUniv(const CScript& script, UniValue& out, bool include_address)
+{
+ out.pushKV("asm", ScriptToAsmStr(script));
+ out.pushKV("hex", HexStr(script.begin(), script.end()));
+
+ std::vector<std::vector<unsigned char>> solns;
+ txnouttype type;
+ Solver(script, type, solns);
+ out.pushKV("type", GetTxnOutputType(type));
+
+ CTxDestination address;
+ if (include_address && ExtractDestination(script, address)) {
+ out.pushKV("address", EncodeDestination(address));
+ }
+}
+
void ScriptPubKeyToUniv(const CScript& scriptPubKey,
UniValue& out, bool fIncludeHex)
{
diff --git a/src/httprpc.cpp b/src/httprpc.cpp
index c49ad12283..0e5b497960 100644
--- a/src/httprpc.cpp
+++ b/src/httprpc.cpp
@@ -86,7 +86,7 @@ static void JSONErrorReply(HTTPRequest* req, const UniValue& objError, const Uni
//This function checks username and password against -rpcauth
//entries from config file.
static bool multiUserAuthorized(std::string strUserPass)
-{
+{
if (strUserPass.find(':') == std::string::npos) {
return false;
}
@@ -223,7 +223,7 @@ static bool InitRPCAuthentication()
return false;
}
} else {
- LogPrintf("Config options rpcuser and rpcpassword will soon be deprecated. Locally-run instances may remove rpcuser to use cookie-based auth, or may be replaced with rpcauth. Please see share/rpcuser for rpcauth auth generation.\n");
+ LogPrintf("Config options rpcuser and rpcpassword will soon be deprecated. Locally-run instances may remove rpcuser to use cookie-based auth, or may be replaced with rpcauth. Please see share/rpcauth for rpcauth auth generation.\n");
strRPCUserColonPass = gArgs.GetArg("-rpcuser", "") + ":" + gArgs.GetArg("-rpcpassword", "");
}
if (gArgs.GetArg("-rpcauth","") != "")
diff --git a/src/httpserver.cpp b/src/httpserver.cpp
index 4ed734da41..9daf3d1968 100644
--- a/src/httpserver.cpp
+++ b/src/httpserver.cpp
@@ -423,7 +423,7 @@ std::thread threadHTTP;
std::future<bool> threadResult;
static std::vector<std::thread> g_thread_http_workers;
-bool StartHTTPServer()
+void StartHTTPServer()
{
LogPrint(BCLog::HTTP, "Starting HTTP server\n");
int rpcThreads = std::max((long)gArgs.GetArg("-rpcthreads", DEFAULT_HTTP_THREADS), 1L);
@@ -435,7 +435,6 @@ bool StartHTTPServer()
for (int i = 0; i < rpcThreads; i++) {
g_thread_http_workers.emplace_back(HTTPWorkQueueRun, workQueue);
}
- return true;
}
void InterruptHTTPServer()
diff --git a/src/httpserver.h b/src/httpserver.h
index 8132c887b5..8a3adaf91f 100644
--- a/src/httpserver.h
+++ b/src/httpserver.h
@@ -26,7 +26,7 @@ bool InitHTTPServer();
* This is separate from InitHTTPServer to give users race-condition-free time
* to register their handlers between InitHTTPServer and StartHTTPServer.
*/
-bool StartHTTPServer();
+void StartHTTPServer();
/** Interrupt HTTP server threads */
void InterruptHTTPServer();
/** Stop HTTP server */
diff --git a/src/init.cpp b/src/init.cpp
index 66b0b65eb4..18402ef5d7 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -501,7 +501,7 @@ void SetupServerArgs()
gArgs.AddArg("-rest", strprintf("Accept public REST requests (default: %u)", DEFAULT_REST_ENABLE), false, OptionsCategory::RPC);
gArgs.AddArg("-rpcallowip=<ip>", "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", false, OptionsCategory::RPC);
- gArgs.AddArg("-rpcauth=<userpw>", "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", false, OptionsCategory::RPC);
+ gArgs.AddArg("-rpcauth=<userpw>", "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/rpcauth. The client then connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> pair of arguments. This option can be specified multiple times", false, OptionsCategory::RPC);
gArgs.AddArg("-rpcbind=<addr>[:port]", "Bind to given address to listen for JSON-RPC connections. This option is ignored unless -rpcallowip is also passed. Port is optional and overrides -rpcport. Use [host]:port notation for IPv6. This option can be specified multiple times (default: 127.0.0.1 and ::1 i.e., localhost, or if -rpcallowip has been specified, 0.0.0.0 and :: i.e., all addresses)", false, OptionsCategory::RPC);
gArgs.AddArg("-rpccookiefile=<loc>", "Location of the auth cookie. Relative paths will be prefixed by a net-specific datadir location. (default: data dir)", false, OptionsCategory::RPC);
gArgs.AddArg("-rpcpassword=<pw>", "Password for JSON-RPC connections", false, OptionsCategory::RPC);
@@ -731,14 +731,12 @@ static bool AppInitServers()
RPCServer::OnStopped(&OnRPCStopped);
if (!InitHTTPServer())
return false;
- if (!StartRPC())
- return false;
+ StartRPC();
if (!StartHTTPRPC())
return false;
if (gArgs.GetBoolArg("-rest", DEFAULT_REST_ENABLE) && !StartREST())
return false;
- if (!StartHTTPServer())
- return false;
+ StartHTTPServer();
return true;
}
@@ -927,7 +925,7 @@ bool AppInitParameterInteraction()
// also see: InitParameterInteraction()
if (!fs::is_directory(GetBlocksDir(false))) {
- return InitError(strprintf(_("Specified blocks directory \"%s\" does not exist.\n"), gArgs.GetArg("-blocksdir", "").c_str()));
+ return InitError(strprintf(_("Specified blocks directory \"%s\" does not exist."), gArgs.GetArg("-blocksdir", "").c_str()));
}
// if using block pruning, then disallow txindex
@@ -1122,8 +1120,6 @@ bool AppInitParameterInteraction()
if (gArgs.GetBoolArg("-peerbloomfilters", DEFAULT_PEERBLOOMFILTERS))
nLocalServices = ServiceFlags(nLocalServices | NODE_BLOOM);
- g_enable_bip61 = gArgs.GetBoolArg("-enablebip61", DEFAULT_ENABLE_BIP61);
-
if (gArgs.GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) < 0)
return InitError("rpcserialversion must be non-negative.");
@@ -1308,7 +1304,7 @@ bool AppInitMain()
g_connman = std::unique_ptr<CConnman>(new CConnman(GetRand(std::numeric_limits<uint64_t>::max()), GetRand(std::numeric_limits<uint64_t>::max())));
CConnman& connman = *g_connman;
- peerLogic.reset(new PeerLogicValidation(&connman, scheduler));
+ peerLogic.reset(new PeerLogicValidation(&connman, scheduler, gArgs.GetBoolArg("-enablebip61", DEFAULT_ENABLE_BIP61)));
RegisterValidationInterface(peerLogic.get());
// sanitize comments per BIP-0014, format user agent and check total size
diff --git a/src/interfaces/handler.cpp b/src/interfaces/handler.cpp
index 8e45faa2a5..80f461f4d3 100644
--- a/src/interfaces/handler.cpp
+++ b/src/interfaces/handler.cpp
@@ -4,7 +4,7 @@
#include <interfaces/handler.h>
-#include <util.h>
+#include <utilmemory.h>
#include <boost/signals2/connection.hpp>
#include <utility>
diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp
index e98acba0df..55a6f771e5 100644
--- a/src/interfaces/wallet.cpp
+++ b/src/interfaces/wallet.cpp
@@ -90,10 +90,9 @@ WalletTxStatus MakeWalletTxStatus(const CWalletTx& wtx)
WalletTxStatus result;
auto mi = ::mapBlockIndex.find(wtx.hashBlock);
CBlockIndex* block = mi != ::mapBlockIndex.end() ? mi->second : nullptr;
- result.block_height = (block ? block->nHeight : std::numeric_limits<int>::max()),
+ result.block_height = (block ? block->nHeight : std::numeric_limits<int>::max());
result.blocks_to_maturity = wtx.GetBlocksToMaturity();
result.depth_in_main_chain = wtx.GetDepthInMainChain();
- result.request_count = wtx.GetRequestCount();
result.time_received = wtx.nTimeReceived;
result.lock_time = wtx.tx->nLockTime;
result.is_final = CheckFinalTx(*wtx.tx);
@@ -339,7 +338,7 @@ public:
result.immature_balance = m_wallet.GetImmatureBalance();
result.have_watch_only = m_wallet.HaveWatchOnly();
if (result.have_watch_only) {
- result.watch_only_balance = m_wallet.GetWatchOnlyBalance();
+ result.watch_only_balance = m_wallet.GetBalance(ISMINE_WATCH_ONLY);
result.unconfirmed_watch_only_balance = m_wallet.GetUnconfirmedWatchOnlyBalance();
result.immature_watch_only_balance = m_wallet.GetImmatureWatchOnlyBalance();
}
@@ -427,6 +426,7 @@ public:
}
unsigned int getConfirmTarget() override { return m_wallet.m_confirm_target; }
bool hdEnabled() override { return m_wallet.IsHDEnabled(); }
+ bool IsWalletFlagSet(uint64_t flag) override { return m_wallet.IsWalletFlagSet(flag); }
OutputType getDefaultAddressType() override { return m_wallet.m_default_address_type; }
OutputType getDefaultChangeType() override { return m_wallet.m_default_change_type; }
std::unique_ptr<Handler> handleUnload(UnloadFn fn) override
diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h
index ce42e14eea..ae54d42442 100644
--- a/src/interfaces/wallet.h
+++ b/src/interfaces/wallet.h
@@ -236,6 +236,9 @@ public:
// Return whether HD enabled.
virtual bool hdEnabled() = 0;
+ // check if a certain wallet flag is set.
+ virtual bool IsWalletFlagSet(uint64_t flag) = 0;
+
// Get default address type.
virtual OutputType getDefaultAddressType() = 0;
@@ -346,7 +349,6 @@ struct WalletTxStatus
int block_height;
int blocks_to_maturity;
int depth_in_main_chain;
- int request_count;
unsigned int time_received;
uint32_t lock_time;
bool is_final;
diff --git a/src/keystore.h b/src/keystore.h
index cd5ded9203..f64024c7e7 100644
--- a/src/keystore.h
+++ b/src/keystore.h
@@ -38,21 +38,21 @@ public:
virtual bool HaveWatchOnly() const =0;
};
-typedef std::map<CKeyID, CKey> KeyMap;
-typedef std::map<CKeyID, CPubKey> WatchKeyMap;
-typedef std::map<CScriptID, CScript > ScriptMap;
-typedef std::set<CScript> WatchOnlySet;
-
/** Basic key store, that keeps keys in an address->secret map */
class CBasicKeyStore : public CKeyStore
{
protected:
mutable CCriticalSection cs_KeyStore;
- KeyMap mapKeys;
- WatchKeyMap mapWatchKeys;
- ScriptMap mapScripts;
- WatchOnlySet setWatchOnly;
+ using KeyMap = std::map<CKeyID, CKey>;
+ using WatchKeyMap = std::map<CKeyID, CPubKey>;
+ using ScriptMap = std::map<CScriptID, CScript>;
+ using WatchOnlySet = std::set<CScript>;
+
+ KeyMap mapKeys GUARDED_BY(cs_KeyStore);
+ WatchKeyMap mapWatchKeys GUARDED_BY(cs_KeyStore);
+ ScriptMap mapScripts GUARDED_BY(cs_KeyStore);
+ WatchOnlySet setWatchOnly GUARDED_BY(cs_KeyStore);
void ImplicitlyLearnRelatedKeyScripts(const CPubKey& pubkey) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
@@ -74,9 +74,6 @@ public:
bool HaveWatchOnly() const override;
};
-typedef std::vector<unsigned char, secure_allocator<unsigned char> > CKeyingMaterial;
-typedef std::map<CKeyID, std::pair<CPubKey, std::vector<unsigned char> > > CryptedKeyMap;
-
/** Return the CKeyID of the key involved in a script (if there is a unique one). */
CKeyID GetKeyForDestination(const CKeyStore& store, const CTxDestination& dest);
diff --git a/src/limitedmap.h b/src/limitedmap.h
index d5f00f1fee..22dde5ec36 100644
--- a/src/limitedmap.h
+++ b/src/limitedmap.h
@@ -70,7 +70,7 @@ public:
// since it is a constant time operation in C++11. For more details, see
// https://stackoverflow.com/questions/765148/how-to-remove-constness-of-const-iterator
iterator itTarget = map.erase(itIn, itIn);
-
+
if (itTarget == map.end())
return;
std::pair<rmap_iterator, rmap_iterator> itPair = rmap.equal_range(itTarget->second);
diff --git a/src/net.cpp b/src/net.cpp
index b24ceb67d6..0ebfefa757 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -235,12 +235,11 @@ bool AddLocal(const CNetAddr &addr, int nScore)
return AddLocal(CService(addr, GetListenPort()), nScore);
}
-bool RemoveLocal(const CService& addr)
+void RemoveLocal(const CService& addr)
{
LOCK(cs_mapLocalHost);
LogPrintf("RemoveLocal(%s)\n", addr.ToString());
mapLocalHost.erase(addr);
- return true;
}
/** Make a particular network entirely off-limits (no automatic connects to it) */
@@ -1163,6 +1162,17 @@ void CConnman::ThreadSocketHandler()
//
{
LOCK(cs_vNodes);
+
+ if (!fNetworkActive) {
+ // Disconnect any connected nodes
+ for (CNode* pnode : vNodes) {
+ if (!pnode->fDisconnect) {
+ LogPrint(BCLog::NET, "Network not active, dropping peer=%d\n", pnode->GetId());
+ pnode->fDisconnect = true;
+ }
+ }
+ }
+
// Disconnect unused nodes
std::vector<CNode*> vNodesCopy = vNodes;
for (CNode* pnode : vNodesCopy)
@@ -2198,14 +2208,6 @@ void CConnman::SetNetworkActive(bool active)
fNetworkActive = active;
- if (!fNetworkActive) {
- LOCK(cs_vNodes);
- // Close sockets to all nodes
- for (CNode* pnode : vNodes) {
- pnode->CloseSocketDisconnect();
- }
- }
-
uiInterface.NotifyNetworkActiveChanged(fNetworkActive);
}
@@ -2254,7 +2256,8 @@ bool CConnman::InitBinds(const std::vector<CService>& binds, const std::vector<C
if (binds.empty() && whiteBinds.empty()) {
struct in_addr inaddr_any;
inaddr_any.s_addr = INADDR_ANY;
- fBound |= Bind(CService(in6addr_any, GetListenPort()), BF_NONE);
+ struct in6_addr inaddr6_any = IN6ADDR_ANY_INIT;
+ fBound |= Bind(CService(inaddr6_any, GetListenPort()), BF_NONE);
fBound |= Bind(CService(inaddr_any, GetListenPort()), !fBound ? BF_REPORT_ERROR : BF_NONE);
}
return fBound;
@@ -2864,8 +2867,20 @@ bool CConnman::ForNode(NodeId id, std::function<bool(CNode* pnode)> func)
return found != nullptr && NodeFullyConnected(found) && func(found);
}
-int64_t PoissonNextSend(int64_t nNow, int average_interval_seconds) {
- return nNow + (int64_t)(log1p(GetRand(1ULL << 48) * -0.0000000000000035527136788 /* -1/2^48 */) * average_interval_seconds * -1000000.0 + 0.5);
+int64_t CConnman::PoissonNextSendInbound(int64_t now, int average_interval_seconds)
+{
+ if (m_next_send_inv_to_incoming < now) {
+ // If this function were called from multiple threads simultaneously
+ // it would possible that both update the next send variable, and return a different result to their caller.
+ // This is not possible in practice as only the net processing thread invokes this function.
+ m_next_send_inv_to_incoming = PoissonNextSend(now, average_interval_seconds);
+ }
+ return m_next_send_inv_to_incoming;
+}
+
+int64_t PoissonNextSend(int64_t now, int average_interval_seconds)
+{
+ return now + (int64_t)(log1p(GetRand(1ULL << 48) * -0.0000000000000035527136788 /* -1/2^48 */) * average_interval_seconds * -1000000.0 + 0.5);
}
CSipHasher CConnman::GetDeterministicRandomizer(uint64_t id) const
diff --git a/src/net.h b/src/net.h
index 697aa37a58..36c2a4b8f5 100644
--- a/src/net.h
+++ b/src/net.h
@@ -310,6 +310,13 @@ public:
unsigned int GetReceiveFloodSize() const;
void WakeMessageHandler();
+
+ /** Attempts to obfuscate tx time through exponentially distributed emitting.
+ Works assuming that a single interval is used.
+ Variable intervals will result in privacy decrease.
+ */
+ int64_t PoissonNextSendInbound(int64_t now, int average_interval_seconds);
+
private:
struct ListenSocket {
SOCKET socket;
@@ -434,6 +441,8 @@ private:
* This takes the place of a feeler connection */
std::atomic_bool m_try_another_outbound_peer;
+ std::atomic<int64_t> m_next_send_inv_to_incoming{0};
+
friend struct CConnmanTest;
};
extern std::unique_ptr<CConnman> g_connman;
@@ -496,7 +505,7 @@ bool IsLimited(enum Network net);
bool IsLimited(const CNetAddr& addr);
bool AddLocal(const CService& addr, int nScore = LOCAL_NONE);
bool AddLocal(const CNetAddr& addr, int nScore = LOCAL_NONE);
-bool RemoveLocal(const CService& addr);
+void RemoveLocal(const CService& addr);
bool SeenLocal(const CService& addr);
bool IsLocal(const CService& addr);
bool GetLocal(CService &addr, const CNetAddr *paddrPeer = nullptr);
@@ -863,6 +872,6 @@ public:
/** Return a timestamp in the future (in microseconds) for exponentially distributed events. */
-int64_t PoissonNextSend(int64_t nNow, int average_interval_seconds);
+int64_t PoissonNextSend(int64_t now, int average_interval_seconds);
#endif // BITCOIN_NET_H
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 458e9c2e9f..d5d664b27e 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -35,17 +35,34 @@
# error "Bitcoin cannot be compiled without assertions."
#endif
-std::atomic<int64_t> nTimeBestReceived(0); // Used only to inform the wallet of when we last received a block
-bool g_enable_bip61 = DEFAULT_ENABLE_BIP61;
-
-struct IteratorComparator
-{
- template<typename I>
- bool operator()(const I& a, const I& b) const
- {
- return &(*a) < &(*b);
- }
-};
+/** Expiration time for orphan transactions in seconds */
+static constexpr int64_t ORPHAN_TX_EXPIRE_TIME = 20 * 60;
+/** Minimum time between orphan transactions expire time checks in seconds */
+static constexpr int64_t ORPHAN_TX_EXPIRE_INTERVAL = 5 * 60;
+/** Headers download timeout expressed in microseconds
+ * Timeout = base + per_header * (expected number of headers) */
+static constexpr int64_t HEADERS_DOWNLOAD_TIMEOUT_BASE = 15 * 60 * 1000000; // 15 minutes
+static constexpr int64_t HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER = 1000; // 1ms/header
+/** Protect at least this many outbound peers from disconnection due to slow/
+ * behind headers chain.
+ */
+static constexpr int32_t MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT = 4;
+/** Timeout for (unprotected) outbound peers to sync to our chainwork, in seconds */
+static constexpr int64_t CHAIN_SYNC_TIMEOUT = 20 * 60; // 20 minutes
+/** How frequently to check for stale tips, in seconds */
+static constexpr int64_t STALE_CHECK_INTERVAL = 10 * 60; // 10 minutes
+/** How frequently to check for extra outbound peers and disconnect, in seconds */
+static constexpr int64_t EXTRA_PEER_CHECK_INTERVAL = 45;
+/** Minimum time an outbound-peer-eviction candidate must be connected for, in order to evict, in seconds */
+static constexpr int64_t MINIMUM_CONNECT_TIME = 30;
+/** SHA256("main address relay")[0:8] */
+static constexpr uint64_t RANDOMIZER_ID_ADDRESS_RELAY = 0x3cac0035b5866b90ULL;
+/// Age after which a stale block will no longer be served if requested as
+/// protection against fingerprinting. Set to one month, denominated in seconds.
+static constexpr int STALE_RELAY_AGE_LIMIT = 30 * 24 * 60 * 60;
+/// Age after which a block is considered historical for purposes of rate
+/// limiting block relay. Set to one week, denominated in seconds.
+static constexpr int HISTORICAL_BLOCK_AGE = 7 * 24 * 60 * 60;
struct COrphanTx {
// When modifying, adapt the copy of this definition in tests/DoS_tests.
@@ -55,41 +72,44 @@ struct COrphanTx {
};
static CCriticalSection g_cs_orphans;
std::map<uint256, COrphanTx> mapOrphanTransactions GUARDED_BY(g_cs_orphans);
-std::map<COutPoint, std::set<std::map<uint256, COrphanTx>::iterator, IteratorComparator>> mapOrphanTransactionsByPrev GUARDED_BY(g_cs_orphans);
-void EraseOrphansFor(NodeId peer);
-static size_t vExtraTxnForCompactIt GUARDED_BY(g_cs_orphans) = 0;
-static std::vector<std::pair<uint256, CTransactionRef>> vExtraTxnForCompact GUARDED_BY(g_cs_orphans);
-
-static const uint64_t RANDOMIZER_ID_ADDRESS_RELAY = 0x3cac0035b5866b90ULL; // SHA256("main address relay")[0:8]
+void EraseOrphansFor(NodeId peer);
-/// Age after which a stale block will no longer be served if requested as
-/// protection against fingerprinting. Set to one month, denominated in seconds.
-static const int STALE_RELAY_AGE_LIMIT = 30 * 24 * 60 * 60;
-
-/// Age after which a block is considered historical for purposes of rate
-/// limiting block relay. Set to one week, denominated in seconds.
-static const int HISTORICAL_BLOCK_AGE = 7 * 24 * 60 * 60;
+/** Increase a node's misbehavior score. */
+void Misbehaving(NodeId nodeid, int howmuch, const std::string& message="") EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+/** Average delay between local address broadcasts in seconds. */
+static constexpr unsigned int AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL = 24 * 60 * 60;
+/** Average delay between peer address broadcasts in seconds. */
+static const unsigned int AVG_ADDRESS_BROADCAST_INTERVAL = 30;
+/** Average delay between trickled inventory transmissions in seconds.
+ * Blocks and whitelisted receivers bypass this, outbound peers get half this delay. */
+static const unsigned int INVENTORY_BROADCAST_INTERVAL = 5;
+/** Maximum number of inventory items to send per transmission.
+ * Limits the impact of low-fee transaction floods. */
+static constexpr unsigned int INVENTORY_BROADCAST_MAX = 7 * INVENTORY_BROADCAST_INTERVAL;
+/** Average delay between feefilter broadcasts in seconds. */
+static constexpr unsigned int AVG_FEEFILTER_BROADCAST_INTERVAL = 10 * 60;
+/** Maximum feefilter broadcast delay after significant change. */
+static constexpr unsigned int MAX_FEEFILTER_CHANGE_DELAY = 5 * 60;
// Internal stuff
namespace {
/** Number of nodes with fSyncStarted. */
- int nSyncStarted = 0;
+ int nSyncStarted GUARDED_BY(cs_main) = 0;
/**
* Sources of received blocks, saved to be able to send them reject
- * messages or ban them when processing happens afterwards. Protected by
- * cs_main.
+ * messages or ban them when processing happens afterwards.
* Set mapBlockSource[hash].second to false if the node should not be
* punished if the block is invalid.
*/
- std::map<uint256, std::pair<NodeId, bool>> mapBlockSource;
+ std::map<uint256, std::pair<NodeId, bool>> mapBlockSource GUARDED_BY(cs_main);
/**
* Filter for transactions that were recently rejected by
* AcceptToMemoryPool. These are not rerequested until the chain tip
- * changes, at which point the entire filter is reset. Protected by
- * cs_main.
+ * changes, at which point the entire filter is reset.
*
* Without this filter we'd be re-requesting txs from each of our peers,
* increasing bandwidth consumption considerably. For instance, with 100
@@ -105,42 +125,56 @@ namespace {
*
* Memory used: 1.3 MB
*/
- std::unique_ptr<CRollingBloomFilter> recentRejects;
- uint256 hashRecentRejectsChainTip;
+ std::unique_ptr<CRollingBloomFilter> recentRejects GUARDED_BY(cs_main);
+ uint256 hashRecentRejectsChainTip GUARDED_BY(cs_main);
- /** Blocks that are in flight, and that are in the queue to be downloaded. Protected by cs_main. */
+ /** Blocks that are in flight, and that are in the queue to be downloaded. */
struct QueuedBlock {
uint256 hash;
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
};
- std::map<uint256, std::pair<NodeId, std::list<QueuedBlock>::iterator> > mapBlocksInFlight;
+ std::map<uint256, std::pair<NodeId, std::list<QueuedBlock>::iterator> > mapBlocksInFlight GUARDED_BY(cs_main);
/** Stack of nodes which we have set to announce using compact blocks */
- std::list<NodeId> lNodesAnnouncingHeaderAndIDs;
+ std::list<NodeId> lNodesAnnouncingHeaderAndIDs GUARDED_BY(cs_main);
/** Number of preferable block download peers. */
- int nPreferredDownload = 0;
+ int nPreferredDownload GUARDED_BY(cs_main) = 0;
/** Number of peers from which we're downloading blocks. */
- int nPeersWithValidatedDownloads = 0;
+ int nPeersWithValidatedDownloads GUARDED_BY(cs_main) = 0;
/** Number of outbound peers with m_chain_sync.m_protect. */
- int g_outbound_peers_with_protect_from_disconnect = 0;
+ int g_outbound_peers_with_protect_from_disconnect GUARDED_BY(cs_main) = 0;
/** When our tip was last updated. */
std::atomic<int64_t> g_last_tip_update(0);
- /** Relay map, protected by cs_main. */
+ /** Relay map */
typedef std::map<uint256, CTransactionRef> MapRelay;
- MapRelay mapRelay;
- /** Expiration-time ordered list of (expire time, relay map entry) pairs, protected by cs_main). */
- std::deque<std::pair<int64_t, MapRelay::iterator>> vRelayExpiration;
+ MapRelay mapRelay GUARDED_BY(cs_main);
+ /** Expiration-time ordered list of (expire time, relay map entry) pairs. */
+ std::deque<std::pair<int64_t, MapRelay::iterator>> vRelayExpiration GUARDED_BY(cs_main);
+
+ std::atomic<int64_t> nTimeBestReceived(0); // Used only to inform the wallet of when we last received a block
+
+ struct IteratorComparator
+ {
+ template<typename I>
+ bool operator()(const I& a, const I& b) const
+ {
+ return &(*a) < &(*b);
+ }
+ };
+ std::map<COutPoint, std::set<std::map<uint256, COrphanTx>::iterator, IteratorComparator>> mapOrphanTransactionsByPrev GUARDED_BY(g_cs_orphans);
+
+ static size_t vExtraTxnForCompactIt GUARDED_BY(g_cs_orphans) = 0;
+ static std::vector<std::pair<uint256, CTransactionRef>> vExtraTxnForCompact GUARDED_BY(g_cs_orphans);
} // namespace
namespace {
-
struct CBlockReject {
unsigned char chRejectCode;
std::string strRejectReason;
@@ -266,18 +300,17 @@ struct CNodeState {
}
};
-/** Map maintaining per-node state. Requires cs_main. */
-std::map<NodeId, CNodeState> mapNodeState;
+/** Map maintaining per-node state. */
+static std::map<NodeId, CNodeState> mapNodeState GUARDED_BY(cs_main);
-// Requires cs_main.
-CNodeState *State(NodeId pnode) {
+static CNodeState *State(NodeId pnode) EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
std::map<NodeId, CNodeState>::iterator it = mapNodeState.find(pnode);
if (it == mapNodeState.end())
return nullptr;
return &it->second;
}
-static void UpdatePreferredDownload(CNode* node, CNodeState* state)
+static void UpdatePreferredDownload(CNode* node, CNodeState* state) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
nPreferredDownload -= state->fPreferredDownload;
@@ -308,10 +341,9 @@ static void PushNodeVersion(CNode *pnode, CConnman* connman, int64_t nTime)
}
}
-// Requires cs_main.
// Returns a bool indicating whether we requested this block.
// Also used if a block was /not/ received and timed out or started with another peer
-static bool MarkBlockAsReceived(const uint256& hash) {
+static bool MarkBlockAsReceived(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
std::map<uint256, std::pair<NodeId, std::list<QueuedBlock>::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash);
if (itInFlight != mapBlocksInFlight.end()) {
CNodeState *state = State(itInFlight->second.first);
@@ -334,10 +366,9 @@ static bool MarkBlockAsReceived(const uint256& hash) {
return false;
}
-// 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
-static bool MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const CBlockIndex* pindex = nullptr, std::list<QueuedBlock>::iterator** pit = nullptr) {
+static bool MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const CBlockIndex* pindex = nullptr, std::list<QueuedBlock>::iterator** pit = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
CNodeState *state = State(nodeid);
assert(state != nullptr);
@@ -371,7 +402,7 @@ static bool MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const CBlock
}
/** Check whether the last unknown block a peer advertised is not yet known. */
-static void ProcessBlockAvailability(NodeId nodeid) {
+static void ProcessBlockAvailability(NodeId nodeid) EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
CNodeState *state = State(nodeid);
assert(state != nullptr);
@@ -387,7 +418,7 @@ static void ProcessBlockAvailability(NodeId nodeid) {
}
/** Update tracking information about which blocks a peer is assumed to have. */
-static void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) {
+static void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
CNodeState *state = State(nodeid);
assert(state != nullptr);
@@ -411,7 +442,8 @@ static void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) {
* lNodesAnnouncingHeaderAndIDs, and keeping that list under a certain size by
* removing the first element if necessary.
*/
-static void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman* connman) {
+static void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman* connman)
+{
AssertLockHeld(cs_main);
CNodeState* nodestate = State(nodeid);
if (!nodestate || !nodestate->fSupportsDesiredCmpctVersion) {
@@ -427,11 +459,13 @@ static void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman* connma
}
}
connman->ForNode(nodeid, [connman](CNode* pfrom){
+ AssertLockHeld(cs_main);
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, nCMPCTBLOCKVersion](CNode* pnodeStop){
+ AssertLockHeld(cs_main);
connman->PushMessage(pnodeStop, CNetMsgMaker(pnodeStop->GetSendVersion()).Make(NetMsgType::SENDCMPCT, /*fAnnounceUsingCMPCTBLOCK=*/false, nCMPCTBLOCKVersion));
return true;
});
@@ -444,7 +478,7 @@ static void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman* connma
}
}
-static bool TipMayBeStale(const Consensus::Params &consensusParams)
+static bool TipMayBeStale(const Consensus::Params &consensusParams) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
AssertLockHeld(cs_main);
if (g_last_tip_update == 0) {
@@ -453,14 +487,12 @@ static bool TipMayBeStale(const Consensus::Params &consensusParams)
return g_last_tip_update < GetTime() - consensusParams.nPowTargetSpacing * 3 && mapBlocksInFlight.empty();
}
-// Requires cs_main
-static bool CanDirectFetch(const Consensus::Params &consensusParams)
+static bool CanDirectFetch(const Consensus::Params &consensusParams) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
return chainActive.Tip()->GetBlockTime() > GetAdjustedTime() - consensusParams.nPowTargetSpacing * 20;
}
-// Requires cs_main
-static bool PeerHasHeader(CNodeState *state, const CBlockIndex *pindex)
+static bool PeerHasHeader(CNodeState *state, const CBlockIndex *pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
if (state->pindexBestKnownBlock && pindex == state->pindexBestKnownBlock->GetAncestor(pindex->nHeight))
return true;
@@ -471,7 +503,8 @@ static bool PeerHasHeader(CNodeState *state, const CBlockIndex *pindex)
/** Update pindexLastCommonBlock and add not-in-flight missing successors to vBlocks, until it has
* at most count entries. */
-static void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<const CBlockIndex*>& vBlocks, NodeId& nodeStaller, const Consensus::Params& consensusParams) {
+static void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<const CBlockIndex*>& vBlocks, NodeId& nodeStaller, const Consensus::Params& consensusParams) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+{
if (count == 0)
return;
@@ -761,10 +794,8 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans)
/**
* Mark a misbehaving peer to be banned depending upon the value of `-banscore`.
- *
- * Requires cs_main.
*/
-void Misbehaving(NodeId pnode, int howmuch, const std::string& message)
+void Misbehaving(NodeId pnode, int howmuch, const std::string& message) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
if (howmuch == 0)
return;
@@ -809,7 +840,9 @@ static bool BlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Para
(GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, consensusParams) < STALE_RELAY_AGE_LIMIT);
}
-PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn, CScheduler &scheduler) : connman(connmanIn), m_stale_tip_check_time(0) {
+PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn, CScheduler &scheduler, bool enable_bip61)
+ : connman(connmanIn), m_stale_tip_check_time(0), m_enable_bip61(enable_bip61) {
+
// Initialize global variables that cannot be constructed at startup.
recentRejects.reset(new CRollingBloomFilter(120000, 0.000001));
@@ -860,13 +893,13 @@ void PeerLogicValidation::BlockConnected(const std::shared_ptr<const CBlock>& pb
// All of the following cache a recent block, and are protected by cs_most_recent_block
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;
-static bool fWitnessesPresentInMostRecentCompactBlock;
+static std::shared_ptr<const CBlock> most_recent_block GUARDED_BY(cs_most_recent_block);
+static std::shared_ptr<const CBlockHeaderAndShortTxIDs> most_recent_compact_block GUARDED_BY(cs_most_recent_block);
+static uint256 most_recent_block_hash GUARDED_BY(cs_most_recent_block);
+static bool fWitnessesPresentInMostRecentCompactBlock GUARDED_BY(cs_most_recent_block);
/**
- * Maintain state about the best-seen block and fast-announce a compact block
+ * Maintain state about the best-seen block and fast-announce a compact block
* to compatible peers.
*/
void PeerLogicValidation::NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& pblock) {
@@ -892,6 +925,8 @@ void PeerLogicValidation::NewPoWValidBlock(const CBlockIndex *pindex, const std:
}
connman->ForEachNode([this, &pcmpctblock, pindex, &msgMaker, fWitnessEnabled, &hashBlock](CNode* pnode) {
+ AssertLockHeld(cs_main);
+
// TODO: Avoid the repeated-serialization here
if (pnode->nVersion < INVALID_CB_NO_BAN_VERSION || pnode->fDisconnect)
return;
@@ -911,7 +946,7 @@ void PeerLogicValidation::NewPoWValidBlock(const CBlockIndex *pindex, const std:
}
/**
- * Update our best height and announce any block hashes which weren't previously
+ * Update our best height and announce any block hashes which weren't previously
* in chainActive to our peers.
*/
void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {
@@ -947,7 +982,7 @@ void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CB
}
/**
- * Handle invalid block rejection and consequent peer banning, maintain which
+ * Handle invalid block rejection and consequent peer banning, maintain which
* peers announce compact blocks.
*/
void PeerLogicValidation::BlockChecked(const CBlock& block, const CValidationState& state) {
@@ -1264,9 +1299,6 @@ void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnm
if (!push) {
vNotFound.push_back(inv);
}
-
- // Track requests for our stuff.
- GetMainSignals().Inventory(inv.hash);
}
} // release cs_main
@@ -1292,7 +1324,7 @@ void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnm
}
}
-static uint32_t GetFetchFlags(CNode* pfrom) {
+static uint32_t GetFetchFlags(CNode* pfrom) EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
uint32_t nFetchFlags = 0;
if ((pfrom->GetLocalServices() & NODE_WITNESS) && State(pfrom->GetId())->fHaveWitness) {
nFetchFlags |= MSG_WITNESS_FLAG;
@@ -1537,7 +1569,7 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve
return true;
}
-bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, CConnman* connman, const std::atomic<bool>& interruptMsgProc)
+bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, CConnman* connman, const std::atomic<bool>& interruptMsgProc, bool enable_bip61)
{
LogPrint(BCLog::NET, "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->GetId());
if (gArgs.IsArgSet("-dropmessagestest") && GetRand(gArgs.GetArg("-dropmessagestest", 0)) == 0)
@@ -1591,7 +1623,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// Each connection can only send one version message
if (pfrom->nVersion != 0)
{
- if (g_enable_bip61) {
+ if (enable_bip61) {
connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_DUPLICATE, std::string("Duplicate version message")));
}
LOCK(cs_main);
@@ -1622,7 +1654,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (!pfrom->fInbound && !pfrom->fFeeler && !pfrom->m_manual_connection && !HasAllDesirableServiceFlags(nServices))
{
LogPrint(BCLog::NET, "peer=%d does not offer the expected services (%08x offered, %08x expected); disconnecting\n", pfrom->GetId(), nServices, GetDesirableServiceFlags(nServices));
- if (g_enable_bip61) {
+ if (enable_bip61) {
connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_NONSTANDARD,
strprintf("Expected to offer services %08x", GetDesirableServiceFlags(nServices))));
}
@@ -1630,22 +1662,11 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
return false;
}
- if (nServices & ((1 << 7) | (1 << 5))) {
- if (GetTime() < 1533096000) {
- // Immediately disconnect peers that use service bits 6 or 8 until August 1st, 2018
- // These bits have been used as a flag to indicate that a node is running incompatible
- // consensus rules instead of changing the network magic, so we're stuck disconnecting
- // based on these service bits, at least for a while.
- pfrom->fDisconnect = true;
- return false;
- }
- }
-
if (nVersion < MIN_PEER_PROTO_VERSION)
{
// disconnect from peers older than this proto version
LogPrint(BCLog::NET, "peer=%d using obsolete version %i; disconnecting\n", pfrom->GetId(), nVersion);
- if (g_enable_bip61) {
+ if (enable_bip61) {
connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE,
strprintf("Version must be %d or greater", MIN_PEER_PROTO_VERSION)));
}
@@ -1965,9 +1986,6 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
pfrom->AskFor(inv);
}
}
-
- // Track requests for our stuff
- GetMainSignals().Inventory(inv.hash);
}
}
@@ -2346,7 +2364,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
LogPrint(BCLog::MEMPOOLREJ, "%s from peer=%d was not accepted: %s\n", tx.GetHash().ToString(),
pfrom->GetId(),
FormatStateMessage(state));
- if (g_enable_bip61 && state.GetRejectCode() > 0 && state.GetRejectCode() < REJECT_INTERNAL) { // Never send AcceptToMemoryPool's internal codes over P2P
+ if (enable_bip61 && state.GetRejectCode() > 0 && state.GetRejectCode() < REJECT_INTERNAL) { // Never send AcceptToMemoryPool's internal codes over P2P
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::REJECT, strCommand, (unsigned char)state.GetRejectCode(),
state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash));
}
@@ -2531,7 +2549,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
} // cs_main
if (fProcessBLOCKTXN)
- return ProcessMessage(pfrom, NetMsgType::BLOCKTXN, blockTxnMsg, nTimeReceived, chainparams, connman, interruptMsgProc);
+ return ProcessMessage(pfrom, NetMsgType::BLOCKTXN, blockTxnMsg, nTimeReceived, chainparams, connman, interruptMsgProc, enable_bip61);
if (fRevertToHeaderProcessing) {
// Headers received from HB compact block peers are permitted to be
@@ -2917,12 +2935,12 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
return true;
}
-static bool SendRejectsAndCheckIfBanned(CNode* pnode, CConnman* connman)
+static bool SendRejectsAndCheckIfBanned(CNode* pnode, CConnman* connman, bool enable_bip61)
{
AssertLockHeld(cs_main);
CNodeState &state = *State(pnode->GetId());
- if (g_enable_bip61) {
+ if (enable_bip61) {
for (const CBlockReject& reject : state.rejects) {
connman->PushMessage(pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, std::string(NetMsgType::BLOCK), reject.chRejectCode, reject.strRejectReason, reject.hashBlock));
}
@@ -3024,7 +3042,7 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
bool fRet = false;
try
{
- fRet = ProcessMessage(pfrom, strCommand, vRecv, msg.nTime, chainparams, connman, interruptMsgProc);
+ fRet = ProcessMessage(pfrom, strCommand, vRecv, msg.nTime, chainparams, connman, interruptMsgProc, m_enable_bip61);
if (interruptMsgProc)
return false;
if (!pfrom->vRecvGetData.empty())
@@ -3032,7 +3050,7 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
}
catch (const std::ios_base::failure& e)
{
- if (g_enable_bip61) {
+ if (m_enable_bip61) {
connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_MALFORMED, std::string("error parsing message")));
}
if (strstr(e.what(), "end of data"))
@@ -3066,7 +3084,7 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
}
LOCK(cs_main);
- SendRejectsAndCheckIfBanned(pfrom, connman);
+ SendRejectsAndCheckIfBanned(pfrom, connman, m_enable_bip61);
return fMoreWork;
}
@@ -3139,6 +3157,8 @@ void PeerLogicValidation::EvictExtraOutboundPeers(int64_t time_in_seconds)
LOCK(cs_main);
connman->ForEachNode([&](CNode* pnode) {
+ AssertLockHeld(cs_main);
+
// Ignore non-outbound peers, or nodes marked for disconnect already
if (!IsOutboundDisconnectionCandidate(pnode) || pnode->fDisconnect) return;
CNodeState *state = State(pnode->GetId());
@@ -3152,6 +3172,8 @@ void PeerLogicValidation::EvictExtraOutboundPeers(int64_t time_in_seconds)
});
if (worst_peer != -1) {
bool disconnected = connman->ForNode(worst_peer, [&](CNode *pnode) {
+ AssertLockHeld(cs_main);
+
// Only disconnect a peer that has been connected to us for
// some reasonable fraction of our check-frequency, to give
// it time for new information to have arrived.
@@ -3201,6 +3223,7 @@ void PeerLogicValidation::CheckForStaleTipAndEvictPeers(const Consensus::Params
}
}
+namespace {
class CompareInvMempoolOrder
{
CTxMemPool *mp;
@@ -3217,6 +3240,7 @@ public:
return mp->CompareDepthAndScore(*b, *a);
}
};
+}
bool PeerLogicValidation::SendMessages(CNode* pto)
{
@@ -3262,7 +3286,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
if (!lockMain)
return true;
- if (SendRejectsAndCheckIfBanned(pto, connman))
+ if (SendRejectsAndCheckIfBanned(pto, connman, m_enable_bip61))
return true;
CNodeState &state = *State(pto->GetId());
@@ -3496,8 +3520,12 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
bool fSendTrickle = pto->fWhitelisted;
if (pto->nNextInvSend < nNow) {
fSendTrickle = true;
- // Use half the delay for outbound peers, as there is less privacy concern for them.
- pto->nNextInvSend = PoissonNextSend(nNow, INVENTORY_BROADCAST_INTERVAL >> !pto->fInbound);
+ if (pto->fInbound) {
+ pto->nNextInvSend = connman->PoissonNextSendInbound(nNow, INVENTORY_BROADCAST_INTERVAL);
+ } else {
+ // Use half the delay for outbound peers, as there is less privacy concern for them.
+ pto->nNextInvSend = PoissonNextSend(nNow, INVENTORY_BROADCAST_INTERVAL >> 1);
+ }
}
// Time to send but the peer has requested we not relay transactions.
diff --git a/src/net_processing.h b/src/net_processing.h
index 0d97b316eb..4dab0ada2b 100644
--- a/src/net_processing.h
+++ b/src/net_processing.h
@@ -12,40 +12,17 @@
/** Default for -maxorphantx, maximum number of orphan transactions kept in memory */
static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS = 100;
-/** Expiration time for orphan transactions in seconds */
-static const int64_t ORPHAN_TX_EXPIRE_TIME = 20 * 60;
-/** Minimum time between orphan transactions expire time checks in seconds */
-static const int64_t ORPHAN_TX_EXPIRE_INTERVAL = 5 * 60;
/** Default number of orphan+recently-replaced txn to keep around for block reconstruction */
static const unsigned int DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN = 100;
-/** Headers download timeout expressed in microseconds
- * Timeout = base + per_header * (expected number of headers) */
-static constexpr int64_t HEADERS_DOWNLOAD_TIMEOUT_BASE = 15 * 60 * 1000000; // 15 minutes
-static constexpr int64_t HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER = 1000; // 1ms/header
-/** Protect at least this many outbound peers from disconnection due to slow/
- * behind headers chain.
- */
-static constexpr int32_t MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT = 4;
-/** Timeout for (unprotected) outbound peers to sync to our chainwork, in seconds */
-static constexpr int64_t CHAIN_SYNC_TIMEOUT = 20 * 60; // 20 minutes
-/** How frequently to check for stale tips, in seconds */
-static constexpr int64_t STALE_CHECK_INTERVAL = 10 * 60; // 10 minutes
-/** How frequently to check for extra outbound peers and disconnect, in seconds */
-static constexpr int64_t EXTRA_PEER_CHECK_INTERVAL = 45;
-/** Minimum time an outbound-peer-eviction candidate must be connected for, in order to evict, in seconds */
-static constexpr int64_t MINIMUM_CONNECT_TIME = 30;
-
/** Default for BIP61 (sending reject messages) */
static constexpr bool DEFAULT_ENABLE_BIP61 = true;
-/** Enable BIP61 (sending reject messages) */
-extern bool g_enable_bip61;
class PeerLogicValidation final : public CValidationInterface, public NetEventsInterface {
private:
CConnman* const connman;
public:
- explicit PeerLogicValidation(CConnman* connman, CScheduler &scheduler);
+ explicit PeerLogicValidation(CConnman* connman, CScheduler &scheduler, bool enable_bip61);
/**
* Overridden from CValidationInterface.
@@ -92,6 +69,9 @@ public:
private:
int64_t m_stale_tip_check_time; //! Next time to check for stale tip
+
+ /** Enable BIP61 (sending reject messages) */
+ const bool m_enable_bip61;
};
struct CNodeStateStats {
@@ -103,7 +83,5 @@ struct CNodeStateStats {
/** Get statistics from node state */
bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats);
-/** Increase a node's misbehavior score. */
-void Misbehaving(NodeId nodeid, int howmuch, const std::string& message="");
#endif // BITCOIN_NET_PROCESSING_H
diff --git a/src/netbase.cpp b/src/netbase.cpp
index 4ce63cb0ec..607eb89573 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -20,7 +20,6 @@
#endif
#include <boost/algorithm/string/case_conv.hpp> // for to_lower()
-#include <boost/algorithm/string/predicate.hpp> // for startswith() and endswith()
#if !defined(MSG_NOSIGNAL)
#define MSG_NOSIGNAL 0
@@ -121,8 +120,7 @@ bool LookupHost(const char *pszName, std::vector<CNetAddr>& vIP, unsigned int nM
std::string strHost(pszName);
if (strHost.empty())
return false;
- if (boost::algorithm::starts_with(strHost, "[") && boost::algorithm::ends_with(strHost, "]"))
- {
+ if (strHost.front() == '[' && strHost.back() == ']') {
strHost = strHost.substr(1, strHost.size() - 2);
}
diff --git a/src/outputtype.cpp b/src/outputtype.cpp
new file mode 100644
index 0000000000..3ff28bf9c2
--- /dev/null
+++ b/src/outputtype.cpp
@@ -0,0 +1,101 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2017 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <outputtype.h>
+
+#include <keystore.h>
+#include <pubkey.h>
+#include <script/script.h>
+#include <script/standard.h>
+
+#include <assert.h>
+#include <string>
+
+static const std::string OUTPUT_TYPE_STRING_LEGACY = "legacy";
+static const std::string OUTPUT_TYPE_STRING_P2SH_SEGWIT = "p2sh-segwit";
+static const std::string OUTPUT_TYPE_STRING_BECH32 = "bech32";
+
+bool ParseOutputType(const std::string& type, OutputType& output_type)
+{
+ if (type == OUTPUT_TYPE_STRING_LEGACY) {
+ output_type = OutputType::LEGACY;
+ return true;
+ } else if (type == OUTPUT_TYPE_STRING_P2SH_SEGWIT) {
+ output_type = OutputType::P2SH_SEGWIT;
+ return true;
+ } else if (type == OUTPUT_TYPE_STRING_BECH32) {
+ output_type = OutputType::BECH32;
+ return true;
+ }
+ return false;
+}
+
+const std::string& FormatOutputType(OutputType type)
+{
+ switch (type) {
+ case OutputType::LEGACY: return OUTPUT_TYPE_STRING_LEGACY;
+ case OutputType::P2SH_SEGWIT: return OUTPUT_TYPE_STRING_P2SH_SEGWIT;
+ case OutputType::BECH32: return OUTPUT_TYPE_STRING_BECH32;
+ default: assert(false);
+ }
+}
+
+CTxDestination GetDestinationForKey(const CPubKey& key, OutputType type)
+{
+ switch (type) {
+ case OutputType::LEGACY: return key.GetID();
+ case OutputType::P2SH_SEGWIT:
+ case OutputType::BECH32: {
+ if (!key.IsCompressed()) return key.GetID();
+ CTxDestination witdest = WitnessV0KeyHash(key.GetID());
+ CScript witprog = GetScriptForDestination(witdest);
+ if (type == OutputType::P2SH_SEGWIT) {
+ return CScriptID(witprog);
+ } else {
+ return witdest;
+ }
+ }
+ default: assert(false);
+ }
+}
+
+std::vector<CTxDestination> GetAllDestinationsForKey(const CPubKey& key)
+{
+ CKeyID keyid = key.GetID();
+ if (key.IsCompressed()) {
+ CTxDestination segwit = WitnessV0KeyHash(keyid);
+ CTxDestination p2sh = CScriptID(GetScriptForDestination(segwit));
+ return std::vector<CTxDestination>{std::move(keyid), std::move(p2sh), std::move(segwit)};
+ } else {
+ return std::vector<CTxDestination>{std::move(keyid)};
+ }
+}
+
+CTxDestination AddAndGetDestinationForScript(CKeyStore& keystore, const CScript& script, OutputType type)
+{
+ // Add script to keystore
+ keystore.AddCScript(script);
+ // Note that scripts over 520 bytes are not yet supported.
+ switch (type) {
+ case OutputType::LEGACY:
+ return CScriptID(script);
+ case OutputType::P2SH_SEGWIT:
+ case OutputType::BECH32: {
+ CTxDestination witdest = WitnessV0ScriptHash(script);
+ CScript witprog = GetScriptForDestination(witdest);
+ // Check if the resulting program is solvable (i.e. doesn't use an uncompressed key)
+ if (!IsSolvable(keystore, witprog)) return CScriptID(script);
+ // Add the redeemscript, so that P2WSH and P2SH-P2WSH outputs are recognized as ours.
+ keystore.AddCScript(witprog);
+ if (type == OutputType::BECH32) {
+ return witdest;
+ } else {
+ return CScriptID(witprog);
+ }
+ }
+ default: assert(false);
+ }
+}
+
diff --git a/src/outputtype.h b/src/outputtype.h
new file mode 100644
index 0000000000..21623e3b49
--- /dev/null
+++ b/src/outputtype.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2017 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_OUTPUTTYPE_H
+#define BITCOIN_OUTPUTTYPE_H
+
+#include <keystore.h>
+#include <script/standard.h>
+
+#include <string>
+#include <vector>
+
+enum class OutputType {
+ LEGACY,
+ P2SH_SEGWIT,
+ BECH32,
+
+ /**
+ * Special output type for change outputs only. Automatically choose type
+ * based on address type setting and the types other of non-change outputs
+ * (see -changetype option documentation and implementation in
+ * CWallet::TransactionChangeType for details).
+ */
+ CHANGE_AUTO,
+};
+
+bool ParseOutputType(const std::string& str, OutputType& output_type);
+const std::string& FormatOutputType(OutputType type);
+
+/**
+ * Get a destination of the requested type (if possible) to the specified key.
+ * The caller must make sure LearnRelatedScripts has been called beforehand.
+ */
+CTxDestination GetDestinationForKey(const CPubKey& key, OutputType);
+
+/** Get all destinations (potentially) supported by the wallet for the given key. */
+std::vector<CTxDestination> GetAllDestinationsForKey(const CPubKey& key);
+
+/**
+ * Get a destination of the requested type (if possible) to the specified script.
+ * This function will automatically add the script (and any other
+ * necessary scripts) to the keystore.
+ */
+CTxDestination AddAndGetDestinationForScript(CKeyStore& keystore, const CScript& script, OutputType);
+
+#endif // BITCOIN_OUTPUTTYPE_H
+
diff --git a/src/pubkey.cpp b/src/pubkey.cpp
index 6e601a6f13..1eb126434b 100644
--- a/src/pubkey.cpp
+++ b/src/pubkey.cpp
@@ -171,7 +171,7 @@ bool CPubKey::Verify(const uint256 &hash, const std::vector<unsigned char>& vchS
return false;
secp256k1_pubkey pubkey;
secp256k1_ecdsa_signature sig;
- if (!secp256k1_ec_pubkey_parse(secp256k1_context_verify, &pubkey, &(*this)[0], size())) {
+ if (!secp256k1_ec_pubkey_parse(secp256k1_context_verify, &pubkey, vch, size())) {
return false;
}
if (!ecdsa_signature_parse_der_lax(secp256k1_context_verify, &sig, vchSig.data(), vchSig.size())) {
@@ -207,14 +207,14 @@ bool CPubKey::IsFullyValid() const {
if (!IsValid())
return false;
secp256k1_pubkey pubkey;
- return secp256k1_ec_pubkey_parse(secp256k1_context_verify, &pubkey, &(*this)[0], size());
+ return secp256k1_ec_pubkey_parse(secp256k1_context_verify, &pubkey, vch, size());
}
bool CPubKey::Decompress() {
if (!IsValid())
return false;
secp256k1_pubkey pubkey;
- if (!secp256k1_ec_pubkey_parse(secp256k1_context_verify, &pubkey, &(*this)[0], size())) {
+ if (!secp256k1_ec_pubkey_parse(secp256k1_context_verify, &pubkey, vch, size())) {
return false;
}
unsigned char pub[PUBLIC_KEY_SIZE];
@@ -232,7 +232,7 @@ bool CPubKey::Derive(CPubKey& pubkeyChild, ChainCode &ccChild, unsigned int nChi
BIP32Hash(cc, nChild, *begin(), begin()+1, out);
memcpy(ccChild.begin(), out+32, 32);
secp256k1_pubkey pubkey;
- if (!secp256k1_ec_pubkey_parse(secp256k1_context_verify, &pubkey, &(*this)[0], size())) {
+ if (!secp256k1_ec_pubkey_parse(secp256k1_context_verify, &pubkey, vch, size())) {
return false;
}
if (!secp256k1_ec_pubkey_tweak_add(secp256k1_context_verify, &pubkey, out)) {
diff --git a/src/pubkey.h b/src/pubkey.h
index bb254547c8..0985273f34 100644
--- a/src/pubkey.h
+++ b/src/pubkey.h
@@ -107,6 +107,7 @@ public:
//! Simple read-only vector-like interface to the pubkey data.
unsigned int size() const { return GetLen(vch[0]); }
+ const unsigned char* data() const { return vch; }
const unsigned char* begin() const { return vch; }
const unsigned char* end() const { return vch + size(); }
const unsigned char& operator[](unsigned int pos) const { return vch[pos]; }
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 3454d3421e..5d0e384cf8 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -614,7 +614,6 @@ int main(int argc, char *argv[])
QApplication::setOrganizationName(QAPP_ORG_NAME);
QApplication::setOrganizationDomain(QAPP_ORG_DOMAIN);
QApplication::setApplicationName(QAPP_APP_NAME_DEFAULT);
- GUIUtil::SubstituteFonts(GetLangTerritory());
/// 4. Initialization of translations, so that intro dialog is in user's language
// Now that QSettings are accessible, initialize translations
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 2438361a58..c8f8b9c7d3 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -470,13 +470,13 @@ void BitcoinGUI::setClientModel(ClientModel *_clientModel)
}
#endif // ENABLE_WALLET
unitDisplayControl->setOptionsModel(_clientModel->getOptionsModel());
-
+
OptionsModel* optionsModel = _clientModel->getOptionsModel();
if(optionsModel)
{
// be aware of the tray icon disable state change reported by the OptionsModel object.
connect(optionsModel,SIGNAL(hideTrayIconChanged(bool)),this,SLOT(setTrayIconVisible(bool)));
-
+
// initialize the disable state of the tray icon with the current value in the model.
setTrayIconVisible(optionsModel->getHideTrayIcon());
}
@@ -1057,7 +1057,7 @@ void BitcoinGUI::setHDStatus(int hdEnabled)
labelWalletHDStatusIcon->setPixmap(platformStyle->SingleColorIcon(hdEnabled ? ":/icons/hd_enabled" : ":/icons/hd_disabled").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
labelWalletHDStatusIcon->setToolTip(hdEnabled ? tr("HD key generation is <b>enabled</b>") : tr("HD key generation is <b>disabled</b>"));
- // eventually disable the QLabel to set its opacity to 50%
+ // eventually disable the QLabel to set its opacity to 50%
labelWalletHDStatusIcon->setEnabled(hdEnabled);
}
diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h
index 4deeb325b3..d7ca8081d1 100644
--- a/src/qt/bitcoingui.h
+++ b/src/qt/bitcoingui.h
@@ -262,7 +262,7 @@ private Q_SLOTS:
/** Show progress dialog e.g. for verifychain */
void showProgress(const QString &title, int nProgress);
-
+
/** When hideTrayIcon setting is changed in OptionsModel hide or show the icon accordingly. */
void setTrayIconVisible(bool);
diff --git a/src/qt/bitcoinstrings.cpp b/src/qt/bitcoinstrings.cpp
index adf001c968..ab7e56a9da 100644
--- a/src/qt/bitcoinstrings.cpp
+++ b/src/qt/bitcoinstrings.cpp
@@ -12,128 +12,37 @@ static const char UNUSED *bitcoin_strings[] = {
QT_TRANSLATE_NOOP("bitcoin-core", "Bitcoin Core"),
QT_TRANSLATE_NOOP("bitcoin-core", "The %s developers"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
-"(1 = keep tx meta data e.g. account owner and payment request information, 2 "
-"= drop tx meta data)"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
"-maxtxfee is set very high! Fees this large could be paid on a single "
"transaction."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
-"A fee rate (in %s/kB) that will be used when fee estimation has insufficient "
-"data (default: %s)"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Accept relayed transactions received from whitelisted peers even when not "
-"relaying transactions (default: %d)"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Add a node to connect to and attempt to keep the connection open (see the "
-"`addnode` RPC command help for more info)"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
-"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"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Bind to given address and always listen on it. Use [host]:port notation for "
-"IPv6"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Bind to given address and whitelist peers connecting to it. Use [host]:port "
-"notation for IPv6"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Bind to given address to listen for JSON-RPC connections. This option is "
-"ignored unless -rpcallowip is also passed. Port is optional and overrides -"
-"rpcport. Use [host]:port notation for IPv6. This option can be specified "
-"multiple times (default: 127.0.0.1 and ::1 i.e., localhost, or if -"
-"rpcallowip has been specified, 0.0.0.0 and :: i.e., all addresses)"),
+"Can't generate a change-address key. Private keys are disabled for this "
+"wallet."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"Cannot obtain a lock on data directory %s. %s is probably already running."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"Cannot provide specific connections and have addrman find outgoing "
"connections at the same."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Connect only to the specified node(s); -connect=0 disables automatic "
-"connections (the rules for this peer are the same as for -addnode)"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Create new files with system default permissions, instead of umask 077 (only "
-"effective with disabled wallet functionality)"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Delete all wallet transactions and only recover those parts of the "
-"blockchain through -rescan on startup"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Discover own IP addresses (default: 1 when listening and no -externalip or -"
-"proxy)"),
+"Cannot upgrade a non HD split wallet without upgrading to support pre split "
+"keypool. Please use -upgradewallet=169900 or -upgradewallet with no version "
+"specified."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"Distributed under the MIT software license, see the accompanying file %s or "
"%s"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Do not keep transactions in the mempool longer than <n> hours (default: %u)"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Equivalent bytes per sigop in transactions for relay and mining (default: %u)"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
"Error loading %s: You can't enable HD on an already existing non-HD wallet"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Error loading wallet %s. -wallet parameter must only specify a filename (not "
-"a path)."),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
"Error reading %s! All keys read correctly, but transaction data or address "
"book entries might be missing or incorrect."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"Error: Listening for incoming connections failed (listen returned error %s)"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Exclude debugging information for a category. Can be used in conjunction "
-"with -debug=1 to output debug logs for all categories except one or more "
-"specified categories."),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Execute command when a relevant alert is received or we see a really long "
-"fork (%s in cmd is replaced by message)"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Execute command when a wallet transaction changes (%s in cmd is replaced by "
-"TxID)"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Execute command when the best block changes (%s in cmd is replaced by block "
-"hash)"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Extra transactions to keep in memory for compact block reconstructions "
-"(default: %u)"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Fees (in %s/kB) smaller than this are considered zero fee for relaying, "
-"mining and transaction creation (default: %s)"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Fees (in %s/kB) smaller than this are considered zero fee for transaction "
-"creation (default: %s)"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Force relay of transactions from whitelisted peers even if they violate "
-"local relay policy (default: %d)"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
-"How thorough the block verification of -checkblocks is (0-4, default: %u)"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
-"If <category> is not supplied or if <category> = 1, output all debugging "
-"information."),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
-"If paytxfee is not set, include enough fee so transactions begin "
-"confirmation on average within n blocks (default: %u)"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
-"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)"),
+"Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -"
+"fallbackfee."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"Invalid amount for -maxtxfee=<amount>: '%s' (must be at least the minrelay "
"fee of %s to prevent stuck transactions)"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Maintain a full transaction index, used by the getrawtransaction rpc call "
-"(default: %u)"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Maximum allowed median peer time offset adjustment. Local perspective of "
-"time may be influenced by peers forward or backward by this amount. "
-"(default: %u seconds)"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Maximum size of data in data carrier transactions we relay and mine "
-"(default: %u)"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Maximum total fees (in %s) to use in a single wallet transaction or raw "
-"transaction; setting this too low may abort large transactions (default: %s)"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Number of seconds to keep misbehaving peers from reconnecting (default: %u)"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Output debugging information (default: %u, supplying <category> is optional)"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
"Please check that your computer's date and time are correct! If your clock "
"is wrong, %s will not work properly."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
@@ -145,53 +54,14 @@ QT_TRANSLATE_NOOP("bitcoin-core", ""
"Prune: last wallet synchronisation goes beyond pruned data. You need to -"
"reindex (download the whole blockchain again in case of pruned node)"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Query for peer addresses via DNS lookup, if low on addresses (default: 1 "
-"unless -connect used)"),
-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 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 BIP141 block weight to this * 4. Deprecated, use blockmaxweight"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Set the number of script verification threads (%u to %d, 0 = auto, <0 = "
-"leave that many cores free, default: %d)"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Sets the serialization of raw transaction or block hex returned in non-"
-"verbose mode, non-segwit(0) or segwit(1) (default: %d)"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Specify directory to hold wallets (default: <datadir>/wallets if it exists, "
-"otherwise <datadir>)"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Specify location of debug log file: this can be an absolute path or a path "
-"relative to the data directory (default: %s)"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Support filtering of blocks and transaction with bloom filters (default: %u)"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
"The block database contains a block which appears to be from the future. "
"This may be due to your computer's date and time being set incorrectly. Only "
"rebuild the block database if you are sure that your computer's date and "
"time are correct"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
-"The fee rate (in %s/kB) that indicates your tolerance for discarding change "
-"by adding it to the fee (default: %s). Note: An output is discarded if it is "
-"dust at this rate, but we will always discard up to the dust relay fee and a "
-"discard fee above that is limited by the fee estimate for the longest target"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
"The transaction amount is too small to send after the fee has been deducted"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"This is a pre-release test build - use at your own risk - do not use for "
@@ -209,9 +79,6 @@ QT_TRANSLATE_NOOP("bitcoin-core", ""
"Total length of network version string (%i) exceeds maximum length (%i). "
"Reduce the number or size of uacomments."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = "
-"no limit (default: %d)"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
"Unable to replay blocks. You will need to rebuild the database using -"
"reindex-chainstate."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
@@ -224,19 +91,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", ""
"Unsupported argument -whitelistalwaysrelay ignored, use -whitelistrelay and/"
"or -whitelistforcerelay."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Use UPnP to map the listening port (default: 1 when listening and no -proxy)"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: "
-"%s)"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
-"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"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Wallet will not create transactions that violate mempool chain limits "
-"(default: %u)"),
+"Warning: Private keys detected in wallet {%s} with disabled private keys"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"Warning: The network does not appear to fully agree! Some miners appear to "
"be experiencing issues."),
@@ -251,61 +106,30 @@ QT_TRANSLATE_NOOP("bitcoin-core", ""
"Warning: We do not appear to fully agree with our peers! You may need to "
"upgrade, or other nodes may need to upgrade."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Whether to save the mempool on shutdown and load on restart (default: %u)"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Whitelist peers connecting from the given IP address (e.g. 1.2.3.4) or CIDR "
-"notated network (e.g. 1.2.3.0/24). Can be specified multiple times."),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Whitelisted peers cannot be DoS banned and their transactions are always "
-"relayed, even if they are already in the mempool, useful e.g. for a gateway"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
"You need to rebuild the database using -reindex to go back to unpruned "
"mode. This will redownload the entire blockchain"),
QT_TRANSLATE_NOOP("bitcoin-core", "%d of last 100 blocks have unexpected version"),
QT_TRANSLATE_NOOP("bitcoin-core", "%s corrupt, salvage failed"),
QT_TRANSLATE_NOOP("bitcoin-core", "%s is set very high!"),
-QT_TRANSLATE_NOOP("bitcoin-core", "(default: %s)"),
-QT_TRANSLATE_NOOP("bitcoin-core", "(default: %u)"),
QT_TRANSLATE_NOOP("bitcoin-core", "-maxmempool must be at least %d MB"),
-QT_TRANSLATE_NOOP("bitcoin-core", "<category> can be:"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Accept command line and JSON-RPC commands"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Accept connections from outside (default: 1 if no -proxy or -connect)"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Accept public REST requests (default: %u)"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Allow DNS lookups for -addnode, -seednode and -connect"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Always query for peer addresses via DNS lookup (default: %u)"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Append comment to the user agent string"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Attempt to recover private keys from a corrupt wallet on startup"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Automatically create Tor hidden service (default: %d)"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Block creation options:"),
QT_TRANSLATE_NOOP("bitcoin-core", "Cannot downgrade wallet"),
QT_TRANSLATE_NOOP("bitcoin-core", "Cannot resolve -%s address: '%s'"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Chain selection options:"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Cannot write to data directory '%s'; check permissions."),
QT_TRANSLATE_NOOP("bitcoin-core", "Change index out of range"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Connect through SOCKS5 proxy"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Connect to a node to retrieve peer addresses, and disconnect"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Connection options:"),
QT_TRANSLATE_NOOP("bitcoin-core", "Copyright (C) %i-%i"),
QT_TRANSLATE_NOOP("bitcoin-core", "Corrupted block database detected"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Debugging/Testing options:"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Do not load the wallet and disable wallet RPC calls"),
QT_TRANSLATE_NOOP("bitcoin-core", "Do you want to rebuild the block database now?"),
QT_TRANSLATE_NOOP("bitcoin-core", "Done loading"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Enable publish hash block in <address>"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Enable publish hash transaction in <address>"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Enable publish raw block in <address>"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Enable publish raw transaction in <address>"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Enable transaction replacement in the memory pool (default: %u)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error creating %s: You can't create non-HD wallets with this version."),
QT_TRANSLATE_NOOP("bitcoin-core", "Error initializing block database"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error initializing wallet database environment %s!"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error loading %s"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Error loading %s: Private keys can only be disabled during creation"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error loading %s: Wallet corrupted"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error loading %s: Wallet requires newer version of %s"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error loading %s: You can't disable HD on an already existing HD wallet"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error loading block database"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Error loading wallet %s. -wallet filename must be a regular file."),
QT_TRANSLATE_NOOP("bitcoin-core", "Error loading wallet %s. Duplicate -wallet filename specified."),
-QT_TRANSLATE_NOOP("bitcoin-core", "Error loading wallet %s. Invalid characters in -wallet filename."),
QT_TRANSLATE_NOOP("bitcoin-core", "Error opening block database"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error reading from database, shutting down."),
QT_TRANSLATE_NOOP("bitcoin-core", "Error upgrading chainstate database"),
@@ -314,11 +138,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Error: A fatal internal error occurred, see d
QT_TRANSLATE_NOOP("bitcoin-core", "Error: Disk space is low!"),
QT_TRANSLATE_NOOP("bitcoin-core", "Failed to listen on any port. Use -listen=0 if you want this."),
QT_TRANSLATE_NOOP("bitcoin-core", "Failed to rescan the wallet during initialization"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Fee (in %s/kB) to add to transactions you send (default: %s)"),
-QT_TRANSLATE_NOOP("bitcoin-core", "How many blocks to check at startup (default: %u, 0 = all)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Importing..."),
-QT_TRANSLATE_NOOP("bitcoin-core", "Imports blocks from external blk000??.dat file on startup"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Include IP addresses in debug output (default: %u)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Incorrect or no genesis block found. Wrong datadir for network?"),
QT_TRANSLATE_NOOP("bitcoin-core", "Information"),
QT_TRANSLATE_NOOP("bitcoin-core", "Initialization sanity check failed. %s is shutting down."),
@@ -330,62 +150,25 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -discardfee=<amount>: '%s'
QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -fallbackfee=<amount>: '%s'"),
QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Invalid netmask specified in -whitelist: '%s'"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Keep at most <n> unconnectable transactions in memory (default: %u)"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Keep the transaction memory pool below <n> megabytes (default: %u)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Keypool ran out, please call keypoolrefill first"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Listen for JSON-RPC connections on <port> (default: %u or testnet: %u)"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Listen for connections on <port> (default: %u or testnet: %u)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Loading P2P addresses..."),
QT_TRANSLATE_NOOP("bitcoin-core", "Loading banlist..."),
QT_TRANSLATE_NOOP("bitcoin-core", "Loading block index..."),
QT_TRANSLATE_NOOP("bitcoin-core", "Loading wallet..."),
-QT_TRANSLATE_NOOP("bitcoin-core", "Location of the auth cookie (default: data dir)"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Maintain at most <n> connections to peers (default: %u)"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Make the wallet broadcast transactions"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Maximum per-connection receive buffer, <n>*1000 bytes (default: %u)"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Maximum per-connection send buffer, <n>*1000 bytes (default: %u)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Need to specify a port with -whitebind: '%s'"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Node relay options:"),
QT_TRANSLATE_NOOP("bitcoin-core", "Not enough file descriptors available."),
-QT_TRANSLATE_NOOP("bitcoin-core", "Only connect to nodes in network <net> (ipv4, ipv6 or onion)"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Options:"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Password for JSON-RPC connections"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Prepend debug output with timestamp (default: %u)"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Print this help message and exit"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Print version and exit"),
QT_TRANSLATE_NOOP("bitcoin-core", "Prune cannot be configured with a negative value."),
QT_TRANSLATE_NOOP("bitcoin-core", "Prune mode is incompatible with -txindex."),
QT_TRANSLATE_NOOP("bitcoin-core", "Pruning blockstore..."),
-QT_TRANSLATE_NOOP("bitcoin-core", "RPC server options:"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Rebuild chain state and block index from the blk*.dat files on disk"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Rebuild chain state from the currently indexed blocks"),
QT_TRANSLATE_NOOP("bitcoin-core", "Reducing -maxconnections from %d to %d, because of system limitations."),
-QT_TRANSLATE_NOOP("bitcoin-core", "Relay and mine data carrier transactions (default: %u)"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Relay non-P2SH multisig (default: %u)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Replaying blocks..."),
-QT_TRANSLATE_NOOP("bitcoin-core", "Rescan the block chain for missing wallet transactions on startup"),
QT_TRANSLATE_NOOP("bitcoin-core", "Rescanning..."),
QT_TRANSLATE_NOOP("bitcoin-core", "Rewinding blocks..."),
-QT_TRANSLATE_NOOP("bitcoin-core", "Run in the background as a daemon and accept commands"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Send trace/debug info to console instead of debug.log file"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Send transactions with full-RBF opt-in enabled (RPC only, default: %u)"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Set database cache size in megabytes (%d to %d, default: %d)"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Set key pool size to <n> (default: %u)"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Set maximum BIP141 block weight (default: %d)"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Set the number of threads to service RPC calls (default: %d)"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Show all debugging options (usage: --help -help-debug)"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Shrink debug.log file on client startup (default: 1 when no -debug)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Signing transaction failed"),
QT_TRANSLATE_NOOP("bitcoin-core", "Specified -walletdir \"%s\" does not exist"),
QT_TRANSLATE_NOOP("bitcoin-core", "Specified -walletdir \"%s\" is a relative path"),
QT_TRANSLATE_NOOP("bitcoin-core", "Specified -walletdir \"%s\" is not a directory"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Specify configuration file (default: %s)"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Specify connection timeout in milliseconds (minimum: 1, default: %d)"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Specify data directory"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Specify pid file (default: %s)"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Specify wallet file (within data directory)"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Specify your own public address"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Spend unconfirmed change when sending transactions (default: %u)"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Specified blocks directory \"%s\" does not exist.\n"),
QT_TRANSLATE_NOOP("bitcoin-core", "Starting network threads..."),
QT_TRANSLATE_NOOP("bitcoin-core", "The source code is available from %s."),
QT_TRANSLATE_NOOP("bitcoin-core", "The transaction amount is too small to pay the fee"),
@@ -393,9 +176,6 @@ QT_TRANSLATE_NOOP("bitcoin-core", "The wallet will avoid paying less than the mi
QT_TRANSLATE_NOOP("bitcoin-core", "This is experimental software."),
QT_TRANSLATE_NOOP("bitcoin-core", "This is the minimum transaction fee you pay on every transaction."),
QT_TRANSLATE_NOOP("bitcoin-core", "This is the transaction fee you will pay if you send a transaction."),
-QT_TRANSLATE_NOOP("bitcoin-core", "Threshold for disconnecting misbehaving peers (default: %u)"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Tor control port password (default: empty)"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Tor control port to use if onion listening enabled (default: %s)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Transaction amount too small"),
QT_TRANSLATE_NOOP("bitcoin-core", "Transaction amounts must not be negative"),
QT_TRANSLATE_NOOP("bitcoin-core", "Transaction fee and change calculation failed"),
@@ -406,28 +186,21 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Transaction too large"),
QT_TRANSLATE_NOOP("bitcoin-core", "Unable to bind to %s on this computer (bind returned error %s)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Unable to bind to %s on this computer. %s is probably already running."),
QT_TRANSLATE_NOOP("bitcoin-core", "Unable to generate initial keys"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Unable to generate keys"),
QT_TRANSLATE_NOOP("bitcoin-core", "Unable to start HTTP server. See debug log for details."),
QT_TRANSLATE_NOOP("bitcoin-core", "Unknown network specified in -onlynet: '%s'"),
QT_TRANSLATE_NOOP("bitcoin-core", "Unsupported argument -benchmark ignored, use -debug=bench."),
QT_TRANSLATE_NOOP("bitcoin-core", "Unsupported argument -debugnet ignored, use -debug=net."),
QT_TRANSLATE_NOOP("bitcoin-core", "Unsupported argument -tor found, use -onion."),
QT_TRANSLATE_NOOP("bitcoin-core", "Unsupported logging category %s=%s."),
-QT_TRANSLATE_NOOP("bitcoin-core", "Upgrade wallet to latest format on startup"),
QT_TRANSLATE_NOOP("bitcoin-core", "Upgrading UTXO database"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Use UPnP to map the listening port (default: %u)"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Use the test chain"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Upgrading txindex database"),
QT_TRANSLATE_NOOP("bitcoin-core", "User Agent comment (%s) contains unsafe characters."),
-QT_TRANSLATE_NOOP("bitcoin-core", "Username for JSON-RPC connections"),
QT_TRANSLATE_NOOP("bitcoin-core", "Verifying blocks..."),
QT_TRANSLATE_NOOP("bitcoin-core", "Verifying wallet(s)..."),
QT_TRANSLATE_NOOP("bitcoin-core", "Wallet %s resides outside wallet directory %s"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Wallet debugging/testing options:"),
QT_TRANSLATE_NOOP("bitcoin-core", "Wallet needed to be rewritten: restart %s to complete"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Wallet options:"),
QT_TRANSLATE_NOOP("bitcoin-core", "Warning"),
QT_TRANSLATE_NOOP("bitcoin-core", "Warning: unknown new rules activated (versionbit %i)"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Whether to operate in a blocks only mode (default: %u)"),
-QT_TRANSLATE_NOOP("bitcoin-core", "You need to rebuild the database using -reindex to change -txindex"),
QT_TRANSLATE_NOOP("bitcoin-core", "Zapping all transactions from wallet..."),
-QT_TRANSLATE_NOOP("bitcoin-core", "ZeroMQ notification options:"),
};
diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp
index a623771aa0..8a4a2955f6 100644
--- a/src/qt/clientmodel.cpp
+++ b/src/qt/clientmodel.cpp
@@ -28,8 +28,6 @@
#include <QDebug>
#include <QTimer>
-class CBlockIndex;
-
static int64_t nLastHeaderTipUpdateNotification = 0;
static int64_t nLastBlockTipUpdateNotification = 0;
diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h
index 1d21d8c766..ff47653fb7 100644
--- a/src/qt/guiconstants.h
+++ b/src/qt/guiconstants.h
@@ -27,8 +27,6 @@ static const bool DEFAULT_SPLASHSCREEN = true;
#define COLOR_BAREADDRESS QColor(140, 140, 140)
/* Transaction list -- TX status decoration - open until date */
#define COLOR_TX_STATUS_OPENUNTILDATE QColor(64, 64, 255)
-/* Transaction list -- TX status decoration - offline */
-#define COLOR_TX_STATUS_OFFLINE QColor(192, 192, 192)
/* Transaction list -- TX status decoration - danger, tx needs attention */
#define COLOR_TX_STATUS_DANGER QColor(200, 100, 100)
/* Transaction list -- TX status decoration - default color */
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index acd9f7b35c..9dde2c392c 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -63,16 +63,6 @@
static fs::detail::utf8_codecvt_facet utf8;
-#if defined(Q_OS_MAC)
-extern double NSAppKitVersionNumber;
-#if !defined(NSAppKitVersionNumber10_8)
-#define NSAppKitVersionNumber10_8 1187
-#endif
-#if !defined(NSAppKitVersionNumber10_9)
-#define NSAppKitVersionNumber10_9 1265
-#endif
-#endif
-
namespace GUIUtil {
QString dateTimeStr(const QDateTime &date)
@@ -381,52 +371,16 @@ bool openBitcoinConf()
/* Create the file */
boost::filesystem::ofstream configFile(pathConfig, std::ios_base::app);
-
+
if (!configFile.good())
return false;
-
+
configFile.close();
-
+
/* Open bitcoin.conf with the associated application */
return QDesktopServices::openUrl(QUrl::fromLocalFile(boostPathToQString(pathConfig)));
}
-void SubstituteFonts(const QString& language)
-{
-#if defined(Q_OS_MAC)
-// Background:
-// OSX's default font changed in 10.9 and Qt is unable to find it with its
-// usual fallback methods when building against the 10.7 sdk or lower.
-// The 10.8 SDK added a function to let it find the correct fallback font.
-// If this fallback is not properly loaded, some characters may fail to
-// render correctly.
-//
-// The same thing happened with 10.10. .Helvetica Neue DeskInterface is now default.
-//
-// Solution: If building with the 10.7 SDK or lower and the user's platform
-// is 10.9 or higher at runtime, substitute the correct font. This needs to
-// happen before the QApplication is created.
-#if defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_8
- if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_8)
- {
- if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_9)
- /* On a 10.9 - 10.9.x system */
- QFont::insertSubstitution(".Lucida Grande UI", "Lucida Grande");
- else
- {
- /* 10.10 or later system */
- if (language == "zh_CN" || language == "zh_TW" || language == "zh_HK") // traditional or simplified Chinese
- QFont::insertSubstitution(".Helvetica Neue DeskInterface", "Heiti SC");
- else if (language == "ja") // Japanese
- QFont::insertSubstitution(".Helvetica Neue DeskInterface", "Songti SC");
- else
- QFont::insertSubstitution(".Helvetica Neue DeskInterface", "Lucida Grande");
- }
- }
-#endif
-#endif
-}
-
ToolTipToRichTextFilter::ToolTipToRichTextFilter(int _size_threshold, QObject *parent) :
QObject(parent),
size_threshold(_size_threshold)
@@ -741,7 +695,7 @@ LSSharedFileListItemRef findStartupItemInList(LSSharedFileListRef list, CFURLRef
if (listSnapshot == nullptr) {
return nullptr;
}
-
+
// loop through the list of startup items and try to find the bitcoin app
for(int i = 0; i < CFArrayGetCount(listSnapshot); i++) {
LSSharedFileListItemRef item = (LSSharedFileListItemRef)CFArrayGetValueAtIndex(listSnapshot, i);
@@ -769,7 +723,7 @@ LSSharedFileListItemRef findStartupItemInList(LSSharedFileListRef list, CFURLRef
CFRelease(currentItemURL);
}
}
-
+
CFRelease(listSnapshot);
return nullptr;
}
@@ -780,7 +734,7 @@ bool GetStartOnSystemStartup()
if (bitcoinAppUrl == nullptr) {
return false;
}
-
+
LSSharedFileListRef loginItems = LSSharedFileListCreate(nullptr, kLSSharedFileListSessionLoginItems, nullptr);
LSSharedFileListItemRef foundItem = findStartupItemInList(loginItems, bitcoinAppUrl);
@@ -794,7 +748,7 @@ bool SetStartOnSystemStartup(bool fAutoStart)
if (bitcoinAppUrl == nullptr) {
return false;
}
-
+
LSSharedFileListRef loginItems = LSSharedFileListCreate(nullptr, kLSSharedFileListSessionLoginItems, nullptr);
LSSharedFileListItemRef foundItem = findStartupItemInList(loginItems, bitcoinAppUrl);
@@ -806,7 +760,7 @@ bool SetStartOnSystemStartup(bool fAutoStart)
// remove item
LSSharedFileListItemRemove(loginItems, foundItem);
}
-
+
CFRelease(bitcoinAppUrl);
return true;
}
@@ -967,7 +921,7 @@ void ClickableLabel::mouseReleaseEvent(QMouseEvent *event)
{
Q_EMIT clicked(event->pos());
}
-
+
void ClickableProgressBar::mouseReleaseEvent(QMouseEvent *event)
{
Q_EMIT clicked(event->pos());
diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h
index e965a91d18..6aa65369fa 100644
--- a/src/qt/guiutil.h
+++ b/src/qt/guiutil.h
@@ -120,9 +120,6 @@ namespace GUIUtil
// Open the config file
bool openBitcoinConf();
- // Replace invalid default fonts with known good ones
- void SubstituteFonts(const QString& language);
-
/** Qt event filter that intercepts ToolTipChange events, and replaces the tooltip with a rich text
representation if needed. This assures that Qt can word-wrap long tooltip messages.
Tooltips longer than the provided size threshold (in characters) are wrapped.
@@ -219,11 +216,11 @@ namespace GUIUtil
protected:
void mouseReleaseEvent(QMouseEvent *event);
};
-
+
class ClickableProgressBar : public QProgressBar
{
Q_OBJECT
-
+
Q_SIGNALS:
/** Emitted when the progressbar is clicked. The relative mouse coordinates of the click are
* passed to the signal.
@@ -233,19 +230,7 @@ namespace GUIUtil
void mouseReleaseEvent(QMouseEvent *event);
};
-#if defined(Q_OS_MAC)
- // workaround for Qt OSX Bug:
- // https://bugreports.qt-project.org/browse/QTBUG-15631
- // QProgressBar uses around 10% CPU even when app is in background
- class ProgressBar : public ClickableProgressBar
- {
- bool event(QEvent *e) {
- return (e->type() != QEvent::StyleAnimationUpdate) ? QProgressBar::event(e) : false;
- }
- };
-#else
typedef ClickableProgressBar ProgressBar;
-#endif
} // namespace GUIUtil
diff --git a/src/qt/locale/bitcoin_en.ts b/src/qt/locale/bitcoin_en.ts
index 2b712b4317..1b51699b31 100644
--- a/src/qt/locale/bitcoin_en.ts
+++ b/src/qt/locale/bitcoin_en.ts
@@ -4,7 +4,7 @@
<context>
<name>AddressBookPage</name>
<message>
- <location filename="../forms/addressbookpage.ui" line="+30"/>
+ <location filename="../forms/addressbookpage.ui" line="+37"/>
<source>Right-click to edit address or label</source>
<translation type="unfinished"></translation>
</message>
@@ -39,7 +39,12 @@
<translation>Delete the currently selected address from the list</translation>
</message>
<message>
- <location line="+30"/>
+ <location line="-71"/>
+ <source>Enter address or label to search</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+101"/>
<source>Export the data in the current tab to a file</source>
<translation>Export the data in the current tab to a file</translation>
</message>
@@ -54,7 +59,7 @@
<translation>&amp;Delete</translation>
</message>
<message>
- <location filename="../addressbookpage.cpp" line="+50"/>
+ <location filename="../addressbookpage.cpp" line="+85"/>
<source>Choose the address to send coins to</source>
<translation type="unfinished"></translation>
</message>
@@ -84,12 +89,12 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+4"/>
+ <location line="+5"/>
<source>These are your Bitcoin addresses for receiving payments. It is recommended to use a new receiving address for each transaction.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+6"/>
+ <location line="+7"/>
<source>&amp;Copy Address</source>
<translation type="unfinished"></translation>
</message>
@@ -104,7 +109,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+193"/>
+ <location line="+177"/>
<source>Export Address List</source>
<translation type="unfinished"></translation>
</message>
@@ -127,7 +132,7 @@
<context>
<name>AddressTableModel</name>
<message>
- <location filename="../addresstablemodel.cpp" line="+169"/>
+ <location filename="../addresstablemodel.cpp" line="+164"/>
<source>Label</source>
<translation type="unfinished"></translation>
</message>
@@ -291,7 +296,7 @@
<context>
<name>BanTableModel</name>
<message>
- <location filename="../bantablemodel.cpp" line="+89"/>
+ <location filename="../bantablemodel.cpp" line="+88"/>
<source>IP/Netmask</source>
<translation type="unfinished"></translation>
</message>
@@ -304,27 +309,27 @@
<context>
<name>BitcoinGUI</name>
<message>
- <location filename="../bitcoingui.cpp" line="+358"/>
+ <location filename="../bitcoingui.cpp" line="+307"/>
<source>Sign &amp;message...</source>
<translation>Sign &amp;message...</translation>
</message>
<message>
- <location line="+430"/>
+ <location line="+483"/>
<source>Synchronizing with network...</source>
<translation>Synchronizing with network...</translation>
</message>
<message>
- <location line="-508"/>
+ <location line="-561"/>
<source>&amp;Overview</source>
<translation>&amp;Overview</translation>
</message>
<message>
- <location line="-144"/>
+ <location line="-140"/>
<source>Node</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+145"/>
+ <location line="+141"/>
<source>Show general overview of wallet</source>
<translation>Show general overview of wallet</translation>
</message>
@@ -409,7 +414,17 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+360"/>
+ <location line="+104"/>
+ <source>Wallet:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+83"/>
+ <source>default wallet</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+226"/>
<source>Click to disable network activity.</source>
<translation type="unfinished"></translation>
</message>
@@ -434,7 +449,12 @@
<translation>Reindexing blocks on disk...</translation>
</message>
<message>
- <location line="-511"/>
+ <location line="+316"/>
+ <source>Proxy is &lt;b&gt;enabled&lt;/b&gt;: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="-880"/>
<source>Send coins to a Bitcoin address</source>
<translation>Send coins to a Bitcoin address</translation>
</message>
@@ -464,17 +484,17 @@
<translation>&amp;Verify message...</translation>
</message>
<message>
- <location line="+517"/>
+ <location line="+570"/>
<source>Bitcoin</source>
<translation>Bitcoin</translation>
</message>
<message>
- <location line="-743"/>
+ <location line="-792"/>
<source>Wallet</source>
<translation>Wallet</translation>
</message>
<message>
- <location line="+153"/>
+ <location line="+149"/>
<source>&amp;Send</source>
<translation>&amp;Send</translation>
</message>
@@ -554,7 +574,7 @@
<translation type="unfinished"></translation>
</message>
<message numerus="yes">
- <location line="+357"/>
+ <location line="+410"/>
<source>%n active connection(s) to Bitcoin network</source>
<translation>
<numerusform>%n active connection to Bitcoin network</numerusform>
@@ -615,12 +635,12 @@
<translation>Up to date</translation>
</message>
<message>
- <location line="-441"/>
+ <location line="-494"/>
<source>Show the %1 help message to get a list with possible Bitcoin command-line options</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+200"/>
+ <location line="+253"/>
<source>%1 client</source>
<translation type="unfinished"></translation>
</message>
@@ -635,7 +655,7 @@
<translation>Catching up...</translation>
</message>
<message>
- <location line="+145"/>
+ <location line="+151"/>
<source>Date: %1
</source>
<translation type="unfinished"></translation>
@@ -647,7 +667,13 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
+ <location line="+2"/>
+ <source>Wallet: %1
+</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
<source>Type: %1
</source>
<translation type="unfinished"></translation>
@@ -695,7 +721,7 @@
<translation>Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;locked&lt;/b&gt;</translation>
</message>
<message>
- <location filename="../bitcoin.cpp" line="+534"/>
+ <location filename="../bitcoin.cpp" line="+529"/>
<source>A fatal error occurred. Bitcoin can no longer continue safely and will quit.</source>
<translation type="unfinished"></translation>
</message>
@@ -849,7 +875,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+325"/>
+ <location line="+315"/>
<source>(%1 locked)</source>
<translation type="unfinished"></translation>
</message>
@@ -874,8 +900,8 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+42"/>
- <location line="+52"/>
+ <location line="+45"/>
+ <location line="+54"/>
<source>(no label)</source>
<translation type="unfinished"></translation>
</message>
@@ -918,12 +944,7 @@
<translation>&amp;Address</translation>
</message>
<message>
- <location filename="../editaddressdialog.cpp" line="+30"/>
- <source>New receiving address</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+4"/>
+ <location filename="../editaddressdialog.cpp" line="+29"/>
<source>New sending address</source>
<translation type="unfinished"></translation>
</message>
@@ -938,17 +959,22 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+72"/>
+ <location line="+71"/>
<source>The entered address &quot;%1&quot; is not a valid Bitcoin address.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+5"/>
- <source>The entered address &quot;%1&quot; is already in the address book.</source>
+ <location line="+33"/>
+ <source>Address &quot;%1&quot; already exists as a receiving address with label &quot;%2&quot; and so cannot be added as a sending address.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+5"/>
+ <source>The entered address &quot;%1&quot; is already in the address book with label &quot;%2&quot;.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="-28"/>
<source>Could not unlock wallet.</source>
<translation type="unfinished"></translation>
</message>
@@ -961,7 +987,7 @@
<context>
<name>FreespaceChecker</name>
<message>
- <location filename="../intro.cpp" line="+76"/>
+ <location filename="../intro.cpp" line="+77"/>
<source>A new data directory will be created.</source>
<translation>A new data directory will be created.</translation>
</message>
@@ -989,7 +1015,7 @@
<context>
<name>HelpMessageDialog</name>
<message>
- <location filename="../utilitydialog.cpp" line="+40"/>
+ <location filename="../utilitydialog.cpp" line="+41"/>
<source>version</source>
<translation type="unfinished">version</translation>
</message>
@@ -1009,51 +1035,6 @@
<source>Command-line options</source>
<translation type="unfinished"></translation>
</message>
- <message>
- <location line="+1"/>
- <source>Usage:</source>
- <translation type="unfinished">Usage:</translation>
- </message>
- <message>
- <location line="+1"/>
- <source>command-line options</source>
- <translation type="unfinished">command-line options</translation>
- </message>
- <message>
- <location line="+9"/>
- <source>UI Options:</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+4"/>
- <source>Choose data directory on startup (default: %u)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+1"/>
- <source>Set language, for example &quot;de_DE&quot; (default: system locale)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+1"/>
- <source>Start minimized</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+1"/>
- <source>Set SSL root certificates for payment request (default: -system-)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+1"/>
- <source>Show splash screen on startup (default: %u)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+1"/>
- <source>Reset all settings changed in the GUI</source>
- <translation type="unfinished"></translation>
- </message>
</context>
<context>
<name>Intro</name>
@@ -1128,7 +1109,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+26"/>
+ <location line="+27"/>
<source>Error</source>
<translation>Error</translation>
</message>
@@ -1238,7 +1219,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../openuridialog.cpp" line="+47"/>
+ <location filename="../openuridialog.cpp" line="+45"/>
<source>Select payment request file to open</source>
<translation type="unfinished"></translation>
</message>
@@ -1266,7 +1247,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+9"/>
+ <location line="+72"/>
<source>Size of &amp;database cache</source>
<translation type="unfinished"></translation>
</message>
@@ -1350,7 +1331,27 @@
<translation>&amp;Network</translation>
</message>
<message>
- <location line="-85"/>
+ <location line="-191"/>
+ <source>Disables some advanced features but all blocks will still be fully validated. Reverting this setting requires re-downloading the entire blockchain. Actual disk usage may be somewhat higher.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>Prune &amp;block storage to</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+10"/>
+ <source>GB</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+25"/>
+ <source>Reverting this setting requires re-downloading the entire blockchain.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+68"/>
<source>(0 = auto, &lt;0 = leave that many cores free)</source>
<translation type="unfinished"></translation>
</message>
@@ -1518,28 +1519,28 @@
<translation>&amp;Cancel</translation>
</message>
<message>
- <location filename="../optionsdialog.cpp" line="+82"/>
+ <location filename="../optionsdialog.cpp" line="+92"/>
<source>default</source>
<translation>default</translation>
</message>
<message>
- <location line="+64"/>
+ <location line="+52"/>
<source>none</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+72"/>
+ <location line="+77"/>
<source>Confirm options reset</source>
<translation>Confirm options reset</translation>
</message>
<message>
<location line="+1"/>
- <location line="+55"/>
+ <location line="+60"/>
<source>Client restart required to activate changes.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-55"/>
+ <location line="-60"/>
<source>Client will be shut down. Do you want to proceed?</source>
<translation type="unfinished"></translation>
</message>
@@ -1564,7 +1565,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+38"/>
+ <location line="+43"/>
<source>This change would require a client restart.</source>
<translation type="unfinished"></translation>
</message>
@@ -1671,10 +1672,10 @@
<context>
<name>PaymentServer</name>
<message>
- <location filename="../paymentserver.cpp" line="+326"/>
- <location line="+214"/>
+ <location filename="../paymentserver.cpp" line="+317"/>
+ <location line="+215"/>
<location line="+42"/>
- <location line="+111"/>
+ <location line="+110"/>
<location line="+14"/>
<location line="+18"/>
<source>Payment request error</source>
@@ -1686,14 +1687,20 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+102"/>
+ <location line="+82"/>
+ <location line="+21"/>
<location line="+13"/>
<location line="+7"/>
<source>URI handling</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-19"/>
+ <location line="-41"/>
+ <source>&apos;bitcoin://&apos; is not a valid URI. Use &apos;bitcoin:&apos; instead.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+22"/>
<source>Payment request fetch URL is invalid: %1</source>
<translation type="unfinished"></translation>
</message>
@@ -1723,12 +1730,12 @@
<location line="+31"/>
<location line="+10"/>
<location line="+17"/>
- <location line="+86"/>
+ <location line="+85"/>
<source>Payment request rejected</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-153"/>
+ <location line="-152"/>
<source>Payment request network doesn&apos;t match client network.</source>
<translation type="unfinished"></translation>
</message>
@@ -1759,12 +1766,12 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+55"/>
+ <location line="+65"/>
<source>Refund from %1</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+42"/>
+ <location line="+31"/>
<source>Payment request %1 is too large (%2 bytes, allowed %3 bytes).</source>
<translation type="unfinished"></translation>
</message>
@@ -1797,7 +1804,7 @@
<context>
<name>PeerTableModel</name>
<message>
- <location filename="../peertablemodel.cpp" line="+121"/>
+ <location filename="../peertablemodel.cpp" line="+109"/>
<source>User Agent</source>
<translation type="unfinished"></translation>
</message>
@@ -1830,17 +1837,17 @@
<context>
<name>QObject</name>
<message>
- <location filename="../bitcoinunits.cpp" line="+185"/>
+ <location filename="../bitcoinunits.cpp" line="+197"/>
<source>Amount</source>
<translation type="unfinished">Amount</translation>
</message>
<message>
- <location filename="../guiutil.cpp" line="+130"/>
+ <location filename="../guiutil.cpp" line="+124"/>
<source>Enter a Bitcoin address (e.g. %1)</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+760"/>
+ <location line="+722"/>
<source>%1 d</source>
<translation type="unfinished"></translation>
</message>
@@ -1950,7 +1957,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../bitcoin.cpp" line="+178"/>
+ <location filename="../bitcoin.cpp" line="+193"/>
<source>%1 didn&apos;t yet exit safely...</source>
<translation type="unfinished"></translation>
</message>
@@ -1963,13 +1970,18 @@
<context>
<name>QObject::QObject</name>
<message>
- <location filename="../bitcoin.cpp" line="-86"/>
+ <location filename="../bitcoin.cpp" line="-118"/>
+ <source>Error parsing command line arguments: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+39"/>
<source>Error: Specified data directory &quot;%1&quot; does not exist.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+7"/>
- <source>Error: Cannot parse configuration file: %1. Only use key=value syntax.</source>
+ <location line="+5"/>
+ <source>Error: Cannot parse configuration file: %1.</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -1981,7 +1993,7 @@
<context>
<name>QRImageWidget</name>
<message>
- <location filename="../receiverequestdialog.cpp" line="+35"/>
+ <location filename="../receiverequestdialog.cpp" line="+32"/>
<source>&amp;Save Image...</source>
<translation type="unfinished"></translation>
</message>
@@ -2015,7 +2027,7 @@
<location line="+23"/>
<location line="+36"/>
<location line="+23"/>
- <location line="+663"/>
+ <location line="+679"/>
<location line="+23"/>
<location line="+23"/>
<location line="+23"/>
@@ -2037,7 +2049,7 @@
<translation>N/A</translation>
</message>
<message>
- <location line="-1345"/>
+ <location line="-1361"/>
<source>Client version</source>
<translation>Client version</translation>
</message>
@@ -2112,7 +2124,17 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+324"/>
+ <location line="+94"/>
+ <source>Wallet: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+8"/>
+ <source>(none)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+238"/>
<source>&amp;Reset</source>
<translation type="unfinished"></translation>
</message>
@@ -2140,8 +2162,8 @@
</message>
<message>
<location line="+60"/>
- <location filename="../rpcconsole.cpp" line="+496"/>
- <location line="+718"/>
+ <location filename="../rpcconsole.cpp" line="+502"/>
+ <location line="+760"/>
<source>Select a peer to view detailed information.</source>
<translation type="unfinished"></translation>
</message>
@@ -2176,18 +2198,18 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-1079"/>
- <location line="+987"/>
+ <location line="-1095"/>
+ <location line="+1003"/>
<source>User Agent</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-684"/>
+ <location line="-700"/>
<source>Open the %1 debug log file from the current data directory. This can take a few seconds for large log files.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+68"/>
+ <location line="+84"/>
<source>Decrease font size</source>
<translation type="unfinished"></translation>
</message>
@@ -2247,7 +2269,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-1116"/>
+ <location line="-1132"/>
<source>Last block time</source>
<translation>Last block time</translation>
</message>
@@ -2262,7 +2284,7 @@
<translation>&amp;Console</translation>
</message>
<message>
- <location line="+195"/>
+ <location line="+211"/>
<source>&amp;Network Traffic</source>
<translation type="unfinished"></translation>
</message>
@@ -2272,7 +2294,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../rpcconsole.cpp" line="-401"/>
+ <location filename="../rpcconsole.cpp" line="-414"/>
<source>In:</source>
<translation type="unfinished"></translation>
</message>
@@ -2282,17 +2304,17 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../forms/debugwindow.ui" line="-299"/>
+ <location filename="../forms/debugwindow.ui" line="-315"/>
<source>Debug log file</source>
<translation>Debug log file</translation>
</message>
<message>
- <location line="+136"/>
+ <location line="+152"/>
<source>Clear console</source>
<translation>Clear console</translation>
</message>
<message>
- <location filename="../rpcconsole.cpp" line="-225"/>
+ <location filename="../rpcconsole.cpp" line="-253"/>
<source>1 &amp;hour</source>
<translation type="unfinished"></translation>
</message>
@@ -2330,7 +2352,12 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+136"/>
+ <location line="+54"/>
+ <source>default wallet</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+110"/>
<source>Welcome to the %1 RPC console.</source>
<translation type="unfinished"></translation>
</message>
@@ -2360,7 +2387,17 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+243"/>
+ <location line="+68"/>
+ <source>Executing command without any wallet</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Executing command using &quot;%1&quot; wallet</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+190"/>
<source>(node id: %1)</source>
<translation type="unfinished"></translation>
</message>
@@ -2453,27 +2490,27 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+142"/>
- <source>Requested payments history</source>
+ <location line="+78"/>
+ <source>Native segwit addresses (aka Bech32 or BIP-173) reduce your transaction fees later on and offer better protection against typos, but old wallets don&apos;t support them. When unchecked, an address compatible with older wallets will be created instead.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-162"/>
- <source>&amp;Request payment</source>
+ <location line="+3"/>
+ <source>Generate native segwit (Bech32) address</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+98"/>
- <source>Bech32 addresses (BIP-173) are cheaper to spend from and offer better protection against typos. When unchecked a P2SH wrapped SegWit address will be created, compatible with older wallets.</source>
+ <location line="+61"/>
+ <source>Requested payments history</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+3"/>
- <source>Generate Bech32 address</source>
+ <location line="-162"/>
+ <source>&amp;Request payment</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+86"/>
+ <location line="+187"/>
<source>Show the selected request (does the same as double clicking an entry)</source>
<translation type="unfinished"></translation>
</message>
@@ -2571,7 +2608,12 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+10"/>
+ <location line="+2"/>
+ <source>Wallet</source>
+ <translation type="unfinished">Wallet</translation>
+ </message>
+ <message>
+ <location line="+11"/>
<source>Resulting URI too long, try to reduce the text for label / message.</source>
<translation type="unfinished"></translation>
</message>
@@ -2584,7 +2626,7 @@
<context>
<name>RecentRequestsTableModel</name>
<message>
- <location filename="../recentrequeststablemodel.cpp" line="+28"/>
+ <location filename="../recentrequeststablemodel.cpp" line="+27"/>
<source>Date</source>
<translation type="unfinished">Date</translation>
</message>
@@ -2623,7 +2665,7 @@
<name>SendCoinsDialog</name>
<message>
<location filename="../forms/sendcoinsdialog.ui" line="+14"/>
- <location filename="../sendcoinsdialog.cpp" line="+578"/>
+ <location filename="../sendcoinsdialog.cpp" line="+588"/>
<source>Send Coins</source>
<translation>Send Coins</translation>
</message>
@@ -2713,22 +2755,24 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+54"/>
- <source>per kilobyte</source>
+ <location line="+51"/>
+ <source>Specify a custom fee per kB (1,000 bytes) of the transaction&apos;s virtual size.
+
+Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satoshis per kB&quot; for a transaction size of 500 bytes (half of 1 kB) would ultimately yield a fee of only 50 satoshis.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-3"/>
- <source>If the custom fee is set to 1000 satoshis and the transaction is only 250 bytes, then &quot;per kilobyte&quot; only pays 250 satoshis in fee, while &quot;total at least&quot; pays 1000 satoshis. For transactions bigger than a kilobyte both pay by kilobyte.</source>
+ <location line="+5"/>
+ <source>per kilobyte</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-48"/>
+ <location line="-53"/>
<source>Hide</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+78"/>
+ <location line="+80"/>
<location line="+13"/>
<source>Paying only the minimum fee is just fine as long as there is less transaction volume than space in the blocks. But be aware that this can end up in a never confirming transaction once there is more demand for bitcoin transactions than the network can process.</source>
<translation type="unfinished"></translation>
@@ -2769,12 +2813,12 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-833"/>
+ <location line="-835"/>
<source>Dust:</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+694"/>
+ <location line="+696"/>
<source>Confirmation time target:</source>
<translation type="unfinished"></translation>
</message>
@@ -2809,7 +2853,7 @@
<translation>S&amp;end</translation>
</message>
<message>
- <location filename="../sendcoinsdialog.cpp" line="-494"/>
+ <location filename="../sendcoinsdialog.cpp" line="-504"/>
<source>Copy quantity</source>
<translation type="unfinished"></translation>
</message>
@@ -2849,7 +2893,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+127"/>
+ <location line="+131"/>
<location line="+5"/>
<location line="+5"/>
<location line="+4"/>
@@ -2862,31 +2906,41 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+9"/>
- <source>added as transaction fee</source>
+ <location line="+42"/>
+ <source>or</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+15"/>
- <source>Total Amount %1</source>
+ <location line="-19"/>
+ <source>You can increase the fee later (signals Replace-By-Fee, BIP-125).</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+3"/>
- <source>or</source>
+ <location line="-57"/>
+ <source>from wallet %1</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+4"/>
- <source>You can increase the fee later (signals Replace-By-Fee, BIP-125).</source>
+ <location line="+36"/>
+ <source>Please, review your transaction.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+2"/>
+ <location line="+7"/>
+ <source>Transaction fee</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+16"/>
<source>Not signalling Replace-By-Fee, BIP-125.</source>
<translation type="unfinished"></translation>
</message>
<message>
+ <location line="+14"/>
+ <source>Total Amount</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
<location line="+5"/>
<source>Confirm send coins</source>
<translation type="unfinished"></translation>
@@ -2942,7 +2996,7 @@
<translation type="unfinished"></translation>
</message>
<message numerus="yes">
- <location line="+42"/>
+ <location line="+43"/>
<source>Estimated to begin confirmation within %n block(s).</source>
<translation>
<numerusform>Estimated to begin confirmation within %n block.</numerusform>
@@ -3084,7 +3138,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../sendcoinsentry.cpp" line="+36"/>
+ <location filename="../sendcoinsentry.cpp" line="+35"/>
<source>Enter a label for this address to add it to your address book</source>
<translation type="unfinished"></translation>
</message>
@@ -3101,7 +3155,7 @@
<context>
<name>ShutdownWindow</name>
<message>
- <location filename="../utilitydialog.cpp" line="+78"/>
+ <location filename="../utilitydialog.cpp" line="+84"/>
<source>%1 is shutting down...</source>
<translation type="unfinished"></translation>
</message>
@@ -3222,12 +3276,12 @@
<translation>Reset all verify message fields</translation>
</message>
<message>
- <location filename="../signverifymessagedialog.cpp" line="+41"/>
+ <location filename="../signverifymessagedialog.cpp" line="+39"/>
<source>Click &quot;Sign Message&quot; to generate signature</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+82"/>
+ <location line="+81"/>
<location line="+78"/>
<source>The entered address is invalid.</source>
<translation type="unfinished"></translation>
@@ -3304,7 +3358,7 @@
<context>
<name>TrafficGraphWidget</name>
<message>
- <location filename="../trafficgraphwidget.cpp" line="+80"/>
+ <location filename="../trafficgraphwidget.cpp" line="+81"/>
<source>KB/s</source>
<translation type="unfinished"></translation>
</message>
@@ -3312,7 +3366,7 @@
<context>
<name>TransactionDesc</name>
<message numerus="yes">
- <location filename="../transactiondesc.cpp" line="+30"/>
+ <location filename="../transactiondesc.cpp" line="+31"/>
<source>Open for %n more block(s)</source>
<translation>
<numerusform>Open for %n more block</numerusform>
@@ -3331,11 +3385,6 @@
</message>
<message>
<location line="+2"/>
- <source>%1/offline</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+2"/>
<source>0/unconfirmed, %1</source>
<translation type="unfinished"></translation>
</message>
@@ -3365,25 +3414,12 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+17"/>
+ <location line="+23"/>
<source>Status</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+5"/>
- <source>, has not been successfully broadcast yet</source>
- <translation type="unfinished"></translation>
- </message>
- <message numerus="yes">
- <location line="+2"/>
- <source>, broadcast through %n node(s)</source>
- <translation>
- <numerusform>, broadcast through %n node</numerusform>
- <numerusform>, broadcast through %n nodes</numerusform>
- </translation>
- </message>
- <message>
- <location line="+4"/>
+ <location line="+3"/>
<source>Date</source>
<translation type="unfinished">Date</translation>
</message>
@@ -3399,7 +3435,7 @@
</message>
<message>
<location line="+5"/>
- <location line="+12"/>
+ <location line="+14"/>
<location line="+72"/>
<source>From</source>
<translation type="unfinished"></translation>
@@ -3412,12 +3448,12 @@
<message>
<location line="+1"/>
<location line="+20"/>
- <location line="+69"/>
+ <location line="+70"/>
<source>To</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-87"/>
+ <location line="-88"/>
<source>own address</source>
<translation type="unfinished"></translation>
</message>
@@ -3433,16 +3469,16 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+34"/>
+ <location line="+36"/>
<location line="+12"/>
- <location line="+53"/>
- <location line="+26"/>
- <location line="+55"/>
+ <location line="+54"/>
+ <location line="+30"/>
+ <location line="+58"/>
<source>Credit</source>
<translation type="unfinished"></translation>
</message>
<message numerus="yes">
- <location line="-144"/>
+ <location line="-152"/>
<source>matures in %n more block(s)</source>
<translation>
<numerusform>matures in %n more block</numerusform>
@@ -3455,14 +3491,14 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+59"/>
- <location line="+25"/>
- <location line="+55"/>
+ <location line="+60"/>
+ <location line="+26"/>
+ <location line="+61"/>
<source>Debit</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-70"/>
+ <location line="-77"/>
<source>Total debit</source>
<translation type="unfinished"></translation>
</message>
@@ -3477,18 +3513,18 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+16"/>
+ <location line="+22"/>
<source>Net amount</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+6"/>
- <location line="+11"/>
+ <location line="+12"/>
<source>Message</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-9"/>
+ <location line="-10"/>
<source>Comment</source>
<translation type="unfinished"></translation>
</message>
@@ -3504,6 +3540,11 @@
</message>
<message>
<location line="+1"/>
+ <source>Transaction virtual size</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
<source>Output index</source>
<translation type="unfinished"></translation>
</message>
@@ -3533,7 +3574,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+20"/>
+ <location line="+21"/>
<source>Amount</source>
<translation type="unfinished">Amount</translation>
</message>
@@ -3566,7 +3607,7 @@
<context>
<name>TransactionTableModel</name>
<message>
- <location filename="../transactiontablemodel.cpp" line="+248"/>
+ <location filename="../transactiontablemodel.cpp" line="+226"/>
<source>Date</source>
<translation type="unfinished">Date</translation>
</message>
@@ -3595,11 +3636,6 @@
</message>
<message>
<location line="+3"/>
- <source>Offline</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+3"/>
<source>Unconfirmed</source>
<translation type="unfinished"></translation>
</message>
@@ -3630,11 +3666,6 @@
</message>
<message>
<location line="+3"/>
- <source>This block was not received by any other nodes and will probably not be accepted!</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+3"/>
<source>Generated but not accepted</source>
<translation type="unfinished"></translation>
</message>
@@ -3674,7 +3705,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+213"/>
+ <location line="+208"/>
<source>(no label)</source>
<translation type="unfinished"></translation>
</message>
@@ -3773,17 +3804,17 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+6"/>
+ <location line="+5"/>
<source>Enter address, transaction id, or label to search</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+6"/>
+ <location line="+4"/>
<source>Min amount</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+48"/>
+ <location line="+47"/>
<source>Abandon transaction</source>
<translation type="unfinished"></translation>
</message>
@@ -3833,7 +3864,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+194"/>
+ <location line="+193"/>
<source>Export Transaction History</source>
<translation type="unfinished"></translation>
</message>
@@ -3911,7 +3942,7 @@
<context>
<name>UnitDisplayStatusBarControl</name>
<message>
- <location filename="../bitcoingui.cpp" line="+129"/>
+ <location filename="../bitcoingui.cpp" line="+159"/>
<source>Unit to show amounts in. Click to select another unit.</source>
<translation type="unfinished"></translation>
</message>
@@ -3919,7 +3950,7 @@
<context>
<name>WalletFrame</name>
<message>
- <location filename="../walletframe.cpp" line="+28"/>
+ <location filename="../walletframe.cpp" line="+29"/>
<source>No wallet has been loaded.</source>
<translation type="unfinished"></translation>
</message>
@@ -3927,12 +3958,12 @@
<context>
<name>WalletModel</name>
<message>
- <location filename="../walletmodel.cpp" line="+290"/>
+ <location filename="../walletmodel.cpp" line="+215"/>
<source>Send Coins</source>
<translation type="unfinished">Send Coins</translation>
</message>
<message>
- <location line="+384"/>
+ <location line="+289"/>
<location line="+39"/>
<location line="+6"/>
<source>Fee bump error</source>
@@ -3982,7 +4013,7 @@
<context>
<name>WalletView</name>
<message>
- <location filename="../walletview.cpp" line="+46"/>
+ <location filename="../walletview.cpp" line="+47"/>
<source>&amp;Export</source>
<translation type="unfinished">&amp;Export</translation>
</message>
@@ -3992,7 +4023,7 @@
<translation type="unfinished">Export the data in the current tab to a file</translation>
</message>
<message>
- <location line="+201"/>
+ <location line="+207"/>
<source>Backup Wallet</source>
<translation type="unfinished"></translation>
</message>
@@ -4021,46 +4052,21 @@
<source>The wallet data was successfully saved to %1.</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <location line="+54"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>bitcoin-core</name>
<message>
- <location filename="../bitcoinstrings.cpp" line="+351"/>
- <source>Options:</source>
- <translation>Options:</translation>
- </message>
- <message>
- <location line="+33"/>
- <source>Specify data directory</source>
- <translation>Specify data directory</translation>
- </message>
- <message>
- <location line="-99"/>
- <source>Connect to a node to retrieve peer addresses, and disconnect</source>
- <translation>Connect to a node to retrieve peer addresses, and disconnect</translation>
- </message>
- <message>
- <location line="+102"/>
- <source>Specify your own public address</source>
- <translation>Specify your own public address</translation>
- </message>
- <message>
- <location line="-116"/>
- <source>Accept command line and JSON-RPC commands</source>
- <translation>Accept command line and JSON-RPC commands</translation>
- </message>
- <message>
- <location line="-209"/>
+ <location filename="../bitcoinstrings.cpp" line="+29"/>
<source>Distributed under the MIT software license, see the accompanying file %s or %s</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+44"/>
- <source>If &lt;category&gt; is not supplied or if &lt;category&gt; = 1, output all debugging information.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+36"/>
+ <location line="+22"/>
<source>Prune configured below the minimum of %d MiB. Please use a higher number.</source>
<translation type="unfinished"></translation>
</message>
@@ -4070,37 +4076,27 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+18"/>
+ <location line="+3"/>
<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="+151"/>
+ <location line="+81"/>
<source>Error: A fatal internal error occurred, see debug.log for details</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+4"/>
- <source>Fee (in %s/kB) to add to transactions you send (default: %s)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+41"/>
+ <location line="+25"/>
<source>Pruning blockstore...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+11"/>
- <source>Run in the background as a daemon and accept commands</source>
- <translation>Run in the background as a daemon and accept commands</translation>
- </message>
- <message>
- <location line="+40"/>
+ <location line="+28"/>
<source>Unable to start HTTP server. See debug log for details.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-397"/>
+ <location line="-178"/>
<source>Bitcoin Core</source>
<translation type="unfinished">Bitcoin Core</translation>
</message>
@@ -4111,26 +4107,6 @@
</message>
<message>
<location line="+7"/>
- <source>A fee rate (in %s/kB) that will be used when fee estimation has insufficient data (default: %s)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+3"/>
- <source>Accept relayed transactions received from whitelisted peers even when not relaying transactions (default: %d)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+3"/>
- <source>Add a node to connect to and attempt to keep the connection open (see the `addnode` RPC command help for more info)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+7"/>
- <source>Bind to given address and always listen on it. Use [host]:port notation for IPv6</source>
- <translation>Bind to given address and always listen on it. Use [host]:port notation for IPv6</translation>
- </message>
- <message>
- <location line="+12"/>
<source>Cannot obtain a lock on data directory %s. %s is probably already running.</source>
<translation type="unfinished"></translation>
</message>
@@ -4140,52 +4116,12 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+3"/>
- <source>Connect only to the specified node(s); -connect=0 disables automatic connections (the rules for this peer are the same as for -addnode)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+6"/>
- <source>Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+18"/>
+ <location line="+12"/>
<source>Error reading %s! All keys read correctly, but transaction data or address book entries might be missing or incorrect.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+5"/>
- <source>Exclude debugging information for a category. Can be used in conjunction with -debug=1 to output debug logs for all categories except one or more specified categories.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+7"/>
- <source>Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)</source>
- <translation>Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)</translation>
- </message>
- <message>
- <location line="+6"/>
- <source>Extra transactions to keep in memory for compact block reconstructions (default: %u)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+20"/>
- <source>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)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+10"/>
- <source>Maximum allowed median peer time offset adjustment. Local perspective of time may be influenced by peers forward or backward by this amount. (default: %u seconds)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+7"/>
- <source>Maximum total fees (in %s) to use in a single wallet transaction or raw transaction; setting this too low may abort large transactions (default: %s)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+7"/>
+ <location line="+11"/>
<source>Please check that your computer&apos;s date and time are correct! If your clock is wrong, %s will not work properly.</source>
<translation type="unfinished"></translation>
</message>
@@ -4195,32 +4131,12 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+8"/>
- <source>Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect used)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+6"/>
- <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, &gt;%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, &lt;0 = leave that many cores free, default: %d)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+14"/>
+ <location line="+11"/>
<source>The block database contains a block which appears to be from the future. This may be due to your computer&apos;s date and time being set incorrectly. Only rebuild the block database if you are sure that your computer&apos;s date and time are correct</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+12"/>
+ <location line="+7"/>
<source>This is a pre-release test build - use at your own risk - do not use for mining or merchant applications</source>
<translation type="unfinished"></translation>
</message>
@@ -4230,7 +4146,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+15"/>
+ <location line="+12"/>
<source>Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.</source>
<translation type="unfinished"></translation>
</message>
@@ -4240,22 +4156,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+9"/>
- <source>Use UPnP to map the listening port (default: 1 when listening and no -proxy)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+5"/>
- <source>Username and hashed password for JSON-RPC connections. The field &lt;userpw&gt; comes in the format: &lt;USERNAME&gt;:&lt;SALT&gt;$&lt;HASH&gt;. A canonical python script is included in share/rpcuser. The client then connects normally using the rpcuser=&lt;USERNAME&gt;/rpcpassword=&lt;PASSWORD&gt; pair of arguments. This option can be specified multiple times</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+6"/>
- <source>Wallet will not create transactions that violate mempool chain limits (default: %u)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+3"/>
+ <location line="+11"/>
<source>Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.</source>
<translation type="unfinished"></translation>
</message>
@@ -4265,12 +4166,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+3"/>
- <source>Whether to save the mempool on shutdown and load on restart (default: %u)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+11"/>
+ <location line="+6"/>
<source>%d of last 100 blocks have unexpected version</source>
<translation type="unfinished"></translation>
</message>
@@ -4280,56 +4176,21 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+4"/>
- <source>-maxmempool must be at least %d MB</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+1"/>
- <source>&lt;category&gt; can be:</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
<location line="+2"/>
- <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+4"/>
- <source>Append comment to the user agent string</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+1"/>
- <source>Attempt to recover private keys from a corrupt wallet on startup</source>
+ <source>-maxmempool must be at least %d MB</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+2"/>
- <source>Block creation options:</source>
- <translation>Block creation options:</translation>
- </message>
- <message>
- <location line="+2"/>
<source>Cannot resolve -%s address: &apos;%s&apos;</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
- <source>Chain selection options:</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+1"/>
+ <location line="+2"/>
<source>Change index out of range</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+3"/>
- <source>Connection options:</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
<location line="+1"/>
<source>Copyright (C) %i-%i</source>
<translation type="unfinished"></translation>
@@ -4341,46 +4202,11 @@
</message>
<message>
<location line="+1"/>
- <source>Debugging/Testing options:</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+1"/>
- <source>Do not load the wallet and disable wallet RPC calls</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+1"/>
<source>Do you want to rebuild the block database now?</source>
<translation>Do you want to rebuild the block database now?</translation>
</message>
<message>
<location line="+2"/>
- <source>Enable publish hash block in &lt;address&gt;</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+1"/>
- <source>Enable publish hash transaction in &lt;address&gt;</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+1"/>
- <source>Enable publish raw block in &lt;address&gt;</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+1"/>
- <source>Enable publish raw transaction in &lt;address&gt;</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+1"/>
- <source>Enable transaction replacement in the memory pool (default: %u)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+1"/>
<source>Error creating %s: You can&apos;t create non-HD wallets with this version.</source>
<translation type="unfinished"></translation>
</message>
@@ -4401,6 +4227,11 @@
</message>
<message>
<location line="+1"/>
+ <source>Error loading %s: Private keys can only be disabled during creation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
<source>Error loading %s: Wallet corrupted</source>
<translation type="unfinished"></translation>
</message>
@@ -4415,7 +4246,7 @@
<translation>Error loading block database</translation>
</message>
<message>
- <location line="+4"/>
+ <location line="+2"/>
<source>Error opening block database</source>
<translation>Error opening block database</translation>
</message>
@@ -4435,12 +4266,12 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+3"/>
+ <location line="+1"/>
<source>Importing...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+3"/>
+ <location line="+1"/>
<source>Incorrect or no genesis block found. Wrong datadir for network?</source>
<translation>Incorrect or no genesis block found. Wrong datadir for network?</translation>
</message>
@@ -4465,12 +4296,12 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+4"/>
- <source>Keep the transaction memory pool below &lt;n&gt; megabytes (default: %u)</source>
+ <location line="+47"/>
+ <source>Upgrading txindex database</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+4"/>
+ <location line="-43"/>
<source>Loading P2P addresses...</source>
<translation type="unfinished"></translation>
</message>
@@ -4480,32 +4311,12 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+3"/>
- <source>Location of the auth cookie (default: data dir)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+7"/>
+ <location line="+4"/>
<source>Not enough file descriptors available.</source>
<translation>Not enough file descriptors available.</translation>
</message>
<message>
<location line="+1"/>
- <source>Only connect to nodes in network &lt;net&gt; (ipv4, ipv6 or onion)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+4"/>
- <source>Print this help message and exit</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+1"/>
- <source>Print version and exit</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+1"/>
<source>Prune cannot be configured with a negative value.</source>
<translation type="unfinished"></translation>
</message>
@@ -4516,46 +4327,21 @@
</message>
<message>
<location line="+3"/>
- <source>Rebuild chain state and block index from the blk*.dat files on disk</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+1"/>
- <source>Rebuild chain state from the currently indexed blocks</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+4"/>
<source>Replaying blocks...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+3"/>
+ <location line="+2"/>
<source>Rewinding blocks...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+3"/>
- <source>Send transactions with full-RBF opt-in enabled (RPC only, default: %u)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+1"/>
- <source>Set database cache size in megabytes (%d to %d, default: %d)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+14"/>
- <source>Specify wallet file (within data directory)</source>
- <translation>Specify wallet file (within data directory)</translation>
- </message>
- <message>
- <location line="+4"/>
+ <location line="+7"/>
<source>The source code is available from %s.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+11"/>
+ <location line="+8"/>
<source>Transaction fee and change calculation failed</source>
<translation type="unfinished"></translation>
</message>
@@ -4565,7 +4351,12 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+4"/>
+ <location line="+2"/>
+ <source>Unable to generate keys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
<source>Unsupported argument -benchmark ignored, use -debug=bench.</source>
<translation type="unfinished"></translation>
</message>
@@ -4585,142 +4376,52 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+2"/>
- <source>Upgrading UTXO database</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+1"/>
- <source>Use UPnP to map the listening port (default: %u)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
<location line="+1"/>
- <source>Use the test chain</source>
+ <source>Upgrading UTXO database</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
+ <location line="+2"/>
<source>User Agent comment (%s) contains unsafe characters.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+2"/>
+ <location line="+1"/>
<source>Verifying blocks...</source>
<translation>Verifying blocks...</translation>
</message>
<message>
<location line="+3"/>
- <source>Wallet debugging/testing options:</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+1"/>
<source>Wallet needed to be rewritten: restart %s to complete</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
- <source>Wallet options:</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="-397"/>
- <source>Allow JSON-RPC connections from specified source. Valid for &lt;ip&gt; 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>
- <message>
- <location line="+7"/>
- <source>Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+17"/>
- <source>Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+6"/>
- <source>Discover own IP addresses (default: 1 when listening and no -externalip or -proxy)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+18"/>
+ <location line="-165"/>
<source>Error: Listening for incoming connections failed (listen returned error %s)</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+6"/>
- <source>Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)</source>
- <translation>Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)</translation>
- </message>
- <message>
- <location line="+12"/>
- <source>Fees (in %s/kB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+14"/>
- <source>If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+7"/>
+ <location line="+5"/>
<source>Invalid amount for -maxtxfee=&lt;amount&gt;: &apos;%s&apos; (must be at least the minrelay fee of %s to prevent stuck transactions)</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+10"/>
- <source>Maximum size of data in data carrier transactions we relay and mine (default: %u)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+24"/>
- <source>Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+44"/>
+ <location line="+22"/>
<source>The transaction amount is too small to send after the fee has been deducted</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+64"/>
- <source>Whitelisted peers cannot be DoS banned and their transactions are always relayed, even if they are already in the mempool, useful e.g. for a gateway</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+3"/>
+ <location line="+44"/>
<source>You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+7"/>
- <source>(default: %u)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+5"/>
- <source>Accept public REST requests (default: %u)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+5"/>
- <source>Automatically create Tor hidden service (default: %d)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+6"/>
- <source>Connect through SOCKS5 proxy</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+20"/>
+ <location line="+22"/>
<source>Error loading %s: You can&apos;t disable HD on an already existing HD wallet</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+6"/>
+ <location line="+4"/>
<source>Error reading from database, shutting down.</source>
<translation type="unfinished"></translation>
</message>
@@ -4730,12 +4431,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+9"/>
- <source>Imports blocks from external blk000??.dat file on startup</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+3"/>
+ <location line="+8"/>
<source>Information</source>
<translation>Information</translation>
</message>
@@ -4760,52 +4456,17 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
- <source>Keep at most &lt;n&gt; unconnectable transactions in memory (default: %u)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+14"/>
+ <location line="+6"/>
<source>Need to specify a port with -whitebind: &apos;%s&apos;</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
- <source>Node relay options:</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+11"/>
- <source>RPC server options:</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+3"/>
+ <location line="+5"/>
<source>Reducing -maxconnections from %d to %d, because of system limitations.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+4"/>
- <source>Rescan the block chain for missing wallet transactions on startup</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+4"/>
- <source>Send trace/debug info to console instead of debug.log file</source>
- <translation>Send trace/debug info to console instead of debug.log file</translation>
- </message>
- <message>
- <location line="+6"/>
- <source>Show all debugging options (usage: --help -help-debug)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+1"/>
- <source>Shrink debug.log file on client startup (default: 1 when no -debug)</source>
- <translation>Shrink debug.log file on client startup (default: 1 when no -debug)</translation>
- </message>
- <message>
- <location line="+1"/>
<source>Signing transaction failed</source>
<translation>Signing transaction failed</translation>
</message>
@@ -4825,27 +4486,23 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+10"/>
- <source>The transaction amount is too small to pay the fee</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+2"/>
- <source>This is experimental software.</source>
+ <location line="+1"/>
+ <source>Specified blocks directory &quot;%s&quot; does not exist.
+</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+4"/>
- <source>Tor control port password (default: empty)</source>
+ <location line="+3"/>
+ <source>The transaction amount is too small to pay the fee</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
- <source>Tor control port to use if onion listening enabled (default: %s)</source>
+ <location line="+2"/>
+ <source>This is experimental software.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
+ <location line="+3"/>
<source>Transaction amount too small</source>
<translation>Transaction amount too small</translation>
</message>
@@ -4870,17 +4527,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+7"/>
- <source>Upgrade wallet to latest format on startup</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+5"/>
- <source>Username for JSON-RPC connections</source>
- <translation>Username for JSON-RPC connections</translation>
- </message>
- <message>
- <location line="+2"/>
+ <location line="+12"/>
<source>Verifying wallet(s)...</source>
<translation type="unfinished"></translation>
</message>
@@ -4890,7 +4537,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+4"/>
+ <location line="+2"/>
<source>Warning</source>
<translation>Warning</translation>
</message>
@@ -4901,136 +4548,21 @@
</message>
<message>
<location line="+1"/>
- <source>Whether to operate in a blocks only mode (default: %u)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+1"/>
- <source>You need to rebuild the database using -reindex to change -txindex</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+1"/>
<source>Zapping all transactions from wallet...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
- <source>ZeroMQ notification options:</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="-80"/>
- <source>Password for JSON-RPC connections</source>
- <translation>Password for JSON-RPC connections</translation>
- </message>
- <message>
- <location line="-263"/>
- <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="+185"/>
- <source>Allow DNS lookups for -addnode, -seednode and -connect</source>
- <translation>Allow DNS lookups for -addnode, -seednode and -connect</translation>
- </message>
- <message>
- <location line="-260"/>
- <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>
- <message>
- <location line="+3"/>
+ <location line="-191"/>
<source>-maxtxfee is set very high! Fees this large could be paid on a single transaction.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+22"/>
- <source>Bind to given address to listen for JSON-RPC connections. This option is ignored unless -rpcallowip is also passed. Port is optional and overrides -rpcport. Use [host]:port notation for IPv6. This option can be specified multiple times (default: 127.0.0.1 and ::1 i.e., localhost, or if -rpcallowip has been specified, 0.0.0.0 and :: i.e., all addresses)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+26"/>
- <source>Do not keep transactions in the mempool longer than &lt;n&gt; hours (default: %u)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+2"/>
- <source>Equivalent bytes per sigop in transactions for relay and mining (default: %u)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+2"/>
+ <location line="+18"/>
<source>Error loading %s: You can&apos;t enable HD on an already existing non-HD wallet</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+2"/>
- <source>Error loading wallet %s. -wallet parameter must only specify a filename (not a path).</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+27"/>
- <source>Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+3"/>
- <source>Force relay of transactions from whitelisted peers even if they violate local relay policy (default: %d)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+3"/>
- <source>How thorough the block verification of -checkblocks is (0-4, default: %u)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+15"/>
- <source>Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+13"/>
- <source>Number of seconds to keep misbehaving peers from reconnecting (default: %u)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+2"/>
- <source>Output debugging information (default: %u, supplying &lt;category&gt; is optional)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+34"/>
- <source>Set maximum BIP141 block weight to this * 4. Deprecated, use blockmaxweight</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+5"/>
- <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>
- <message>
- <location line="+3"/>
- <source>Specify directory to hold wallets (default: &lt;datadir&gt;/wallets if it exists, otherwise &lt;datadir&gt;)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+3"/>
- <source>Specify location of debug log file: this can be an absolute path or a path relative to the data directory (default: %s)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+3"/>
- <source>Support filtering of blocks and transaction with bloom filters (default: %u)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+7"/>
- <source>The fee rate (in %s/kB) that indicates your tolerance for discarding change by adding it to the fee (default: %s). Note: An output is discarded if it is dust at this rate, but we will always discard up to the dust relay fee and a discard fee above that is limited by the fee estimate for the longest target</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+13"/>
+ <location line="+40"/>
<source>This is the transaction fee you may pay when fee estimates are not available.</source>
<translation type="unfinished"></translation>
</message>
@@ -5045,11 +4577,6 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+3"/>
- <source>Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = no limit (default: %d)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
<location line="+9"/>
<source>Unsupported argument -socks found. Setting SOCKS version isn&apos;t possible anymore, only SOCKS5 proxies are supported.</source>
<translation type="unfinished"></translation>
@@ -5060,12 +4587,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+5"/>
- <source>Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: %s)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+15"/>
+ <location line="+8"/>
<source>Warning: Unknown block versions being mined! It&apos;s possible unknown rules are in effect</source>
<translation type="unfinished"></translation>
</message>
@@ -5075,187 +4597,92 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+9"/>
- <source>Whitelist peers connecting from the given IP address (e.g. 1.2.3.4) or CIDR notated network (e.g. 1.2.3.0/24). Can be specified multiple times.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+11"/>
+ <location line="+12"/>
<source>%s is set very high!</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
- <source>(default: %s)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+8"/>
- <source>Always query for peer addresses via DNS lookup (default: %u)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+31"/>
- <source>Error loading wallet %s. -wallet filename must be a regular file.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+1"/>
+ <location line="+19"/>
<source>Error loading wallet %s. Duplicate -wallet filename specified.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
- <source>Error loading wallet %s. Invalid characters in -wallet filename.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+10"/>
- <source>How many blocks to check at startup (default: %u, 0 = all)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+3"/>
- <source>Include IP addresses in debug output (default: %u)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+14"/>
+ <location line="+21"/>
<source>Keypool ran out, please call keypoolrefill first</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
- <source>Listen for JSON-RPC connections on &lt;port&gt; (default: %u or testnet: %u)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+1"/>
- <source>Listen for connections on &lt;port&gt; (default: %u or testnet: %u)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+6"/>
- <source>Maintain at most &lt;n&gt; connections to peers (default: %u)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+1"/>
- <source>Make the wallet broadcast transactions</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+1"/>
- <source>Maximum per-connection receive buffer, &lt;n&gt;*1000 bytes (default: %u)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+1"/>
- <source>Maximum per-connection send buffer, &lt;n&gt;*1000 bytes (default: %u)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+7"/>
- <source>Prepend debug output with timestamp (default: %u)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+10"/>
- <source>Relay and mine data carrier transactions (default: %u)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+1"/>
- <source>Relay non-P2SH multisig (default: %u)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+9"/>
- <source>Set key pool size to &lt;n&gt; (default: %u)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+1"/>
- <source>Set maximum BIP141 block weight (default: %d)</source>
+ <location line="+19"/>
+ <source>Starting network threads...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
- <source>Set the number of threads to service RPC calls (default: %d)</source>
+ <location line="+3"/>
+ <source>The wallet will avoid paying less than the minimum relay fee.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+7"/>
- <source>Specify configuration file (default: %s)</source>
+ <location line="+2"/>
+ <source>This is the minimum transaction fee you pay on every transaction.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Specify connection timeout in milliseconds (minimum: 1, default: %d)</source>
+ <source>This is the transaction fee you will pay if you send a transaction.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+2"/>
- <source>Specify pid file (default: %s)</source>
+ <source>Transaction amounts must not be negative</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+3"/>
- <source>Spend unconfirmed change when sending transactions (default: %u)</source>
+ <location line="+2"/>
+ <source>Transaction has too long of a mempool chain</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Starting network threads...</source>
+ <source>Transaction must have at least one recipient</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+3"/>
- <source>The wallet will avoid paying less than the minimum relay fee.</source>
- <translation type="unfinished"></translation>
+ <location line="+8"/>
+ <source>Unknown network specified in -onlynet: &apos;%s&apos;</source>
+ <translation>Unknown network specified in -onlynet: &apos;%s&apos;</translation>
</message>
<message>
- <location line="+2"/>
- <source>This is the minimum transaction fee you pay on every transaction.</source>
- <translation type="unfinished"></translation>
+ <location line="-46"/>
+ <source>Insufficient funds</source>
+ <translation>Insufficient funds</translation>
</message>
<message>
- <location line="+1"/>
- <source>This is the transaction fee you will pay if you send a transaction.</source>
+ <location line="-128"/>
+ <source>Can&apos;t generate a change-address key. Private keys are disabled for this wallet.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
- <source>Threshold for disconnecting misbehaving peers (default: %u)</source>
+ <location line="+8"/>
+ <source>Cannot upgrade a non HD split wallet without upgrading to support pre split keypool. Please use -upgradewallet=169900 or -upgradewallet with no version specified.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+4"/>
- <source>Transaction amounts must not be negative</source>
+ <location line="+14"/>
+ <source>Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+2"/>
- <source>Transaction has too long of a mempool chain</source>
+ <location line="+54"/>
+ <source>Warning: Private keys detected in wallet {%s} with disabled private keys</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
- <source>Transaction must have at least one recipient</source>
+ <location line="+24"/>
+ <source>Cannot write to data directory &apos;%s&apos;; check permissions.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+7"/>
- <source>Unknown network specified in -onlynet: &apos;%s&apos;</source>
- <translation>Unknown network specified in -onlynet: &apos;%s&apos;</translation>
- </message>
- <message>
- <location line="-85"/>
- <source>Insufficient funds</source>
- <translation>Insufficient funds</translation>
- </message>
- <message>
- <location line="+15"/>
+ <location line="+39"/>
<source>Loading block index...</source>
<translation>Loading block index...</translation>
</message>
@@ -5265,22 +4692,22 @@
<translation>Loading wallet...</translation>
</message>
<message>
- <location line="-61"/>
+ <location line="-42"/>
<source>Cannot downgrade wallet</source>
<translation>Cannot downgrade wallet</translation>
</message>
<message>
- <location line="+87"/>
+ <location line="+50"/>
<source>Rescanning...</source>
<translation>Rescanning...</translation>
</message>
<message>
- <location line="-75"/>
+ <location line="-43"/>
<source>Done loading</source>
<translation>Done loading</translation>
</message>
<message>
- <location line="+20"/>
+ <location line="+14"/>
<source>Error</source>
<translation>Error</translation>
</message>
diff --git a/src/qt/macdockiconhandler.mm b/src/qt/macdockiconhandler.mm
index a0b62ae000..b9ad191da7 100644
--- a/src/qt/macdockiconhandler.mm
+++ b/src/qt/macdockiconhandler.mm
@@ -19,9 +19,9 @@ static MacDockIconHandler *s_instance = nullptr;
bool dockClickHandler(id self,SEL _cmd,...) {
Q_UNUSED(self)
Q_UNUSED(_cmd)
-
+
s_instance->handleDockIconClickEvent();
-
+
// Return NO (false) to suppress the default OS X actions
return false;
}
@@ -29,7 +29,7 @@ bool dockClickHandler(id self,SEL _cmd,...) {
void setupDockClickHandler() {
Class cls = objc_getClass("NSApplication");
id appInst = objc_msgSend((id)cls, sel_registerName("sharedApplication"));
-
+
if (appInst != nullptr) {
id delegate = objc_msgSend(appInst, sel_registerName("delegate"));
Class delClass = (Class)objc_msgSend(delegate, sel_registerName("class"));
diff --git a/src/qt/macnotificationhandler.mm b/src/qt/macnotificationhandler.mm
index 1b16c5f524..0e04d50baa 100644
--- a/src/qt/macnotificationhandler.mm
+++ b/src/qt/macnotificationhandler.mm
@@ -64,7 +64,7 @@ MacNotificationHandler *MacNotificationHandler::instance()
static MacNotificationHandler *s_instance = nullptr;
if (!s_instance) {
s_instance = new MacNotificationHandler();
-
+
Class aPossibleClass = objc_getClass("NSBundle");
if (aPossibleClass) {
// change NSBundle -bundleIdentifier method to return a correct bundle identifier
diff --git a/src/qt/modaloverlay.cpp b/src/qt/modaloverlay.cpp
index 249418213f..e9d03c5828 100644
--- a/src/qt/modaloverlay.cpp
+++ b/src/qt/modaloverlay.cpp
@@ -104,7 +104,7 @@ void ModalOverlay::tipUpdate(int count, const QDateTime& blockDate, double nVeri
ui->progressIncreasePerH->setText(QString::number(progressPerHour * 100, 'f', 2)+"%");
// show expected remaining time
- if(remainingMSecs >= 0) {
+ if(remainingMSecs >= 0) {
ui->expectedTimeLeft->setText(GUIUtil::formatNiceTimeOffset(remainingMSecs / 1000.0));
} else {
ui->expectedTimeLeft->setText(QObject::tr("unknown"));
diff --git a/src/qt/optionsdialog.h b/src/qt/optionsdialog.h
index 5aad484ce7..54e1a8a9b1 100644
--- a/src/qt/optionsdialog.h
+++ b/src/qt/optionsdialog.h
@@ -50,7 +50,7 @@ private Q_SLOTS:
void on_openBitcoinConfButton_clicked();
void on_okButton_clicked();
void on_cancelButton_clicked();
-
+
void on_hideTrayIcon_stateChanged(int fState);
void togglePruneWarning(bool enabled);
diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp
index 31a85f4e23..834ff7633c 100644
--- a/src/qt/optionsmodel.cpp
+++ b/src/qt/optionsmodel.cpp
@@ -57,7 +57,7 @@ void OptionsModel::Init(bool resetSettings)
settings.setValue("fHideTrayIcon", false);
fHideTrayIcon = settings.value("fHideTrayIcon").toBool();
Q_EMIT hideTrayIconChanged(fHideTrayIcon);
-
+
if (!settings.contains("fMinimizeToTray"))
settings.setValue("fMinimizeToTray", false);
fMinimizeToTray = settings.value("fMinimizeToTray").toBool() && !fHideTrayIcon;
diff --git a/src/qt/qvalidatedlineedit.h b/src/qt/qvalidatedlineedit.h
index 66734cc9d4..9ad9e66ae2 100644
--- a/src/qt/qvalidatedlineedit.h
+++ b/src/qt/qvalidatedlineedit.h
@@ -34,7 +34,7 @@ public Q_SLOTS:
Q_SIGNALS:
void validationDidChange(QValidatedLineEdit *validatedLineEdit);
-
+
private Q_SLOTS:
void markValid();
void checkValidity();
diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp
index e458a52856..8c430e0af1 100644
--- a/src/qt/receivecoinsdialog.cpp
+++ b/src/qt/receivecoinsdialog.cpp
@@ -99,6 +99,9 @@ void ReceiveCoinsDialog::setModel(WalletModel *_model)
} else {
ui->useBech32->setCheckState(Qt::Unchecked);
}
+
+ // eventually disable the main receive button if private key operations are disabled
+ ui->receiveButton->setEnabled(!model->privateKeysDisabled());
}
}
diff --git a/src/qt/trafficgraphwidget.cpp b/src/qt/trafficgraphwidget.cpp
index 5a3b645f65..a5551ec014 100644
--- a/src/qt/trafficgraphwidget.cpp
+++ b/src/qt/trafficgraphwidget.cpp
@@ -80,7 +80,7 @@ void TrafficGraphWidget::paintEvent(QPaintEvent *)
const QString units = tr("KB/s");
const float yMarginText = 2.0;
-
+
// draw lines
painter.setPen(axisCol);
painter.drawText(XMARGIN, YMARGIN + h - h * val / fMax-yMarginText, QString("%1 %2").arg(val).arg(units));
diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp
index 2cb446c459..8297f75799 100644
--- a/src/qt/transactiondesc.cpp
+++ b/src/qt/transactiondesc.cpp
@@ -37,8 +37,6 @@ QString TransactionDesc::FormatTxStatus(const interfaces::WalletTx& wtx, const i
int nDepth = status.depth_in_main_chain;
if (nDepth < 0)
return tr("conflicted with a transaction with %1 confirmations").arg(-nDepth);
- else if (adjustedTime - status.time_received > 2 * 60 && status.request_count == 0)
- return tr("%1/offline").arg(nDepth);
else if (nDepth == 0)
return tr("0/unconfirmed, %1").arg((inMempool ? tr("in memory pool") : tr("not in memory pool"))) + (status.is_abandoned ? ", "+tr("abandoned") : "");
else if (nDepth < 6)
@@ -68,14 +66,6 @@ QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wall
CAmount nNet = nCredit - nDebit;
strHTML += "<b>" + tr("Status") + ":</b> " + FormatTxStatus(wtx, status, inMempool, numBlocks, adjustedTime);
- int nRequests = status.request_count;
- if (nRequests != -1)
- {
- if (nRequests == 0)
- strHTML += tr(", has not been successfully broadcast yet");
- else if (nRequests > 0)
- strHTML += tr(", broadcast through %n node(s)", "", nRequests);
- }
strHTML += "<br>";
strHTML += "<b>" + tr("Date") + ":</b> " + (nTime ? GUIUtil::dateTimeStr(nTime) : "") + "<br>";
diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp
index b6ed66ad96..65f5e87d15 100644
--- a/src/qt/transactionrecord.cpp
+++ b/src/qt/transactionrecord.cpp
@@ -195,10 +195,6 @@ void TransactionRecord::updateStatus(const interfaces::WalletTxStatus& wtx, int
if (wtx.is_in_main_chain)
{
status.matures_in = wtx.blocks_to_maturity;
-
- // Check if the block was requested by anyone
- if (adjustedTime - wtx.time_received > 2 * 60 && wtx.request_count == 0)
- status.status = TransactionStatus::MaturesWarning;
}
else
{
@@ -216,10 +212,6 @@ void TransactionRecord::updateStatus(const interfaces::WalletTxStatus& wtx, int
{
status.status = TransactionStatus::Conflicted;
}
- else if (adjustedTime - wtx.time_received > 2 * 60 && wtx.request_count == 0)
- {
- status.status = TransactionStatus::Offline;
- }
else if (status.depth == 0)
{
status.status = TransactionStatus::Unconfirmed;
diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h
index 62961434ed..a6424e74fa 100644
--- a/src/qt/transactionrecord.h
+++ b/src/qt/transactionrecord.h
@@ -25,7 +25,7 @@ class TransactionStatus
public:
TransactionStatus():
countsForBalance(false), sortKey(""),
- matures_in(0), status(Offline), depth(0), open_for(0), cur_num_blocks(-1)
+ matures_in(0), status(Unconfirmed), depth(0), open_for(0), cur_num_blocks(-1)
{ }
enum Status {
@@ -33,14 +33,12 @@ public:
/// Normal (sent/received) transactions
OpenUntilDate, /**< Transaction not yet final, waiting for date */
OpenUntilBlock, /**< Transaction not yet final, waiting for block */
- Offline, /**< Not sent to any other nodes **/
Unconfirmed, /**< Not yet mined into a block **/
Confirming, /**< Confirmed, but waiting for the recommended number of confirmations **/
Conflicted, /**< Conflicts with other transaction or mempool **/
Abandoned, /**< Abandoned from the wallet **/
/// Generated (mined) transactions
Immature, /**< Mined but waiting for maturity */
- MaturesWarning, /**< Transaction will likely not mature because no nodes have confirmed */
NotAccepted /**< Mined but not accepted */
};
diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp
index 46169a91d1..63a4afe191 100644
--- a/src/qt/transactiontablemodel.cpp
+++ b/src/qt/transactiontablemodel.cpp
@@ -286,9 +286,6 @@ QString TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) cons
case TransactionStatus::OpenUntilDate:
status = tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx->status.open_for));
break;
- case TransactionStatus::Offline:
- status = tr("Offline");
- break;
case TransactionStatus::Unconfirmed:
status = tr("Unconfirmed");
break;
@@ -307,9 +304,6 @@ QString TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) cons
case TransactionStatus::Immature:
status = tr("Immature (%1 confirmations, will be available after %2)").arg(wtx->status.depth).arg(wtx->status.depth + wtx->status.matures_in);
break;
- case TransactionStatus::MaturesWarning:
- status = tr("This block was not received by any other nodes and will probably not be accepted!");
- break;
case TransactionStatus::NotAccepted:
status = tr("Generated but not accepted");
break;
@@ -447,8 +441,6 @@ QVariant TransactionTableModel::txStatusDecoration(const TransactionRecord *wtx)
case TransactionStatus::OpenUntilBlock:
case TransactionStatus::OpenUntilDate:
return COLOR_TX_STATUS_OPENUNTILDATE;
- case TransactionStatus::Offline:
- return COLOR_TX_STATUS_OFFLINE;
case TransactionStatus::Unconfirmed:
return QIcon(":/icons/transaction_0");
case TransactionStatus::Abandoned:
@@ -471,7 +463,6 @@ QVariant TransactionTableModel::txStatusDecoration(const TransactionRecord *wtx)
int part = (wtx->status.depth * 4 / total) + 1;
return QIcon(QString(":/icons/transaction_%1").arg(part));
}
- case TransactionStatus::MaturesWarning:
case TransactionStatus::NotAccepted:
return QIcon(":/icons/transaction_0");
default:
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index 389acf0a95..cd55b40b71 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -558,6 +558,11 @@ bool WalletModel::isWalletEnabled()
return !gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET);
}
+bool WalletModel::privateKeysDisabled() const
+{
+ return m_wallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
+}
+
QString WalletModel::getWalletName() const
{
return QString::fromStdString(m_wallet->getWalletName());
diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h
index 35ededb121..d8935c2fa8 100644
--- a/src/qt/walletmodel.h
+++ b/src/qt/walletmodel.h
@@ -197,6 +197,7 @@ public:
bool bumpFee(uint256 hash);
static bool isWalletEnabled();
+ bool privateKeysDisabled() const;
interfaces::Node& node() const { return m_node; }
interfaces::Wallet& wallet() const { return *m_wallet; }
diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp
index d529595dec..c53a2910e5 100644
--- a/src/qt/walletview.cpp
+++ b/src/qt/walletview.cpp
@@ -107,7 +107,7 @@ void WalletView::setBitcoinGUI(BitcoinGUI *gui)
// Pass through transaction notifications
connect(this, SIGNAL(incomingTransaction(QString,int,CAmount,QString,QString,QString,QString)), gui, SLOT(incomingTransaction(QString,int,CAmount,QString,QString,QString,QString)));
- // Connect HD enabled state signal
+ // Connect HD enabled state signal
connect(this, SIGNAL(hdEnabledStatusChanged()), gui, SLOT(updateWalletStatus()));
}
}
diff --git a/src/reverse_iterator.h b/src/reverse_iterator.h
index ab467f07c9..729d8c11cc 100644
--- a/src/reverse_iterator.h
+++ b/src/reverse_iterator.h
@@ -5,7 +5,7 @@
/**
* Template used for reverse iteration in C++11 range-based for loops.
- *
+ *
* std::vector<int> v = {1, 2, 3, 4, 5};
* for (auto x : reverse_iterate(v))
* std::cout << x << " ";
@@ -15,21 +15,21 @@ template <typename T>
class reverse_range
{
T &m_x;
-
+
public:
explicit reverse_range(T &x) : m_x(x) {}
-
+
auto begin() const -> decltype(this->m_x.rbegin())
{
return m_x.rbegin();
}
-
+
auto end() const -> decltype(this->m_x.rend())
{
return m_x.rend();
}
};
-
+
template <typename T>
reverse_range<T> reverse_iterate(T &x)
{
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index d9d803ac7d..012e3e3ac1 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -6,6 +6,8 @@
#include <rpc/blockchain.h>
#include <amount.h>
+#include <base58.h>
+#include <chain.h>
#include <chainparams.h>
#include <checkpoints.h>
#include <coins.h>
@@ -13,6 +15,7 @@
#include <validation.h>
#include <core_io.h>
#include <index/txindex.h>
+#include <key_io.h>
#include <policy/feerate.h>
#include <policy/policy.h>
#include <primitives/transaction.h>
@@ -27,6 +30,7 @@
#include <validationinterface.h>
#include <warnings.h>
+#include <assert.h>
#include <stdint.h>
#include <univalue.h>
@@ -1920,6 +1924,290 @@ static UniValue savemempool(const JSONRPCRequest& request)
return NullUniValue;
}
+//! Search for a given set of pubkey scripts
+bool FindScriptPubKey(std::atomic<int>& scan_progress, const std::atomic<bool>& should_abort, int64_t& count, CCoinsViewCursor* cursor, const std::set<CScript>& needles, std::map<COutPoint, Coin>& out_results) {
+ scan_progress = 0;
+ count = 0;
+ while (cursor->Valid()) {
+ COutPoint key;
+ Coin coin;
+ if (!cursor->GetKey(key) || !cursor->GetValue(coin)) return false;
+ if (++count % 8192 == 0) {
+ boost::this_thread::interruption_point();
+ if (should_abort) {
+ // allow to abort the scan via the abort reference
+ return false;
+ }
+ }
+ if (count % 256 == 0) {
+ // update progress reference every 256 item
+ uint32_t high = 0x100 * *key.hash.begin() + *(key.hash.begin() + 1);
+ scan_progress = (int)(high * 100.0 / 65536.0 + 0.5);
+ }
+ if (needles.count(coin.out.scriptPubKey)) {
+ out_results.emplace(key, coin);
+ }
+ cursor->Next();
+ }
+ scan_progress = 100;
+ return true;
+}
+
+/** RAII object to prevent concurrency issue when scanning the txout set */
+static std::mutex g_utxosetscan;
+static std::atomic<int> g_scan_progress;
+static std::atomic<bool> g_scan_in_progress;
+static std::atomic<bool> g_should_abort_scan;
+class CoinsViewScanReserver
+{
+private:
+ bool m_could_reserve;
+public:
+ explicit CoinsViewScanReserver() : m_could_reserve(false) {}
+
+ bool reserve() {
+ assert (!m_could_reserve);
+ std::lock_guard<std::mutex> lock(g_utxosetscan);
+ if (g_scan_in_progress) {
+ return false;
+ }
+ g_scan_in_progress = true;
+ m_could_reserve = true;
+ return true;
+ }
+
+ ~CoinsViewScanReserver() {
+ if (m_could_reserve) {
+ std::lock_guard<std::mutex> lock(g_utxosetscan);
+ g_scan_in_progress = false;
+ }
+ }
+};
+
+static const char *g_default_scantxoutset_script_types[] = { "P2PKH", "P2SH_P2WPKH", "P2WPKH" };
+
+enum class OutputScriptType {
+ UNKNOWN,
+ P2PK,
+ P2PKH,
+ P2SH_P2WPKH,
+ P2WPKH
+};
+
+static inline OutputScriptType GetOutputScriptTypeFromString(const std::string& outputtype)
+{
+ if (outputtype == "P2PK") return OutputScriptType::P2PK;
+ else if (outputtype == "P2PKH") return OutputScriptType::P2PKH;
+ else if (outputtype == "P2SH_P2WPKH") return OutputScriptType::P2SH_P2WPKH;
+ else if (outputtype == "P2WPKH") return OutputScriptType::P2WPKH;
+ else return OutputScriptType::UNKNOWN;
+}
+
+CTxDestination GetDestinationForKey(const CPubKey& key, OutputScriptType type)
+{
+ switch (type) {
+ case OutputScriptType::P2PKH: return key.GetID();
+ case OutputScriptType::P2SH_P2WPKH:
+ case OutputScriptType::P2WPKH: {
+ if (!key.IsCompressed()) return key.GetID();
+ CTxDestination witdest = WitnessV0KeyHash(key.GetID());
+ if (type == OutputScriptType::P2SH_P2WPKH) {
+ CScript witprog = GetScriptForDestination(witdest);
+ return CScriptID(witprog);
+ } else {
+ return witdest;
+ }
+ }
+ default: assert(false);
+ }
+}
+
+UniValue scantxoutset(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
+ throw std::runtime_error(
+ "scantxoutset <action> ( <scanobjects> )\n"
+ "\nScans the unspent transaction output set for possible entries that matches common scripts of given public keys.\n"
+ "Using addresses as scanobjects will _not_ detect unspent P2PK txouts\n"
+ "\nArguments:\n"
+ "1. \"action\" (string, required) The action to execute\n"
+ " \"start\" for starting a scan\n"
+ " \"abort\" for aborting the current scan (returns true when abort was successful)\n"
+ " \"status\" for progress report (in %) of the current scan\n"
+ "2. \"scanobjects\" (array, optional) Array of scan objects (only one object type per scan object allowed)\n"
+ " [\n"
+ " { \"address\" : \"<address>\" }, (string, optional) Bitcoin address\n"
+ " { \"script\" : \"<scriptPubKey>\" }, (string, optional) HEX encoded script (scriptPubKey)\n"
+ " { \"pubkey\" : (object, optional) Public key\n"
+ " {\n"
+ " \"pubkey\" : \"<pubkey\">, (string, required) HEX encoded public key\n"
+ " \"script_types\" : [ ... ], (array, optional) Array of script-types to derive from the pubkey (possible values: \"P2PK\", \"P2PKH\", \"P2SH-P2WPKH\", \"P2WPKH\")\n"
+ " }\n"
+ " },\n"
+ " ]\n"
+ "\nResult:\n"
+ "{\n"
+ " \"unspents\": [\n"
+ " {\n"
+ " \"txid\" : \"transactionid\", (string) The transaction id\n"
+ " \"vout\": n, (numeric) the vout value\n"
+ " \"scriptPubKey\" : \"script\", (string) the script key\n"
+ " \"amount\" : x.xxx, (numeric) The total amount in " + CURRENCY_UNIT + " of the unspent output\n"
+ " \"height\" : n, (numeric) Height of the unspent transaction output\n"
+ " }\n"
+ " ,...], \n"
+ " \"total_amount\" : x.xxx, (numeric) The total amount of all found unspent outputs in " + CURRENCY_UNIT + "\n"
+ "]\n"
+ );
+
+ RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR});
+
+ UniValue result(UniValue::VOBJ);
+ if (request.params[0].get_str() == "status") {
+ CoinsViewScanReserver reserver;
+ if (reserver.reserve()) {
+ // no scan in progress
+ return NullUniValue;
+ }
+ result.pushKV("progress", g_scan_progress);
+ return result;
+ } else if (request.params[0].get_str() == "abort") {
+ CoinsViewScanReserver reserver;
+ if (reserver.reserve()) {
+ // reserve was possible which means no scan was running
+ return false;
+ }
+ // set the abort flag
+ g_should_abort_scan = true;
+ return true;
+ } else if (request.params[0].get_str() == "start") {
+ CoinsViewScanReserver reserver;
+ if (!reserver.reserve()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan already in progress, use action \"abort\" or \"status\"");
+ }
+ std::set<CScript> needles;
+ CAmount total_in = 0;
+
+ // loop through the scan objects
+ for (const UniValue& scanobject : request.params[1].get_array().getValues()) {
+ if (!scanobject.isObject()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid scan object");
+ }
+ UniValue address_uni = find_value(scanobject, "address");
+ UniValue pubkey_uni = find_value(scanobject, "pubkey");
+ UniValue script_uni = find_value(scanobject, "script");
+
+ // make sure only one object type is present
+ if (1 != !address_uni.isNull() + !pubkey_uni.isNull() + !script_uni.isNull()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Only one object type is allowed per scan object");
+ } else if (!address_uni.isNull() && !address_uni.isStr()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Scanobject \"address\" must contain a single string as value");
+ } else if (!pubkey_uni.isNull() && !pubkey_uni.isObject()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Scanobject \"pubkey\" must contain an object as value");
+ } else if (!script_uni.isNull() && !script_uni.isStr()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Scanobject \"script\" must contain a single string as value");
+ } else if (address_uni.isStr()) {
+ // type: address
+ // decode destination and derive the scriptPubKey
+ // add the script to the scan containers
+ CTxDestination dest = DecodeDestination(address_uni.get_str());
+ if (!IsValidDestination(dest)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
+ }
+ CScript script = GetScriptForDestination(dest);
+ assert(!script.empty());
+ needles.insert(script);
+ } else if (pubkey_uni.isObject()) {
+ // type: pubkey
+ // derive script(s) according to the script_type parameter
+ UniValue script_types_uni = find_value(pubkey_uni, "script_types");
+ UniValue pubkeydata_uni = find_value(pubkey_uni, "pubkey");
+
+ // check the script types and use the default if not provided
+ if (!script_types_uni.isNull() && !script_types_uni.isArray()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "script_types must be an array");
+ } else if (script_types_uni.isNull()) {
+ // use the default script types
+ script_types_uni = UniValue(UniValue::VARR);
+ for (const char *t : g_default_scantxoutset_script_types) {
+ script_types_uni.push_back(t);
+ }
+ }
+
+ // check the acctual pubkey
+ if (!pubkeydata_uni.isStr() || !IsHex(pubkeydata_uni.get_str())) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Public key must be hex encoded");
+ }
+ CPubKey pubkey(ParseHexV(pubkeydata_uni, "pubkey"));
+ if (!pubkey.IsFullyValid()) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid public key");
+ }
+
+ // loop through the script types and derive the script
+ for (const UniValue& script_type_uni : script_types_uni.get_array().getValues()) {
+ OutputScriptType script_type = GetOutputScriptTypeFromString(script_type_uni.get_str());
+ if (script_type == OutputScriptType::UNKNOWN) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid script type");
+ CScript script;
+ if (script_type == OutputScriptType::P2PK) {
+ // support legacy P2PK scripts
+ script << ToByteVector(pubkey) << OP_CHECKSIG;
+ } else {
+ script = GetScriptForDestination(GetDestinationForKey(pubkey, script_type));
+ }
+ assert(!script.empty());
+ needles.insert(script);
+ }
+ } else if (script_uni.isStr()) {
+ // type: script
+ // check and add the script to the scan containers (needles array)
+ CScript script(ParseHexV(script_uni, "script"));
+ // TODO: check script: max length, has OP, is unspenable etc.
+ needles.insert(script);
+ }
+ }
+
+ // Scan the unspent transaction output set for inputs
+ UniValue unspents(UniValue::VARR);
+ std::vector<CTxOut> input_txos;
+ std::map<COutPoint, Coin> coins;
+ g_should_abort_scan = false;
+ g_scan_progress = 0;
+ int64_t count = 0;
+ std::unique_ptr<CCoinsViewCursor> pcursor;
+ {
+ LOCK(cs_main);
+ FlushStateToDisk();
+ pcursor = std::unique_ptr<CCoinsViewCursor>(pcoinsdbview->Cursor());
+ assert(pcursor);
+ }
+ bool res = FindScriptPubKey(g_scan_progress, g_should_abort_scan, count, pcursor.get(), needles, coins);
+ result.pushKV("success", res);
+ result.pushKV("searched_items", count);
+
+ for (const auto& it : coins) {
+ const COutPoint& outpoint = it.first;
+ const Coin& coin = it.second;
+ const CTxOut& txo = coin.out;
+ input_txos.push_back(txo);
+ total_in += txo.nValue;
+
+ UniValue unspent(UniValue::VOBJ);
+ unspent.pushKV("txid", outpoint.hash.GetHex());
+ unspent.pushKV("vout", (int32_t)outpoint.n);
+ unspent.pushKV("scriptPubKey", HexStr(txo.scriptPubKey.begin(), txo.scriptPubKey.end()));
+ unspent.pushKV("amount", ValueFromAmount(txo.nValue));
+ unspent.pushKV("height", (int32_t)coin.nHeight);
+
+ unspents.push_back(unspent);
+ }
+ result.pushKV("unspents", unspents);
+ result.pushKV("total_amount", ValueFromAmount(total_in));
+ } else {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid command");
+ }
+ return result;
+}
+
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
// --------------------- ------------------------ ----------------------- ----------
@@ -1945,6 +2233,7 @@ static const CRPCCommand commands[] =
{ "blockchain", "verifychain", &verifychain, {"checklevel","nblocks"} },
{ "blockchain", "preciousblock", &preciousblock, {"blockhash"} },
+ { "blockchain", "scantxoutset", &scantxoutset, {"action", "scanobjects"} },
/* Not shown in help */
{ "hidden", "invalidateblock", &invalidateblock, {"blockhash"} },
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index 0f35fd3770..eb6b164075 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -78,6 +78,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "sendmany", 4, "subtractfeefrom" },
{ "sendmany", 5 , "replaceable" },
{ "sendmany", 6 , "conf_target" },
+ { "scantxoutset", 1, "scanobjects" },
{ "addmultisigaddress", 0, "nrequired" },
{ "addmultisigaddress", 1, "keys" },
{ "createmultisig", 0, "nrequired" },
@@ -109,6 +110,22 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "combinerawtransaction", 0, "txs" },
{ "fundrawtransaction", 1, "options" },
{ "fundrawtransaction", 2, "iswitness" },
+ { "walletcreatefundedpsbt", 0, "inputs" },
+ { "walletcreatefundedpsbt", 1, "outputs" },
+ { "walletcreatefundedpsbt", 2, "locktime" },
+ { "walletcreatefundedpsbt", 3, "replaceable" },
+ { "walletcreatefundedpsbt", 4, "options" },
+ { "walletcreatefundedpsbt", 5, "bip32derivs" },
+ { "walletprocesspsbt", 1, "sign" },
+ { "walletprocesspsbt", 3, "bip32derivs" },
+ { "createpsbt", 0, "inputs" },
+ { "createpsbt", 1, "outputs" },
+ { "createpsbt", 2, "locktime" },
+ { "createpsbt", 3, "replaceable" },
+ { "combinepsbt", 0, "txs"},
+ { "finalizepsbt", 1, "extract"},
+ { "converttopsbt", 1, "permitsigdata"},
+ { "converttopsbt", 2, "iswitness"},
{ "gettxout", 1, "n" },
{ "gettxout", 2, "include_mempool" },
{ "gettxoutproof", 0, "txids" },
@@ -155,6 +172,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "echojson", 9, "arg9" },
{ "rescanblockchain", 0, "start_height"},
{ "rescanblockchain", 1, "stop_height"},
+ { "createwallet", 1, "disable_private_keys"},
};
class CRPCConvertTable
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 1f1044d80b..e751587dc7 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -337,7 +337,6 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
" \"fee\": n, (numeric) difference in value between transaction inputs and outputs (in satoshis); for coinbase transactions, this is a negative Number of the total collected block fees (ie, not including the block subsidy); if key is not present, fee is unknown and clients MUST NOT assume there isn't one\n"
" \"sigops\" : n, (numeric) total SigOps cost, as counted for purposes of block limits; if key is not present, sigop cost is unknown and clients MUST NOT assume it is zero\n"
" \"weight\" : n, (numeric) total transaction weight, as counted for purposes of block limits\n"
- " \"required\" : true|false (boolean) if provided and true, this transaction must be in the final block\n"
" }\n"
" ,...\n"
" ],\n"
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index 4eeb7f29d2..09812bb980 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -12,6 +12,7 @@
#include <httpserver.h>
#include <net.h>
#include <netbase.h>
+#include <outputtype.h>
#include <rpc/blockchain.h>
#include <rpc/server.h>
#include <rpc/util.h>
@@ -91,9 +92,9 @@ class CWallet;
static UniValue createmultisig(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() < 2 || request.params.size() > 2)
+ if (request.fHelp || request.params.size() < 2 || request.params.size() > 3)
{
- std::string msg = "createmultisig nrequired [\"key\",...]\n"
+ std::string msg = "createmultisig nrequired [\"key\",...] ( \"address_type\" )\n"
"\nCreates a multi-signature address with n signature of m keys required.\n"
"It returns a json object with the address and redeemScript.\n"
"\nArguments:\n"
@@ -103,6 +104,7 @@ static UniValue createmultisig(const JSONRPCRequest& request)
" \"key\" (string) The hex-encoded public key\n"
" ,...\n"
" ]\n"
+ "3. \"address_type\" (string, optional) The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\". Default is legacy.\n"
"\nResult:\n"
"{\n"
@@ -133,12 +135,21 @@ static UniValue createmultisig(const JSONRPCRequest& request)
}
}
+ // Get the output type
+ OutputType output_type = OutputType::LEGACY;
+ if (!request.params[2].isNull()) {
+ if (!ParseOutputType(request.params[2].get_str(), output_type)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[2].get_str()));
+ }
+ }
+
// Construct using pay-to-script-hash:
- CScript inner = CreateMultisigRedeemscript(required, pubkeys);
- CScriptID innerID(inner);
+ const CScript inner = CreateMultisigRedeemscript(required, pubkeys);
+ CBasicKeyStore keystore;
+ const CTxDestination dest = AddAndGetDestinationForScript(keystore, inner, output_type);
UniValue result(UniValue::VOBJ);
- result.pushKV("address", EncodeDestination(innerID));
+ result.pushKV("address", EncodeDestination(dest));
result.pushKV("redeemScript", HexStr(inner.begin(), inner.end()));
return result;
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 499b0c5e16..bb94e11fea 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -5,6 +5,7 @@
#include <chain.h>
#include <coins.h>
+#include <compat/byteswap.h>
#include <consensus/validation.h>
#include <core_io.h>
#include <index/txindex.h>
@@ -337,80 +338,25 @@ static UniValue verifytxoutproof(const JSONRPCRequest& request)
return res;
}
-static UniValue createrawtransaction(const JSONRPCRequest& request)
+CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf)
{
- if (request.fHelp || request.params.size() < 2 || request.params.size() > 4) {
- throw std::runtime_error(
- // clang-format off
- "createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] [{\"address\":amount},{\"data\":\"hex\"},...] ( locktime ) ( replaceable )\n"
- "\nCreate a transaction spending the given inputs and creating new outputs.\n"
- "Outputs can be addresses or data.\n"
- "Returns hex-encoded raw transaction.\n"
- "Note that the transaction's inputs are not signed, and\n"
- "it is not stored in the wallet or transmitted to the network.\n"
-
- "\nArguments:\n"
- "1. \"inputs\" (array, required) A json array of json objects\n"
- " [\n"
- " {\n"
- " \"txid\":\"id\", (string, required) The transaction id\n"
- " \"vout\":n, (numeric, required) The output number\n"
- " \"sequence\":n (numeric, optional) The sequence number\n"
- " } \n"
- " ,...\n"
- " ]\n"
- "2. \"outputs\" (array, required) a json array with outputs (key-value pairs)\n"
- " [\n"
- " {\n"
- " \"address\": x.xxx, (obj, optional) A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT + "\n"
- " },\n"
- " {\n"
- " \"data\": \"hex\" (obj, optional) A key-value pair. The key must be \"data\", the value is hex encoded data\n"
- " }\n"
- " ,... More key-value pairs of the above form. For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n"
- " accepted as second parameter.\n"
- " ]\n"
- "3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n"
- "4. replaceable (boolean, optional, default=false) Marks this transaction as BIP125 replaceable.\n"
- " Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible.\n"
- "\nResult:\n"
- "\"transaction\" (string) hex string of the transaction\n"
-
- "\nExamples:\n"
- + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"address\\\":0.01}]\"")
- + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"data\\\":\\\"00010203\\\"}]\"")
- + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"[{\\\"address\\\":0.01}]\"")
- + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"[{\\\"data\\\":\\\"00010203\\\"}]\"")
- // clang-format on
- );
- }
-
- RPCTypeCheck(request.params, {
- UniValue::VARR,
- UniValueType(), // ARR or OBJ, checked later
- UniValue::VNUM,
- UniValue::VBOOL
- }, true
- );
- if (request.params[0].isNull() || request.params[1].isNull())
+ if (inputs_in.isNull() || outputs_in.isNull())
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, arguments 1 and 2 must be non-null");
- UniValue inputs = request.params[0].get_array();
- const bool outputs_is_obj = request.params[1].isObject();
- UniValue outputs = outputs_is_obj ?
- request.params[1].get_obj() :
- request.params[1].get_array();
+ UniValue inputs = inputs_in.get_array();
+ const bool outputs_is_obj = outputs_in.isObject();
+ UniValue outputs = outputs_is_obj ? outputs_in.get_obj() : outputs_in.get_array();
CMutableTransaction rawTx;
- if (!request.params[2].isNull()) {
- int64_t nLockTime = request.params[2].get_int64();
+ if (!locktime.isNull()) {
+ int64_t nLockTime = locktime.get_int64();
if (nLockTime < 0 || nLockTime > std::numeric_limits<uint32_t>::max())
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, locktime out of range");
rawTx.nLockTime = nLockTime;
}
- bool rbfOptIn = request.params[3].isTrue();
+ bool rbfOptIn = rbf.isTrue();
for (unsigned int idx = 0; idx < inputs.size(); idx++) {
const UniValue& input = inputs[idx];
@@ -490,10 +436,71 @@ static UniValue createrawtransaction(const JSONRPCRequest& request)
}
}
- if (!request.params[3].isNull() && rbfOptIn != SignalsOptInRBF(rawTx)) {
+ if (!rbf.isNull() && rbfOptIn != SignalsOptInRBF(rawTx)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter combination: Sequence number(s) contradict replaceable option");
}
+ return rawTx;
+}
+
+static UniValue createrawtransaction(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() < 2 || request.params.size() > 4) {
+ throw std::runtime_error(
+ // clang-format off
+ "createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] [{\"address\":amount},{\"data\":\"hex\"},...] ( locktime ) ( replaceable )\n"
+ "\nCreate a transaction spending the given inputs and creating new outputs.\n"
+ "Outputs can be addresses or data.\n"
+ "Returns hex-encoded raw transaction.\n"
+ "Note that the transaction's inputs are not signed, and\n"
+ "it is not stored in the wallet or transmitted to the network.\n"
+
+ "\nArguments:\n"
+ "1. \"inputs\" (array, required) A json array of json objects\n"
+ " [\n"
+ " {\n"
+ " \"txid\":\"id\", (string, required) The transaction id\n"
+ " \"vout\":n, (numeric, required) The output number\n"
+ " \"sequence\":n (numeric, optional) The sequence number\n"
+ " } \n"
+ " ,...\n"
+ " ]\n"
+ "2. \"outputs\" (array, required) a json array with outputs (key-value pairs)\n"
+ " [\n"
+ " {\n"
+ " \"address\": x.xxx, (obj, optional) A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT + "\n"
+ " },\n"
+ " {\n"
+ " \"data\": \"hex\" (obj, optional) A key-value pair. The key must be \"data\", the value is hex encoded data\n"
+ " }\n"
+ " ,... More key-value pairs of the above form. For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n"
+ " accepted as second parameter.\n"
+ " ]\n"
+ "3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n"
+ "4. replaceable (boolean, optional, default=false) Marks this transaction as BIP125 replaceable.\n"
+ " Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible.\n"
+ "\nResult:\n"
+ "\"transaction\" (string) hex string of the transaction\n"
+
+ "\nExamples:\n"
+ + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"address\\\":0.01}]\"")
+ + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"data\\\":\\\"00010203\\\"}]\"")
+ + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"[{\\\"address\\\":0.01}]\"")
+ + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"[{\\\"data\\\":\\\"00010203\\\"}]\"")
+ // clang-format on
+ );
+ }
+
+ RPCTypeCheck(request.params, {
+ UniValue::VARR,
+ UniValueType(), // ARR or OBJ, checked later
+ UniValue::VNUM,
+ UniValue::VBOOL
+ }, true
+ );
+
+ CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], request.params[3]);
+
return EncodeHexTx(rawTx);
}
@@ -838,23 +845,7 @@ UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival
}
}
- int nHashType = SIGHASH_ALL;
- if (!hashType.isNull()) {
- static std::map<std::string, int> mapSigHashValues = {
- {std::string("ALL"), int(SIGHASH_ALL)},
- {std::string("ALL|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_ANYONECANPAY)},
- {std::string("NONE"), int(SIGHASH_NONE)},
- {std::string("NONE|ANYONECANPAY"), int(SIGHASH_NONE|SIGHASH_ANYONECANPAY)},
- {std::string("SINGLE"), int(SIGHASH_SINGLE)},
- {std::string("SINGLE|ANYONECANPAY"), int(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY)},
- };
- std::string strHashType = hashType.get_str();
- if (mapSigHashValues.count(strHashType)) {
- nHashType = mapSigHashValues[strHashType];
- } else {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid sighash param");
- }
- }
+ int nHashType = ParseSighashString(hashType);
bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
@@ -1263,6 +1254,553 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
return result;
}
+static std::string WriteHDKeypath(std::vector<uint32_t>& keypath)
+{
+ std::string keypath_str = "m";
+ for (uint32_t num : keypath) {
+ keypath_str += "/";
+ bool hardened = false;
+ if (num & 0x80000000) {
+ hardened = true;
+ num &= ~0x80000000;
+ }
+
+ keypath_str += std::to_string(num);
+ if (hardened) {
+ keypath_str += "'";
+ }
+ }
+ return keypath_str;
+}
+
+UniValue decodepsbt(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 1)
+ throw std::runtime_error(
+ "decodepsbt \"psbt\"\n"
+ "\nReturn a JSON object representing the serialized, base64-encoded partially signed Bitcoin transaction.\n"
+
+ "\nArguments:\n"
+ "1. \"psbt\" (string, required) The PSBT base64 string\n"
+
+ "\nResult:\n"
+ "{\n"
+ " \"tx\" : { (json object) The decoded network-serialized unsigned transaction.\n"
+ " ... The layout is the same as the output of decoderawtransaction.\n"
+ " },\n"
+ " \"unknown\" : { (json object) The unknown global fields\n"
+ " \"key\" : \"value\" (key-value pair) An unknown key-value pair\n"
+ " ...\n"
+ " },\n"
+ " \"inputs\" : [ (array of json objects)\n"
+ " {\n"
+ " \"non_witness_utxo\" : { (json object, optional) Decoded network transaction for non-witness UTXOs\n"
+ " ...\n"
+ " },\n"
+ " \"witness_utxo\" : { (json object, optional) Transaction output for witness UTXOs\n"
+ " \"amount\" : x.xxx, (numeric) The value in " + CURRENCY_UNIT + "\n"
+ " \"scriptPubKey\" : { (json object)\n"
+ " \"asm\" : \"asm\", (string) The asm\n"
+ " \"hex\" : \"hex\", (string) The hex\n"
+ " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n"
+ " \"address\" : \"address\" (string) Bitcoin address if there is one\n"
+ " }\n"
+ " },\n"
+ " \"partial_signatures\" : { (json object, optional)\n"
+ " \"pubkey\" : \"signature\", (string) The public key and signature that corresponds to it.\n"
+ " ,...\n"
+ " }\n"
+ " \"sighash\" : \"type\", (string, optional) The sighash type to be used\n"
+ " \"redeem_script\" : { (json object, optional)\n"
+ " \"asm\" : \"asm\", (string) The asm\n"
+ " \"hex\" : \"hex\", (string) The hex\n"
+ " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n"
+ " }\n"
+ " \"witness_script\" : { (json object, optional)\n"
+ " \"asm\" : \"asm\", (string) The asm\n"
+ " \"hex\" : \"hex\", (string) The hex\n"
+ " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n"
+ " }\n"
+ " \"bip32_derivs\" : { (json object, optional)\n"
+ " \"pubkey\" : { (json object, optional) The public key with the derivation path as the value.\n"
+ " \"master_fingerprint\" : \"fingerprint\" (string) The fingerprint of the master key\n"
+ " \"path\" : \"path\", (string) The path\n"
+ " }\n"
+ " ,...\n"
+ " }\n"
+ " \"final_scriptsig\" : { (json object, optional)\n"
+ " \"asm\" : \"asm\", (string) The asm\n"
+ " \"hex\" : \"hex\", (string) The hex\n"
+ " }\n"
+ " \"final_scriptwitness\": [\"hex\", ...] (array of string) hex-encoded witness data (if any)\n"
+ " \"unknown\" : { (json object) The unknown global fields\n"
+ " \"key\" : \"value\" (key-value pair) An unknown key-value pair\n"
+ " ...\n"
+ " },\n"
+ " }\n"
+ " ,...\n"
+ " ]\n"
+ " \"outputs\" : [ (array of json objects)\n"
+ " {\n"
+ " \"redeem_script\" : { (json object, optional)\n"
+ " \"asm\" : \"asm\", (string) The asm\n"
+ " \"hex\" : \"hex\", (string) The hex\n"
+ " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n"
+ " }\n"
+ " \"witness_script\" : { (json object, optional)\n"
+ " \"asm\" : \"asm\", (string) The asm\n"
+ " \"hex\" : \"hex\", (string) The hex\n"
+ " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n"
+ " }\n"
+ " \"bip32_derivs\" : [ (array of json objects, optional)\n"
+ " {\n"
+ " \"pubkey\" : \"pubkey\", (string) The public key this path corresponds to\n"
+ " \"master_fingerprint\" : \"fingerprint\" (string) The fingerprint of the master key\n"
+ " \"path\" : \"path\", (string) The path\n"
+ " }\n"
+ " }\n"
+ " ,...\n"
+ " ],\n"
+ " \"unknown\" : { (json object) The unknown global fields\n"
+ " \"key\" : \"value\" (key-value pair) An unknown key-value pair\n"
+ " ...\n"
+ " },\n"
+ " }\n"
+ " ,...\n"
+ " ]\n"
+ " \"fee\" : fee (numeric, optional) The transaction fee paid if all UTXOs slots in the PSBT have been filled.\n"
+ "}\n"
+
+ "\nExamples:\n"
+ + HelpExampleCli("decodepsbt", "\"psbt\"")
+ );
+
+ RPCTypeCheck(request.params, {UniValue::VSTR});
+
+ // Unserialize the transactions
+ PartiallySignedTransaction psbtx;
+ std::string error;
+ if (!DecodePSBT(psbtx, request.params[0].get_str(), error)) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
+ }
+
+ UniValue result(UniValue::VOBJ);
+
+ // Add the decoded tx
+ UniValue tx_univ(UniValue::VOBJ);
+ TxToUniv(CTransaction(*psbtx.tx), uint256(), tx_univ, false);
+ result.pushKV("tx", tx_univ);
+
+ // Unknown data
+ UniValue unknowns(UniValue::VOBJ);
+ for (auto entry : psbtx.unknown) {
+ unknowns.pushKV(HexStr(entry.first), HexStr(entry.second));
+ }
+ result.pushKV("unknown", unknowns);
+
+ // inputs
+ CAmount total_in = 0;
+ bool have_all_utxos = true;
+ UniValue inputs(UniValue::VARR);
+ for (unsigned int i = 0; i < psbtx.inputs.size(); ++i) {
+ const PSBTInput& input = psbtx.inputs[i];
+ UniValue in(UniValue::VOBJ);
+ // UTXOs
+ if (!input.witness_utxo.IsNull()) {
+ const CTxOut& txout = input.witness_utxo;
+
+ UniValue out(UniValue::VOBJ);
+
+ out.pushKV("amount", ValueFromAmount(txout.nValue));
+ total_in += txout.nValue;
+
+ UniValue o(UniValue::VOBJ);
+ ScriptToUniv(txout.scriptPubKey, o, true);
+ out.pushKV("scriptPubKey", o);
+ in.pushKV("witness_utxo", out);
+ } else if (input.non_witness_utxo) {
+ UniValue non_wit(UniValue::VOBJ);
+ TxToUniv(*input.non_witness_utxo, uint256(), non_wit, false);
+ in.pushKV("non_witness_utxo", non_wit);
+ total_in += input.non_witness_utxo->vout[psbtx.tx->vin[i].prevout.n].nValue;
+ } else {
+ have_all_utxos = false;
+ }
+
+ // Partial sigs
+ if (!input.partial_sigs.empty()) {
+ UniValue partial_sigs(UniValue::VOBJ);
+ for (const auto& sig : input.partial_sigs) {
+ partial_sigs.pushKV(HexStr(sig.second.first), HexStr(sig.second.second));
+ }
+ in.pushKV("partial_signatures", partial_sigs);
+ }
+
+ // Sighash
+ if (input.sighash_type > 0) {
+ in.pushKV("sighash", SighashToStr((unsigned char)input.sighash_type));
+ }
+
+ // Redeem script and witness script
+ if (!input.redeem_script.empty()) {
+ UniValue r(UniValue::VOBJ);
+ ScriptToUniv(input.redeem_script, r, false);
+ in.pushKV("redeem_script", r);
+ }
+ if (!input.witness_script.empty()) {
+ UniValue r(UniValue::VOBJ);
+ ScriptToUniv(input.witness_script, r, false);
+ in.pushKV("witness_script", r);
+ }
+
+ // keypaths
+ if (!input.hd_keypaths.empty()) {
+ UniValue keypaths(UniValue::VARR);
+ for (auto entry : input.hd_keypaths) {
+ UniValue keypath(UniValue::VOBJ);
+ keypath.pushKV("pubkey", HexStr(entry.first));
+
+ uint32_t fingerprint = entry.second.at(0);
+ keypath.pushKV("master_fingerprint", strprintf("%08x", bswap_32(fingerprint)));
+
+ entry.second.erase(entry.second.begin());
+ keypath.pushKV("path", WriteHDKeypath(entry.second));
+ keypaths.push_back(keypath);
+ }
+ in.pushKV("bip32_derivs", keypaths);
+ }
+
+ // Final scriptSig and scriptwitness
+ if (!input.final_script_sig.empty()) {
+ UniValue scriptsig(UniValue::VOBJ);
+ scriptsig.pushKV("asm", ScriptToAsmStr(input.final_script_sig, true));
+ scriptsig.pushKV("hex", HexStr(input.final_script_sig));
+ in.pushKV("final_scriptSig", scriptsig);
+ }
+ if (!input.final_script_witness.IsNull()) {
+ UniValue txinwitness(UniValue::VARR);
+ for (const auto& item : input.final_script_witness.stack) {
+ txinwitness.push_back(HexStr(item.begin(), item.end()));
+ }
+ in.pushKV("final_scriptwitness", txinwitness);
+ }
+
+ // Unknown data
+ if (input.unknown.size() > 0) {
+ UniValue unknowns(UniValue::VOBJ);
+ for (auto entry : input.unknown) {
+ unknowns.pushKV(HexStr(entry.first), HexStr(entry.second));
+ }
+ in.pushKV("unknown", unknowns);
+ }
+
+ inputs.push_back(in);
+ }
+ result.pushKV("inputs", inputs);
+
+ // outputs
+ CAmount output_value = 0;
+ UniValue outputs(UniValue::VARR);
+ for (unsigned int i = 0; i < psbtx.outputs.size(); ++i) {
+ const PSBTOutput& output = psbtx.outputs[i];
+ UniValue out(UniValue::VOBJ);
+ // Redeem script and witness script
+ if (!output.redeem_script.empty()) {
+ UniValue r(UniValue::VOBJ);
+ ScriptToUniv(output.redeem_script, r, false);
+ out.pushKV("redeem_script", r);
+ }
+ if (!output.witness_script.empty()) {
+ UniValue r(UniValue::VOBJ);
+ ScriptToUniv(output.witness_script, r, false);
+ out.pushKV("witness_script", r);
+ }
+
+ // keypaths
+ if (!output.hd_keypaths.empty()) {
+ UniValue keypaths(UniValue::VARR);
+ for (auto entry : output.hd_keypaths) {
+ UniValue keypath(UniValue::VOBJ);
+ keypath.pushKV("pubkey", HexStr(entry.first));
+
+ uint32_t fingerprint = entry.second.at(0);
+ keypath.pushKV("master_fingerprint", strprintf("%08x", bswap_32(fingerprint)));
+
+ entry.second.erase(entry.second.begin());
+ keypath.pushKV("path", WriteHDKeypath(entry.second));
+ keypaths.push_back(keypath);
+ }
+ out.pushKV("bip32_derivs", keypaths);
+ }
+
+ // Unknown data
+ if (output.unknown.size() > 0) {
+ UniValue unknowns(UniValue::VOBJ);
+ for (auto entry : output.unknown) {
+ unknowns.pushKV(HexStr(entry.first), HexStr(entry.second));
+ }
+ out.pushKV("unknown", unknowns);
+ }
+
+ outputs.push_back(out);
+
+ // Fee calculation
+ output_value += psbtx.tx->vout[i].nValue;
+ }
+ result.pushKV("outputs", outputs);
+ if (have_all_utxos) {
+ result.pushKV("fee", ValueFromAmount(total_in - output_value));
+ }
+
+ return result;
+}
+
+UniValue combinepsbt(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 1)
+ throw std::runtime_error(
+ "combinepsbt [\"psbt\",...]\n"
+ "\nCombine multiple partially signed Bitcoin transactions into one transaction.\n"
+ "Implements the Combiner role.\n"
+ "\nArguments:\n"
+ "1. \"txs\" (string) A json array of base64 strings of partially signed transactions\n"
+ " [\n"
+ " \"psbt\" (string) A base64 string of a PSBT\n"
+ " ,...\n"
+ " ]\n"
+
+ "\nResult:\n"
+ " \"psbt\" (string) The base64-encoded partially signed transaction\n"
+ "\nExamples:\n"
+ + HelpExampleCli("combinepsbt", "[\"mybase64_1\", \"mybase64_2\", \"mybase64_3\"]")
+ );
+
+ RPCTypeCheck(request.params, {UniValue::VARR}, true);
+
+ // Unserialize the transactions
+ std::vector<PartiallySignedTransaction> psbtxs;
+ UniValue txs = request.params[0].get_array();
+ for (unsigned int i = 0; i < txs.size(); ++i) {
+ PartiallySignedTransaction psbtx;
+ std::string error;
+ if (!DecodePSBT(psbtx, txs[i].get_str(), error)) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
+ }
+ psbtxs.push_back(psbtx);
+ }
+
+ PartiallySignedTransaction merged_psbt(psbtxs[0]); // Copy the first one
+
+ // Merge
+ for (auto it = std::next(psbtxs.begin()); it != psbtxs.end(); ++it) {
+ if (*it != merged_psbt) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "PSBTs do not refer to the same transactions.");
+ }
+ merged_psbt.Merge(*it);
+ }
+ if (!merged_psbt.IsSane()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Merged PSBT is inconsistent");
+ }
+
+ UniValue result(UniValue::VOBJ);
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
+ ssTx << merged_psbt;
+ return EncodeBase64((unsigned char*)ssTx.data(), ssTx.size());
+}
+
+UniValue finalizepsbt(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
+ throw std::runtime_error(
+ "finalizepsbt \"psbt\" ( extract )\n"
+ "Finalize the inputs of a PSBT. If the transaction is fully signed, it will produce a\n"
+ "network serialized transaction which can be broadcast with sendrawtransaction. Otherwise a PSBT will be\n"
+ "created which has the final_scriptSig and final_scriptWitness fields filled for inputs that are complete.\n"
+ "Implements the Finalizer and Extractor roles.\n"
+ "\nArguments:\n"
+ "1. \"psbt\" (string) A base64 string of a PSBT\n"
+ "2. \"extract\" (boolean, optional, default=true) If true and the transaction is complete, \n"
+ " extract and return the complete transaction in normal network serialization instead of the PSBT.\n"
+
+ "\nResult:\n"
+ "{\n"
+ " \"psbt\" : \"value\", (string) The base64-encoded partially signed transaction if not extracted\n"
+ " \"hex\" : \"value\", (string) The hex-encoded network transaction if extracted\n"
+ " \"complete\" : true|false, (boolean) If the transaction has a complete set of signatures\n"
+ " ]\n"
+ "}\n"
+
+ "\nExamples:\n"
+ + HelpExampleCli("finalizepsbt", "\"psbt\"")
+ );
+
+ RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL}, true);
+
+ // Unserialize the transactions
+ PartiallySignedTransaction psbtx;
+ std::string error;
+ if (!DecodePSBT(psbtx, request.params[0].get_str(), error)) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
+ }
+
+ // Get all of the previous transactions
+ bool complete = true;
+ for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
+ PSBTInput& input = psbtx.inputs.at(i);
+
+ SignatureData sigdata;
+ complete &= SignPSBTInput(DUMMY_SIGNING_PROVIDER, *psbtx.tx, input, sigdata, i, 1);
+ }
+
+ UniValue result(UniValue::VOBJ);
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
+ bool extract = request.params[1].isNull() || (!request.params[1].isNull() && request.params[1].get_bool());
+ if (complete && extract) {
+ CMutableTransaction mtx(*psbtx.tx);
+ for (unsigned int i = 0; i < mtx.vin.size(); ++i) {
+ mtx.vin[i].scriptSig = psbtx.inputs[i].final_script_sig;
+ mtx.vin[i].scriptWitness = psbtx.inputs[i].final_script_witness;
+ }
+ ssTx << mtx;
+ result.push_back(Pair("hex", HexStr(ssTx.begin(), ssTx.end())));
+ } else {
+ ssTx << psbtx;
+ result.push_back(Pair("psbt", EncodeBase64((unsigned char*)ssTx.data(), ssTx.size())));
+ }
+ result.push_back(Pair("complete", complete));
+
+ return result;
+}
+
+UniValue createpsbt(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() < 2 || request.params.size() > 4)
+ throw std::runtime_error(
+ "createpsbt [{\"txid\":\"id\",\"vout\":n},...] [{\"address\":amount},{\"data\":\"hex\"},...] ( locktime ) ( replaceable )\n"
+ "\nCreates a transaction in the Partially Signed Transaction format.\n"
+ "Implements the Creator role.\n"
+ "\nArguments:\n"
+ "1. \"inputs\" (array, required) A json array of json objects\n"
+ " [\n"
+ " {\n"
+ " \"txid\":\"id\", (string, required) The transaction id\n"
+ " \"vout\":n, (numeric, required) The output number\n"
+ " \"sequence\":n (numeric, optional) The sequence number\n"
+ " } \n"
+ " ,...\n"
+ " ]\n"
+ "2. \"outputs\" (array, required) a json array with outputs (key-value pairs)\n"
+ " [\n"
+ " {\n"
+ " \"address\": x.xxx, (obj, optional) A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT + "\n"
+ " },\n"
+ " {\n"
+ " \"data\": \"hex\" (obj, optional) A key-value pair. The key must be \"data\", the value is hex encoded data\n"
+ " }\n"
+ " ,... More key-value pairs of the above form. For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n"
+ " accepted as second parameter.\n"
+ " ]\n"
+ "3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n"
+ "4. replaceable (boolean, optional, default=false) Marks this transaction as BIP125 replaceable.\n"
+ " Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible.\n"
+ "\nResult:\n"
+ " \"psbt\" (string) The resulting raw transaction (base64-encoded string)\n"
+ "\nExamples:\n"
+ + HelpExampleCli("createpsbt", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"data\\\":\\\"00010203\\\"}]\"")
+ );
+
+
+ RPCTypeCheck(request.params, {
+ UniValue::VARR,
+ UniValueType(), // ARR or OBJ, checked later
+ UniValue::VNUM,
+ UniValue::VBOOL,
+ }, true
+ );
+
+ CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], request.params[3]);
+
+ // Make a blank psbt
+ PartiallySignedTransaction psbtx;
+ psbtx.tx = rawTx;
+ for (unsigned int i = 0; i < rawTx.vin.size(); ++i) {
+ psbtx.inputs.push_back(PSBTInput());
+ }
+ for (unsigned int i = 0; i < rawTx.vout.size(); ++i) {
+ psbtx.outputs.push_back(PSBTOutput());
+ }
+
+ // Serialize the PSBT
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
+ ssTx << psbtx;
+
+ return EncodeBase64((unsigned char*)ssTx.data(), ssTx.size());
+}
+
+UniValue converttopsbt(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
+ throw std::runtime_error(
+ "converttopsbt \"hexstring\" ( permitsigdata iswitness )\n"
+ "\nConverts a network serialized transaction to a PSBT. This should be used only with createrawtransaction and fundrawtransaction\n"
+ "createpsbt and walletcreatefundedpsbt should be used for new applications.\n"
+ "\nArguments:\n"
+ "1. \"hexstring\" (string, required) The hex string of a raw transaction\n"
+ "2. permitsigdata (boolean, optional, default=false) If true, any signatures in the input will be discarded and conversion.\n"
+ " will continue. If false, RPC will fail if any signatures are present.\n"
+ "3. iswitness (boolean, optional) Whether the transaction hex is a serialized witness transaction.\n"
+ " If iswitness is not present, heuristic tests will be used in decoding. If true, only witness deserializaion\n"
+ " will be tried. If false, only non-witness deserialization wil be tried. Only has an effect if\n"
+ " permitsigdata is true.\n"
+ "\nResult:\n"
+ " \"psbt\" (string) The resulting raw transaction (base64-encoded string)\n"
+ "\nExamples:\n"
+ "\nCreate a transaction\n"
+ + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"data\\\":\\\"00010203\\\"}]\"") +
+ "\nConvert the transaction to a PSBT\n"
+ + HelpExampleCli("converttopsbt", "\"rawtransaction\"")
+ );
+
+
+ RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL, UniValue::VBOOL}, true);
+
+ // parse hex string from parameter
+ CMutableTransaction tx;
+ bool permitsigdata = request.params[1].isNull() ? false : request.params[1].get_bool();
+ bool witness_specified = !request.params[2].isNull();
+ bool iswitness = witness_specified ? request.params[2].get_bool() : false;
+ bool try_witness = permitsigdata ? (witness_specified ? iswitness : true) : false;
+ bool try_no_witness = permitsigdata ? (witness_specified ? !iswitness : true) : true;
+ if (!DecodeHexTx(tx, request.params[0].get_str(), try_no_witness, try_witness)) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
+ }
+
+ // Remove all scriptSigs and scriptWitnesses from inputs
+ for (CTxIn& input : tx.vin) {
+ if ((!input.scriptSig.empty() || !input.scriptWitness.IsNull()) && (request.params[1].isNull() || (!request.params[1].isNull() && request.params[1].get_bool()))) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Inputs must not have scriptSigs and scriptWitnesses");
+ }
+ input.scriptSig.clear();
+ input.scriptWitness.SetNull();
+ }
+
+ // Make a blank psbt
+ PartiallySignedTransaction psbtx;
+ psbtx.tx = tx;
+ for (unsigned int i = 0; i < tx.vin.size(); ++i) {
+ psbtx.inputs.push_back(PSBTInput());
+ }
+ for (unsigned int i = 0; i < tx.vout.size(); ++i) {
+ psbtx.outputs.push_back(PSBTOutput());
+ }
+
+ // Serialize the PSBT
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
+ ssTx << psbtx;
+
+ return EncodeBase64((unsigned char*)ssTx.data(), ssTx.size());
+}
+
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
// --------------------- ------------------------ ----------------------- ----------
@@ -1275,6 +1813,11 @@ static const CRPCCommand commands[] =
{ "rawtransactions", "signrawtransaction", &signrawtransaction, {"hexstring","prevtxs","privkeys","sighashtype"} }, /* uses wallet if enabled */
{ "rawtransactions", "signrawtransactionwithkey", &signrawtransactionwithkey, {"hexstring","privkeys","prevtxs","sighashtype"} },
{ "rawtransactions", "testmempoolaccept", &testmempoolaccept, {"rawtxs","allowhighfees"} },
+ { "rawtransactions", "decodepsbt", &decodepsbt, {"psbt"} },
+ { "rawtransactions", "combinepsbt", &combinepsbt, {"txs"} },
+ { "rawtransactions", "finalizepsbt", &finalizepsbt, {"psbt", "extract"} },
+ { "rawtransactions", "createpsbt", &createpsbt, {"inputs","outputs","locktime","replaceable"} },
+ { "rawtransactions", "converttopsbt", &converttopsbt, {"hexstring","permitsigdata","iswitness"} },
{ "blockchain", "gettxoutproof", &gettxoutproof, {"txids", "blockhash"} },
{ "blockchain", "verifytxoutproof", &verifytxoutproof, {"proof"} },
diff --git a/src/rpc/rawtransaction.h b/src/rpc/rawtransaction.h
index ec9d1f2cf0..52dccc90e8 100644
--- a/src/rpc/rawtransaction.h
+++ b/src/rpc/rawtransaction.h
@@ -12,4 +12,7 @@ class UniValue;
/** Sign a transaction with the given keystore and previous transactions */
UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxs, CBasicKeyStore *keystore, bool tempKeystore, const UniValue& hashType);
+/** Create a transaction from univalue parameters */
+CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf);
+
#endif // BITCOIN_RPC_RAWTRANSACTION_H
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
index b420e9d8b3..e46bf2f765 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -301,12 +301,11 @@ bool CRPCTable::appendCommand(const std::string& name, const CRPCCommand* pcmd)
return true;
}
-bool StartRPC()
+void StartRPC()
{
LogPrint(BCLog::RPC, "Starting RPC\n");
fRPCRunning = true;
g_rpcSignals.Started();
- return true;
}
void InterruptRPC()
diff --git a/src/rpc/server.h b/src/rpc/server.h
index 373914885c..a9dbfbd21f 100644
--- a/src/rpc/server.h
+++ b/src/rpc/server.h
@@ -198,7 +198,7 @@ extern CAmount AmountFromValue(const UniValue& value);
extern std::string HelpExampleCli(const std::string& methodname, const std::string& args);
extern std::string HelpExampleRpc(const std::string& methodname, const std::string& args);
-bool StartRPC();
+void StartRPC();
void InterruptRPC();
void StopRPC();
std::string JSONRPCExecBatch(const JSONRPCRequest& jreq, const UniValue& vReq);
diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp
index 8cc44b675f..e2370c5e50 100644
--- a/src/script/bitcoinconsensus.cpp
+++ b/src/script/bitcoinconsensus.cpp
@@ -81,7 +81,7 @@ static int verify_script(const unsigned char *scriptPubKey, unsigned int scriptP
unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err)
{
if (!verify_flags(flags)) {
- return bitcoinconsensus_ERR_INVALID_FLAGS;
+ return set_error(err, bitcoinconsensus_ERR_INVALID_FLAGS);
}
try {
TxInputStream stream(SER_NETWORK, PROTOCOL_VERSION, txTo, txToLen);
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index e9b63b466e..be2138d502 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -99,7 +99,7 @@ bool static IsCompressedPubKey(const valtype &vchPubKey) {
* Where R and S are not negative (their first byte has its highest bit not set), and not
* excessively padded (do not start with a 0 byte, unless an otherwise negative number follows,
* in which case a single 0 byte is necessary and even required).
- *
+ *
* See https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623
*
* This function is consensus-critical since BIP66.
@@ -139,7 +139,7 @@ bool static IsValidSignatureEncoding(const std::vector<unsigned char> &sig) {
// Verify that the length of the signature matches the sum of the length
// of the elements.
if ((size_t)(lenR + lenS + 7) != sig.size()) return false;
-
+
// Check whether the R element is an integer.
if (sig[2] != 0x02) return false;
@@ -899,7 +899,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
popstack(stack);
stack.push_back(vchHash);
}
- break;
+ break;
case OP_CODESEPARATOR:
{
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index 60a8a2655d..d10b1c4fd7 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -49,9 +49,10 @@ static bool GetCScript(const SigningProvider& provider, const SignatureData& sig
return false;
}
-static bool GetPubKey(const SigningProvider& provider, const SignatureData& sigdata, const CKeyID& address, CPubKey& pubkey)
+static bool GetPubKey(const SigningProvider& provider, SignatureData& sigdata, const CKeyID& address, CPubKey& pubkey)
{
if (provider.GetPubKey(address, pubkey)) {
+ sigdata.misc_pubkeys.emplace(pubkey.GetID(), pubkey);
return true;
}
// Look for pubkey in all partial sigs
@@ -60,6 +61,12 @@ static bool GetPubKey(const SigningProvider& provider, const SignatureData& sigd
pubkey = it->second.first;
return true;
}
+ // Look for pubkey in pubkey list
+ const auto& pk_it = sigdata.misc_pubkeys.find(address);
+ if (pk_it != sigdata.misc_pubkeys.end()) {
+ pubkey = pk_it->second;
+ return true;
+ }
return false;
}
@@ -70,9 +77,9 @@ static bool CreateSig(const BaseSignatureCreator& creator, SignatureData& sigdat
sig_out = it->second.second;
return true;
}
+ CPubKey pubkey;
+ GetPubKey(provider, sigdata, keyid, pubkey);
if (creator.CreateSig(provider, sig_out, keyid, scriptcode, sigversion)) {
- CPubKey pubkey;
- GetPubKey(provider, sigdata, keyid, pubkey);
auto i = sigdata.signatures.emplace(keyid, SigPair(pubkey, sig_out));
assert(i.second);
return true;
@@ -200,6 +207,7 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
txnouttype subType;
solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0, sigdata);
sigdata.scriptWitness.stack = result;
+ sigdata.witness = true;
result.clear();
}
else if (solved && whichType == TX_WITNESS_V0_SCRIPTHASH)
@@ -210,7 +218,10 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0, sigdata) && subType != TX_SCRIPTHASH && subType != TX_WITNESS_V0_SCRIPTHASH && subType != TX_WITNESS_V0_KEYHASH;
result.push_back(std::vector<unsigned char>(witnessscript.begin(), witnessscript.end()));
sigdata.scriptWitness.stack = result;
+ sigdata.witness = true;
result.clear();
+ } else if (solved && whichType == TX_WITNESS_UNKNOWN) {
+ sigdata.witness = true;
}
if (P2SH) {
@@ -223,6 +234,32 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
return sigdata.complete;
}
+bool SignPSBTInput(const SigningProvider& provider, const CMutableTransaction& tx, PSBTInput& input, SignatureData& sigdata, int index, int sighash)
+{
+ // if this input has a final scriptsig or scriptwitness, don't do anything with it
+ if (!input.final_script_sig.empty() || !input.final_script_witness.IsNull()) {
+ return true;
+ }
+
+ // Fill SignatureData with input info
+ input.FillSignatureData(sigdata);
+
+ // Get UTXO
+ CTxOut utxo;
+ if (input.non_witness_utxo) {
+ utxo = input.non_witness_utxo->vout[tx.vin[index].prevout.n];
+ } else if (!input.witness_utxo.IsNull()) {
+ utxo = input.witness_utxo;
+ } else {
+ return false;
+ }
+
+ MutableTransactionSignatureCreator creator(&tx, index, utxo.nValue, sighash);
+ bool sig_complete = ProduceSignature(provider, creator, utxo.scriptPubKey, sigdata);
+ input.FromSignatureData(sigdata);
+ return sig_complete;
+}
+
class SignatureExtractorChecker final : public BaseSignatureChecker
{
private:
@@ -251,18 +288,11 @@ struct Stacks
std::vector<valtype> script;
std::vector<valtype> witness;
- Stacks() {}
- explicit Stacks(const std::vector<valtype>& scriptSigStack_) : script(scriptSigStack_), witness() {}
+ Stacks() = delete;
+ Stacks(const Stacks&) = delete;
explicit Stacks(const SignatureData& data) : witness(data.scriptWitness.stack) {
EvalScript(script, data.scriptSig, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker(), SigVersion::BASE);
}
-
- SignatureData Output() const {
- SignatureData result;
- result.scriptSig = PushAll(script);
- result.scriptWitness.stack = witness;
- return result;
- }
};
}
@@ -429,3 +459,162 @@ bool IsSolvable(const SigningProvider& provider, const CScript& script)
}
return false;
}
+
+
+bool PartiallySignedTransaction::IsNull() const
+{
+ return !tx && inputs.empty() && outputs.empty() && unknown.empty();
+}
+
+void PartiallySignedTransaction::Merge(const PartiallySignedTransaction& psbt)
+{
+ for (unsigned int i = 0; i < inputs.size(); ++i) {
+ inputs[i].Merge(psbt.inputs[i]);
+ }
+ for (unsigned int i = 0; i < outputs.size(); ++i) {
+ outputs[i].Merge(psbt.outputs[i]);
+ }
+ unknown.insert(psbt.unknown.begin(), psbt.unknown.end());
+}
+
+bool PartiallySignedTransaction::IsSane() const
+{
+ for (PSBTInput input : inputs) {
+ if (!input.IsSane()) return false;
+ }
+ return true;
+}
+
+bool PSBTInput::IsNull() const
+{
+ return !non_witness_utxo && witness_utxo.IsNull() && partial_sigs.empty() && unknown.empty() && hd_keypaths.empty() && redeem_script.empty() && witness_script.empty();
+}
+
+void PSBTInput::FillSignatureData(SignatureData& sigdata) const
+{
+ if (!final_script_sig.empty()) {
+ sigdata.scriptSig = final_script_sig;
+ sigdata.complete = true;
+ }
+ if (!final_script_witness.IsNull()) {
+ sigdata.scriptWitness = final_script_witness;
+ sigdata.complete = true;
+ }
+ if (sigdata.complete) {
+ return;
+ }
+
+ sigdata.signatures.insert(partial_sigs.begin(), partial_sigs.end());
+ if (!redeem_script.empty()) {
+ sigdata.redeem_script = redeem_script;
+ }
+ if (!witness_script.empty()) {
+ sigdata.witness_script = witness_script;
+ }
+ for (const auto& key_pair : hd_keypaths) {
+ sigdata.misc_pubkeys.emplace(key_pair.first.GetID(), key_pair.first);
+ }
+}
+
+void PSBTInput::FromSignatureData(const SignatureData& sigdata)
+{
+ if (sigdata.complete) {
+ partial_sigs.clear();
+ hd_keypaths.clear();
+ redeem_script.clear();
+ witness_script.clear();
+
+ if (!sigdata.scriptSig.empty()) {
+ final_script_sig = sigdata.scriptSig;
+ }
+ if (!sigdata.scriptWitness.IsNull()) {
+ final_script_witness = sigdata.scriptWitness;
+ }
+ return;
+ }
+
+ partial_sigs.insert(sigdata.signatures.begin(), sigdata.signatures.end());
+ if (redeem_script.empty() && !sigdata.redeem_script.empty()) {
+ redeem_script = sigdata.redeem_script;
+ }
+ if (witness_script.empty() && !sigdata.witness_script.empty()) {
+ witness_script = sigdata.witness_script;
+ }
+}
+
+void PSBTInput::Merge(const PSBTInput& input)
+{
+ if (!non_witness_utxo && input.non_witness_utxo) non_witness_utxo = input.non_witness_utxo;
+ if (witness_utxo.IsNull() && !input.witness_utxo.IsNull()) {
+ witness_utxo = input.witness_utxo;
+ non_witness_utxo = nullptr; // Clear out any non-witness utxo when we set a witness one.
+ }
+
+ partial_sigs.insert(input.partial_sigs.begin(), input.partial_sigs.end());
+ hd_keypaths.insert(input.hd_keypaths.begin(), input.hd_keypaths.end());
+ unknown.insert(input.unknown.begin(), input.unknown.end());
+
+ if (redeem_script.empty() && !input.redeem_script.empty()) redeem_script = input.redeem_script;
+ if (witness_script.empty() && !input.witness_script.empty()) witness_script = input.witness_script;
+ if (final_script_sig.empty() && !input.final_script_sig.empty()) final_script_sig = input.final_script_sig;
+ if (final_script_witness.IsNull() && !input.final_script_witness.IsNull()) final_script_witness = input.final_script_witness;
+}
+
+bool PSBTInput::IsSane() const
+{
+ // Cannot have both witness and non-witness utxos
+ if (!witness_utxo.IsNull() && non_witness_utxo) return false;
+
+ // If we have a witness_script or a scriptWitness, we must also have a witness utxo
+ if (!witness_script.empty() && witness_utxo.IsNull()) return false;
+ if (!final_script_witness.IsNull() && witness_utxo.IsNull()) return false;
+
+ return true;
+}
+
+void PSBTOutput::FillSignatureData(SignatureData& sigdata) const
+{
+ if (!redeem_script.empty()) {
+ sigdata.redeem_script = redeem_script;
+ }
+ if (!witness_script.empty()) {
+ sigdata.witness_script = witness_script;
+ }
+ for (const auto& key_pair : hd_keypaths) {
+ sigdata.misc_pubkeys.emplace(key_pair.first.GetID(), key_pair.first);
+ }
+}
+
+void PSBTOutput::FromSignatureData(const SignatureData& sigdata)
+{
+ if (redeem_script.empty() && !sigdata.redeem_script.empty()) {
+ redeem_script = sigdata.redeem_script;
+ }
+ if (witness_script.empty() && !sigdata.witness_script.empty()) {
+ witness_script = sigdata.witness_script;
+ }
+}
+
+bool PSBTOutput::IsNull() const
+{
+ return redeem_script.empty() && witness_script.empty() && hd_keypaths.empty() && unknown.empty();
+}
+
+void PSBTOutput::Merge(const PSBTOutput& output)
+{
+ hd_keypaths.insert(output.hd_keypaths.begin(), output.hd_keypaths.end());
+ unknown.insert(output.unknown.begin(), output.unknown.end());
+
+ if (redeem_script.empty() && !output.redeem_script.empty()) redeem_script = output.redeem_script;
+ if (witness_script.empty() && !output.witness_script.empty()) witness_script = output.witness_script;
+}
+
+bool PublicOnlySigningProvider::GetCScript(const CScriptID &scriptid, CScript& script) const
+{
+ return m_provider->GetCScript(scriptid, script);
+}
+
+bool PublicOnlySigningProvider::GetPubKey(const CKeyID &address, CPubKey& pubkey) const
+{
+ return m_provider->GetPubKey(address, pubkey);
+}
diff --git a/src/script/sign.h b/src/script/sign.h
index 3666859641..d12d0b5874 100644
--- a/src/script/sign.h
+++ b/src/script/sign.h
@@ -6,7 +6,11 @@
#ifndef BITCOIN_SCRIPT_SIGN_H
#define BITCOIN_SCRIPT_SIGN_H
+#include <boost/optional.hpp>
+#include <hash.h>
+#include <pubkey.h>
#include <script/interpreter.h>
+#include <streams.h>
class CKey;
class CKeyID;
@@ -28,6 +32,17 @@ public:
extern const SigningProvider& DUMMY_SIGNING_PROVIDER;
+class PublicOnlySigningProvider : public SigningProvider
+{
+private:
+ const SigningProvider* m_provider;
+
+public:
+ PublicOnlySigningProvider(const SigningProvider* provider) : m_provider(provider) {}
+ bool GetCScript(const CScriptID &scriptid, CScript& script) const;
+ bool GetPubKey(const CKeyID &address, CPubKey& pubkey) const;
+};
+
/** Interface for signature creators. */
class BaseSignatureCreator {
public:
@@ -62,17 +77,585 @@ typedef std::pair<CPubKey, std::vector<unsigned char>> SigPair;
// in order to construct final scriptSigs and scriptWitnesses.
struct SignatureData {
bool complete = false; ///< Stores whether the scriptSig and scriptWitness are complete
+ bool witness = false; ///< Stores whether the input this SigData corresponds to is a witness input
CScript scriptSig; ///< The scriptSig of an input. Contains complete signatures or the traditional partial signatures format
CScript redeem_script; ///< The redeemScript (if any) for the input
CScript witness_script; ///< The witnessScript (if any) for the input. witnessScripts are used in P2WSH outputs.
CScriptWitness scriptWitness; ///< The scriptWitness of an input. Contains complete signatures or the traditional partial signatures format. scriptWitness is part of a transaction input per BIP 144.
std::map<CKeyID, SigPair> signatures; ///< BIP 174 style partial signatures for the input. May contain all signatures necessary for producing a final scriptSig or scriptWitness.
+ std::map<CKeyID, CPubKey> misc_pubkeys;
SignatureData() {}
explicit SignatureData(const CScript& script) : scriptSig(script) {}
void MergeSignatureData(SignatureData sigdata);
};
+// Magic bytes
+static constexpr uint8_t PSBT_MAGIC_BYTES[5] = {'p', 's', 'b', 't', 0xff};
+
+// Global types
+static constexpr uint8_t PSBT_GLOBAL_UNSIGNED_TX = 0x00;
+
+// Input types
+static constexpr uint8_t PSBT_IN_NON_WITNESS_UTXO = 0x00;
+static constexpr uint8_t PSBT_IN_WITNESS_UTXO = 0x01;
+static constexpr uint8_t PSBT_IN_PARTIAL_SIG = 0x02;
+static constexpr uint8_t PSBT_IN_SIGHASH = 0x03;
+static constexpr uint8_t PSBT_IN_REDEEMSCRIPT = 0x04;
+static constexpr uint8_t PSBT_IN_WITNESSSCRIPT = 0x05;
+static constexpr uint8_t PSBT_IN_BIP32_DERIVATION = 0x06;
+static constexpr uint8_t PSBT_IN_SCRIPTSIG = 0x07;
+static constexpr uint8_t PSBT_IN_SCRIPTWITNESS = 0x08;
+
+// Output types
+static constexpr uint8_t PSBT_OUT_REDEEMSCRIPT = 0x00;
+static constexpr uint8_t PSBT_OUT_WITNESSSCRIPT = 0x01;
+static constexpr uint8_t PSBT_OUT_BIP32_DERIVATION = 0x02;
+
+// The separator is 0x00. Reading this in means that the unserializer can interpret it
+// as a 0 length key which indicates that this is the separator. The separator has no value.
+static constexpr uint8_t PSBT_SEPARATOR = 0x00;
+
+// Takes a stream and multiple arguments and serializes them as if first serialized into a vector and then into the stream
+// The resulting output into the stream has the total serialized length of all of the objects followed by all objects concatenated with each other.
+template<typename Stream, typename... X>
+void SerializeToVector(Stream& s, const X&... args)
+{
+ WriteCompactSize(s, GetSerializeSizeMany(s, args...));
+ SerializeMany(s, args...);
+}
+
+// Takes a stream and multiple arguments and unserializes them first as a vector then each object individually in the order provided in the arguments
+template<typename Stream, typename... X>
+void UnserializeFromVector(Stream& s, X&... args)
+{
+ size_t expected_size = ReadCompactSize(s);
+ size_t remaining_before = s.size();
+ UnserializeMany(s, args...);
+ size_t remaining_after = s.size();
+ if (remaining_after + expected_size != remaining_before) {
+ throw std::ios_base::failure("Size of value was not the stated size");
+ }
+}
+
+// Deserialize HD keypaths into a map
+template<typename Stream>
+void DeserializeHDKeypaths(Stream& s, const std::vector<unsigned char>& key, std::map<CPubKey, std::vector<uint32_t>>& hd_keypaths)
+{
+ // Make sure that the key is the size of pubkey + 1
+ if (key.size() != CPubKey::PUBLIC_KEY_SIZE + 1 && key.size() != CPubKey::COMPRESSED_PUBLIC_KEY_SIZE + 1) {
+ throw std::ios_base::failure("Size of key was not the expected size for the type BIP32 keypath");
+ }
+ // Read in the pubkey from key
+ CPubKey pubkey(key.begin() + 1, key.end());
+ if (!pubkey.IsFullyValid()) {
+ throw std::ios_base::failure("Invalid pubkey");
+ }
+ if (hd_keypaths.count(pubkey) > 0) {
+ throw std::ios_base::failure("Duplicate Key, pubkey derivation path already provided");
+ }
+
+ // Read in key path
+ uint64_t value_len = ReadCompactSize(s);
+ std::vector<uint32_t> keypath;
+ for (unsigned int i = 0; i < value_len; i += sizeof(uint32_t)) {
+ uint32_t index;
+ s >> index;
+ keypath.push_back(index);
+ }
+
+ // Add to map
+ hd_keypaths.emplace(pubkey, keypath);
+}
+
+// Serialize HD keypaths to a stream from a map
+template<typename Stream>
+void SerializeHDKeypaths(Stream& s, const std::map<CPubKey, std::vector<uint32_t>>& hd_keypaths, uint8_t type)
+{
+ for (auto keypath_pair : hd_keypaths) {
+ SerializeToVector(s, type, MakeSpan(keypath_pair.first));
+ WriteCompactSize(s, keypath_pair.second.size() * sizeof(uint32_t));
+ for (auto& path : keypath_pair.second) {
+ s << path;
+ }
+ }
+}
+
+/** A structure for PSBTs which contain per-input information */
+struct PSBTInput
+{
+ CTransactionRef non_witness_utxo;
+ CTxOut witness_utxo;
+ CScript redeem_script;
+ CScript witness_script;
+ CScript final_script_sig;
+ CScriptWitness final_script_witness;
+ std::map<CPubKey, std::vector<uint32_t>> hd_keypaths;
+ std::map<CKeyID, SigPair> partial_sigs;
+ std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown;
+ int sighash_type = 0;
+
+ bool IsNull() const;
+ void FillSignatureData(SignatureData& sigdata) const;
+ void FromSignatureData(const SignatureData& sigdata);
+ void Merge(const PSBTInput& input);
+ bool IsSane() const;
+ PSBTInput() {}
+
+ template <typename Stream>
+ inline void Serialize(Stream& s) const {
+ // Write the utxo
+ // If there is a non-witness utxo, then don't add the witness one.
+ if (non_witness_utxo) {
+ SerializeToVector(s, PSBT_IN_NON_WITNESS_UTXO);
+ SerializeToVector(s, non_witness_utxo);
+ } else if (!witness_utxo.IsNull()) {
+ SerializeToVector(s, PSBT_IN_WITNESS_UTXO);
+ SerializeToVector(s, witness_utxo);
+ }
+
+ if (final_script_sig.empty() && final_script_witness.IsNull()) {
+ // Write any partial signatures
+ for (auto sig_pair : partial_sigs) {
+ SerializeToVector(s, PSBT_IN_PARTIAL_SIG, MakeSpan(sig_pair.second.first));
+ s << sig_pair.second.second;
+ }
+
+ // Write the sighash type
+ if (sighash_type > 0) {
+ SerializeToVector(s, PSBT_IN_SIGHASH);
+ SerializeToVector(s, sighash_type);
+ }
+
+ // Write the redeem script
+ if (!redeem_script.empty()) {
+ SerializeToVector(s, PSBT_IN_REDEEMSCRIPT);
+ s << redeem_script;
+ }
+
+ // Write the witness script
+ if (!witness_script.empty()) {
+ SerializeToVector(s, PSBT_IN_WITNESSSCRIPT);
+ s << witness_script;
+ }
+
+ // Write any hd keypaths
+ SerializeHDKeypaths(s, hd_keypaths, PSBT_IN_BIP32_DERIVATION);
+ }
+
+ // Write script sig
+ if (!final_script_sig.empty()) {
+ SerializeToVector(s, PSBT_IN_SCRIPTSIG);
+ s << final_script_sig;
+ }
+ // write script witness
+ if (!final_script_witness.IsNull()) {
+ SerializeToVector(s, PSBT_IN_SCRIPTWITNESS);
+ SerializeToVector(s, final_script_witness.stack);
+ }
+
+ // Write unknown things
+ for (auto& entry : unknown) {
+ s << entry.first;
+ s << entry.second;
+ }
+
+ s << PSBT_SEPARATOR;
+ }
+
+
+ template <typename Stream>
+ inline void Unserialize(Stream& s) {
+ // Read loop
+ while(!s.empty()) {
+ // Read
+ std::vector<unsigned char> key;
+ s >> key;
+
+ // the key is empty if that was actually a separator byte
+ // This is a special case for key lengths 0 as those are not allowed (except for separator)
+ if (key.empty()) return;
+
+ // First byte of key is the type
+ unsigned char type = key[0];
+
+ // Do stuff based on type
+ switch(type) {
+ case PSBT_IN_NON_WITNESS_UTXO:
+ if (non_witness_utxo) {
+ throw std::ios_base::failure("Duplicate Key, input non-witness utxo already provided");
+ } else if (key.size() != 1) {
+ throw std::ios_base::failure("Non-witness utxo key is more than one byte type");
+ }
+ UnserializeFromVector(s, non_witness_utxo);
+ break;
+ case PSBT_IN_WITNESS_UTXO:
+ if (!witness_utxo.IsNull()) {
+ throw std::ios_base::failure("Duplicate Key, input witness utxo already provided");
+ } else if (key.size() != 1) {
+ throw std::ios_base::failure("Witness utxo key is more than one byte type");
+ }
+ UnserializeFromVector(s, witness_utxo);
+ break;
+ case PSBT_IN_PARTIAL_SIG:
+ {
+ // Make sure that the key is the size of pubkey + 1
+ if (key.size() != CPubKey::PUBLIC_KEY_SIZE + 1 && key.size() != CPubKey::COMPRESSED_PUBLIC_KEY_SIZE + 1) {
+ throw std::ios_base::failure("Size of key was not the expected size for the type partial signature pubkey");
+ }
+ // Read in the pubkey from key
+ CPubKey pubkey(key.begin() + 1, key.end());
+ if (!pubkey.IsFullyValid()) {
+ throw std::ios_base::failure("Invalid pubkey");
+ }
+ if (partial_sigs.count(pubkey.GetID()) > 0) {
+ throw std::ios_base::failure("Duplicate Key, input partial signature for pubkey already provided");
+ }
+
+ // Read in the signature from value
+ std::vector<unsigned char> sig;
+ s >> sig;
+
+ // Add to list
+ partial_sigs.emplace(pubkey.GetID(), SigPair(pubkey, std::move(sig)));
+ break;
+ }
+ case PSBT_IN_SIGHASH:
+ if (sighash_type > 0) {
+ throw std::ios_base::failure("Duplicate Key, input sighash type already provided");
+ } else if (key.size() != 1) {
+ throw std::ios_base::failure("Sighash type key is more than one byte type");
+ }
+ UnserializeFromVector(s, sighash_type);
+ break;
+ case PSBT_IN_REDEEMSCRIPT:
+ {
+ if (!redeem_script.empty()) {
+ throw std::ios_base::failure("Duplicate Key, input redeemScript already provided");
+ } else if (key.size() != 1) {
+ throw std::ios_base::failure("Input redeemScript key is more than one byte type");
+ }
+ s >> redeem_script;
+ break;
+ }
+ case PSBT_IN_WITNESSSCRIPT:
+ {
+ if (!witness_script.empty()) {
+ throw std::ios_base::failure("Duplicate Key, input witnessScript already provided");
+ } else if (key.size() != 1) {
+ throw std::ios_base::failure("Input witnessScript key is more than one byte type");
+ }
+ s >> witness_script;
+ break;
+ }
+ case PSBT_IN_BIP32_DERIVATION:
+ {
+ DeserializeHDKeypaths(s, key, hd_keypaths);
+ break;
+ }
+ case PSBT_IN_SCRIPTSIG:
+ {
+ if (!final_script_sig.empty()) {
+ throw std::ios_base::failure("Duplicate Key, input final scriptSig already provided");
+ } else if (key.size() != 1) {
+ throw std::ios_base::failure("Final scriptSig key is more than one byte type");
+ }
+ s >> final_script_sig;
+ break;
+ }
+ case PSBT_IN_SCRIPTWITNESS:
+ {
+ if (!final_script_witness.IsNull()) {
+ throw std::ios_base::failure("Duplicate Key, input final scriptWitness already provided");
+ } else if (key.size() != 1) {
+ throw std::ios_base::failure("Final scriptWitness key is more than one byte type");
+ }
+ UnserializeFromVector(s, final_script_witness.stack);
+ break;
+ }
+ // Unknown stuff
+ default:
+ if (unknown.count(key) > 0) {
+ throw std::ios_base::failure("Duplicate Key, key for unknown value already provided");
+ }
+ // Read in the value
+ std::vector<unsigned char> val_bytes;
+ s >> val_bytes;
+ unknown.emplace(std::move(key), std::move(val_bytes));
+ break;
+ }
+ }
+ }
+
+ template <typename Stream>
+ PSBTInput(deserialize_type, Stream& s) {
+ Unserialize(s);
+ }
+};
+
+/** A structure for PSBTs which contains per output information */
+struct PSBTOutput
+{
+ CScript redeem_script;
+ CScript witness_script;
+ std::map<CPubKey, std::vector<uint32_t>> hd_keypaths;
+ std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown;
+
+ bool IsNull() const;
+ void FillSignatureData(SignatureData& sigdata) const;
+ void FromSignatureData(const SignatureData& sigdata);
+ void Merge(const PSBTOutput& output);
+ bool IsSane() const;
+ PSBTOutput() {}
+
+ template <typename Stream>
+ inline void Serialize(Stream& s) const {
+ // Write the redeem script
+ if (!redeem_script.empty()) {
+ SerializeToVector(s, PSBT_OUT_REDEEMSCRIPT);
+ s << redeem_script;
+ }
+
+ // Write the witness script
+ if (!witness_script.empty()) {
+ SerializeToVector(s, PSBT_OUT_WITNESSSCRIPT);
+ s << witness_script;
+ }
+
+ // Write any hd keypaths
+ SerializeHDKeypaths(s, hd_keypaths, PSBT_OUT_BIP32_DERIVATION);
+
+ // Write unknown things
+ for (auto& entry : unknown) {
+ s << entry.first;
+ s << entry.second;
+ }
+
+ s << PSBT_SEPARATOR;
+ }
+
+
+ template <typename Stream>
+ inline void Unserialize(Stream& s) {
+ // Read loop
+ while(!s.empty()) {
+ // Read
+ std::vector<unsigned char> key;
+ s >> key;
+
+ // the key is empty if that was actually a separator byte
+ // This is a special case for key lengths 0 as those are not allowed (except for separator)
+ if (key.empty()) return;
+
+ // First byte of key is the type
+ unsigned char type = key[0];
+
+ // Do stuff based on type
+ switch(type) {
+ case PSBT_OUT_REDEEMSCRIPT:
+ {
+ if (!redeem_script.empty()) {
+ throw std::ios_base::failure("Duplicate Key, output redeemScript already provided");
+ } else if (key.size() != 1) {
+ throw std::ios_base::failure("Output redeemScript key is more than one byte type");
+ }
+ s >> redeem_script;
+ break;
+ }
+ case PSBT_OUT_WITNESSSCRIPT:
+ {
+ if (!witness_script.empty()) {
+ throw std::ios_base::failure("Duplicate Key, output witnessScript already provided");
+ } else if (key.size() != 1) {
+ throw std::ios_base::failure("Output witnessScript key is more than one byte type");
+ }
+ s >> witness_script;
+ break;
+ }
+ case PSBT_OUT_BIP32_DERIVATION:
+ {
+ DeserializeHDKeypaths(s, key, hd_keypaths);
+ break;
+ }
+ // Unknown stuff
+ default: {
+ if (unknown.count(key) > 0) {
+ throw std::ios_base::failure("Duplicate Key, key for unknown value already provided");
+ }
+ // Read in the value
+ std::vector<unsigned char> val_bytes;
+ s >> val_bytes;
+ unknown.emplace(std::move(key), std::move(val_bytes));
+ break;
+ }
+ }
+ }
+ }
+
+ template <typename Stream>
+ PSBTOutput(deserialize_type, Stream& s) {
+ Unserialize(s);
+ }
+};
+
+/** A version of CTransaction with the PSBT format*/
+struct PartiallySignedTransaction
+{
+ boost::optional<CMutableTransaction> tx;
+ std::vector<PSBTInput> inputs;
+ std::vector<PSBTOutput> outputs;
+ std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown;
+
+ bool IsNull() const;
+ void Merge(const PartiallySignedTransaction& psbt);
+ bool IsSane() const;
+ PartiallySignedTransaction() {}
+ PartiallySignedTransaction(const PartiallySignedTransaction& psbt_in) : tx(psbt_in.tx), inputs(psbt_in.inputs), outputs(psbt_in.outputs), unknown(psbt_in.unknown) {}
+
+ // Only checks if they refer to the same transaction
+ friend bool operator==(const PartiallySignedTransaction& a, const PartiallySignedTransaction &b)
+ {
+ return a.tx->GetHash() == b.tx->GetHash();
+ }
+ friend bool operator!=(const PartiallySignedTransaction& a, const PartiallySignedTransaction &b)
+ {
+ return !(a == b);
+ }
+
+ template <typename Stream>
+ inline void Serialize(Stream& s) const {
+
+ // magic bytes
+ s << PSBT_MAGIC_BYTES;
+
+ // unsigned tx flag
+ SerializeToVector(s, PSBT_GLOBAL_UNSIGNED_TX);
+
+ // Write serialized tx to a stream
+ SerializeToVector(s, *tx);
+
+ // Write the unknown things
+ for (auto& entry : unknown) {
+ s << entry.first;
+ s << entry.second;
+ }
+
+ // Separator
+ s << PSBT_SEPARATOR;
+
+ // Write inputs
+ for (const PSBTInput& input : inputs) {
+ s << input;
+ }
+ // Write outputs
+ for (const PSBTOutput& output : outputs) {
+ s << output;
+ }
+ }
+
+
+ template <typename Stream>
+ inline void Unserialize(Stream& s) {
+ // Read the magic bytes
+ uint8_t magic[5];
+ s >> magic;
+ if (!std::equal(magic, magic + 5, PSBT_MAGIC_BYTES)) {
+ throw std::ios_base::failure("Invalid PSBT magic bytes");
+ }
+
+ // Read global data
+ while(!s.empty()) {
+ // Read
+ std::vector<unsigned char> key;
+ s >> key;
+
+ // the key is empty if that was actually a separator byte
+ // This is a special case for key lengths 0 as those are not allowed (except for separator)
+ if (key.empty()) break;
+
+ // First byte of key is the type
+ unsigned char type = key[0];
+
+ // Do stuff based on type
+ switch(type) {
+ case PSBT_GLOBAL_UNSIGNED_TX:
+ {
+ if (tx) {
+ throw std::ios_base::failure("Duplicate Key, unsigned tx already provided");
+ } else if (key.size() != 1) {
+ throw std::ios_base::failure("Global unsigned tx key is more than one byte type");
+ }
+ CMutableTransaction mtx;
+ UnserializeFromVector(s, mtx);
+ tx = std::move(mtx);
+ // Make sure that all scriptSigs and scriptWitnesses are empty
+ for (const CTxIn& txin : tx->vin) {
+ if (!txin.scriptSig.empty() || !txin.scriptWitness.IsNull()) {
+ throw std::ios_base::failure("Unsigned tx does not have empty scriptSigs and scriptWitnesses.");
+ }
+ }
+ break;
+ }
+ // Unknown stuff
+ default: {
+ if (unknown.count(key) > 0) {
+ throw std::ios_base::failure("Duplicate Key, key for unknown value already provided");
+ }
+ // Read in the value
+ std::vector<unsigned char> val_bytes;
+ s >> val_bytes;
+ unknown.emplace(std::move(key), std::move(val_bytes));
+ }
+ }
+ }
+
+ // Make sure that we got an unsigned tx
+ if (!tx) {
+ throw std::ios_base::failure("No unsigned transcation was provided");
+ }
+
+ // Read input data
+ unsigned int i = 0;
+ while (!s.empty() && i < tx->vin.size()) {
+ PSBTInput input;
+ s >> input;
+ inputs.push_back(input);
+
+ // Make sure the non-witness utxo matches the outpoint
+ if (input.non_witness_utxo && input.non_witness_utxo->GetHash() != tx->vin[i].prevout.hash) {
+ throw std::ios_base::failure("Non-witness UTXO does not match outpoint hash");
+ }
+ ++i;
+ }
+ // Make sure that the number of inputs matches the number of inputs in the transaction
+ if (inputs.size() != tx->vin.size()) {
+ throw std::ios_base::failure("Inputs provided does not match the number of inputs in transaction.");
+ }
+
+ // Read output data
+ i = 0;
+ while (!s.empty() && i < tx->vout.size()) {
+ PSBTOutput output;
+ s >> output;
+ outputs.push_back(output);
+ ++i;
+ }
+ // Make sure that the number of outputs matches the number of outputs in the transaction
+ if (outputs.size() != tx->vout.size()) {
+ throw std::ios_base::failure("Outputs provided does not match the number of outputs in transaction.");
+ }
+ // Sanity check
+ if (!IsSane()) {
+ throw std::ios_base::failure("PSBT is not sane.");
+ }
+ }
+
+ template <typename Stream>
+ PartiallySignedTransaction(deserialize_type, Stream& s) {
+ Unserialize(s);
+ }
+};
+
/** Produce a script signature using a generic signature creator. */
bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& scriptPubKey, SignatureData& sigdata);
@@ -80,6 +663,9 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType);
bool SignSignature(const SigningProvider &provider, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType);
+/** Signs a PSBTInput */
+bool SignPSBTInput(const SigningProvider& provider, const CMutableTransaction& tx, PSBTInput& input, SignatureData& sigdata, int index, int sighash = 1);
+
/** Extract signature data from a transaction input, and insert it. */
SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn, const CTxOut& txout);
void UpdateInput(CTxIn& input, const SignatureData& data);
diff --git a/src/script/standard.cpp b/src/script/standard.cpp
index f0b2c62a91..d7b1724790 100644
--- a/src/script/standard.cpp
+++ b/src/script/standard.cpp
@@ -324,8 +324,6 @@ CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys)
CScript GetScriptForWitness(const CScript& redeemscript)
{
- CScript ret;
-
txnouttype typ;
std::vector<std::vector<unsigned char> > vSolutions;
if (Solver(redeemscript, typ, vSolutions)) {
diff --git a/src/serialize.h b/src/serialize.h
index df3b47ba87..99d4c893f2 100644
--- a/src/serialize.h
+++ b/src/serialize.h
@@ -173,11 +173,11 @@ template<typename X> const X& ReadWriteAsHelper(const X& x) { return x; }
#define READWRITE(...) (::SerReadWriteMany(s, ser_action, __VA_ARGS__))
#define READWRITEAS(type, obj) (::SerReadWriteMany(s, ser_action, ReadWriteAsHelper<type>(obj)))
-/**
+/**
* Implement three methods for serializable objects. These are actually wrappers over
* "SerializationOp" template, which implements the body of each class' serialization
* code. Adding "ADD_SERIALIZE_METHODS" in the body of the class causes these wrappers to be
- * added as members.
+ * added as members.
*/
#define ADD_SERIALIZE_METHODS \
template<typename Stream> \
@@ -312,16 +312,16 @@ uint64_t ReadCompactSize(Stream& is)
* sure the encoding is one-to-one, one is subtracted from all but the last digit.
* Thus, the byte sequence a[] with length len, where all but the last byte
* has bit 128 set, encodes the number:
- *
+ *
* (a[len-1] & 0x7F) + sum(i=1..len-1, 128^i*((a[len-i-1] & 0x7F)+1))
- *
+ *
* Properties:
* * Very small (0-127: 1 byte, 128-16511: 2 bytes, 16512-2113663: 3 bytes)
* * Every integer has exactly one encoding
* * Encoding does not depend on size of original integer type
* * No redundancy: every (infinite) byte sequence corresponds to a list
* of encoded integers.
- *
+ *
* 0: [0x00] 256: [0x81 0x00]
* 1: [0x01] 16383: [0xFE 0x7F]
* 127: [0x7F] 16384: [0xFF 0x00]
@@ -991,4 +991,12 @@ size_t GetSerializeSize(const S& s, const T& t)
return (CSizeComputer(s.GetType(), s.GetVersion()) << t).size();
}
+template <typename S, typename... T>
+size_t GetSerializeSizeMany(const S& s, const T&... t)
+{
+ CSizeComputer sc(s.GetType(), s.GetVersion());
+ SerializeMany(sc, t...);
+ return sc.size();
+}
+
#endif // BITCOIN_SERIALIZE_H
diff --git a/src/streams.h b/src/streams.h
index 6ba4f103da..ed2e985d76 100644
--- a/src/streams.h
+++ b/src/streams.h
@@ -348,7 +348,7 @@ public:
if (nReadPosNext > vch.size()) {
throw std::ios_base::failure("CDataStream::read(): end of data");
}
- memcpy(pch, &vch[nReadPos], nSize);
+ memcpy(pch, &vch[nReadPos], nSize);
if (nReadPosNext == vch.size())
{
nReadPos = 0;
@@ -456,7 +456,7 @@ private:
const int nType;
const int nVersion;
- FILE* file;
+ FILE* file;
public:
CAutoFile(FILE* filenew, int nTypeIn, int nVersionIn) : nType(nTypeIn), nVersion(nVersionIn)
diff --git a/src/sync.h b/src/sync.h
index 3c451af376..882ad5dc4c 100644
--- a/src/sync.h
+++ b/src/sync.h
@@ -74,13 +74,13 @@ public:
void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false);
void LeaveCritical();
std::string LocksHeld();
-void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs);
+void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) ASSERT_EXCLUSIVE_LOCK(cs);
void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs);
void DeleteLock(void* cs);
#else
void static inline EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false) {}
void static inline LeaveCritical() {}
-void static inline AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) {}
+void static inline AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) ASSERT_EXCLUSIVE_LOCK(cs) {}
void static inline AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) {}
void static inline DeleteLock(void* cs) {}
#endif
diff --git a/src/test/README.md b/src/test/README.md
index 01da32109b..f2a4cb1818 100644
--- a/src/test/README.md
+++ b/src/test/README.md
@@ -42,12 +42,12 @@ unit tests as possible).
The build system is setup to compile an executable called `test_bitcoin`
that runs all of the unit tests. The main source file is called
-test_bitcoin.cpp. To add a new unit test file to our test suite you need
-to add the file to `src/Makefile.test.include`. The pattern is to create
-one test file for each class or source file for which you want to create
-unit tests. The file naming convention is `<source_filename>_tests.cpp`
-and such files should wrap their tests in a test suite
-called `<source_filename>_tests`. For an example of this pattern,
+test_bitcoin.cpp. To add a new unit test file to our test suite you need
+to add the file to `src/Makefile.test.include`. The pattern is to create
+one test file for each class or source file for which you want to create
+unit tests. The file naming convention is `<source_filename>_tests.cpp`
+and such files should wrap their tests in a test suite
+called `<source_filename>_tests`. For an example of this pattern,
examine `uint256_tests.cpp`.
For further reading, I found the following website to be helpful in
diff --git a/src/test/data/key_io_invalid.json b/src/test/data/key_io_invalid.json
index 2056c7491c..9b52943ac6 100644
--- a/src/test/data/key_io_invalid.json
+++ b/src/test/data/key_io_invalid.json
@@ -1,151 +1,151 @@
[
[
""
- ],
+ ],
[
"x"
- ],
+ ],
[
"37qgekLpCCHrQuSjvX3fs496FWTGsHFHizjJAs6NPcR47aefnnCWECAhHV6E3g4YN7u7Yuwod5Y"
- ],
+ ],
[
"dzb7VV1Ui55BARxv7ATxAtCUeJsANKovDGWFVgpTbhq9gvPqP3yv"
- ],
+ ],
[
"MuNu7ZAEDFiHthiunm7dPjwKqrVNCM3mAz6rP9zFveQu14YA8CxExSJTHcVP9DErn6u84E6Ej7S"
- ],
+ ],
[
"rPpQpYknyNQ5AEHuY6H8ijJJrYc2nDKKk9jjmKEXsWzyAQcFGpDLU2Zvsmoi8JLR7hAwoy3RQWf"
- ],
+ ],
[
"4Uc3FmN6NQ6zLBK5QQBXRBUREaaHwCZYsGCueHauuDmJpZKn6jkEskMB2Zi2CNgtb5r6epWEFfUJq"
- ],
+ ],
[
"7aQgR5DFQ25vyXmqZAWmnVCjL3PkBcdVkBUpjrjMTcghHx3E8wb"
- ],
+ ],
[
"17QpPprjeg69fW1DV8DcYYCKvWjYhXvWkov6MJ1iTTvMFj6weAqW7wybZeH57WTNxXVCRH4veVs"
- ],
+ ],
[
"KxuACDviz8Xvpn1xAh9MfopySZNuyajYMZWz16Dv2mHHryznWUp3"
- ],
+ ],
[
"7nK3GSmqdXJQtdohvGfJ7KsSmn3TmGqExug49583bDAL91pVSGq5xS9SHoAYL3Wv3ijKTit65th"
- ],
+ ],
[
"cTivdBmq7bay3RFGEBBuNfMh2P1pDCgRYN2Wbxmgwr4ki3jNUL2va"
- ],
+ ],
[
"gjMV4vjNjyMrna4fsAr8bWxAbwtmMUBXJS3zL4NJt5qjozpbQLmAfK1uA3CquSqsZQMpoD1g2nk"
- ],
+ ],
[
"emXm1naBMoVzPjbk7xpeTVMFy4oDEe25UmoyGgKEB1gGWsK8kRGs"
- ],
+ ],
[
"7VThQnNRj1o3Zyvc7XHPRrjDf8j2oivPTeDXnRPYWeYGE4pXeRJDZgf28ppti5hsHWXS2GSobdqyo"
- ],
+ ],
[
"1G9u6oCVCPh2o8m3t55ACiYvG1y5BHewUkDSdiQarDcYXXhFHYdzMdYfUAhfxn5vNZBwpgUNpso"
- ],
+ ],
[
"31QQ7ZMLkScDiB4VyZjuptr7AEc9j1SjstF7pRoLhHTGkW4Q2y9XELobQmhhWxeRvqcukGd1XCq"
- ],
+ ],
[
"DHqKSnpxa8ZdQyH8keAhvLTrfkyBMQxqngcQA5N8LQ9KVt25kmGN"
- ],
+ ],
[
"2LUHcJPbwLCy9GLH1qXmfmAwvadWw4bp4PCpDfduLqV17s6iDcy1imUwhQJhAoNoN1XNmweiJP4i"
- ],
+ ],
[
"7USRzBXAnmck8fX9HmW7RAb4qt92VFX6soCnts9s74wxm4gguVhtG5of8fZGbNPJA83irHVY6bCos"
- ],
+ ],
[
"1DGezo7BfVebZxAbNT3XGujdeHyNNBF3vnficYoTSp4PfK2QaML9bHzAMxke3wdKdHYWmsMTJVu"
- ],
+ ],
[
"2D12DqDZKwCxxkzs1ZATJWvgJGhQ4cFi3WrizQ5zLAyhN5HxuAJ1yMYaJp8GuYsTLLxTAz6otCfb"
- ],
+ ],
[
"8AFJzuTujXjw1Z6M3fWhQ1ujDW7zsV4ePeVjVo7D1egERqSW9nZ"
- ],
+ ],
[
"163Q17qLbTCue8YY3AvjpUhotuaodLm2uqMhpYirsKjVqnxJRWTEoywMVY3NbBAHuhAJ2cF9GAZ"
- ],
+ ],
[
"2MnmgiRH4eGLyLc9eAqStzk7dFgBjFtUCtu"
- ],
+ ],
[
"461QQ2sYWxU7H2PV4oBwJGNch8XVTYYbZxU"
- ],
+ ],
[
"2UCtv53VttmQYkVU4VMtXB31REvQg4ABzs41AEKZ8UcB7DAfVzdkV9JDErwGwyj5AUHLkmgZeobs"
- ],
+ ],
[
"cSNjAsnhgtiFMi6MtfvgscMB2Cbhn2v1FUYfviJ1CdjfidvmeW6mn"
- ],
+ ],
[
"gmsow2Y6EWAFDFE1CE4Hd3Tpu2BvfmBfG1SXsuRARbnt1WjkZnFh1qGTiptWWbjsq2Q6qvpgJVj"
- ],
+ ],
[
"nksUKSkzS76v8EsSgozXGMoQFiCoCHzCVajFKAXqzK5on9ZJYVHMD5CKwgmX3S3c7M1U3xabUny"
- ],
+ ],
[
"L3favK1UzFGgdzYBF2oBT5tbayCo4vtVBLJhg2iYuMeePxWG8SQc"
- ],
+ ],
[
"7VxLxGGtYT6N99GdEfi6xz56xdQ8nP2dG1CavuXx7Rf2PrvNMTBNevjkfgs9JmkcGm6EXpj8ipyPZ"
- ],
+ ],
[
"2mbZwFXF6cxShaCo2czTRB62WTx9LxhTtpP"
- ],
+ ],
[
"dB7cwYdcPSgiyAwKWL3JwCVwSk6epU2txw"
- ],
+ ],
[
"HPhFUhUAh8ZQQisH8QQWafAxtQYju3SFTX"
- ],
+ ],
[
"4ctAH6AkHzq5ioiM1m9T3E2hiYEev5mTsB"
- ],
+ ],
[
"Hn1uFi4dNexWrqARpjMqgT6cX1UsNPuV3cHdGg9ExyXw8HTKadbktRDtdeVmY3M1BxJStiL4vjJ"
- ],
+ ],
[
"Sq3fDbvutABmnAHHExJDgPLQn44KnNC7UsXuT7KZecpaYDMU9Txs"
- ],
+ ],
[
"6TqWyrqdgUEYDQU1aChMuFMMEimHX44qHFzCUgGfqxGgZNMUVWJ"
- ],
+ ],
[
"giqJo7oWqFxNKWyrgcBxAVHXnjJ1t6cGoEffce5Y1y7u649Noj5wJ4mmiUAKEVVrYAGg2KPB3Y4"
- ],
+ ],
[
"cNzHY5e8vcmM3QVJUcjCyiKMYfeYvyueq5qCMV3kqcySoLyGLYUK"
- ],
+ ],
[
"37uTe568EYc9WLoHEd9jXEvUiWbq5LFLscNyqvAzLU5vBArUJA6eydkLmnMwJDjkL5kXc2VK7ig"
- ],
+ ],
[
"EsYbG4tWWWY45G31nox838qNdzksbPySWc"
- ],
+ ],
[
"nbuzhfwMoNzA3PaFnyLcRxE9bTJPDkjZ6Rf6Y6o2ckXZfzZzXBT"
- ],
+ ],
[
"cQN9PoxZeCWK1x56xnz6QYAsvR11XAce3Ehp3gMUdfSQ53Y2mPzx"
- ],
+ ],
[
"1Gm3N3rkef6iMbx4voBzaxtXcmmiMTqZPhcuAepRzYUJQW4qRpEnHvMojzof42hjFRf8PE2jPde"
- ],
+ ],
[
"2TAq2tuN6x6m233bpT7yqdYQPELdTDJn1eU"
- ],
+ ],
[
"ntEtnnGhqPii4joABvBtSEJG6BxjT2tUZqE8PcVYgk3RHpgxgHDCQxNbLJf7ardf1dDk2oCQ7Cf"
- ],
+ ],
[
"Ky1YjoZNgQ196HJV3HpdkecfhRBmRZdMJk89Hi5KGfpfPwS2bUbfd"
- ],
+ ],
[
"2A1q1YsMZowabbvta7kTy2Fd6qN4r5ZCeG3qLpvZBMzCixMUdkN2Y4dHB1wPsZAeVXUGD83MfRED"
],
diff --git a/src/test/data/script_tests.json b/src/test/data/script_tests.json
index 97edc98bf6..9b320b6943 100644
--- a/src/test/data/script_tests.json
+++ b/src/test/data/script_tests.json
@@ -712,7 +712,7 @@
["0x17 0x3014020002107777777777777777777777777777777701", "0 CHECKSIG NOT", "", "OK", "Zero-length R is correctly encoded"],
["0x17 0x3014021077777777777777777777777777777777020001", "0 CHECKSIG NOT", "", "OK", "Zero-length S is correctly encoded for DERSIG"],
["0x27 0x302402107777777777777777777777777777777702108777777777777777777777777777777701", "0 CHECKSIG NOT", "", "OK", "Negative S is correctly encoded"],
-
+
["2147483648", "CHECKSEQUENCEVERIFY", "CHECKSEQUENCEVERIFY", "OK", "CSV passes if stack top bit 1 << 31 is set"],
["", "DEPTH", "P2SH,STRICTENC", "EVAL_FALSE", "Test the test: we should have an empty stack after scriptSig evaluation"],
diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp
index 6df5aec9c9..5ba54ef9b0 100644
--- a/src/test/dbwrapper_tests.cpp
+++ b/src/test/dbwrapper_tests.cpp
@@ -20,14 +20,14 @@ static bool is_null_key(const std::vector<unsigned char>& key) {
return isnull;
}
-
+
BOOST_FIXTURE_TEST_SUITE(dbwrapper_tests, BasicTestingSetup)
-
+
BOOST_AUTO_TEST_CASE(dbwrapper)
{
// Perform tests both obfuscated and non-obfuscated.
for (bool obfuscate : {false, true}) {
- fs::path ph = fs::temp_directory_path() / fs::unique_path();
+ fs::path ph = SetDataDir(std::string("dbwrapper").append(obfuscate ? "_true" : "_false"));
CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate);
char key = 'k';
uint256 in = InsecureRand256();
@@ -47,7 +47,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper_batch)
{
// Perform tests both obfuscated and non-obfuscated.
for (bool obfuscate : {false, true}) {
- fs::path ph = fs::temp_directory_path() / fs::unique_path();
+ fs::path ph = SetDataDir(std::string("dbwrapper_batch").append(obfuscate ? "_true" : "_false"));
CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate);
char key = 'i';
@@ -83,7 +83,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper_iterator)
{
// Perform tests both obfuscated and non-obfuscated.
for (bool obfuscate : {false, true}) {
- fs::path ph = fs::temp_directory_path() / fs::unique_path();
+ fs::path ph = SetDataDir(std::string("dbwrapper_iterator").append(obfuscate ? "_true" : "_false"));
CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate);
// The two keys are intentionally chosen for ordering
@@ -123,7 +123,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper_iterator)
BOOST_AUTO_TEST_CASE(existing_data_no_obfuscate)
{
// We're going to share this fs::path between two wrappers
- fs::path ph = fs::temp_directory_path() / fs::unique_path();
+ fs::path ph = SetDataDir("existing_data_no_obfuscate");
create_directories(ph);
// Set up a non-obfuscated wrapper to write some initial data.
@@ -142,7 +142,7 @@ BOOST_AUTO_TEST_CASE(existing_data_no_obfuscate)
// Now, set up another wrapper that wants to obfuscate the same directory
CDBWrapper odbw(ph, (1 << 10), false, false, true);
- // Check that the key/val we wrote with unobfuscated wrapper exists and
+ // Check that the key/val we wrote with unobfuscated wrapper exists and
// is readable.
uint256 res2;
BOOST_CHECK(odbw.Read(key, res2));
@@ -153,18 +153,18 @@ BOOST_AUTO_TEST_CASE(existing_data_no_obfuscate)
uint256 in2 = InsecureRand256();
uint256 res3;
-
+
// Check that we can write successfully
BOOST_CHECK(odbw.Write(key, in2));
BOOST_CHECK(odbw.Read(key, res3));
BOOST_CHECK_EQUAL(res3.ToString(), in2.ToString());
}
-
+
// Ensure that we start obfuscating during a reindex.
BOOST_AUTO_TEST_CASE(existing_data_reindex)
{
// We're going to share this fs::path between two wrappers
- fs::path ph = fs::temp_directory_path() / fs::unique_path();
+ fs::path ph = SetDataDir("existing_data_reindex");
create_directories(ph);
// Set up a non-obfuscated wrapper to write some initial data.
@@ -190,7 +190,7 @@ BOOST_AUTO_TEST_CASE(existing_data_reindex)
uint256 in2 = InsecureRand256();
uint256 res3;
-
+
// Check that we can write successfully
BOOST_CHECK(odbw.Write(key, in2));
BOOST_CHECK(odbw.Read(key, res3));
@@ -199,7 +199,7 @@ BOOST_AUTO_TEST_CASE(existing_data_reindex)
BOOST_AUTO_TEST_CASE(iterator_ordering)
{
- fs::path ph = fs::temp_directory_path() / fs::unique_path();
+ fs::path ph = SetDataDir("iterator_ordering");
CDBWrapper dbw(ph, (1 << 20), true, false, false);
for (int x=0x00; x<256; ++x) {
uint8_t key = x;
@@ -277,7 +277,7 @@ BOOST_AUTO_TEST_CASE(iterator_string_ordering)
{
char buf[10];
- fs::path ph = fs::temp_directory_path() / fs::unique_path();
+ fs::path ph = SetDataDir("iterator_string_ordering");
CDBWrapper dbw(ph, (1 << 20), true, false, false);
for (int x=0x00; x<10; ++x) {
for (int y = 0; y < 10; y++) {
diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp
index cc871726fd..49037adb9a 100644
--- a/src/test/denialofservice_tests.cpp
+++ b/src/test/denialofservice_tests.cpp
@@ -24,6 +24,8 @@
extern bool AddOrphanTx(const CTransactionRef& tx, NodeId peer);
extern void EraseOrphansFor(NodeId peer);
extern unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans);
+extern void Misbehaving(NodeId nodeid, int howmuch, const std::string& message="");
+
struct COrphanTx {
CTransactionRef tx;
NodeId fromPeer;
diff --git a/src/test/limitedmap_tests.cpp b/src/test/limitedmap_tests.cpp
index a4bd63cdef..7871ff6f74 100644
--- a/src/test/limitedmap_tests.cpp
+++ b/src/test/limitedmap_tests.cpp
@@ -50,10 +50,10 @@ BOOST_AUTO_TEST_CASE(limitedmap_test)
// use the iterator to check for the expected key and value
BOOST_CHECK(it->first == i);
BOOST_CHECK(it->second == i + 1);
-
+
// use find to check for the value
BOOST_CHECK(map.find(i)->second == i + 1);
-
+
// update and recheck
map.update(it, i + 2);
BOOST_CHECK(map.find(i)->second == i + 2);
diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp
index 0264d29455..e2d76dc293 100644
--- a/src/test/mempool_tests.cpp
+++ b/src/test/mempool_tests.cpp
@@ -66,7 +66,7 @@ BOOST_AUTO_TEST_CASE(MempoolRemoveTest)
poolSize = testPool.size();
testPool.removeRecursive(txParent);
BOOST_CHECK_EQUAL(testPool.size(), poolSize - 1);
-
+
// Parent, children, grandchildren:
testPool.addUnchecked(txParent.GetHash(), entry.FromTx(txParent));
for (int i = 0; i < 3; i++)
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index 9a325f5f4c..10c7fd00e8 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -209,7 +209,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
const CChainParams& chainparams = *chainParams;
CScript scriptPubKey = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG;
std::unique_ptr<CBlockTemplate> pblocktemplate;
- CMutableTransaction tx,tx2;
+ CMutableTransaction tx;
CScript script;
uint256 hash;
TestMemPoolEntryHelper entry;
diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp
index 42e615ab0c..f7861d9b8a 100644
--- a/src/test/net_tests.cpp
+++ b/src/test/net_tests.cpp
@@ -89,6 +89,7 @@ BOOST_AUTO_TEST_CASE(cnode_listen_port)
BOOST_AUTO_TEST_CASE(caddrdb_read)
{
+ SetDataDir("caddrdb_read");
CAddrManUncorrupted addrmanUncorrupted;
addrmanUncorrupted.MakeDeterministic();
@@ -134,6 +135,7 @@ BOOST_AUTO_TEST_CASE(caddrdb_read)
BOOST_AUTO_TEST_CASE(caddrdb_read_corrupted)
{
+ SetDataDir("caddrdb_read_corrupted");
CAddrManCorrupted addrmanCorrupted;
addrmanCorrupted.MakeDeterministic();
@@ -171,7 +173,7 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test)
in_addr ipv4Addr;
ipv4Addr.s_addr = 0xa0b0c001;
-
+
CAddress addr = CAddress(CService(ipv4Addr, 7777), NODE_NETWORK);
std::string pszDest;
bool fInboundIn = false;
diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp
index bc90e5ae09..67f7c37f3b 100644
--- a/src/test/netbase_tests.cpp
+++ b/src/test/netbase_tests.cpp
@@ -82,10 +82,10 @@ bool static TestSplitHost(std::string test, std::string host, int port)
BOOST_AUTO_TEST_CASE(netbase_splithost)
{
- BOOST_CHECK(TestSplitHost("www.bitcoin.org", "www.bitcoin.org", -1));
- BOOST_CHECK(TestSplitHost("[www.bitcoin.org]", "www.bitcoin.org", -1));
- BOOST_CHECK(TestSplitHost("www.bitcoin.org:80", "www.bitcoin.org", 80));
- BOOST_CHECK(TestSplitHost("[www.bitcoin.org]:80", "www.bitcoin.org", 80));
+ BOOST_CHECK(TestSplitHost("www.bitcoincore.org", "www.bitcoincore.org", -1));
+ BOOST_CHECK(TestSplitHost("[www.bitcoincore.org]", "www.bitcoincore.org", -1));
+ BOOST_CHECK(TestSplitHost("www.bitcoincore.org:80", "www.bitcoincore.org", 80));
+ BOOST_CHECK(TestSplitHost("[www.bitcoincore.org]:80", "www.bitcoincore.org", 80));
BOOST_CHECK(TestSplitHost("127.0.0.1", "127.0.0.1", -1));
BOOST_CHECK(TestSplitHost("127.0.0.1:8333", "127.0.0.1", 8333));
BOOST_CHECK(TestSplitHost("[127.0.0.1]", "127.0.0.1", -1));
diff --git a/src/test/prevector_tests.cpp b/src/test/prevector_tests.cpp
index fe6f10d845..5f00009354 100644
--- a/src/test/prevector_tests.cpp
+++ b/src/test/prevector_tests.cpp
@@ -36,7 +36,7 @@ class prevector_tester {
{
local_check(a == b);
}
- void local_check(bool b)
+ void local_check(bool b)
{
passed &= b;
}
diff --git a/src/test/raii_event_tests.cpp b/src/test/raii_event_tests.cpp
index 7bc81c7f64..3dfa09e09e 100644
--- a/src/test/raii_event_tests.cpp
+++ b/src/test/raii_event_tests.cpp
@@ -41,7 +41,7 @@ BOOST_FIXTURE_TEST_SUITE(raii_event_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(raii_event_creation)
{
event_set_mem_functions(tag_malloc, realloc, tag_free);
-
+
void* base_ptr = nullptr;
{
auto base = obtain_event_base();
@@ -49,7 +49,7 @@ BOOST_AUTO_TEST_CASE(raii_event_creation)
BOOST_CHECK(tags[base_ptr] == 1);
}
BOOST_CHECK(tags[base_ptr] == 0);
-
+
void* event_ptr = nullptr;
{
auto base = obtain_event_base();
@@ -63,14 +63,14 @@ BOOST_AUTO_TEST_CASE(raii_event_creation)
}
BOOST_CHECK(tags[base_ptr] == 0);
BOOST_CHECK(tags[event_ptr] == 0);
-
+
event_set_mem_functions(malloc, realloc, free);
}
BOOST_AUTO_TEST_CASE(raii_event_order)
{
event_set_mem_functions(tag_malloc, realloc, tag_free);
-
+
void* base_ptr = nullptr;
void* event_ptr = nullptr;
{
diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp
index c7cdd7ca82..510910e149 100644
--- a/src/test/script_tests.cpp
+++ b/src/test/script_tests.cpp
@@ -515,7 +515,7 @@ BOOST_AUTO_TEST_CASE(script_build)
tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey0C) << OP_CHECKSIG,
"P2SH(P2PK), bad redeemscript", SCRIPT_VERIFY_P2SH, true
).PushSig(keys.key0).PushRedeem().DamagePush(10).ScriptError(SCRIPT_ERR_EVAL_FALSE));
-
+
tests.push_back(TestBuilder(CScript() << OP_DUP << OP_HASH160 << ToByteVector(keys.pubkey0.GetID()) << OP_EQUALVERIFY << OP_CHECKSIG,
"P2SH(P2PKH)", SCRIPT_VERIFY_P2SH, true
).PushSig(keys.key0).Push(keys.pubkey0).PushRedeem());
@@ -1495,4 +1495,146 @@ BOOST_AUTO_TEST_CASE(script_can_append_self)
BOOST_CHECK(s == d);
}
+
+#if defined(HAVE_CONSENSUS_LIB)
+
+/* Test simple (successful) usage of bitcoinconsensus_verify_script */
+BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_returns_true)
+{
+ unsigned int libconsensus_flags = 0;
+ int nIn = 0;
+
+ CScript scriptPubKey;
+ CScript scriptSig;
+ CScriptWitness wit;
+
+ scriptPubKey << OP_1;
+ CTransaction creditTx = BuildCreditingTransaction(scriptPubKey, 1);
+ CTransaction spendTx = BuildSpendingTransaction(scriptSig, wit, creditTx);
+
+ CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
+ stream << spendTx;
+
+ bitcoinconsensus_error err;
+ int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), (const unsigned char*)&stream[0], stream.size(), nIn, libconsensus_flags, &err);
+ BOOST_CHECK_EQUAL(result, 1);
+ BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_OK);
+}
+
+/* Test bitcoinconsensus_verify_script returns invalid tx index err*/
+BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_tx_index_err)
+{
+ unsigned int libconsensus_flags = 0;
+ int nIn = 3;
+
+ CScript scriptPubKey;
+ CScript scriptSig;
+ CScriptWitness wit;
+
+ scriptPubKey << OP_EQUAL;
+ CTransaction creditTx = BuildCreditingTransaction(scriptPubKey, 1);
+ CTransaction spendTx = BuildSpendingTransaction(scriptSig, wit, creditTx);
+
+ CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
+ stream << spendTx;
+
+ bitcoinconsensus_error err;
+ int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), (const unsigned char*)&stream[0], stream.size(), nIn, libconsensus_flags, &err);
+ BOOST_CHECK_EQUAL(result, 0);
+ BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_TX_INDEX);
+}
+
+/* Test bitcoinconsensus_verify_script returns tx size mismatch err*/
+BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_tx_size)
+{
+ unsigned int libconsensus_flags = 0;
+ int nIn = 0;
+
+ CScript scriptPubKey;
+ CScript scriptSig;
+ CScriptWitness wit;
+
+ scriptPubKey << OP_EQUAL;
+ CTransaction creditTx = BuildCreditingTransaction(scriptPubKey, 1);
+ CTransaction spendTx = BuildSpendingTransaction(scriptSig, wit, creditTx);
+
+ CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
+ stream << spendTx;
+
+ bitcoinconsensus_error err;
+ int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), (const unsigned char*)&stream[0], stream.size() * 2, nIn, libconsensus_flags, &err);
+ BOOST_CHECK_EQUAL(result, 0);
+ BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_TX_SIZE_MISMATCH);
+}
+
+/* Test bitcoinconsensus_verify_script returns invalid tx serialization error */
+BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_tx_serialization)
+{
+ unsigned int libconsensus_flags = 0;
+ int nIn = 0;
+
+ CScript scriptPubKey;
+ CScript scriptSig;
+ CScriptWitness wit;
+
+ scriptPubKey << OP_EQUAL;
+ CTransaction creditTx = BuildCreditingTransaction(scriptPubKey, 1);
+ CTransaction spendTx = BuildSpendingTransaction(scriptSig, wit, creditTx);
+
+ CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
+ stream << 0xffffffff;
+
+ bitcoinconsensus_error err;
+ int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), (const unsigned char*)&stream[0], stream.size(), nIn, libconsensus_flags, &err);
+ BOOST_CHECK_EQUAL(result, 0);
+ BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_TX_DESERIALIZE);
+}
+
+/* Test bitcoinconsensus_verify_script returns amount required error */
+BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_amount_required_err)
+{
+ unsigned int libconsensus_flags = bitcoinconsensus_SCRIPT_FLAGS_VERIFY_WITNESS;
+ int nIn = 0;
+
+ CScript scriptPubKey;
+ CScript scriptSig;
+ CScriptWitness wit;
+
+ scriptPubKey << OP_EQUAL;
+ CTransaction creditTx = BuildCreditingTransaction(scriptPubKey, 1);
+ CTransaction spendTx = BuildSpendingTransaction(scriptSig, wit, creditTx);
+
+ CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
+ stream << spendTx;
+
+ bitcoinconsensus_error err;
+ int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), (const unsigned char*)&stream[0], stream.size(), nIn, libconsensus_flags, &err);
+ BOOST_CHECK_EQUAL(result, 0);
+ BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_AMOUNT_REQUIRED);
+}
+
+/* Test bitcoinconsensus_verify_script returns invalid flags err */
+BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_invalid_flags)
+{
+ unsigned int libconsensus_flags = 1 << 3;
+ int nIn = 0;
+
+ CScript scriptPubKey;
+ CScript scriptSig;
+ CScriptWitness wit;
+
+ scriptPubKey << OP_EQUAL;
+ CTransaction creditTx = BuildCreditingTransaction(scriptPubKey, 1);
+ CTransaction spendTx = BuildSpendingTransaction(scriptSig, wit, creditTx);
+
+ CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
+ stream << spendTx;
+
+ bitcoinconsensus_error err;
+ int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), (const unsigned char*)&stream[0], stream.size(), nIn, libconsensus_flags, &err);
+ BOOST_CHECK_EQUAL(result, 0);
+ BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_INVALID_FLAGS);
+}
+
+#endif
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/serialize_tests.cpp b/src/test/serialize_tests.cpp
index eba58e0042..a0dafaaf5c 100644
--- a/src/test/serialize_tests.cpp
+++ b/src/test/serialize_tests.cpp
@@ -252,8 +252,8 @@ static bool isCanonicalException(const std::ios_base::failure& ex)
// The string returned by what() can be different for different platforms.
// Instead of directly comparing the ex.what() with an expected string,
- // create an instance of exception to see if ex.what() matches
- // the expected explanatory string returned by the exception instance.
+ // create an instance of exception to see if ex.what() matches
+ // the expected explanatory string returned by the exception instance.
return strcmp(expectedException.what(), ex.what()) == 0;
}
diff --git a/src/test/streams_tests.cpp b/src/test/streams_tests.cpp
index e1e77f7c92..8ca668ba01 100644
--- a/src/test/streams_tests.cpp
+++ b/src/test/streams_tests.cpp
@@ -76,19 +76,19 @@ BOOST_AUTO_TEST_CASE(streams_serializedata_xor)
CDataStream ds(in, 0, 0);
// Degenerate case
-
+
key.push_back('\x00');
key.push_back('\x00');
ds.Xor(key);
BOOST_CHECK_EQUAL(
- std::string(expected_xor.begin(), expected_xor.end()),
+ std::string(expected_xor.begin(), expected_xor.end()),
std::string(ds.begin(), ds.end()));
in.push_back('\x0f');
in.push_back('\xf0');
expected_xor.push_back('\xf0');
expected_xor.push_back('\x0f');
-
+
// Single character key
ds.clear();
@@ -98,9 +98,9 @@ BOOST_AUTO_TEST_CASE(streams_serializedata_xor)
key.push_back('\xff');
ds.Xor(key);
BOOST_CHECK_EQUAL(
- std::string(expected_xor.begin(), expected_xor.end()),
- std::string(ds.begin(), ds.end()));
-
+ std::string(expected_xor.begin(), expected_xor.end()),
+ std::string(ds.begin(), ds.end()));
+
// Multi character key
in.clear();
@@ -109,7 +109,7 @@ BOOST_AUTO_TEST_CASE(streams_serializedata_xor)
in.push_back('\x0f');
expected_xor.push_back('\x0f');
expected_xor.push_back('\x00');
-
+
ds.clear();
ds.insert(ds.begin(), in.begin(), in.end());
@@ -119,8 +119,8 @@ BOOST_AUTO_TEST_CASE(streams_serializedata_xor)
ds.Xor(key);
BOOST_CHECK_EQUAL(
- std::string(expected_xor.begin(), expected_xor.end()),
- std::string(ds.begin(), ds.end()));
-}
+ std::string(expected_xor.begin(), expected_xor.end()),
+ std::string(ds.begin(), ds.end()));
+}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp
index e9814edc23..b35b21335e 100644
--- a/src/test/test_bitcoin.cpp
+++ b/src/test/test_bitcoin.cpp
@@ -46,35 +46,43 @@ std::ostream& operator<<(std::ostream& os, const uint256& num)
}
BasicTestingSetup::BasicTestingSetup(const std::string& chainName)
+ : m_path_root(fs::temp_directory_path() / "test_bitcoin" / strprintf("%lu_%i", (unsigned long)GetTime(), (int)(InsecureRandRange(1 << 30))))
{
- SHA256AutoDetect();
- RandomInit();
- ECC_Start();
- SetupEnvironment();
- SetupNetworking();
- InitSignatureCache();
- InitScriptExecutionCache();
- fCheckBlockIndex = true;
- SelectParams(chainName);
- noui_connect();
+ SHA256AutoDetect();
+ RandomInit();
+ ECC_Start();
+ SetupEnvironment();
+ SetupNetworking();
+ InitSignatureCache();
+ InitScriptExecutionCache();
+ fCheckBlockIndex = true;
+ SelectParams(chainName);
+ noui_connect();
}
BasicTestingSetup::~BasicTestingSetup()
{
- ECC_Stop();
+ fs::remove_all(m_path_root);
+ ECC_Stop();
+}
+
+fs::path BasicTestingSetup::SetDataDir(const std::string& name)
+{
+ fs::path ret = m_path_root / name;
+ fs::create_directories(ret);
+ gArgs.ForceSetArg("-datadir", ret.string());
+ return ret;
}
TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(chainName)
{
+ SetDataDir("tempdir");
const CChainParams& chainparams = Params();
// Ideally we'd move all the RPC tests to the functional testing framework
// instead of unit tests, but for now we need these here.
RegisterAllCoreRPCCommands(tableRPC);
ClearDatadirCache();
- pathTemp = fs::temp_directory_path() / strprintf("test_bitcoin_%lu_%i", (unsigned long)GetTime(), (int)(InsecureRandRange(1 << 30)));
- fs::create_directories(pathTemp);
- gArgs.ForceSetArg("-datadir", pathTemp.string());
// We have to run a scheduler thread to prevent ActivateBestChain
// from blocking due to queue overrun.
@@ -99,7 +107,7 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
threadGroup.create_thread(&ThreadScriptCheck);
g_connman = std::unique_ptr<CConnman>(new CConnman(0x1337, 0x1337)); // Deterministic randomness for tests.
connman = g_connman.get();
- peerLogic.reset(new PeerLogicValidation(connman, scheduler));
+ peerLogic.reset(new PeerLogicValidation(connman, scheduler, /*enable_bip61=*/true));
}
TestingSetup::~TestingSetup()
@@ -114,7 +122,6 @@ TestingSetup::~TestingSetup()
pcoinsTip.reset();
pcoinsdbview.reset();
pblocktree.reset();
- fs::remove_all(pathTemp);
}
TestChain100Setup::TestChain100Setup() : TestingSetup(CBaseChainParams::REGTEST)
diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h
index d013613de2..88b2d37e87 100644
--- a/src/test/test_bitcoin.h
+++ b/src/test/test_bitcoin.h
@@ -45,6 +45,11 @@ struct BasicTestingSetup {
explicit BasicTestingSetup(const std::string& chainName = CBaseChainParams::MAIN);
~BasicTestingSetup();
+
+ fs::path SetDataDir(const std::string& name);
+
+private:
+ const fs::path m_path_root;
};
/** Testing setup that configures a complete environment.
@@ -59,7 +64,6 @@ struct CConnmanTest {
class PeerLogicValidation;
struct TestingSetup: public BasicTestingSetup {
- fs::path pathTemp;
boost::thread_group threadGroup;
CConnman* connman;
CScheduler scheduler;
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index 45dc0e3571..aff270942e 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -547,7 +547,6 @@ BOOST_AUTO_TEST_CASE(test_witness)
CTransactionRef output1, output2;
CMutableTransaction input1, input2;
- SignatureData sigdata;
// Normal pay-to-compressed-pubkey.
CreateCreditAndSpend(keystore, scriptPubkey1, output1, input1);
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index 611ccc9b77..3661c762fc 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -806,6 +806,21 @@ BOOST_AUTO_TEST_CASE(gettime)
BOOST_CHECK((GetTime() & ~0xFFFFFFFFLL) == 0);
}
+BOOST_AUTO_TEST_CASE(test_IsDigit)
+{
+ BOOST_CHECK_EQUAL(IsDigit('0'), true);
+ BOOST_CHECK_EQUAL(IsDigit('1'), true);
+ BOOST_CHECK_EQUAL(IsDigit('8'), true);
+ BOOST_CHECK_EQUAL(IsDigit('9'), true);
+
+ BOOST_CHECK_EQUAL(IsDigit('0' - 1), false);
+ BOOST_CHECK_EQUAL(IsDigit('9' + 1), false);
+ BOOST_CHECK_EQUAL(IsDigit(0), false);
+ BOOST_CHECK_EQUAL(IsDigit(1), false);
+ BOOST_CHECK_EQUAL(IsDigit(8), false);
+ BOOST_CHECK_EQUAL(IsDigit(9), false);
+}
+
BOOST_AUTO_TEST_CASE(test_ParseInt32)
{
int32_t n;
@@ -1100,7 +1115,7 @@ static void TestOtherProcess(fs::path dirname, std::string lockname, int fd)
BOOST_AUTO_TEST_CASE(test_LockDirectory)
{
- fs::path dirname = fs::temp_directory_path() / fs::unique_path();
+ fs::path dirname = SetDataDir("test_LockDirectory") / fs::unique_path();
const std::string lockname = ".lock";
#ifndef WIN32
// Revert SIGCHLD to default, otherwise boost.test will catch and fail on
@@ -1188,12 +1203,12 @@ BOOST_AUTO_TEST_CASE(test_LockDirectory)
BOOST_AUTO_TEST_CASE(test_DirIsWritable)
{
- // Should be able to write to the system tmp dir.
- fs::path tmpdirname = fs::temp_directory_path();
+ // Should be able to write to the data dir.
+ fs::path tmpdirname = SetDataDir("test_DirIsWritable");
BOOST_CHECK_EQUAL(DirIsWritable(tmpdirname), true);
// Should not be able to write to a non-existent dir.
- tmpdirname = fs::temp_directory_path() / fs::unique_path();
+ tmpdirname = tmpdirname / fs::unique_path();
BOOST_CHECK_EQUAL(DirIsWritable(tmpdirname), false);
fs::create_directory(tmpdirname);
diff --git a/src/threadsafety.h b/src/threadsafety.h
index 61e63dbc7a..d847269dff 100644
--- a/src/threadsafety.h
+++ b/src/threadsafety.h
@@ -31,6 +31,7 @@
#define EXCLUSIVE_LOCKS_REQUIRED(...) __attribute__((exclusive_locks_required(__VA_ARGS__)))
#define SHARED_LOCKS_REQUIRED(...) __attribute__((shared_locks_required(__VA_ARGS__)))
#define NO_THREAD_SAFETY_ANALYSIS __attribute__((no_thread_safety_analysis))
+#define ASSERT_EXCLUSIVE_LOCK(...) __attribute((assert_exclusive_lock(__VA_ARGS__)))
#else
#define LOCKABLE
#define SCOPED_LOCKABLE
@@ -50,6 +51,7 @@
#define EXCLUSIVE_LOCKS_REQUIRED(...)
#define SHARED_LOCKS_REQUIRED(...)
#define NO_THREAD_SAFETY_ANALYSIS
+#define ASSERT_EXCLUSIVE_LOCK(...)
#endif // __GNUC__
#endif // BITCOIN_THREADSAFETY_H
diff --git a/src/timedata.h b/src/timedata.h
index 23584f67e5..a4fefb5983 100644
--- a/src/timedata.h
+++ b/src/timedata.h
@@ -14,7 +14,7 @@ static const int64_t DEFAULT_MAX_TIME_ADJUSTMENT = 70 * 60;
class CNetAddr;
-/**
+/**
* Median filter over a stream of values.
* Returns the median of the last N numbers
*/
diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp
index 1f42ab8fa8..fbc193c2cf 100644
--- a/src/torcontrol.cpp
+++ b/src/torcontrol.cpp
@@ -91,7 +91,7 @@ public:
/**
* Disconnect from Tor control port.
*/
- bool Disconnect();
+ void Disconnect();
/** Send a command, register a handler for the reply.
* A trailing CRLF is automatically added.
@@ -223,12 +223,11 @@ bool TorControlConnection::Connect(const std::string &target, const ConnectionCB
return true;
}
-bool TorControlConnection::Disconnect()
+void TorControlConnection::Disconnect()
{
if (b_conn)
bufferevent_free(b_conn);
b_conn = nullptr;
- return true;
}
bool TorControlConnection::Command(const std::string &cmd, const ReplyHandlerCB& reply_handler)
diff --git a/src/txdb.cpp b/src/txdb.cpp
index 3635d0ab4b..cbea550739 100644
--- a/src/txdb.cpp
+++ b/src/txdb.cpp
@@ -53,7 +53,7 @@ struct CoinEntry {
}
-CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / "chainstate", nCacheSize, fMemory, fWipe, true)
+CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / "chainstate", nCacheSize, fMemory, fWipe, true)
{
}
@@ -160,9 +160,8 @@ bool CBlockTreeDB::WriteReindexing(bool fReindexing) {
return Erase(DB_REINDEX_FLAG);
}
-bool CBlockTreeDB::ReadReindexing(bool &fReindexing) {
+void CBlockTreeDB::ReadReindexing(bool &fReindexing) {
fReindexing = Exists(DB_REINDEX_FLAG);
- return true;
}
bool CBlockTreeDB::ReadLastBlockFile(int &nFile) {
diff --git a/src/txdb.h b/src/txdb.h
index 100adb428d..84011097ce 100644
--- a/src/txdb.h
+++ b/src/txdb.h
@@ -92,7 +92,7 @@ public:
bool ReadBlockFileInfo(int nFile, CBlockFileInfo &info);
bool ReadLastBlockFile(int &nFile);
bool WriteReindexing(bool fReindexing);
- bool ReadReindexing(bool &fReindexing);
+ void ReadReindexing(bool &fReindexing);
bool WriteFlag(const std::string &name, bool fValue);
bool ReadFlag(const std::string &name, bool &fValue);
bool LoadBlockIndexGuts(const Consensus::Params& consensusParams, std::function<CBlockIndex*(const uint256&)> insertBlockIndex);
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 8090172e3f..9d705e3d23 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -284,7 +284,7 @@ void CTxMemPool::UpdateForRemoveFromMempool(const setEntries &entriesToRemove, b
// should be a bit faster.
// However, if we happen to be in the middle of processing a reorg, then
// the mempool can be in an inconsistent state. In this case, the set
- // of ancestors reachable via mapLinks will be the same as the set of
+ // of ancestors reachable via mapLinks will be the same as the set of
// ancestors whose packages include this transaction, because when we
// add a new transaction to the mempool in addUnchecked(), we assume it
// has no children, and in the case of a reorg where that assumption is
@@ -357,7 +357,7 @@ void CTxMemPool::AddTransactionsUpdated(unsigned int n)
nTransactionsUpdated += n;
}
-bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, setEntries &setAncestors, bool validFeeEstimate)
+void CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, setEntries &setAncestors, bool validFeeEstimate)
{
NotifyEntryAdded(entry.GetSharedTx());
// Add to memory pool without checking anything.
@@ -412,8 +412,6 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry,
vTxHashes.emplace_back(tx.GetWitnessHash(), newit);
newit->vTxHashesIdx = vTxHashes.size() - 1;
-
- return true;
}
void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
@@ -542,7 +540,7 @@ void CTxMemPool::removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMem
void CTxMemPool::removeConflicts(const CTransaction &tx)
{
// Remove transactions which depend on inputs of tx, recursively
- LOCK(cs);
+ AssertLockHeld(cs);
for (const CTxIn &txin : tx.vin) {
auto it = mapNextTx.find(txin.prevout);
if (it != mapNextTx.end()) {
@@ -716,7 +714,6 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
while (!waitingOnDependants.empty()) {
const CTxMemPoolEntry* entry = waitingOnDependants.front();
waitingOnDependants.pop_front();
- CValidationState state;
if (!mempoolDuplicate.HaveInputs(entry->GetTx())) {
waitingOnDependants.push_back(entry);
stepsSinceLastRemove++;
@@ -934,7 +931,7 @@ int CTxMemPool::Expire(int64_t time) {
return stage.size();
}
-bool CTxMemPool::addUnchecked(const uint256&hash, const CTxMemPoolEntry &entry, bool validFeeEstimate)
+void CTxMemPool::addUnchecked(const uint256&hash, const CTxMemPoolEntry &entry, bool validFeeEstimate)
{
LOCK(cs);
setEntries setAncestors;
diff --git a/src/txmempool.h b/src/txmempool.h
index ebfcf36e11..bb676cf05d 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -539,12 +539,12 @@ public:
// Note that addUnchecked is ONLY called from ATMP outside of tests
// and any other callers may break wallet's in-mempool tracking (due to
// lack of CValidationInterface::TransactionAddedToMempool callbacks).
- bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, bool validFeeEstimate = true);
- bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, setEntries &setAncestors, bool validFeeEstimate = true);
+ void addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, bool validFeeEstimate = true);
+ void addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, setEntries &setAncestors, bool validFeeEstimate = true);
void removeRecursive(const CTransaction &tx, MemPoolRemovalReason reason = MemPoolRemovalReason::UNKNOWN);
void removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags);
- void removeConflicts(const CTransaction &tx);
+ void removeConflicts(const CTransaction &tx) EXCLUSIVE_LOCKS_REQUIRED(cs);
void removeForBlock(const std::vector<CTransactionRef>& vtx, unsigned int nBlockHeight);
void clear();
@@ -692,7 +692,7 @@ private:
void removeUnchecked(txiter entry, MemPoolRemovalReason reason = MemPoolRemovalReason::UNKNOWN) EXCLUSIVE_LOCKS_REQUIRED(cs);
};
-/**
+/**
* CCoinsView that brings transactions from a mempool into view.
* It does not check for spendings by memory pool transactions.
* Instead, it provides access to all Coins which are either unspent in the
diff --git a/src/util.cpp b/src/util.cpp
index ab262b4063..2f81f50a71 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -72,7 +72,6 @@
#endif
#include <boost/interprocess/sync/file_lock.hpp>
-#include <boost/program_options/detail/config_file.hpp>
#include <boost/thread.hpp>
#include <openssl/crypto.h>
#include <openssl/rand.h>
@@ -447,7 +446,7 @@ bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::strin
// Check that the arg is known
if (!(IsSwitchChar(key[0]) && key.size() == 1)) {
- if (!IsArgKnown(key, error)) {
+ if (!IsArgKnown(key)) {
error = strprintf("Invalid parameter %s", key.c_str());
return false;
}
@@ -467,7 +466,7 @@ bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::strin
return true;
}
-bool ArgsManager::IsArgKnown(const std::string& key, std::string& error)
+bool ArgsManager::IsArgKnown(const std::string& key) const
{
size_t option_index = key.find('.');
std::string arg_no_net;
@@ -592,7 +591,7 @@ void ArgsManager::AddHiddenArgs(const std::vector<std::string>& names)
}
}
-std::string ArgsManager::GetHelpMessage()
+std::string ArgsManager::GetHelpMessage() const
{
const bool show_debug = gArgs.GetBoolArg("-help-debug", false);
@@ -811,17 +810,47 @@ fs::path GetConfigFile(const std::string& confPath)
return AbsPathForConfigVal(fs::path(confPath), false);
}
+static std::string TrimString(const std::string& str, const std::string& pattern)
+{
+ std::string::size_type front = str.find_first_not_of(pattern);
+ if (front == std::string::npos) {
+ return std::string();
+ }
+ std::string::size_type end = str.find_last_not_of(pattern);
+ return str.substr(front, end - front + 1);
+}
+
+static std::vector<std::pair<std::string, std::string>> GetConfigOptions(std::istream& stream)
+{
+ std::vector<std::pair<std::string, std::string>> options;
+ std::string str, prefix;
+ std::string::size_type pos;
+ while (std::getline(stream, str)) {
+ if ((pos = str.find('#')) != std::string::npos) {
+ str = str.substr(0, pos);
+ }
+ const static std::string pattern = " \t\r\n";
+ str = TrimString(str, pattern);
+ if (!str.empty()) {
+ if (*str.begin() == '[' && *str.rbegin() == ']') {
+ prefix = str.substr(1, str.size() - 2) + '.';
+ } else if ((pos = str.find('=')) != std::string::npos) {
+ std::string name = prefix + TrimString(str.substr(0, pos), pattern);
+ std::string value = TrimString(str.substr(pos + 1), pattern);
+ options.emplace_back(name, value);
+ }
+ }
+ }
+ return options;
+}
+
bool ArgsManager::ReadConfigStream(std::istream& stream, std::string& error, bool ignore_invalid_keys)
{
LOCK(cs_args);
- std::set<std::string> setOptions;
- setOptions.insert("*");
-
- for (boost::program_options::detail::config_file_iterator it(stream, setOptions), end; it != end; ++it)
- {
- std::string strKey = std::string("-") + it->string_key;
- std::string strValue = it->value[0];
+ for (const std::pair<std::string, std::string>& option : GetConfigOptions(stream)) {
+ std::string strKey = std::string("-") + option.first;
+ std::string strValue = option.second;
if (InterpretNegatedOption(strKey, strValue)) {
m_config_args[strKey].clear();
@@ -830,8 +859,8 @@ bool ArgsManager::ReadConfigStream(std::istream& stream, std::string& error, boo
}
// Check that the arg is known
- if (!IsArgKnown(strKey, error) && !ignore_invalid_keys) {
- error = strprintf("Invalid configuration value %s", it->string_key.c_str());
+ if (!IsArgKnown(strKey) && !ignore_invalid_keys) {
+ error = strprintf("Invalid configuration value %s", option.first.c_str());
return false;
}
}
@@ -992,7 +1021,7 @@ bool FileCommit(FILE *file)
LogPrintf("%s: fdatasync failed: %d\n", __func__, errno);
return false;
}
- #elif defined(__APPLE__) && defined(F_FULLFSYNC)
+ #elif defined(MAC_OSX) && defined(F_FULLFSYNC)
if (fcntl(fileno(file), F_FULLFSYNC, 0) == -1) { // Manpage says "value other than -1" is returned on success
LogPrintf("%s: fcntl F_FULLFSYNC failed: %d\n", __func__, errno);
return false;
diff --git a/src/util.h b/src/util.h
index 8094d72d6b..23b2a787e4 100644
--- a/src/util.h
+++ b/src/util.h
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2017 The Bitcoin Core developers
+// Copyright (c) 2009-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -20,11 +20,11 @@
#include <sync.h>
#include <tinyformat.h>
#include <utiltime.h>
+#include <utilmemory.h>
#include <atomic>
#include <exception>
#include <map>
-#include <memory>
#include <set>
#include <stdint.h>
#include <string>
@@ -276,12 +276,12 @@ public:
/**
* Get the help string
*/
- std::string GetHelpMessage();
+ std::string GetHelpMessage() const;
/**
* Check whether we know of this arg
*/
- bool IsArgKnown(const std::string& key, std::string& error);
+ bool IsArgKnown(const std::string& key) const;
};
extern ArgsManager gArgs;
@@ -346,13 +346,6 @@ template <typename Callable> void TraceThread(const char* name, Callable func)
std::string CopyrightHolders(const std::string& strPrefix);
-//! Substitute for C++14 std::make_unique.
-template <typename T, typename... Args>
-std::unique_ptr<T> MakeUnique(Args&&... args)
-{
- return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
-}
-
/**
* On platforms that support it, tell the kernel the calling thread is
* CPU-intensive and non-interactive. See SCHED_BATCH in sched(7) for details.
@@ -362,4 +355,18 @@ std::unique_ptr<T> MakeUnique(Args&&... args)
*/
int ScheduleBatchPriority(void);
+namespace util {
+
+//! Simplification of std insertion
+template <typename Tdst, typename Tsrc>
+inline void insert(Tdst& dst, const Tsrc& src) {
+ dst.insert(dst.begin(), src.begin(), src.end());
+}
+template <typename TsetT, typename Tsrc>
+inline void insert(std::set<TsetT>& dst, const Tsrc& src) {
+ dst.insert(src.begin(), src.end());
+}
+
+} // namespace util
+
#endif // BITCOIN_UTIL_H
diff --git a/src/utilmemory.h b/src/utilmemory.h
new file mode 100644
index 0000000000..e71fe92284
--- /dev/null
+++ b/src/utilmemory.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_UTILMEMORY_H
+#define BITCOIN_UTILMEMORY_H
+
+#include <memory>
+#include <utility>
+
+//! Substitute for C++14 std::make_unique.
+template <typename T, typename... Args>
+std::unique_ptr<T> MakeUnique(Args&&... args)
+{
+ return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+}
+
+#endif
diff --git a/src/utilstrencodings.cpp b/src/utilstrencodings.cpp
index d1025fc7bf..c6927021cb 100644
--- a/src/utilstrencodings.cpp
+++ b/src/utilstrencodings.cpp
@@ -473,7 +473,7 @@ bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out)
/* pass single 0 */
++ptr;
} else if (val[ptr] >= '1' && val[ptr] <= '9') {
- while (ptr < end && val[ptr] >= '0' && val[ptr] <= '9') {
+ while (ptr < end && IsDigit(val[ptr])) {
if (!ProcessMantissaDigit(val[ptr], mantissa, mantissa_tzeros))
return false; /* overflow */
++ptr;
@@ -483,9 +483,9 @@ bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out)
if (ptr < end && val[ptr] == '.')
{
++ptr;
- if (ptr < end && val[ptr] >= '0' && val[ptr] <= '9')
+ if (ptr < end && IsDigit(val[ptr]))
{
- while (ptr < end && val[ptr] >= '0' && val[ptr] <= '9') {
+ while (ptr < end && IsDigit(val[ptr])) {
if (!ProcessMantissaDigit(val[ptr], mantissa, mantissa_tzeros))
return false; /* overflow */
++ptr;
@@ -502,8 +502,8 @@ bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out)
exponent_sign = true;
++ptr;
}
- if (ptr < end && val[ptr] >= '0' && val[ptr] <= '9') {
- while (ptr < end && val[ptr] >= '0' && val[ptr] <= '9') {
+ if (ptr < end && IsDigit(val[ptr])) {
+ while (ptr < end && IsDigit(val[ptr])) {
if (exponent > (UPPER_BOUND / 10LL))
return false; /* overflow */
exponent = exponent * 10 + val[ptr] - '0';
diff --git a/src/utilstrencodings.h b/src/utilstrencodings.h
index 1c9cca90b2..47f9afba1c 100644
--- a/src/utilstrencodings.h
+++ b/src/utilstrencodings.h
@@ -62,6 +62,16 @@ int64_t atoi64(const std::string& str);
int atoi(const std::string& str);
/**
+ * Tests if the given character is a decimal digit.
+ * @param[in] c character to test
+ * @return true if the argument is a decimal digit; otherwise false.
+ */
+constexpr bool IsDigit(char c)
+{
+ return c >= '0' && c <= '9';
+}
+
+/**
* Convert string to signed 32-bit integer with strict parse error feedback.
* @returns true if the entire string could be parsed as valid integer,
* false if not the entire string could be parsed or when overflow or underflow occurred.
diff --git a/src/validation.cpp b/src/validation.cpp
index 9921063a52..702a8d7e05 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -45,7 +45,6 @@
#include <sstream>
#include <boost/algorithm/string/replace.hpp>
-#include <boost/algorithm/string/join.hpp>
#include <boost/thread.hpp>
#if defined(NDEBUG)
@@ -179,7 +178,7 @@ public:
// Manual block validity manipulation:
bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main);
bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- bool ResetBlockFailureFlags(CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ void ResetBlockFailureFlags(CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
bool ReplayBlocks(const CChainParams& params, CCoinsView* view);
bool RewindBlockIndex(const CChainParams& params);
@@ -806,13 +805,11 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
// be increased is also an easy-to-reason about way to prevent
// DoS attacks via replacements.
//
- // The mining code doesn't (currently) take children into
- // account (CPFP) so we only consider the feerates of
- // transactions being directly replaced, not their indirect
- // descendants. While that does mean high feerate children are
- // ignored when deciding whether or not to replace, we do
- // require the replacement to pay more overall fees too,
- // mitigating most cases.
+ // We only consider the feerates of transactions being directly
+ // replaced, not their indirect descendants. While that does
+ // mean high feerate children are ignored when deciding whether
+ // or not to replace, we do require the replacement to pay more
+ // overall fees too, mitigating most cases.
CFeeRate oldFeeRate(mi->GetModifiedFee(), mi->GetTxSize());
if (newFeeRate <= oldFeeRate)
{
@@ -2244,6 +2241,13 @@ static void DoWarning(const std::string& strWarning)
}
}
+/** Private helper function that concatenates warning messages. */
+static void AppendWarning(std::string& res, const std::string& warn)
+{
+ if (!res.empty()) res += ", ";
+ res += warn;
+}
+
/** Check warning conditions and do some notifications on new chain tip set. */
void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams& chainParams) {
// New best block
@@ -2255,7 +2259,7 @@ void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams& chainPar
g_best_block_cv.notify_all();
}
- std::vector<std::string> warningMessages;
+ std::string warningMessages;
if (!IsInitialBlockDownload())
{
int nUpgraded = 0;
@@ -2268,7 +2272,7 @@ void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams& chainPar
if (state == ThresholdState::ACTIVE) {
DoWarning(strWarning);
} else {
- warningMessages.push_back(strWarning);
+ AppendWarning(warningMessages, strWarning);
}
}
}
@@ -2281,7 +2285,7 @@ void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams& chainPar
pindex = pindex->pprev;
}
if (nUpgraded > 0)
- warningMessages.push_back(strprintf(_("%d of last 100 blocks have unexpected version"), nUpgraded));
+ AppendWarning(warningMessages, strprintf(_("%d of last 100 blocks have unexpected version"), nUpgraded));
if (nUpgraded > 100/2)
{
std::string strWarning = _("Warning: Unknown block versions being mined! It's possible unknown rules are in effect");
@@ -2295,7 +2299,7 @@ void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams& chainPar
FormatISO8601DateTime(pindexNew->GetBlockTime()),
GuessVerificationProgress(chainParams.TxData(), pindexNew), pcoinsTip->DynamicMemoryUsage() * (1.0 / (1<<20)), pcoinsTip->GetCacheSize());
if (!warningMessages.empty())
- LogPrintf(" warning='%s'", boost::algorithm::join(warningMessages, ", ")); /* Continued */
+ LogPrintf(" warning='%s'", warningMessages); /* Continued */
LogPrintf("\n");
}
@@ -2878,7 +2882,7 @@ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, C
return g_chainstate.InvalidateBlock(state, chainparams, pindex);
}
-bool CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
+void CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
AssertLockHeld(cs_main);
int nHeight = pindex->nHeight;
@@ -2910,9 +2914,9 @@ bool CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
}
pindex = pindex->pprev;
}
- return true;
}
-bool ResetBlockFailureFlags(CBlockIndex *pindex) {
+
+void ResetBlockFailureFlags(CBlockIndex *pindex) {
return g_chainstate.ResetBlockFailureFlags(pindex);
}
@@ -3729,6 +3733,15 @@ static void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfte
int count=0;
if (nCurrentUsage + nBuffer >= nPruneTarget) {
+ // On a prune event, the chainstate DB is flushed.
+ // To avoid excessive prune events negating the benefit of high dbcache
+ // values, we should not prune too rapidly.
+ // So when pruning in IBD, increase the buffer a bit to avoid a re-prune too soon.
+ if (IsInitialBlockDownload()) {
+ // Since this is only relevant during IBD, we use a fixed 10%
+ nBuffer += nPruneTarget / 10;
+ }
+
for (int fileNumber = 0; fileNumber < nLastBlockFile; fileNumber++) {
nBytesToPrune = vinfoBlockFile[fileNumber].nSize + vinfoBlockFile[fileNumber].nUndoSize;
diff --git a/src/validation.h b/src/validation.h
index 07fe99c079..ce46a5a782 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -104,20 +104,6 @@ static const unsigned int DATABASE_WRITE_INTERVAL = 60 * 60;
static const unsigned int DATABASE_FLUSH_INTERVAL = 24 * 60 * 60;
/** Maximum length of reject messages. */
static const unsigned int MAX_REJECT_MESSAGE_LENGTH = 111;
-/** Average delay between local address broadcasts in seconds. */
-static const unsigned int AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL = 24 * 60 * 60;
-/** Average delay between peer address broadcasts in seconds. */
-static const unsigned int AVG_ADDRESS_BROADCAST_INTERVAL = 30;
-/** Average delay between trickled inventory transmissions in seconds.
- * Blocks and whitelisted receivers bypass this, outbound peers get half this delay. */
-static const unsigned int INVENTORY_BROADCAST_INTERVAL = 5;
-/** Maximum number of inventory items to send per transmission.
- * Limits the impact of low-fee transaction floods. */
-static const unsigned int INVENTORY_BROADCAST_MAX = 7 * INVENTORY_BROADCAST_INTERVAL;
-/** Average delay between feefilter broadcasts in seconds. */
-static const unsigned int AVG_FEEFILTER_BROADCAST_INTERVAL = 10 * 60;
-/** Maximum feefilter broadcast delay after significant change. */
-static const unsigned int MAX_FEEFILTER_CHANGE_DELAY = 5 * 60;
/** Block download timeout base, expressed in millionths of the block interval (i.e. 10 min) */
static const int64_t BLOCK_DOWNLOAD_TIMEOUT_BASE = 1000000;
/** Additional block download timeout per parallel downloading peer (i.e. 5 min) */
@@ -221,7 +207,7 @@ static const unsigned int DEFAULT_CHECKLEVEL = 3;
// Setting the target to > than 550MB will make it likely we can respect the target.
static const uint64_t MIN_DISK_SPACE_FOR_BLOCK_FILES = 550 * 1024 * 1024;
-/**
+/**
* Process an incoming block. This only returns after the best known valid
* block is made active. Note that it does not, however, guarantee that the
* specific block passed to it has been checked for validity!
@@ -232,7 +218,7 @@ static const uint64_t MIN_DISK_SPACE_FOR_BLOCK_FILES = 550 * 1024 * 1024;
*
* Note that we guarantee that either the proof-of-work is valid on pblock, or
* (and possibly also) BlockChecked will have been called.
- *
+ *
* May not be called in a
* validationinterface callback.
*
@@ -365,7 +351,7 @@ bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp = null
/**
* Closure representing one script verification
- * Note that this stores references to the spending transaction
+ * Note that this stores references to the spending transaction
*/
class CScriptCheck
{
@@ -463,7 +449,7 @@ bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIn
bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Remove invalidity status from a block and its descendants. */
-bool ResetBlockFailureFlags(CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+void ResetBlockFailureFlags(CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** The currently-connected chain of blocks (protected by cs_main). */
extern CChain& chainActive;
diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp
index 55aa5c2cdf..aff4c44cea 100644
--- a/src/validationinterface.cpp
+++ b/src/validationinterface.cpp
@@ -25,7 +25,6 @@ struct MainSignalsInstance {
boost::signals2::signal<void (const std::shared_ptr<const CBlock> &)> BlockDisconnected;
boost::signals2::signal<void (const CTransactionRef &)> TransactionRemovedFromMempool;
boost::signals2::signal<void (const CBlockLocator &)> ChainStateFlushed;
- boost::signals2::signal<void (const uint256 &)> Inventory;
boost::signals2::signal<void (int64_t nBestBlockTime, CConnman* connman)> Broadcast;
boost::signals2::signal<void (const CBlock&, const CValidationState&)> BlockChecked;
boost::signals2::signal<void (const CBlockIndex *, const std::shared_ptr<const CBlock>&)> NewPoWValidBlock;
@@ -80,7 +79,6 @@ void RegisterValidationInterface(CValidationInterface* pwalletIn) {
g_signals.m_internals->BlockDisconnected.connect(boost::bind(&CValidationInterface::BlockDisconnected, pwalletIn, _1));
g_signals.m_internals->TransactionRemovedFromMempool.connect(boost::bind(&CValidationInterface::TransactionRemovedFromMempool, pwalletIn, _1));
g_signals.m_internals->ChainStateFlushed.connect(boost::bind(&CValidationInterface::ChainStateFlushed, pwalletIn, _1));
- g_signals.m_internals->Inventory.connect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1));
g_signals.m_internals->Broadcast.connect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1, _2));
g_signals.m_internals->BlockChecked.connect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2));
g_signals.m_internals->NewPoWValidBlock.connect(boost::bind(&CValidationInterface::NewPoWValidBlock, pwalletIn, _1, _2));
@@ -89,7 +87,6 @@ void RegisterValidationInterface(CValidationInterface* pwalletIn) {
void UnregisterValidationInterface(CValidationInterface* pwalletIn) {
g_signals.m_internals->BlockChecked.disconnect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2));
g_signals.m_internals->Broadcast.disconnect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1, _2));
- g_signals.m_internals->Inventory.disconnect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1));
g_signals.m_internals->ChainStateFlushed.disconnect(boost::bind(&CValidationInterface::ChainStateFlushed, pwalletIn, _1));
g_signals.m_internals->TransactionAddedToMempool.disconnect(boost::bind(&CValidationInterface::TransactionAddedToMempool, pwalletIn, _1));
g_signals.m_internals->BlockConnected.disconnect(boost::bind(&CValidationInterface::BlockConnected, pwalletIn, _1, _2, _3));
@@ -105,7 +102,6 @@ void UnregisterAllValidationInterfaces() {
}
g_signals.m_internals->BlockChecked.disconnect_all_slots();
g_signals.m_internals->Broadcast.disconnect_all_slots();
- g_signals.m_internals->Inventory.disconnect_all_slots();
g_signals.m_internals->ChainStateFlushed.disconnect_all_slots();
g_signals.m_internals->TransactionAddedToMempool.disconnect_all_slots();
g_signals.m_internals->BlockConnected.disconnect_all_slots();
@@ -171,12 +167,6 @@ void CMainSignals::ChainStateFlushed(const CBlockLocator &locator) {
});
}
-void CMainSignals::Inventory(const uint256 &hash) {
- m_internals->m_schedulerClient.AddToProcessQueue([hash, this] {
- m_internals->Inventory(hash);
- });
-}
-
void CMainSignals::Broadcast(int64_t nBestBlockTime, CConnman* connman) {
m_internals->Broadcast(nBestBlockTime, connman);
}
diff --git a/src/validationinterface.h b/src/validationinterface.h
index 0ca82235da..42cc2e9a20 100644
--- a/src/validationinterface.h
+++ b/src/validationinterface.h
@@ -117,12 +117,6 @@ protected:
* Called on a background thread.
*/
virtual void ChainStateFlushed(const CBlockLocator &locator) {}
- /**
- * Notifies listeners about an inventory item being seen on the network.
- *
- * Called on a background thread.
- */
- virtual void Inventory(const uint256 &hash) {}
/** Tells listeners to broadcast their data. */
virtual void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) {}
/**
@@ -173,7 +167,6 @@ public:
void BlockConnected(const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex, const std::shared_ptr<const std::vector<CTransactionRef>> &);
void BlockDisconnected(const std::shared_ptr<const CBlock> &);
void ChainStateFlushed(const CBlockLocator &);
- void Inventory(const uint256 &);
void Broadcast(int64_t nBestBlockTime, CConnman* connman);
void BlockChecked(const CBlock&, const CValidationState&);
void NewPoWValidBlock(const CBlockIndex *, const std::shared_ptr<const CBlock>&);
diff --git a/src/wallet/coincontrol.cpp b/src/wallet/coincontrol.cpp
new file mode 100644
index 0000000000..645981faa4
--- /dev/null
+++ b/src/wallet/coincontrol.cpp
@@ -0,0 +1,23 @@
+// Copyright (c) 2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <wallet/coincontrol.h>
+
+#include <util.h>
+
+void CCoinControl::SetNull()
+{
+ destChange = CNoDestination();
+ m_change_type.reset();
+ fAllowOtherInputs = false;
+ fAllowWatchOnly = false;
+ m_avoid_partial_spends = gArgs.GetBoolArg("-avoidpartialspends", DEFAULT_AVOIDPARTIALSPENDS);
+ setSelected.clear();
+ m_feerate.reset();
+ fOverrideFeeRate = false;
+ m_confirm_target.reset();
+ m_signal_bip125_rbf.reset();
+ m_fee_mode = FeeEstimateMode::UNSET;
+}
+
diff --git a/src/wallet/coincontrol.h b/src/wallet/coincontrol.h
index 98b4298507..fbe6c43e24 100644
--- a/src/wallet/coincontrol.h
+++ b/src/wallet/coincontrol.h
@@ -32,6 +32,8 @@ public:
boost::optional<unsigned int> m_confirm_target;
//! Override the wallet's m_signal_rbf if set
boost::optional<bool> m_signal_bip125_rbf;
+ //! Avoid partial use of funds sent to a given address
+ bool m_avoid_partial_spends;
//! Fee estimation mode to control arguments to estimateSmartFee
FeeEstimateMode m_fee_mode;
@@ -40,19 +42,7 @@ public:
SetNull();
}
- void SetNull()
- {
- destChange = CNoDestination();
- m_change_type.reset();
- fAllowOtherInputs = false;
- fAllowWatchOnly = false;
- setSelected.clear();
- m_feerate.reset();
- fOverrideFeeRate = false;
- m_confirm_target.reset();
- m_signal_bip125_rbf.reset();
- m_fee_mode = FeeEstimateMode::UNSET;
- }
+ void SetNull();
bool HasSelected() const
{
diff --git a/src/wallet/coinselection.cpp b/src/wallet/coinselection.cpp
index a403411e5b..1a810e763f 100644
--- a/src/wallet/coinselection.cpp
+++ b/src/wallet/coinselection.cpp
@@ -8,7 +8,7 @@
// Descending order comparator
struct {
- bool operator()(const CInputCoin& a, const CInputCoin& b) const
+ bool operator()(const OutputGroup& a, const OutputGroup& b) const
{
return a.effective_value > b.effective_value;
}
@@ -59,7 +59,7 @@ struct {
static const size_t TOTAL_TRIES = 100000;
-bool SelectCoinsBnB(std::vector<CInputCoin>& utxo_pool, const CAmount& target_value, const CAmount& cost_of_change, std::set<CInputCoin>& out_set, CAmount& value_ret, CAmount not_input_fees)
+bool SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool, const CAmount& target_value, const CAmount& cost_of_change, std::set<CInputCoin>& out_set, CAmount& value_ret, CAmount not_input_fees)
{
out_set.clear();
CAmount curr_value = 0;
@@ -70,7 +70,7 @@ bool SelectCoinsBnB(std::vector<CInputCoin>& utxo_pool, const CAmount& target_va
// Calculate curr_available_value
CAmount curr_available_value = 0;
- for (const CInputCoin& utxo : utxo_pool) {
+ for (const OutputGroup& utxo : utxo_pool) {
// Assert that this utxo is not negative. It should never be negative, effective value calculation should have removed it
assert(utxo.effective_value > 0);
curr_available_value += utxo.effective_value;
@@ -115,7 +115,7 @@ bool SelectCoinsBnB(std::vector<CInputCoin>& utxo_pool, const CAmount& target_va
while (!curr_selection.empty() && !curr_selection.back()) {
curr_selection.pop_back();
curr_available_value += utxo_pool.at(curr_selection.size()).effective_value;
- };
+ }
if (curr_selection.empty()) { // We have walked back to the first utxo and no branch is untraversed. All solutions searched
break;
@@ -123,11 +123,11 @@ bool SelectCoinsBnB(std::vector<CInputCoin>& utxo_pool, const CAmount& target_va
// Output was included on previous iterations, try excluding now.
curr_selection.back() = false;
- CInputCoin& utxo = utxo_pool.at(curr_selection.size() - 1);
+ OutputGroup& utxo = utxo_pool.at(curr_selection.size() - 1);
curr_value -= utxo.effective_value;
curr_waste -= utxo.fee - utxo.long_term_fee;
} else { // Moving forwards, continuing down this branch
- CInputCoin& utxo = utxo_pool.at(curr_selection.size());
+ OutputGroup& utxo = utxo_pool.at(curr_selection.size());
// Remove this utxo from the curr_available_value utxo amount
curr_available_value -= utxo.effective_value;
@@ -156,32 +156,32 @@ bool SelectCoinsBnB(std::vector<CInputCoin>& utxo_pool, const CAmount& target_va
value_ret = 0;
for (size_t i = 0; i < best_selection.size(); ++i) {
if (best_selection.at(i)) {
- out_set.insert(utxo_pool.at(i));
- value_ret += utxo_pool.at(i).txout.nValue;
+ util::insert(out_set, utxo_pool.at(i).m_outputs);
+ value_ret += utxo_pool.at(i).m_value;
}
}
return true;
}
-static void ApproximateBestSubset(const std::vector<CInputCoin>& vValue, const CAmount& nTotalLower, const CAmount& nTargetValue,
+static void ApproximateBestSubset(const std::vector<OutputGroup>& groups, const CAmount& nTotalLower, const CAmount& nTargetValue,
std::vector<char>& vfBest, CAmount& nBest, int iterations = 1000)
{
std::vector<char> vfIncluded;
- vfBest.assign(vValue.size(), true);
+ vfBest.assign(groups.size(), true);
nBest = nTotalLower;
FastRandomContext insecure_rand;
for (int nRep = 0; nRep < iterations && nBest != nTargetValue; nRep++)
{
- vfIncluded.assign(vValue.size(), false);
+ vfIncluded.assign(groups.size(), false);
CAmount nTotal = 0;
bool fReachedTarget = false;
for (int nPass = 0; nPass < 2 && !fReachedTarget; nPass++)
{
- for (unsigned int i = 0; i < vValue.size(); i++)
+ for (unsigned int i = 0; i < groups.size(); i++)
{
//The solver here uses a randomized algorithm,
//the randomness serves no real security purpose but is just
@@ -191,7 +191,7 @@ static void ApproximateBestSubset(const std::vector<CInputCoin>& vValue, const C
//the selection random.
if (nPass == 0 ? insecure_rand.randbool() : !vfIncluded[i])
{
- nTotal += vValue[i].txout.nValue;
+ nTotal += groups[i].m_value;
vfIncluded[i] = true;
if (nTotal >= nTargetValue)
{
@@ -201,7 +201,7 @@ static void ApproximateBestSubset(const std::vector<CInputCoin>& vValue, const C
nBest = nTotal;
vfBest = vfIncluded;
}
- nTotal -= vValue[i].txout.nValue;
+ nTotal -= groups[i].m_value;
vfIncluded[i] = false;
}
}
@@ -210,86 +210,75 @@ static void ApproximateBestSubset(const std::vector<CInputCoin>& vValue, const C
}
}
-bool KnapsackSolver(const CAmount& nTargetValue, std::vector<CInputCoin>& vCoins, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet)
+bool KnapsackSolver(const CAmount& nTargetValue, std::vector<OutputGroup>& groups, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet)
{
setCoinsRet.clear();
nValueRet = 0;
// List of values less than target
- boost::optional<CInputCoin> coinLowestLarger;
- std::vector<CInputCoin> vValue;
+ boost::optional<OutputGroup> lowest_larger;
+ std::vector<OutputGroup> applicable_groups;
CAmount nTotalLower = 0;
- random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt);
+ random_shuffle(groups.begin(), groups.end(), GetRandInt);
- for (const CInputCoin &coin : vCoins)
- {
- if (coin.txout.nValue == nTargetValue)
- {
- setCoinsRet.insert(coin);
- nValueRet += coin.txout.nValue;
+ for (const OutputGroup& group : groups) {
+ if (group.m_value == nTargetValue) {
+ util::insert(setCoinsRet, group.m_outputs);
+ nValueRet += group.m_value;
return true;
- }
- else if (coin.txout.nValue < nTargetValue + MIN_CHANGE)
- {
- vValue.push_back(coin);
- nTotalLower += coin.txout.nValue;
- }
- else if (!coinLowestLarger || coin.txout.nValue < coinLowestLarger->txout.nValue)
- {
- coinLowestLarger = coin;
+ } else if (group.m_value < nTargetValue + MIN_CHANGE) {
+ applicable_groups.push_back(group);
+ nTotalLower += group.m_value;
+ } else if (!lowest_larger || group.m_value < lowest_larger->m_value) {
+ lowest_larger = group;
}
}
- if (nTotalLower == nTargetValue)
- {
- for (const auto& input : vValue)
- {
- setCoinsRet.insert(input);
- nValueRet += input.txout.nValue;
+ if (nTotalLower == nTargetValue) {
+ for (const auto& group : applicable_groups) {
+ util::insert(setCoinsRet, group.m_outputs);
+ nValueRet += group.m_value;
}
return true;
}
- if (nTotalLower < nTargetValue)
- {
- if (!coinLowestLarger)
- return false;
- setCoinsRet.insert(coinLowestLarger.get());
- nValueRet += coinLowestLarger->txout.nValue;
+ if (nTotalLower < nTargetValue) {
+ if (!lowest_larger) return false;
+ util::insert(setCoinsRet, lowest_larger->m_outputs);
+ nValueRet += lowest_larger->m_value;
return true;
}
// Solve subset sum by stochastic approximation
- std::sort(vValue.begin(), vValue.end(), descending);
+ std::sort(applicable_groups.begin(), applicable_groups.end(), descending);
std::vector<char> vfBest;
CAmount nBest;
- ApproximateBestSubset(vValue, nTotalLower, nTargetValue, vfBest, nBest);
- if (nBest != nTargetValue && nTotalLower >= nTargetValue + MIN_CHANGE)
- ApproximateBestSubset(vValue, nTotalLower, nTargetValue + MIN_CHANGE, vfBest, nBest);
+ ApproximateBestSubset(applicable_groups, nTotalLower, nTargetValue, vfBest, nBest);
+ if (nBest != nTargetValue && nTotalLower >= nTargetValue + MIN_CHANGE) {
+ ApproximateBestSubset(applicable_groups, nTotalLower, nTargetValue + MIN_CHANGE, vfBest, nBest);
+ }
// If we have a bigger coin and (either the stochastic approximation didn't find a good solution,
// or the next bigger coin is closer), return the bigger coin
- if (coinLowestLarger &&
- ((nBest != nTargetValue && nBest < nTargetValue + MIN_CHANGE) || coinLowestLarger->txout.nValue <= nBest))
- {
- setCoinsRet.insert(coinLowestLarger.get());
- nValueRet += coinLowestLarger->txout.nValue;
- }
- else {
- for (unsigned int i = 0; i < vValue.size(); i++)
- if (vfBest[i])
- {
- setCoinsRet.insert(vValue[i]);
- nValueRet += vValue[i].txout.nValue;
+ if (lowest_larger &&
+ ((nBest != nTargetValue && nBest < nTargetValue + MIN_CHANGE) || lowest_larger->m_value <= nBest)) {
+ util::insert(setCoinsRet, lowest_larger->m_outputs);
+ nValueRet += lowest_larger->m_value;
+ } else {
+ for (unsigned int i = 0; i < applicable_groups.size(); i++) {
+ if (vfBest[i]) {
+ util::insert(setCoinsRet, applicable_groups[i].m_outputs);
+ nValueRet += applicable_groups[i].m_value;
}
+ }
if (LogAcceptCategory(BCLog::SELECTCOINS)) {
LogPrint(BCLog::SELECTCOINS, "SelectCoins() best subset: "); /* Continued */
- for (unsigned int i = 0; i < vValue.size(); i++) {
+ for (unsigned int i = 0; i < applicable_groups.size(); i++) {
if (vfBest[i]) {
- LogPrint(BCLog::SELECTCOINS, "%s ", FormatMoney(vValue[i].txout.nValue)); /* Continued */
+ LogPrint(BCLog::SELECTCOINS, "%s ", FormatMoney(applicable_groups[i].m_value)); /* Continued */
}
}
LogPrint(BCLog::SELECTCOINS, "total %s\n", FormatMoney(nBest));
@@ -298,3 +287,40 @@ bool KnapsackSolver(const CAmount& nTargetValue, std::vector<CInputCoin>& vCoins
return true;
}
+
+/******************************************************************************
+
+ OutputGroup
+
+ ******************************************************************************/
+
+void OutputGroup::Insert(const CInputCoin& output, int depth, bool from_me, size_t ancestors, size_t descendants) {
+ m_outputs.push_back(output);
+ m_from_me &= from_me;
+ m_value += output.effective_value;
+ m_depth = std::min(m_depth, depth);
+ // m_ancestors is currently the max ancestor count for all coins in the group; however, this is
+ // not ideal, as a wallet will consider e.g. thirty 2-ancestor coins as having two ancestors,
+ // when in reality it has 60 ancestors.
+ m_ancestors = std::max(m_ancestors, ancestors);
+ // m_descendants is the count as seen from the top ancestor, not the descendants as seen from the
+ // coin itself; thus, this value is accurate
+ m_descendants = std::max(m_descendants, descendants);
+ effective_value = m_value;
+}
+
+std::vector<CInputCoin>::iterator OutputGroup::Discard(const CInputCoin& output) {
+ auto it = m_outputs.begin();
+ while (it != m_outputs.end() && it->outpoint != output.outpoint) ++it;
+ if (it == m_outputs.end()) return it;
+ m_value -= output.effective_value;
+ effective_value -= output.effective_value;
+ return m_outputs.erase(it);
+}
+
+bool OutputGroup::EligibleForSpending(const CoinEligibilityFilter& eligibility_filter) const
+{
+ return m_depth >= (m_from_me ? eligibility_filter.conf_mine : eligibility_filter.conf_theirs)
+ && m_ancestors <= eligibility_filter.max_ancestors
+ && m_descendants <= eligibility_filter.max_descendants;
+}
diff --git a/src/wallet/coinselection.h b/src/wallet/coinselection.h
index 2b185879c6..1be776e695 100644
--- a/src/wallet/coinselection.h
+++ b/src/wallet/coinselection.h
@@ -28,11 +28,17 @@ public:
effective_value = txout.nValue;
}
+ CInputCoin(const CTransactionRef& tx, unsigned int i, int input_bytes) : CInputCoin(tx, i)
+ {
+ m_input_bytes = input_bytes;
+ }
+
COutPoint outpoint;
CTxOut txout;
CAmount effective_value;
- CAmount fee = 0;
- CAmount long_term_fee = 0;
+
+ /** Pre-computed estimated size of this output as a fully-signed input in a transaction. Can be -1 if it could not be calculated */
+ int m_input_bytes{-1};
bool operator<(const CInputCoin& rhs) const {
return outpoint < rhs.outpoint;
@@ -47,8 +53,49 @@ public:
}
};
-bool SelectCoinsBnB(std::vector<CInputCoin>& utxo_pool, const CAmount& target_value, const CAmount& cost_of_change, std::set<CInputCoin>& out_set, CAmount& value_ret, CAmount not_input_fees);
+struct CoinEligibilityFilter
+{
+ const int conf_mine;
+ const int conf_theirs;
+ const uint64_t max_ancestors;
+ const uint64_t max_descendants;
+
+ CoinEligibilityFilter(int conf_mine, int conf_theirs, uint64_t max_ancestors) : conf_mine(conf_mine), conf_theirs(conf_theirs), max_ancestors(max_ancestors), max_descendants(max_ancestors) {}
+ CoinEligibilityFilter(int conf_mine, int conf_theirs, uint64_t max_ancestors, uint64_t max_descendants) : conf_mine(conf_mine), conf_theirs(conf_theirs), max_ancestors(max_ancestors), max_descendants(max_descendants) {}
+};
+
+struct OutputGroup
+{
+ std::vector<CInputCoin> m_outputs;
+ bool m_from_me{true};
+ CAmount m_value{0};
+ int m_depth{999};
+ size_t m_ancestors{0};
+ size_t m_descendants{0};
+ CAmount effective_value{0};
+ CAmount fee{0};
+ CAmount long_term_fee{0};
+
+ OutputGroup() {}
+ OutputGroup(std::vector<CInputCoin>&& outputs, bool from_me, CAmount value, int depth, size_t ancestors, size_t descendants)
+ : m_outputs(std::move(outputs))
+ , m_from_me(from_me)
+ , m_value(value)
+ , m_depth(depth)
+ , m_ancestors(ancestors)
+ , m_descendants(descendants)
+ {}
+ OutputGroup(const CInputCoin& output, int depth, bool from_me, size_t ancestors, size_t descendants) : OutputGroup() {
+ Insert(output, depth, from_me, ancestors, descendants);
+ }
+ void Insert(const CInputCoin& output, int depth, bool from_me, size_t ancestors, size_t descendants);
+ std::vector<CInputCoin>::iterator Discard(const CInputCoin& output);
+ bool EligibleForSpending(const CoinEligibilityFilter& eligibility_filter) const;
+};
+
+bool SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool, const CAmount& target_value, const CAmount& cost_of_change, std::set<CInputCoin>& out_set, CAmount& value_ret, CAmount not_input_fees);
// Original coin selection algorithm as a fallback
-bool KnapsackSolver(const CAmount& nTargetValue, std::vector<CInputCoin>& vCoins, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet);
+bool KnapsackSolver(const CAmount& nTargetValue, std::vector<OutputGroup>& groups, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet);
+
#endif // BITCOIN_WALLET_COINSELECTION_H
diff --git a/src/wallet/crypter.h b/src/wallet/crypter.h
index 4c0c8ff5ec..52842cd978 100644
--- a/src/wallet/crypter.h
+++ b/src/wallet/crypter.h
@@ -116,7 +116,7 @@ class CCryptoKeyStore : public CBasicKeyStore
{
private:
- CKeyingMaterial vMasterKey;
+ CKeyingMaterial vMasterKey GUARDED_BY(cs_KeyStore);
//! if fUseCrypto is true, mapKeys must be empty
//! if fUseCrypto is false, vMasterKey must be empty
@@ -126,13 +126,15 @@ private:
bool fDecryptionThoroughlyChecked;
protected:
+ using CryptedKeyMap = std::map<CKeyID, std::pair<CPubKey, std::vector<unsigned char>>>;
+
bool SetCrypted();
//! will encrypt previously unencrypted keys
bool EncryptKeys(CKeyingMaterial& vMasterKeyIn);
bool Unlock(const CKeyingMaterial& vMasterKeyIn);
- CryptedKeyMap mapCryptedKeys;
+ CryptedKeyMap mapCryptedKeys GUARDED_BY(cs_KeyStore);
public:
CCryptoKeyStore() : fUseCrypto(false), fDecryptionThoroughlyChecked(false)
diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp
index 74312b7124..52c7e6c70f 100644
--- a/src/wallet/init.cpp
+++ b/src/wallet/init.cpp
@@ -7,6 +7,7 @@
#include <init.h>
#include <net.h>
#include <scheduler.h>
+#include <outputtype.h>
#include <util.h>
#include <utilmoneystr.h>
#include <validation.h>
@@ -53,6 +54,7 @@ const WalletInitInterface& g_wallet_init_interface = WalletInit();
void WalletInit::AddWalletOptions() const
{
gArgs.AddArg("-addresstype", strprintf("What type of addresses to use (\"legacy\", \"p2sh-segwit\", or \"bech32\", default: \"%s\")", FormatOutputType(DEFAULT_ADDRESS_TYPE)), false, OptionsCategory::WALLET);
+ gArgs.AddArg("-avoidpartialspends", strprintf(_("Group outputs by address, selecting all or none, instead of selecting on a per-output basis. Privacy is improved as an address is only used once (unless someone sends to it after spending from it), but may result in slightly higher fees as suboptimal coin selection may result due to the added limitation (default: %u)"), DEFAULT_AVOIDPARTIALSPENDS), false, OptionsCategory::WALLET);
gArgs.AddArg("-changetype", "What type of change to use (\"legacy\", \"p2sh-segwit\", or \"bech32\"). Default is same as -addresstype, except when -addresstype=p2sh-segwit a native segwit output is used when sending to a native segwit address)", false, OptionsCategory::WALLET);
gArgs.AddArg("-disablewallet", "Do not load the wallet and disable wallet RPC calls", false, OptionsCategory::WALLET);
gArgs.AddArg("-discardfee=<amt>", strprintf("The fee rate (in %s/kB) that indicates your tolerance for discarding change by adding it to the fee (default: %s). "
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index 882ddbbe4e..3cb4050f01 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -86,6 +86,17 @@ static bool GetWalletAddressesForKey(CWallet * const pwallet, const CKeyID &keyi
return fLabelFound;
}
+static const int64_t TIMESTAMP_MIN = 0;
+
+static void RescanWallet(CWallet& wallet, const WalletRescanReserver& reserver, int64_t time_begin = TIMESTAMP_MIN, bool update = true)
+{
+ int64_t scanned_time = wallet.RescanFromTime(time_begin, reserver, update);
+ if (wallet.IsAbortingRescan()) {
+ throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted by user.");
+ } else if (scanned_time > time_begin) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Rescan was unable to fully rescan the blockchain. Some transactions may be missing.");
+ }
+}
UniValue importprivkey(const JSONRPCRequest& request)
{
@@ -172,13 +183,7 @@ UniValue importprivkey(const JSONRPCRequest& request)
}
}
if (fRescan) {
- int64_t scanned_time = pwallet->RescanFromTime(TIMESTAMP_MIN, reserver, true /* update */);
- if (pwallet->IsAbortingRescan()) {
- throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted by user.");
- }
- if (scanned_time > TIMESTAMP_MIN) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Rescan was unable to fully rescan the blockchain. Some transactions may be missing.");
- }
+ RescanWallet(*pwallet, reserver);
}
return NullUniValue;
@@ -257,9 +262,9 @@ UniValue importaddress(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 4)
throw std::runtime_error(
"importaddress \"address\" ( \"label\" rescan p2sh )\n"
- "\nAdds a script (in hex) or address that can be watched as if it were in your wallet but cannot be used to spend. Requires a new wallet backup.\n"
+ "\nAdds an address or script (in hex) that can be watched as if it were in your wallet but cannot be used to spend. Requires a new wallet backup.\n"
"\nArguments:\n"
- "1. \"script\" (string, required) The hex-encoded script (or address)\n"
+ "1. \"address\" (string, required) The Bitcoin address (or hex-encoded script)\n"
"2. \"label\" (string, optional, default=\"\") An optional label\n"
"3. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n"
"4. p2sh (boolean, optional, default=false) Add the P2SH version of the script as well\n"
@@ -269,12 +274,12 @@ UniValue importaddress(const JSONRPCRequest& request)
"\nNote: If you import a non-standard raw script in hex form, outputs sending to it will be treated\n"
"as change, and not show up in many RPCs.\n"
"\nExamples:\n"
- "\nImport a script with rescan\n"
- + HelpExampleCli("importaddress", "\"myscript\"") +
+ "\nImport an address with rescan\n"
+ + HelpExampleCli("importaddress", "\"myaddress\"") +
"\nImport using a label without rescan\n"
- + HelpExampleCli("importaddress", "\"myscript\" \"testing\" false") +
+ + HelpExampleCli("importaddress", "\"myaddress\" \"testing\" false") +
"\nAs a JSON-RPC call\n"
- + HelpExampleRpc("importaddress", "\"myscript\", \"testing\", false")
+ + HelpExampleRpc("importaddress", "\"myaddress\", \"testing\", false")
);
@@ -318,13 +323,7 @@ UniValue importaddress(const JSONRPCRequest& request)
}
if (fRescan)
{
- int64_t scanned_time = pwallet->RescanFromTime(TIMESTAMP_MIN, reserver, true /* update */);
- if (pwallet->IsAbortingRescan()) {
- throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted by user.");
- }
- if (scanned_time > TIMESTAMP_MIN) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Rescan was unable to fully rescan the blockchain. Some transactions may be missing.");
- }
+ RescanWallet(*pwallet, reserver);
pwallet->ReacceptWalletTransactions();
}
@@ -496,13 +495,7 @@ UniValue importpubkey(const JSONRPCRequest& request)
}
if (fRescan)
{
- int64_t scanned_time = pwallet->RescanFromTime(TIMESTAMP_MIN, reserver, true /* update */);
- if (pwallet->IsAbortingRescan()) {
- throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted by user.");
- }
- if (scanned_time > TIMESTAMP_MIN) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Rescan was unable to fully rescan the blockchain. Some transactions may be missing.");
- }
+ RescanWallet(*pwallet, reserver);
pwallet->ReacceptWalletTransactions();
}
@@ -585,13 +578,13 @@ UniValue importwallet(const JSONRPCRequest& request)
std::string strLabel;
bool fLabel = true;
for (unsigned int nStr = 2; nStr < vstr.size(); nStr++) {
- if (boost::algorithm::starts_with(vstr[nStr], "#"))
+ if (vstr[nStr].front() == '#')
break;
if (vstr[nStr] == "change=1")
fLabel = false;
if (vstr[nStr] == "reserve=1")
fLabel = false;
- if (boost::algorithm::starts_with(vstr[nStr], "label=")) {
+ if (vstr[nStr].substr(0,6) == "label=") {
strLabel = DecodeDumpString(vstr[nStr].substr(6));
fLabel = true;
}
@@ -630,13 +623,7 @@ UniValue importwallet(const JSONRPCRequest& request)
pwallet->UpdateTimeFirstKey(nTimeBegin);
}
uiInterface.ShowProgress("", 100, false); // hide progress dialog in GUI
- int64_t scanned_time = pwallet->RescanFromTime(nTimeBegin, reserver, false /* update */);
- if (pwallet->IsAbortingRescan()) {
- throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted by user.");
- }
- if (scanned_time > nTimeBegin) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Rescan was unable to fully rescan the blockchain. Some transactions may be missing.");
- }
+ RescanWallet(*pwallet, reserver, nTimeBegin, false /* update */);
pwallet->MarkDirty();
if (!fGood)
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index b1d2532d86..73dfebf114 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -11,6 +11,7 @@
#include <validation.h>
#include <key_io.h>
#include <net.h>
+#include <outputtype.h>
#include <policy/feerate.h>
#include <policy/fees.h>
#include <policy/policy.h>
@@ -160,6 +161,10 @@ static UniValue getnewaddress(const JSONRPCRequest& request)
+ HelpExampleRpc("getnewaddress", "")
);
+ if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet");
+ }
+
LOCK2(cs_main, pwallet->cs_wallet);
// Parse the label first so we don't generate a key if there's an error
@@ -267,6 +272,10 @@ static UniValue getrawchangeaddress(const JSONRPCRequest& request)
+ HelpExampleRpc("getrawchangeaddress", "")
);
+ if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet");
+ }
+
LOCK2(cs_main, pwallet->cs_wallet);
if (!pwallet->IsLocked()) {
@@ -852,8 +861,9 @@ static UniValue getbalance(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || (request.params.size() > 3 && IsDeprecatedRPCEnabled("accounts")) || (request.params.size() != 0 && !IsDeprecatedRPCEnabled("accounts")))
+ if (request.fHelp || (request.params.size() > 3 ))
throw std::runtime_error(
+ (IsDeprecatedRPCEnabled("accounts") ? std::string(
"getbalance ( \"account\" minconf include_watchonly )\n"
"\nIf account is not specified, returns the server's total available balance.\n"
"The available balance is what the wallet considers currently spendable, and is\n"
@@ -875,8 +885,17 @@ static UniValue getbalance(const JSONRPCRequest& request)
" balances. In general, account balance calculation is not considered\n"
" reliable and has resulted in confusing outcomes, so it is recommended to\n"
" avoid passing this argument.\n"
- "2. minconf (numeric, optional, default=1) DEPRECATED. Only valid when an account is specified. This argument will be removed in V0.18. To use this deprecated argument, start bitcoind with -deprecatedrpc=accounts. Only include transactions confirmed at least this many times.\n"
- "3. include_watchonly (bool, optional, default=false) DEPRECATED. Only valid when an account is specified. This argument will be removed in V0.18. To use this deprecated argument, start bitcoind with -deprecatedrpc=accounts. Also include balance in watch-only addresses (see 'importaddress')\n"
+ "2. minconf (numeric, optional) Only include transactions confirmed at least this many times. \n"
+ " The default is 1 if an account is provided or 0 if no account is provided\n")
+ : std::string(
+ "getbalance ( \"(dummy)\" minconf include_watchonly )\n"
+ "\nReturns the total available balance.\n"
+ "The available balance is what the wallet considers currently spendable, and is\n"
+ "thus affected by options which limit spendability such as -spendzeroconfchange.\n"
+ "\nArguments:\n"
+ "1. (dummy) (string, optional) Remains for backward compatibility. Must be excluded or set to \"*\".\n"
+ "2. minconf (numeric, optional, default=0) Only include transactions confirmed at least this many times.\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"
@@ -894,38 +913,35 @@ static UniValue getbalance(const JSONRPCRequest& request)
LOCK2(cs_main, pwallet->cs_wallet);
- if (IsDeprecatedRPCEnabled("accounts")) {
- const UniValue& account_value = request.params[0];
- const UniValue& minconf = request.params[1];
- const UniValue& include_watchonly = request.params[2];
+ const UniValue& account_value = request.params[0];
- if (account_value.isNull()) {
- if (!minconf.isNull()) {
- throw JSONRPCError(RPC_INVALID_PARAMETER,
- "getbalance minconf option is only currently supported if an account is specified");
- }
- if (!include_watchonly.isNull()) {
- throw JSONRPCError(RPC_INVALID_PARAMETER,
- "getbalance include_watchonly option is only currently supported if an account is specified");
- }
- return ValueFromAmount(pwallet->GetBalance());
- }
+ int min_depth = 0;
+ if (IsDeprecatedRPCEnabled("accounts") && !account_value.isNull()) {
+ // Default min_depth to 1 when an account is provided.
+ min_depth = 1;
+ }
+ if (!request.params[1].isNull()) {
+ min_depth = request.params[1].get_int();
+ }
+
+ isminefilter filter = ISMINE_SPENDABLE;
+ if (!request.params[2].isNull() && request.params[2].get_bool()) {
+ filter = filter | ISMINE_WATCH_ONLY;
+ }
+
+ if (!account_value.isNull()) {
const std::string& account_param = account_value.get_str();
const std::string* account = account_param != "*" ? &account_param : nullptr;
- int nMinDepth = 1;
- if (!minconf.isNull())
- nMinDepth = minconf.get_int();
- isminefilter filter = ISMINE_SPENDABLE;
- if(!include_watchonly.isNull())
- if(include_watchonly.get_bool())
- filter = filter | ISMINE_WATCH_ONLY;
-
- return ValueFromAmount(pwallet->GetLegacyBalance(filter, nMinDepth, account));
+ if (!IsDeprecatedRPCEnabled("accounts") && account_param != "*") {
+ throw JSONRPCError(RPC_METHOD_DEPRECATED, "dummy first argument must be excluded or set to \"*\".");
+ } else if (IsDeprecatedRPCEnabled("accounts")) {
+ return ValueFromAmount(pwallet->GetLegacyBalance(filter, min_depth, account));
+ }
}
- return ValueFromAmount(pwallet->GetBalance());
+ return ValueFromAmount(pwallet->GetBalance(filter, min_depth));
}
static UniValue getunconfirmedbalance(const JSONRPCRequest &request)
@@ -1362,8 +1378,7 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request)
// Construct using pay-to-script-hash:
CScript inner = CreateMultisigRedeemscript(required, pubkeys);
- pwallet->AddCScript(inner);
- CTxDestination dest = pwallet->AddAndGetDestinationForScript(inner, output_type);
+ CTxDestination dest = AddAndGetDestinationForScript(*pwallet, inner, output_type);
pwallet->SetAddressBook(dest, label, "send");
UniValue result(UniValue::VOBJ);
@@ -2499,6 +2514,10 @@ static UniValue keypoolrefill(const JSONRPCRequest& request)
+ HelpExampleRpc("keypoolrefill", "")
);
+ if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet");
+ }
+
LOCK2(cs_main, pwallet->cs_wallet);
// 0 is interpreted by TopUpKeyPool() as the default keypool size given by -keypool
@@ -2983,19 +3002,20 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
"Returns an object containing various wallet state info.\n"
"\nResult:\n"
"{\n"
- " \"walletname\": xxxxx, (string) the wallet name\n"
- " \"walletversion\": xxxxx, (numeric) the wallet version\n"
- " \"balance\": xxxxxxx, (numeric) the total confirmed balance of the wallet in " + CURRENCY_UNIT + "\n"
- " \"unconfirmed_balance\": xxx, (numeric) the total unconfirmed balance of the wallet in " + CURRENCY_UNIT + "\n"
- " \"immature_balance\": xxxxxx, (numeric) the total immature balance of the wallet in " + CURRENCY_UNIT + "\n"
- " \"txcount\": xxxxxxx, (numeric) the total number of transactions in the wallet\n"
- " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since Unix epoch) of the oldest pre-generated key in the key pool\n"
- " \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated (only counts external keys)\n"
- " \"keypoolsize_hd_internal\": xxxx, (numeric) how many new keys are pre-generated for internal use (used for change outputs, only appears if the wallet is using this feature, otherwise external keys are used)\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"
- " \"hdseedid\": \"<hash160>\" (string, optional) the Hash160 of the HD seed (only present when HD is enabled)\n"
- " \"hdmasterkeyid\": \"<hash160>\" (string, optional) alias for hdseedid retained for backwards-compatibility. Will be removed in V0.18.\n"
+ " \"walletname\": xxxxx, (string) the wallet name\n"
+ " \"walletversion\": xxxxx, (numeric) the wallet version\n"
+ " \"balance\": xxxxxxx, (numeric) the total confirmed balance of the wallet in " + CURRENCY_UNIT + "\n"
+ " \"unconfirmed_balance\": xxx, (numeric) the total unconfirmed balance of the wallet in " + CURRENCY_UNIT + "\n"
+ " \"immature_balance\": xxxxxx, (numeric) the total immature balance of the wallet in " + CURRENCY_UNIT + "\n"
+ " \"txcount\": xxxxxxx, (numeric) the total number of transactions in the wallet\n"
+ " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since Unix epoch) of the oldest pre-generated key in the key pool\n"
+ " \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated (only counts external keys)\n"
+ " \"keypoolsize_hd_internal\": xxxx, (numeric) how many new keys are pre-generated for internal use (used for change outputs, only appears if the wallet is using this feature, otherwise external keys are used)\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"
+ " \"hdseedid\": \"<hash160>\" (string, optional) the Hash160 of the HD seed (only present when HD is enabled)\n"
+ " \"hdmasterkeyid\": \"<hash160>\" (string, optional) alias for hdseedid retained for backwards-compatibility. Will be removed in V0.18.\n"
+ " \"private_keys_enabled\": true|false (boolean) false if privatekeys are disabled for this wallet (enforced watch-only wallet)\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("getwalletinfo", "")
@@ -3031,6 +3051,7 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
obj.pushKV("hdseedid", seed_id.GetHex());
obj.pushKV("hdmasterkeyid", seed_id.GetHex());
}
+ obj.pushKV("private_keys_enabled", !pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
return obj;
}
@@ -3121,12 +3142,13 @@ static UniValue loadwallet(const JSONRPCRequest& request)
static UniValue createwallet(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 1) {
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
throw std::runtime_error(
- "createwallet \"wallet_name\"\n"
+ "createwallet \"wallet_name\" ( disable_private_keys )\n"
"\nCreates and loads a new wallet.\n"
"\nArguments:\n"
- "1. \"wallet_name\" (string, required) The name for the new wallet. If this is a path, the wallet will be created at the path location.\n"
+ "1. \"wallet_name\" (string, required) The name for the new wallet. If this is a path, the wallet will be created at the path location.\n"
+ "2. disable_private_keys (boolean, optional, default: false) Disable the possibility of private keys (only watchonlys are possible in this mode).\n"
"\nResult:\n"
"{\n"
" \"name\" : <wallet_name>, (string) The wallet name if created successfully. If the wallet was created using a full path, the wallet_name will be the full path.\n"
@@ -3141,6 +3163,11 @@ static UniValue createwallet(const JSONRPCRequest& request)
std::string error;
std::string warning;
+ bool disable_privatekeys = false;
+ if (!request.params[1].isNull()) {
+ disable_privatekeys = request.params[1].get_bool();
+ }
+
fs::path wallet_path = fs::absolute(wallet_name, GetWalletDir());
if (fs::symlink_status(wallet_path).type() != fs::file_not_found) {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet " + wallet_name + " already exists.");
@@ -3151,7 +3178,7 @@ static UniValue createwallet(const JSONRPCRequest& request)
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet file verification failed: " + error);
}
- std::shared_ptr<CWallet> const wallet = CWallet::CreateWalletFromFile(wallet_name, fs::absolute(wallet_name, GetWalletDir()));
+ std::shared_ptr<CWallet> const wallet = CWallet::CreateWalletFromFile(wallet_name, fs::absolute(wallet_name, GetWalletDir()), (disable_privatekeys ? (uint64_t)WALLET_FLAG_DISABLE_PRIVATE_KEYS : 0));
if (!wallet) {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet creation failed.");
}
@@ -3425,95 +3452,25 @@ static UniValue listunspent(const JSONRPCRequest& request)
return results;
}
-static UniValue fundrawtransaction(const JSONRPCRequest& request)
+void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& fee_out, int& change_position, UniValue options)
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
- throw std::runtime_error(
- "fundrawtransaction \"hexstring\" ( options iswitness )\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 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"
- "Note that all inputs selected must be of standard form and P2SH scripts must be\n"
- "in the wallet using importaddress or addmultisigaddress (to calculate fees).\n"
- "You can see whether this is the case by checking the \"solvable\" field in the listunspent output.\n"
- "Only pay-to-pubkey, multisig, and P2SH versions thereof are currently supported for watch-only\n"
- "\nArguments:\n"
- "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"
- " \"change_type\" (string, optional) The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\". Default is set by -changetype.\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 fee rate in " + CURRENCY_UNIT + "/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"
- " \"replaceable\" (boolean, optional) Marks this transaction as BIP125 replaceable.\n"
- " Allows this transaction to be replaced by a transaction with higher fees\n"
- " \"conf_target\" (numeric, optional) Confirmation target (in blocks)\n"
- " \"estimate_mode\" (string, optional, default=UNSET) The fee estimate mode, must be one of:\n"
- " \"UNSET\"\n"
- " \"ECONOMICAL\"\n"
- " \"CONSERVATIVE\"\n"
- " }\n"
- " for backward compatibility: passing in a true instead of an object will result in {\"includeWatching\":true}\n"
- "3. iswitness (boolean, optional) Whether the transaction hex is a serialized witness transaction \n"
- " If iswitness is not present, heuristic tests will be used in decoding\n"
-
- "\nResult:\n"
- "{\n"
- " \"hex\": \"value\", (string) The resulting raw transaction (hex-encoded string)\n"
- " \"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"
- "\nExamples:\n"
- "\nCreate a transaction with no inputs\n"
- + HelpExampleCli("createrawtransaction", "\"[]\" \"{\\\"myaddress\\\":0.01}\"") +
- "\nAdd sufficient unsigned inputs to meet the output value\n"
- + HelpExampleCli("fundrawtransaction", "\"rawtransactionhex\"") +
- "\nSign the transaction\n"
- + HelpExampleCli("signrawtransaction", "\"fundedtransactionhex\"") +
- "\nSend the transaction\n"
- + HelpExampleCli("sendrawtransaction", "\"signedtransactionhex\"")
- );
-
- RPCTypeCheck(request.params, {UniValue::VSTR});
-
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
CCoinControl coinControl;
- int changePosition = -1;
+ change_position = -1;
bool lockUnspents = false;
UniValue subtractFeeFromOutputs;
std::set<int> setSubtractFeeFromOutputs;
- if (!request.params[1].isNull()) {
- if (request.params[1].type() == UniValue::VBOOL) {
+ if (!options.isNull()) {
+ if (options.type() == UniValue::VBOOL) {
// backward compatibility bool only fallback
- coinControl.fAllowWatchOnly = request.params[1].get_bool();
+ coinControl.fAllowWatchOnly = options.get_bool();
}
else {
- RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VOBJ, UniValue::VBOOL});
-
- UniValue options = request.params[1];
-
+ RPCTypeCheckArgument(options, UniValue::VOBJ);
RPCTypeCheckObj(options,
{
{"changeAddress", UniValueType(UniValue::VSTR)},
@@ -3540,7 +3497,7 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
}
if (options.exists("changePosition"))
- changePosition = options["changePosition"].get_int();
+ change_position = options["changePosition"].get_int();
if (options.exists("change_type")) {
if (options.exists("changeAddress")) {
@@ -3587,18 +3544,10 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
}
}
- // parse hex string from parameter
- CMutableTransaction tx;
- bool try_witness = request.params[2].isNull() ? true : request.params[2].get_bool();
- bool try_no_witness = request.params[2].isNull() ? true : !request.params[2].get_bool();
- if (!DecodeHexTx(tx, request.params[0].get_str(), try_no_witness, try_witness)) {
- throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
- }
-
if (tx.vout.size() == 0)
throw JSONRPCError(RPC_INVALID_PARAMETER, "TX must have at least one output");
- if (changePosition != -1 && (changePosition < 0 || (unsigned int)changePosition > tx.vout.size()))
+ if (change_position != -1 && (change_position < 0 || (unsigned int)change_position > tx.vout.size()))
throw JSONRPCError(RPC_INVALID_PARAMETER, "changePosition out of bounds");
for (unsigned int idx = 0; idx < subtractFeeFromOutputs.size(); idx++) {
@@ -3612,17 +3561,98 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
setSubtractFeeFromOutputs.insert(pos);
}
- CAmount nFeeOut;
std::string strFailReason;
- if (!pwallet->FundTransaction(tx, nFeeOut, changePosition, strFailReason, lockUnspents, setSubtractFeeFromOutputs, coinControl)) {
+ if (!pwallet->FundTransaction(tx, fee_out, change_position, strFailReason, lockUnspents, setSubtractFeeFromOutputs, coinControl)) {
throw JSONRPCError(RPC_WALLET_ERROR, strFailReason);
}
+}
+
+static UniValue fundrawtransaction(const JSONRPCRequest& request)
+{
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ CWallet* const pwallet = wallet.get();
+
+ if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
+ return NullUniValue;
+ }
+
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
+ throw std::runtime_error(
+ "fundrawtransaction \"hexstring\" ( options iswitness )\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 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"
+ "Note that all inputs selected must be of standard form and P2SH scripts must be\n"
+ "in the wallet using importaddress or addmultisigaddress (to calculate fees).\n"
+ "You can see whether this is the case by checking the \"solvable\" field in the listunspent output.\n"
+ "Only pay-to-pubkey, multisig, and P2SH versions thereof are currently supported for watch-only\n"
+ "\nArguments:\n"
+ "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"
+ " \"change_type\" (string, optional) The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\". Default is set by -changetype.\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 fee rate in " + CURRENCY_UNIT + "/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"
+ " \"replaceable\" (boolean, optional) Marks this transaction as BIP125 replaceable.\n"
+ " Allows this transaction to be replaced by a transaction with higher fees\n"
+ " \"conf_target\" (numeric, optional) Confirmation target (in blocks)\n"
+ " \"estimate_mode\" (string, optional, default=UNSET) The fee estimate mode, must be one of:\n"
+ " \"UNSET\"\n"
+ " \"ECONOMICAL\"\n"
+ " \"CONSERVATIVE\"\n"
+ " }\n"
+ " for backward compatibility: passing in a true instead of an object will result in {\"includeWatching\":true}\n"
+ "3. iswitness (boolean, optional) Whether the transaction hex is a serialized witness transaction \n"
+ " If iswitness is not present, heuristic tests will be used in decoding\n"
+
+ "\nResult:\n"
+ "{\n"
+ " \"hex\": \"value\", (string) The resulting raw transaction (hex-encoded string)\n"
+ " \"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"
+ "\nExamples:\n"
+ "\nCreate a transaction with no inputs\n"
+ + HelpExampleCli("createrawtransaction", "\"[]\" \"{\\\"myaddress\\\":0.01}\"") +
+ "\nAdd sufficient unsigned inputs to meet the output value\n"
+ + HelpExampleCli("fundrawtransaction", "\"rawtransactionhex\"") +
+ "\nSign the transaction\n"
+ + HelpExampleCli("signrawtransaction", "\"fundedtransactionhex\"") +
+ "\nSend the transaction\n"
+ + HelpExampleCli("sendrawtransaction", "\"signedtransactionhex\"")
+ );
+
+ RPCTypeCheck(request.params, {UniValue::VSTR, UniValueType(), UniValue::VBOOL});
+
+ // parse hex string from parameter
+ CMutableTransaction tx;
+ bool try_witness = request.params[2].isNull() ? true : request.params[2].get_bool();
+ bool try_no_witness = request.params[2].isNull() ? true : !request.params[2].get_bool();
+ if (!DecodeHexTx(tx, request.params[0].get_str(), try_no_witness, try_witness)) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
+ }
+
+ CAmount fee;
+ int change_position;
+ FundTransaction(pwallet, tx, fee, change_position, request.params[1]);
UniValue result(UniValue::VOBJ);
result.pushKV("hex", EncodeHexTx(tx));
- result.pushKV("changepos", changePosition);
- result.pushKV("fee", ValueFromAmount(nFeeOut));
+ result.pushKV("fee", ValueFromAmount(fee));
+ result.pushKV("changepos", change_position);
return result;
}
@@ -4393,6 +4423,334 @@ UniValue sethdseed(const JSONRPCRequest& request)
return NullUniValue;
}
+bool ParseHDKeypath(std::string keypath_str, std::vector<uint32_t>& keypath)
+{
+ std::stringstream ss(keypath_str);
+ std::string item;
+ bool first = true;
+ while (std::getline(ss, item, '/')) {
+ if (item.compare("m") == 0) {
+ if (first) {
+ first = false;
+ continue;
+ }
+ return false;
+ }
+ // Finds whether it is hardened
+ uint32_t path = 0;
+ size_t pos = item.find("'");
+ if (pos != std::string::npos) {
+ // The hardened tick can only be in the last index of the string
+ if (pos != item.size() - 1) {
+ return false;
+ }
+ path |= 0x80000000;
+ item = item.substr(0, item.size() - 1); // Drop the last character which is the hardened tick
+ }
+
+ // Ensure this is only numbers
+ if (item.find_first_not_of( "0123456789" ) != std::string::npos) {
+ return false;
+ }
+ uint32_t number;
+ if (!ParseUInt32(item, &number)) {
+ return false;
+ }
+ path |= number;
+
+ keypath.push_back(path);
+ first = false;
+ }
+ return true;
+}
+
+void AddKeypathToMap(const CWallet* pwallet, const CKeyID& keyID, std::map<CPubKey, std::vector<uint32_t>>& hd_keypaths)
+{
+ CPubKey vchPubKey;
+ if (!pwallet->GetPubKey(keyID, vchPubKey)) {
+ return;
+ }
+ CKeyMetadata meta;
+ auto it = pwallet->mapKeyMetadata.find(keyID);
+ if (it != pwallet->mapKeyMetadata.end()) {
+ meta = it->second;
+ }
+ std::vector<uint32_t> keypath;
+ if (!meta.hdKeypath.empty()) {
+ if (!ParseHDKeypath(meta.hdKeypath, keypath)) {
+ throw JSONRPCError(RPC_INTERNAL_ERROR, "Internal keypath is broken");
+ }
+ // Get the proper master key id
+ CKey key;
+ pwallet->GetKey(meta.hd_seed_id, key);
+ CExtKey masterKey;
+ masterKey.SetSeed(key.begin(), key.size());
+ // Add to map
+ keypath.insert(keypath.begin(), ReadLE32(masterKey.key.GetPubKey().GetID().begin()));
+ } else { // Single pubkeys get the master fingerprint of themselves
+ keypath.insert(keypath.begin(), ReadLE32(vchPubKey.GetID().begin()));
+ }
+ hd_keypaths.emplace(vchPubKey, keypath);
+}
+
+bool FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& psbtx, const CTransaction* txConst, int sighash_type, bool sign, bool bip32derivs)
+{
+ LOCK(pwallet->cs_wallet);
+ // Get all of the previous transactions
+ bool complete = true;
+ for (unsigned int i = 0; i < txConst->vin.size(); ++i) {
+ const CTxIn& txin = txConst->vin[i];
+ PSBTInput& input = psbtx.inputs.at(i);
+
+ // If we don't know about this input, skip it and let someone else deal with it
+ const uint256& txhash = txin.prevout.hash;
+ const auto& it = pwallet->mapWallet.find(txhash);
+ if (it != pwallet->mapWallet.end()) {
+ const CWalletTx& wtx = it->second;
+ CTxOut utxo = wtx.tx->vout[txin.prevout.n];
+ input.non_witness_utxo = wtx.tx;
+ input.witness_utxo = utxo;
+ }
+
+ // Get the Sighash type
+ if (sign && input.sighash_type > 0 && input.sighash_type != sighash_type) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Specified Sighash and sighash in PSBT do not match.");
+ }
+
+ SignatureData sigdata;
+ if (sign) {
+ complete &= SignPSBTInput(*pwallet, *psbtx.tx, input, sigdata, i, sighash_type);
+ } else {
+ complete &= SignPSBTInput(PublicOnlySigningProvider(pwallet), *psbtx.tx, input, sigdata, i, sighash_type);
+ }
+
+ // Drop the unnecessary UTXO
+ if (sigdata.witness) {
+ input.non_witness_utxo = nullptr;
+ } else {
+ input.witness_utxo.SetNull();
+ }
+
+ // Get public key paths
+ if (bip32derivs) {
+ for (const auto& pubkey_it : sigdata.misc_pubkeys) {
+ AddKeypathToMap(pwallet, pubkey_it.first, input.hd_keypaths);
+ }
+ }
+ }
+
+ // Fill in the bip32 keypaths and redeemscripts for the outputs so that hardware wallets can identify change
+ for (unsigned int i = 0; i < txConst->vout.size(); ++i) {
+ const CTxOut& out = txConst->vout.at(i);
+ PSBTOutput& psbt_out = psbtx.outputs.at(i);
+
+ // Dummy tx so we can use ProduceSignature to get stuff out
+ CMutableTransaction dummy_tx;
+ dummy_tx.vin.push_back(CTxIn());
+ dummy_tx.vout.push_back(CTxOut());
+
+ // Fill a SignatureData with output info
+ SignatureData sigdata;
+ psbt_out.FillSignatureData(sigdata);
+
+ MutableTransactionSignatureCreator creator(psbtx.tx.get_ptr(), 0, out.nValue, 1);
+ ProduceSignature(*pwallet, creator, out.scriptPubKey, sigdata);
+ psbt_out.FromSignatureData(sigdata);
+
+ // Get public key paths
+ if (bip32derivs) {
+ for (const auto& pubkey_it : sigdata.misc_pubkeys) {
+ AddKeypathToMap(pwallet, pubkey_it.first, psbt_out.hd_keypaths);
+ }
+ }
+ }
+ return complete;
+}
+
+UniValue walletprocesspsbt(const JSONRPCRequest& request)
+{
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ CWallet* const pwallet = wallet.get();
+
+ if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
+ return NullUniValue;
+ }
+
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 4)
+ throw std::runtime_error(
+ "walletprocesspsbt \"psbt\" ( sign \"sighashtype\" bip32derivs )\n"
+ "\nUpdate a PSBT with input information from our wallet and then sign inputs\n"
+ "that we can sign for.\n"
+ + HelpRequiringPassphrase(pwallet) + "\n"
+
+ "\nArguments:\n"
+ "1. \"psbt\" (string, required) The transaction base64 string\n"
+ "2. sign (boolean, optional, default=true) Also sign the transaction when updating\n"
+ "3. \"sighashtype\" (string, optional, default=ALL) The signature hash type to sign with if not specified by the PSBT. Must be one of\n"
+ " \"ALL\"\n"
+ " \"NONE\"\n"
+ " \"SINGLE\"\n"
+ " \"ALL|ANYONECANPAY\"\n"
+ " \"NONE|ANYONECANPAY\"\n"
+ " \"SINGLE|ANYONECANPAY\"\n"
+ "4. bip32derivs (boolean, optiona, default=false) If true, includes the BIP 32 derivation paths for public keys if we know them\n"
+
+ "\nResult:\n"
+ "{\n"
+ " \"psbt\" : \"value\", (string) The base64-encoded partially signed transaction\n"
+ " \"complete\" : true|false, (boolean) If the transaction has a complete set of signatures\n"
+ " ]\n"
+ "}\n"
+
+ "\nExamples:\n"
+ + HelpExampleCli("walletprocesspsbt", "\"psbt\"")
+ );
+
+ RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL, UniValue::VSTR});
+
+ // Unserialize the transaction
+ PartiallySignedTransaction psbtx;
+ std::string error;
+ if (!DecodePSBT(psbtx, request.params[0].get_str(), error)) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
+ }
+
+ // Get the sighash type
+ int nHashType = ParseSighashString(request.params[2]);
+
+ // Use CTransaction for the constant parts of the
+ // transaction to avoid rehashing.
+ const CTransaction txConst(*psbtx.tx);
+
+ // Fill transaction with our data and also sign
+ bool sign = request.params[1].isNull() ? true : request.params[1].get_bool();
+ bool bip32derivs = request.params[3].isNull() ? false : request.params[3].get_bool();
+ bool complete = FillPSBT(pwallet, psbtx, &txConst, nHashType, sign, bip32derivs);
+
+ UniValue result(UniValue::VOBJ);
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
+ ssTx << psbtx;
+ result.push_back(Pair("psbt", EncodeBase64((unsigned char*)ssTx.data(), ssTx.size())));
+ result.push_back(Pair("complete", complete));
+
+ return result;
+}
+
+UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
+{
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ CWallet* const pwallet = wallet.get();
+
+ if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
+ return NullUniValue;
+ }
+
+ if (request.fHelp || request.params.size() < 2 || request.params.size() > 6)
+ throw std::runtime_error(
+ "walletcreatefundedpsbt [{\"txid\":\"id\",\"vout\":n},...] [{\"address\":amount},{\"data\":\"hex\"},...] ( locktime ) ( replaceable ) ( options bip32derivs )\n"
+ "\nCreates and funds a transaction in the Partially Signed Transaction format. Inputs will be added if supplied inputs are not enough\n"
+ "Implements the Creator and Updater roles.\n"
+ "\nArguments:\n"
+ "1. \"inputs\" (array, required) A json array of json objects\n"
+ " [\n"
+ " {\n"
+ " \"txid\":\"id\", (string, required) The transaction id\n"
+ " \"vout\":n, (numeric, required) The output number\n"
+ " \"sequence\":n (numeric, optional) The sequence number\n"
+ " } \n"
+ " ,...\n"
+ " ]\n"
+ "2. \"outputs\" (array, required) a json array with outputs (key-value pairs)\n"
+ " [\n"
+ " {\n"
+ " \"address\": x.xxx, (obj, optional) A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT + "\n"
+ " },\n"
+ " {\n"
+ " \"data\": \"hex\" (obj, optional) A key-value pair. The key must be \"data\", the value is hex encoded data\n"
+ " }\n"
+ " ,... More key-value pairs of the above form. For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n"
+ " accepted as second parameter.\n"
+ " ]\n"
+ "3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n"
+ "4. replaceable (boolean, optional, default=false) Marks this transaction as BIP125 replaceable.\n"
+ " Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible.\n"
+ "5. 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"
+ " \"change_type\" (string, optional) The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\". Default is set by -changetype.\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 fee rate in " + CURRENCY_UNIT + "/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"
+ " \"replaceable\" (boolean, optional) Marks this transaction as BIP125 replaceable.\n"
+ " Allows this transaction to be replaced by a transaction with higher fees\n"
+ " \"conf_target\" (numeric, optional) Confirmation target (in blocks)\n"
+ " \"estimate_mode\" (string, optional, default=UNSET) The fee estimate mode, must be one of:\n"
+ " \"UNSET\"\n"
+ " \"ECONOMICAL\"\n"
+ " \"CONSERVATIVE\"\n"
+ " }\n"
+ "6. bip32derivs (boolean, optiona, default=false) If true, includes the BIP 32 derivation paths for public keys if we know them\n"
+ "\nResult:\n"
+ "{\n"
+ " \"psbt\": \"value\", (string) The resulting raw transaction (base64-encoded string)\n"
+ " \"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"
+ "\nExamples:\n"
+ "\nCreate a transaction with no inputs\n"
+ + HelpExampleCli("walletcreatefundedpsbt", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"data\\\":\\\"00010203\\\"}]\"")
+ );
+
+ RPCTypeCheck(request.params, {
+ UniValue::VARR,
+ UniValueType(), // ARR or OBJ, checked later
+ UniValue::VNUM,
+ UniValue::VBOOL,
+ UniValue::VOBJ
+ }, true
+ );
+
+ CAmount fee;
+ int change_position;
+ CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], request.params[3]);
+ FundTransaction(pwallet, rawTx, fee, change_position, request.params[4]);
+
+ // Make a blank psbt
+ PartiallySignedTransaction psbtx;
+ psbtx.tx = rawTx;
+ for (unsigned int i = 0; i < rawTx.vin.size(); ++i) {
+ psbtx.inputs.push_back(PSBTInput());
+ }
+ for (unsigned int i = 0; i < rawTx.vout.size(); ++i) {
+ psbtx.outputs.push_back(PSBTOutput());
+ }
+
+ // Use CTransaction for the constant parts of the
+ // transaction to avoid rehashing.
+ const CTransaction txConst(*psbtx.tx);
+
+ // Fill transaction with out data but don't sign
+ bool bip32derivs = request.params[5].isNull() ? false : request.params[5].get_bool();
+ FillPSBT(pwallet, psbtx, &txConst, 1, false, bip32derivs);
+
+ // Serialize the PSBT
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
+ ssTx << psbtx;
+
+ UniValue result(UniValue::VOBJ);
+ result.pushKV("psbt", EncodeBase64((unsigned char*)ssTx.data(), ssTx.size()));
+ result.pushKV("fee", ValueFromAmount(fee));
+ result.pushKV("changepos", change_position);
+ return result;
+}
+
extern UniValue abortrescan(const JSONRPCRequest& request); // in rpcdump.cpp
extern UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp
extern UniValue importprivkey(const JSONRPCRequest& request);
@@ -4409,6 +4767,8 @@ static const CRPCCommand commands[] =
{ // category name actor (function) argNames
// --------------------- ------------------------ ----------------------- ----------
{ "rawtransactions", "fundrawtransaction", &fundrawtransaction, {"hexstring","options","iswitness"} },
+ { "wallet", "walletprocesspsbt", &walletprocesspsbt, {"psbt","sign","sighashtype","bip32derivs"} },
+ { "wallet", "walletcreatefundedpsbt", &walletcreatefundedpsbt, {"inputs","outputs","locktime","replaceable","options","bip32derivs"} },
{ "hidden", "resendwallettransactions", &resendwallettransactions, {} },
{ "wallet", "abandontransaction", &abandontransaction, {"txid"} },
{ "wallet", "abortrescan", &abortrescan, {} },
@@ -4416,12 +4776,12 @@ static const CRPCCommand commands[] =
{ "hidden", "addwitnessaddress", &addwitnessaddress, {"address","p2sh"} },
{ "wallet", "backupwallet", &backupwallet, {"destination"} },
{ "wallet", "bumpfee", &bumpfee, {"txid", "options"} },
- { "wallet", "createwallet", &createwallet, {"wallet_name"} },
+ { "wallet", "createwallet", &createwallet, {"wallet_name", "disable_private_keys"} },
{ "wallet", "dumpprivkey", &dumpprivkey, {"address"} },
{ "wallet", "dumpwallet", &dumpwallet, {"filename"} },
{ "wallet", "encryptwallet", &encryptwallet, {"passphrase"} },
{ "wallet", "getaddressinfo", &getaddressinfo, {"address"} },
- { "wallet", "getbalance", &getbalance, {"account","minconf","include_watchonly"} },
+ { "wallet", "getbalance", &getbalance, {"account|dummy","minconf","include_watchonly"} },
{ "wallet", "getnewaddress", &getnewaddress, {"label|account","address_type"} },
{ "wallet", "getrawchangeaddress", &getrawchangeaddress, {"address_type"} },
{ "wallet", "getreceivedbyaddress", &getreceivedbyaddress, {"address","minconf"} },
diff --git a/src/wallet/rpcwallet.h b/src/wallet/rpcwallet.h
index b841f3e424..64556b5824 100644
--- a/src/wallet/rpcwallet.h
+++ b/src/wallet/rpcwallet.h
@@ -11,6 +11,8 @@ class CRPCTable;
class CWallet;
class JSONRPCRequest;
class UniValue;
+struct PartiallySignedTransaction;
+class CTransaction;
void RegisterWalletRPCCommands(CRPCTable &t);
@@ -28,4 +30,5 @@ bool EnsureWalletIsAvailable(CWallet *, bool avoidException);
UniValue getaddressinfo(const JSONRPCRequest& request);
UniValue signrawtransactionwithwallet(const JSONRPCRequest& request);
+bool FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& psbtx, const CTransaction* txConst, int sighash_type = 1, bool sign = true, bool bip32derivs = false);
#endif //BITCOIN_WALLET_RPCWALLET_H
diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp
index d90be33000..1561760577 100644
--- a/src/wallet/test/coinselector_tests.cpp
+++ b/src/wallet/test/coinselector_tests.cpp
@@ -102,6 +102,22 @@ static CAmount make_hard_case(int utxos, std::vector<CInputCoin>& utxo_pool)
return target;
}
+inline std::vector<OutputGroup>& GroupCoins(const std::vector<CInputCoin>& coins)
+{
+ static std::vector<OutputGroup> static_groups;
+ static_groups.clear();
+ for (auto& coin : coins) static_groups.emplace_back(coin, 0, true, 0, 0);
+ return static_groups;
+}
+
+inline std::vector<OutputGroup>& GroupCoins(const std::vector<COutput>& coins)
+{
+ static std::vector<OutputGroup> static_groups;
+ static_groups.clear();
+ for (auto& coin : coins) static_groups.emplace_back(coin.GetInputCoin(), coin.nDepth, coin.tx->fDebitCached && coin.tx->nDebitCached == 1 /* HACK: we can't figure out the is_me flag so we use the conditions defined above; perhaps set safe to false for !fIsFromMe in add_coin() */, 0, 0);
+ return static_groups;
+}
+
// Branch and bound coin selection tests
BOOST_AUTO_TEST_CASE(bnb_search_test)
{
@@ -121,7 +137,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
BOOST_TEST_MESSAGE("Testing known outcomes");
// Empty utxo pool
- BOOST_CHECK(!SelectCoinsBnB(utxo_pool, 1 * CENT, 0.5 * CENT, selection, value_ret, not_input_fees));
+ BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 1 * CENT, 0.5 * CENT, selection, value_ret, not_input_fees));
selection.clear();
// Add utxos
@@ -132,14 +148,14 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
// Select 1 Cent
add_coin(1 * CENT, 1, actual_selection);
- BOOST_CHECK(SelectCoinsBnB(utxo_pool, 1 * CENT, 0.5 * CENT, selection, value_ret, not_input_fees));
+ BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), 1 * CENT, 0.5 * CENT, selection, value_ret, not_input_fees));
BOOST_CHECK(equal_sets(selection, actual_selection));
actual_selection.clear();
selection.clear();
// Select 2 Cent
add_coin(2 * CENT, 2, actual_selection);
- BOOST_CHECK(SelectCoinsBnB(utxo_pool, 2 * CENT, 0.5 * CENT, selection, value_ret, not_input_fees));
+ BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), 2 * CENT, 0.5 * CENT, selection, value_ret, not_input_fees));
BOOST_CHECK(equal_sets(selection, actual_selection));
actual_selection.clear();
selection.clear();
@@ -147,13 +163,13 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
// Select 5 Cent
add_coin(3 * CENT, 3, actual_selection);
add_coin(2 * CENT, 2, actual_selection);
- BOOST_CHECK(SelectCoinsBnB(utxo_pool, 5 * CENT, 0.5 * CENT, selection, value_ret, not_input_fees));
+ BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), 5 * CENT, 0.5 * CENT, selection, value_ret, not_input_fees));
BOOST_CHECK(equal_sets(selection, actual_selection));
actual_selection.clear();
selection.clear();
// Select 11 Cent, not possible
- BOOST_CHECK(!SelectCoinsBnB(utxo_pool, 11 * CENT, 0.5 * CENT, selection, value_ret, not_input_fees));
+ BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 11 * CENT, 0.5 * CENT, selection, value_ret, not_input_fees));
actual_selection.clear();
selection.clear();
@@ -163,7 +179,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
add_coin(3 * CENT, 3, actual_selection);
add_coin(2 * CENT, 2, actual_selection);
add_coin(1 * CENT, 1, actual_selection);
- BOOST_CHECK(SelectCoinsBnB(utxo_pool, 10 * CENT, 0.5 * CENT, selection, value_ret, not_input_fees));
+ BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), 10 * CENT, 0.5 * CENT, selection, value_ret, not_input_fees));
BOOST_CHECK(equal_sets(selection, actual_selection));
actual_selection.clear();
selection.clear();
@@ -173,18 +189,18 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
add_coin(5 * CENT, 5, actual_selection);
add_coin(3 * CENT, 3, actual_selection);
add_coin(2 * CENT, 2, actual_selection);
- BOOST_CHECK(SelectCoinsBnB(utxo_pool, 10 * CENT, 5000, selection, value_ret, not_input_fees));
+ BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), 10 * CENT, 5000, selection, value_ret, not_input_fees));
// Select 0.25 Cent, not possible
- BOOST_CHECK(!SelectCoinsBnB(utxo_pool, 0.25 * CENT, 0.5 * CENT, selection, value_ret, not_input_fees));
+ BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 0.25 * CENT, 0.5 * CENT, selection, value_ret, not_input_fees));
actual_selection.clear();
selection.clear();
// Iteration exhaustion test
CAmount target = make_hard_case(17, utxo_pool);
- BOOST_CHECK(!SelectCoinsBnB(utxo_pool, target, 0, selection, value_ret, not_input_fees)); // Should exhaust
+ BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), target, 0, selection, value_ret, not_input_fees)); // Should exhaust
target = make_hard_case(14, utxo_pool);
- BOOST_CHECK(SelectCoinsBnB(utxo_pool, target, 0, selection, value_ret, not_input_fees)); // Should not exhaust
+ BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), target, 0, selection, value_ret, not_input_fees)); // Should not exhaust
// Test same value early bailout optimization
add_coin(7 * CENT, 7, actual_selection);
@@ -200,7 +216,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
for (int i = 0; i < 50000; ++i) {
add_coin(5 * CENT, 7, utxo_pool);
}
- BOOST_CHECK(SelectCoinsBnB(utxo_pool, 30 * CENT, 5000, selection, value_ret, not_input_fees));
+ BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), 30 * CENT, 5000, selection, value_ret, not_input_fees));
////////////////////
// Behavior tests //
@@ -212,7 +228,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
}
// Run 100 times, to make sure it is never finding a solution
for (int i = 0; i < 100; ++i) {
- BOOST_CHECK(!SelectCoinsBnB(utxo_pool, 1 * CENT, 2 * CENT, selection, value_ret, not_input_fees));
+ BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 1 * CENT, 2 * CENT, selection, value_ret, not_input_fees));
}
// Make sure that effective value is working in SelectCoinsMinConf when BnB is used
@@ -223,7 +239,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
empty_wallet();
add_coin(1);
vCoins.at(0).nInputBytes = 40; // Make sure that it has a negative effective value. The next check should assert if this somehow got through. Otherwise it will fail
- BOOST_CHECK(!testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params_bnb, bnb_used));
+ BOOST_CHECK(!testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params_bnb, bnb_used));
// Make sure that we aren't using BnB when there are preset inputs
empty_wallet();
@@ -252,24 +268,24 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
empty_wallet();
// with an empty wallet we can't even pay one cent
- BOOST_CHECK(!testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK(!testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used));
add_coin(1*CENT, 4); // add a new 1 cent coin
// with a new 1 cent coin, we still can't find a mature 1 cent
- BOOST_CHECK(!testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK(!testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used));
// but we can find a new 1 cent
- BOOST_CHECK( testWallet.SelectCoinsMinConf( 1 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK( testWallet.SelectCoinsMinConf( 1 * CENT, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used));
BOOST_CHECK_EQUAL(nValueRet, 1 * CENT);
add_coin(2*CENT); // add a mature 2 cent coin
// we can't make 3 cents of mature coins
- BOOST_CHECK(!testWallet.SelectCoinsMinConf( 3 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK(!testWallet.SelectCoinsMinConf( 3 * CENT, filter_standard, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used));
// we can make 3 cents of new coins
- BOOST_CHECK( testWallet.SelectCoinsMinConf( 3 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK( testWallet.SelectCoinsMinConf( 3 * CENT, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used));
BOOST_CHECK_EQUAL(nValueRet, 3 * CENT);
add_coin(5*CENT); // add a mature 5 cent coin,
@@ -279,33 +295,33 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
// now we have new: 1+10=11 (of which 10 was self-sent), and mature: 2+5+20=27. total = 38
// we can't make 38 cents only if we disallow new coins:
- BOOST_CHECK(!testWallet.SelectCoinsMinConf(38 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK(!testWallet.SelectCoinsMinConf(38 * CENT, filter_standard, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used));
// we can't even make 37 cents if we don't allow new coins even if they're from us
- BOOST_CHECK(!testWallet.SelectCoinsMinConf(38 * CENT, filter_standard_extra, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK(!testWallet.SelectCoinsMinConf(38 * CENT, filter_standard_extra, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used));
// but we can make 37 cents if we accept new coins from ourself
- BOOST_CHECK( testWallet.SelectCoinsMinConf(37 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK( testWallet.SelectCoinsMinConf(37 * CENT, filter_standard, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used));
BOOST_CHECK_EQUAL(nValueRet, 37 * CENT);
// and we can make 38 cents if we accept all new coins
- BOOST_CHECK( testWallet.SelectCoinsMinConf(38 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK( testWallet.SelectCoinsMinConf(38 * CENT, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used));
BOOST_CHECK_EQUAL(nValueRet, 38 * CENT);
// try making 34 cents from 1,2,5,10,20 - we can't do it exactly
- BOOST_CHECK( testWallet.SelectCoinsMinConf(34 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK( testWallet.SelectCoinsMinConf(34 * CENT, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used));
BOOST_CHECK_EQUAL(nValueRet, 35 * CENT); // but 35 cents is closest
BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U); // the best should be 20+10+5. it's incredibly unlikely the 1 or 2 got included (but possible)
// when we try making 7 cents, the smaller coins (1,2,5) are enough. We should see just 2+5
- BOOST_CHECK( testWallet.SelectCoinsMinConf( 7 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK( testWallet.SelectCoinsMinConf( 7 * CENT, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used));
BOOST_CHECK_EQUAL(nValueRet, 7 * CENT);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U);
// when we try making 8 cents, the smaller coins (1,2,5) are exactly enough.
- BOOST_CHECK( testWallet.SelectCoinsMinConf( 8 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK( testWallet.SelectCoinsMinConf( 8 * CENT, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used));
BOOST_CHECK(nValueRet == 8 * CENT);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U);
// when we try making 9 cents, no subset of smaller coins is enough, and we get the next bigger coin (10)
- BOOST_CHECK( testWallet.SelectCoinsMinConf( 9 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK( testWallet.SelectCoinsMinConf( 9 * CENT, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used));
BOOST_CHECK_EQUAL(nValueRet, 10 * CENT);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
@@ -319,30 +335,30 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
add_coin(30*CENT); // now we have 6+7+8+20+30 = 71 cents total
// check that we have 71 and not 72
- BOOST_CHECK( testWallet.SelectCoinsMinConf(71 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
- BOOST_CHECK(!testWallet.SelectCoinsMinConf(72 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK( testWallet.SelectCoinsMinConf(71 * CENT, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK(!testWallet.SelectCoinsMinConf(72 * CENT, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used));
// now try making 16 cents. the best smaller coins can do is 6+7+8 = 21; not as good at the next biggest coin, 20
- BOOST_CHECK( testWallet.SelectCoinsMinConf(16 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK( testWallet.SelectCoinsMinConf(16 * CENT, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used));
BOOST_CHECK_EQUAL(nValueRet, 20 * CENT); // we should get 20 in one coin
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
add_coin( 5*CENT); // now we have 5+6+7+8+20+30 = 75 cents total
// now if we try making 16 cents again, the smaller coins can make 5+6+7 = 18 cents, better than the next biggest coin, 20
- BOOST_CHECK( testWallet.SelectCoinsMinConf(16 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK( testWallet.SelectCoinsMinConf(16 * CENT, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used));
BOOST_CHECK_EQUAL(nValueRet, 18 * CENT); // we should get 18 in 3 coins
BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U);
add_coin( 18*CENT); // now we have 5+6+7+8+18+20+30
// and now if we try making 16 cents again, the smaller coins can make 5+6+7 = 18 cents, the same as the next biggest coin, 18
- BOOST_CHECK( testWallet.SelectCoinsMinConf(16 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK( testWallet.SelectCoinsMinConf(16 * CENT, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used));
BOOST_CHECK_EQUAL(nValueRet, 18 * CENT); // we should get 18 in 1 coin
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); // because in the event of a tie, the biggest coin wins
// now try making 11 cents. we should get 5+6
- BOOST_CHECK( testWallet.SelectCoinsMinConf(11 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK( testWallet.SelectCoinsMinConf(11 * CENT, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used));
BOOST_CHECK_EQUAL(nValueRet, 11 * CENT);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U);
@@ -351,11 +367,11 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
add_coin( 2*COIN);
add_coin( 3*COIN);
add_coin( 4*COIN); // now we have 5+6+7+8+18+20+30+100+200+300+400 = 1094 cents
- BOOST_CHECK( testWallet.SelectCoinsMinConf(95 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK( testWallet.SelectCoinsMinConf(95 * CENT, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used));
BOOST_CHECK_EQUAL(nValueRet, 1 * COIN); // we should get 1 BTC in 1 coin
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
- BOOST_CHECK( testWallet.SelectCoinsMinConf(195 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK( testWallet.SelectCoinsMinConf(195 * CENT, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used));
BOOST_CHECK_EQUAL(nValueRet, 2 * COIN); // we should get 2 BTC in 1 coin
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
@@ -370,14 +386,14 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
// try making 1 * MIN_CHANGE from the 1.5 * MIN_CHANGE
// we'll get change smaller than MIN_CHANGE whatever happens, so can expect MIN_CHANGE exactly
- BOOST_CHECK( testWallet.SelectCoinsMinConf(MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK( testWallet.SelectCoinsMinConf(MIN_CHANGE, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used));
BOOST_CHECK_EQUAL(nValueRet, MIN_CHANGE);
// but if we add a bigger coin, small change is avoided
add_coin(1111*MIN_CHANGE);
// try making 1 from 0.1 + 0.2 + 0.3 + 0.4 + 0.5 + 1111 = 1112.5
- BOOST_CHECK( testWallet.SelectCoinsMinConf(1 * MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK( testWallet.SelectCoinsMinConf(1 * MIN_CHANGE, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used));
BOOST_CHECK_EQUAL(nValueRet, 1 * MIN_CHANGE); // we should get the exact amount
// if we add more small coins:
@@ -385,7 +401,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
add_coin(MIN_CHANGE * 7 / 10);
// and try again to make 1.0 * MIN_CHANGE
- BOOST_CHECK( testWallet.SelectCoinsMinConf(1 * MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK( testWallet.SelectCoinsMinConf(1 * MIN_CHANGE, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used));
BOOST_CHECK_EQUAL(nValueRet, 1 * MIN_CHANGE); // we should get the exact amount
// run the 'mtgox' test (see http://blockexplorer.com/tx/29a3efd3ef04f9153d47a990bd7b048a4b2d213daaa5fb8ed670fb85f13bdbcf)
@@ -394,7 +410,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
for (int j = 0; j < 20; j++)
add_coin(50000 * COIN);
- BOOST_CHECK( testWallet.SelectCoinsMinConf(500000 * COIN, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK( testWallet.SelectCoinsMinConf(500000 * COIN, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used));
BOOST_CHECK_EQUAL(nValueRet, 500000 * COIN); // we should get the exact amount
BOOST_CHECK_EQUAL(setCoinsRet.size(), 10U); // in ten coins
@@ -407,7 +423,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
add_coin(MIN_CHANGE * 6 / 10);
add_coin(MIN_CHANGE * 7 / 10);
add_coin(1111 * MIN_CHANGE);
- BOOST_CHECK( testWallet.SelectCoinsMinConf(1 * MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK( testWallet.SelectCoinsMinConf(1 * MIN_CHANGE, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used));
BOOST_CHECK_EQUAL(nValueRet, 1111 * MIN_CHANGE); // we get the bigger coin
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
@@ -417,7 +433,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
add_coin(MIN_CHANGE * 6 / 10);
add_coin(MIN_CHANGE * 8 / 10);
add_coin(1111 * MIN_CHANGE);
- BOOST_CHECK( testWallet.SelectCoinsMinConf(MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK( testWallet.SelectCoinsMinConf(MIN_CHANGE, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used));
BOOST_CHECK_EQUAL(nValueRet, MIN_CHANGE); // we should get the exact amount
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); // in two coins 0.4+0.6
@@ -428,12 +444,12 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
add_coin(MIN_CHANGE * 100);
// trying to make 100.01 from these three coins
- BOOST_CHECK(testWallet.SelectCoinsMinConf(MIN_CHANGE * 10001 / 100, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK(testWallet.SelectCoinsMinConf(MIN_CHANGE * 10001 / 100, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used));
BOOST_CHECK_EQUAL(nValueRet, MIN_CHANGE * 10105 / 100); // we should get all coins
BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U);
// but if we try to make 99.9, we should take the bigger of the two small coins to avoid small change
- BOOST_CHECK(testWallet.SelectCoinsMinConf(MIN_CHANGE * 9990 / 100, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK(testWallet.SelectCoinsMinConf(MIN_CHANGE * 9990 / 100, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used));
BOOST_CHECK_EQUAL(nValueRet, 101 * MIN_CHANGE);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U);
@@ -443,7 +459,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
// Create 676 inputs (= (old MAX_STANDARD_TX_SIZE == 100000) / 148 bytes per input)
for (uint16_t j = 0; j < 676; j++)
add_coin(amt);
- BOOST_CHECK(testWallet.SelectCoinsMinConf(2000, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK(testWallet.SelectCoinsMinConf(2000, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used));
if (amt - 2000 < MIN_CHANGE) {
// needs more than one input:
uint16_t returnSize = std::ceil((2000.0 + MIN_CHANGE)/amt);
@@ -465,8 +481,8 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
// picking 50 from 100 coins doesn't depend on the shuffle,
// but does depend on randomness in the stochastic approximation code
- BOOST_CHECK(testWallet.SelectCoinsMinConf(50 * COIN, filter_standard, vCoins, setCoinsRet , nValueRet, coin_selection_params, bnb_used));
- BOOST_CHECK(testWallet.SelectCoinsMinConf(50 * COIN, filter_standard, vCoins, setCoinsRet2, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK(testWallet.SelectCoinsMinConf(50 * COIN, filter_standard, GroupCoins(vCoins), setCoinsRet , nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK(testWallet.SelectCoinsMinConf(50 * COIN, filter_standard, GroupCoins(vCoins), setCoinsRet2, nValueRet, coin_selection_params, bnb_used));
BOOST_CHECK(!equal_sets(setCoinsRet, setCoinsRet2));
int fails = 0;
@@ -474,8 +490,8 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
{
// selecting 1 from 100 identical coins depends on the shuffle; this test will fail 1% of the time
// run the test RANDOM_REPEATS times and only complain if all of them fail
- BOOST_CHECK(testWallet.SelectCoinsMinConf(COIN, filter_standard, vCoins, setCoinsRet , nValueRet, coin_selection_params, bnb_used));
- BOOST_CHECK(testWallet.SelectCoinsMinConf(COIN, filter_standard, vCoins, setCoinsRet2, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK(testWallet.SelectCoinsMinConf(COIN, filter_standard, GroupCoins(vCoins), setCoinsRet , nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK(testWallet.SelectCoinsMinConf(COIN, filter_standard, GroupCoins(vCoins), setCoinsRet2, nValueRet, coin_selection_params, bnb_used));
if (equal_sets(setCoinsRet, setCoinsRet2))
fails++;
}
@@ -495,8 +511,8 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
{
// selecting 1 from 100 identical coins depends on the shuffle; this test will fail 1% of the time
// run the test RANDOM_REPEATS times and only complain if all of them fail
- BOOST_CHECK(testWallet.SelectCoinsMinConf(90*CENT, filter_standard, vCoins, setCoinsRet , nValueRet, coin_selection_params, bnb_used));
- BOOST_CHECK(testWallet.SelectCoinsMinConf(90*CENT, filter_standard, vCoins, setCoinsRet2, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK(testWallet.SelectCoinsMinConf(90*CENT, filter_standard, GroupCoins(vCoins), setCoinsRet , nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK(testWallet.SelectCoinsMinConf(90*CENT, filter_standard, GroupCoins(vCoins), setCoinsRet2, nValueRet, coin_selection_params, bnb_used));
if (equal_sets(setCoinsRet, setCoinsRet2))
fails++;
}
@@ -521,7 +537,7 @@ BOOST_AUTO_TEST_CASE(ApproximateBestSubset)
add_coin(1000 * COIN);
add_coin(3 * COIN);
- BOOST_CHECK(testWallet.SelectCoinsMinConf(1003 * COIN, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK(testWallet.SelectCoinsMinConf(1003 * COIN, filter_standard, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used));
BOOST_CHECK_EQUAL(nValueRet, 1003 * COIN);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U);
@@ -559,8 +575,8 @@ BOOST_AUTO_TEST_CASE(SelectCoins_test)
CoinSet out_set;
CAmount out_value = 0;
bool bnb_used = false;
- BOOST_CHECK(testWallet.SelectCoinsMinConf(target, filter_standard, vCoins, out_set, out_value, coin_selection_params_bnb, bnb_used) ||
- testWallet.SelectCoinsMinConf(target, filter_standard, vCoins, out_set, out_value, coin_selection_params_knapsack, bnb_used));
+ BOOST_CHECK(testWallet.SelectCoinsMinConf(target, filter_standard, GroupCoins(vCoins), out_set, out_value, coin_selection_params_bnb, bnb_used) ||
+ testWallet.SelectCoinsMinConf(target, filter_standard, GroupCoins(vCoins), out_set, out_value, coin_selection_params_knapsack, bnb_used));
BOOST_CHECK_GE(out_value, target);
}
}
diff --git a/src/wallet/test/psbt_wallet_tests.cpp b/src/wallet/test/psbt_wallet_tests.cpp
new file mode 100644
index 0000000000..2cc995bf04
--- /dev/null
+++ b/src/wallet/test/psbt_wallet_tests.cpp
@@ -0,0 +1,150 @@
+// Copyright (c) 2017 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <key_io.h>
+#include <script/sign.h>
+#include <utilstrencodings.h>
+#include <wallet/rpcwallet.h>
+#include <wallet/wallet.h>
+#include <univalue.h>
+
+#include <boost/test/unit_test.hpp>
+#include <test/test_bitcoin.h>
+#include <wallet/test/wallet_test_fixture.h>
+
+extern bool ParseHDKeypath(std::string keypath_str, std::vector<uint32_t>& keypath);
+
+BOOST_FIXTURE_TEST_SUITE(psbt_wallet_tests, WalletTestingSetup)
+
+BOOST_AUTO_TEST_CASE(psbt_updater_test)
+{
+ // Create prevtxs and add to wallet
+ CDataStream s_prev_tx1(ParseHex("0200000000010158e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd7501000000171600145f275f436b09a8cc9a2eb2a2f528485c68a56323feffffff02d8231f1b0100000017a914aed962d6654f9a2b36608eb9d64d2b260db4f1118700c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e88702483045022100a22edcc6e5bc511af4cc4ae0de0fcd75c7e04d8c1c3a8aa9d820ed4b967384ec02200642963597b9b1bc22c75e9f3e117284a962188bf5e8a74c895089046a20ad770121035509a48eb623e10aace8bfd0212fdb8a8e5af3c94b0b133b95e114cab89e4f7965000000"), SER_NETWORK, PROTOCOL_VERSION);
+ CTransactionRef prev_tx1;
+ s_prev_tx1 >> prev_tx1;
+ CWalletTx prev_wtx1(&m_wallet, prev_tx1);
+ m_wallet.mapWallet.emplace(prev_wtx1.GetHash(), std::move(prev_wtx1));
+
+ CDataStream s_prev_tx2(ParseHex("0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000"), SER_NETWORK, PROTOCOL_VERSION);
+ CTransactionRef prev_tx2;
+ s_prev_tx2 >> prev_tx2;
+ CWalletTx prev_wtx2(&m_wallet, prev_tx2);
+ m_wallet.mapWallet.emplace(prev_wtx2.GetHash(), std::move(prev_wtx2));
+
+ // Add scripts
+ CScript rs1;
+ CDataStream s_rs1(ParseHex("475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae"), SER_NETWORK, PROTOCOL_VERSION);
+ s_rs1 >> rs1;
+ m_wallet.AddCScript(rs1);
+
+ CScript rs2;
+ CDataStream s_rs2(ParseHex("2200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903"), SER_NETWORK, PROTOCOL_VERSION);
+ s_rs2 >> rs2;
+ m_wallet.AddCScript(rs2);
+
+ CScript ws1;
+ CDataStream s_ws1(ParseHex("47522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae"), SER_NETWORK, PROTOCOL_VERSION);
+ s_ws1 >> ws1;
+ m_wallet.AddCScript(ws1);
+
+ // Add hd seed
+ CKey key = DecodeSecret("5KSSJQ7UNfFGwVgpCZDSHm5rVNhMFcFtvWM3zQ8mW4qNDEN7LFd"); // Mainnet and uncompressed form of cUkG8i1RFfWGWy5ziR11zJ5V4U4W3viSFCfyJmZnvQaUsd1xuF3T
+ CPubKey master_pub_key = m_wallet.DeriveNewSeed(key);
+ m_wallet.SetHDSeed(master_pub_key);
+ m_wallet.NewKeyPool();
+
+ // Call FillPSBT
+ PartiallySignedTransaction psbtx;
+ CDataStream ssData(ParseHex("70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f000000000000000000"), SER_NETWORK, PROTOCOL_VERSION);
+ ssData >> psbtx;
+
+ // Use CTransaction for the constant parts of the
+ // transaction to avoid rehashing.
+ const CTransaction txConst(*psbtx.tx);
+
+ // Fill transaction with our data
+ FillPSBT(&m_wallet, psbtx, &txConst, 1, false, true);
+
+ // Get the final tx
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
+ ssTx << psbtx;
+ std::string final_hex = HexStr(ssTx.begin(), ssTx.end());
+ BOOST_CHECK_EQUAL(final_hex, "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f0000008000000080010000800001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e88701042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f00000080000000800200008000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000");
+}
+
+BOOST_AUTO_TEST_CASE(parse_hd_keypath)
+{
+ std::vector<uint32_t> keypath;
+
+ BOOST_CHECK(ParseHDKeypath("1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1", keypath));
+ BOOST_CHECK(!ParseHDKeypath("///////////////////////////", keypath));
+
+ BOOST_CHECK(ParseHDKeypath("1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1'/1", keypath));
+ BOOST_CHECK(!ParseHDKeypath("//////////////////////////'/", keypath));
+
+ BOOST_CHECK(ParseHDKeypath("1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/", keypath));
+ BOOST_CHECK(!ParseHDKeypath("1///////////////////////////", keypath));
+
+ BOOST_CHECK(ParseHDKeypath("1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1'/", keypath));
+ BOOST_CHECK(!ParseHDKeypath("1/'//////////////////////////", keypath));
+
+ BOOST_CHECK(ParseHDKeypath("", keypath));
+ BOOST_CHECK(!ParseHDKeypath(" ", keypath));
+
+ BOOST_CHECK(ParseHDKeypath("0", keypath));
+ BOOST_CHECK(!ParseHDKeypath("O", keypath));
+
+ BOOST_CHECK(ParseHDKeypath("0000'/0000'/0000'", keypath));
+ BOOST_CHECK(!ParseHDKeypath("0000,/0000,/0000,", keypath));
+
+ BOOST_CHECK(ParseHDKeypath("01234", keypath));
+ BOOST_CHECK(!ParseHDKeypath("0x1234", keypath));
+
+ BOOST_CHECK(ParseHDKeypath("1", keypath));
+ BOOST_CHECK(!ParseHDKeypath(" 1", keypath));
+
+ BOOST_CHECK(ParseHDKeypath("42", keypath));
+ BOOST_CHECK(!ParseHDKeypath("m42", keypath));
+
+ BOOST_CHECK(ParseHDKeypath("4294967295", keypath)); // 4294967295 == 0xFFFFFFFF (uint32_t max)
+ BOOST_CHECK(!ParseHDKeypath("4294967296", keypath)); // 4294967296 == 0xFFFFFFFF (uint32_t max) + 1
+
+ BOOST_CHECK(ParseHDKeypath("m", keypath));
+ BOOST_CHECK(!ParseHDKeypath("n", keypath));
+
+ BOOST_CHECK(ParseHDKeypath("m/", keypath));
+ BOOST_CHECK(!ParseHDKeypath("n/", keypath));
+
+ BOOST_CHECK(ParseHDKeypath("m/0", keypath));
+ BOOST_CHECK(!ParseHDKeypath("n/0", keypath));
+
+ BOOST_CHECK(ParseHDKeypath("m/0'", keypath));
+ BOOST_CHECK(!ParseHDKeypath("m/0''", keypath));
+
+ BOOST_CHECK(ParseHDKeypath("m/0'/0'", keypath));
+ BOOST_CHECK(!ParseHDKeypath("m/'0/0'", keypath));
+
+ BOOST_CHECK(ParseHDKeypath("m/0/0", keypath));
+ BOOST_CHECK(!ParseHDKeypath("n/0/0", keypath));
+
+ BOOST_CHECK(ParseHDKeypath("m/0/0/00", keypath));
+ BOOST_CHECK(!ParseHDKeypath("m/0/0/f00", keypath));
+
+ BOOST_CHECK(ParseHDKeypath("m/0/0/000000000000000000000000000000000000000000000000000000000000000000000000000000000000", keypath));
+ BOOST_CHECK(!ParseHDKeypath("m/1/1/111111111111111111111111111111111111111111111111111111111111111111111111111111111111", keypath));
+
+ BOOST_CHECK(ParseHDKeypath("m/0/00/0", keypath));
+ BOOST_CHECK(!ParseHDKeypath("m/0'/00/'0", keypath));
+
+ BOOST_CHECK(ParseHDKeypath("m/1/", keypath));
+ BOOST_CHECK(!ParseHDKeypath("m/1//", keypath));
+
+ BOOST_CHECK(ParseHDKeypath("m/0/4294967295", keypath)); // 4294967295 == 0xFFFFFFFF (uint32_t max)
+ BOOST_CHECK(!ParseHDKeypath("m/0/4294967296", keypath)); // 4294967296 == 0xFFFFFFFF (uint32_t max) + 1
+
+ BOOST_CHECK(ParseHDKeypath("m/4294967295", keypath)); // 4294967295 == 0xFFFFFFFF (uint32_t max)
+ BOOST_CHECK(!ParseHDKeypath("m/4294967296", keypath)); // 4294967296 == 0xFFFFFFFF (uint32_t max) + 1
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 03754154fc..c89b8f252f 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -130,6 +130,8 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
LOCK(cs_main);
+ std::string backup_file = (SetDataDir("importwallet_rescan") / "wallet.backup").string();
+
// Import key into wallet and call dumpwallet to create backup file.
{
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>("dummy", WalletDatabase::CreateDummy());
@@ -139,7 +141,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
JSONRPCRequest request;
request.params.setArray();
- request.params.push_back((pathTemp / "wallet.backup").string());
+ request.params.push_back(backup_file);
AddWallet(wallet);
::dumpwallet(request);
RemoveWallet(wallet);
@@ -152,7 +154,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
JSONRPCRequest request;
request.params.setArray();
- request.params.push_back((pathTemp / "wallet.backup").string());
+ request.params.push_back(backup_file);
AddWallet(wallet);
::importwallet(request);
RemoveWallet(wallet);
@@ -363,4 +365,13 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup)
BOOST_CHECK_EQUAL(list.begin()->second.size(), 2U);
}
+BOOST_FIXTURE_TEST_CASE(wallet_disableprivkeys, TestChain100Setup)
+{
+ std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>("dummy", WalletDatabase::CreateDummy());
+ wallet->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
+ BOOST_CHECK(!wallet->TopUpKeyPool(1000));
+ CPubKey pubkey;
+ BOOST_CHECK(!wallet->GetKeyFromPool(pubkey, false));
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index adc48a8650..4918100b30 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -164,6 +164,7 @@ const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const
CPubKey CWallet::GenerateNewKey(WalletBatch &batch, bool internal)
{
+ assert(!IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
AssertLockHeld(cs_wallet); // mapKeyMetadata
bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets
@@ -303,20 +304,18 @@ bool CWallet::AddCryptedKey(const CPubKey &vchPubKey,
}
}
-bool CWallet::LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata &meta)
+void CWallet::LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata &meta)
{
AssertLockHeld(cs_wallet); // mapKeyMetadata
UpdateTimeFirstKey(meta.nCreateTime);
mapKeyMetadata[keyID] = meta;
- return true;
}
-bool CWallet::LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata &meta)
+void CWallet::LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata &meta)
{
AssertLockHeld(cs_wallet); // m_script_metadata
UpdateTimeFirstKey(meta.nCreateTime);
m_script_metadata[script_id] = meta;
- return true;
}
bool CWallet::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret)
@@ -469,11 +468,11 @@ void CWallet::ChainStateFlushed(const CBlockLocator& loc)
batch.WriteBestBlock(loc);
}
-bool CWallet::SetMinVersion(enum WalletFeature nVersion, WalletBatch* batch_in, bool fExplicit)
+void CWallet::SetMinVersion(enum WalletFeature nVersion, WalletBatch* batch_in, bool fExplicit)
{
LOCK(cs_wallet); // nWalletVersion
if (nWalletVersion >= nVersion)
- return true;
+ return;
// when doing an explicit upgrade, if we pass the max version permitted, upgrade all the way
if (fExplicit && nVersion > nWalletMaxVersion)
@@ -491,8 +490,6 @@ bool CWallet::SetMinVersion(enum WalletFeature nVersion, WalletBatch* batch_in,
if (!batch_in)
delete batch;
}
-
- return true;
}
bool CWallet::SetMaxVersion(int nVersion)
@@ -702,9 +699,7 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
// if we are using HD, replace the HD seed with a new one
if (IsHDEnabled()) {
- if (!SetHDSeed(GenerateNewSeed())) {
- return false;
- }
+ SetHDSeed(GenerateNewSeed());
}
NewKeyPool();
@@ -1005,7 +1000,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
return true;
}
-bool CWallet::LoadToWallet(const CWalletTx& wtxIn)
+void CWallet::LoadToWallet(const CWalletTx& wtxIn)
{
uint256 hash = wtxIn.GetHash();
const auto& ins = mapWallet.emplace(hash, wtxIn);
@@ -1024,23 +1019,8 @@ bool CWallet::LoadToWallet(const CWalletTx& wtxIn)
}
}
}
-
- return true;
}
-/**
- * Add a transaction to the wallet, or update it. pIndex and posInBlock should
- * be set when the transaction was known to be included in a block. When
- * pIndex == nullptr, then wallet state is not updated in AddToWallet, but
- * notifications happen and cached balances are marked dirty.
- *
- * If fUpdate is true, existing transactions will be updated.
- * TODO: One exception to this is that the abandoned state is cleared under the
- * assumption that any further notification of a transaction that was considered
- * abandoned is an indication that it is not safe to be considered abandoned.
- * Abandoned state should probably be more carefully tracked via different
- * posInBlock signals or by checking mempool presence when necessary.
- */
bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate)
{
const CTransaction& tx = *ptx;
@@ -1107,6 +1087,16 @@ bool CWallet::TransactionCanBeAbandoned(const uint256& hashTx) const
return wtx && !wtx->isAbandoned() && wtx->GetDepthInMainChain() == 0 && !wtx->InMempool();
}
+void CWallet::MarkInputsDirty(const CTransactionRef& tx)
+{
+ for (const CTxIn& txin : tx->vin) {
+ auto it = mapWallet.find(txin.prevout.hash);
+ if (it != mapWallet.end()) {
+ it->second.MarkDirty();
+ }
+ }
+}
+
bool CWallet::AbandonTransaction(const uint256& hashTx)
{
LOCK2(cs_main, cs_wallet);
@@ -1146,7 +1136,7 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
batch.WriteTx(wtx);
NotifyTransactionChanged(this, wtx.GetHash(), CT_UPDATED);
// Iterate over all its outputs, and mark transactions in the wallet that spend them abandoned too
- TxSpends::const_iterator iter = mapTxSpends.lower_bound(COutPoint(hashTx, 0));
+ TxSpends::const_iterator iter = mapTxSpends.lower_bound(COutPoint(now, 0));
while (iter != mapTxSpends.end() && iter->first.hash == now) {
if (!done.count(iter->second)) {
todo.insert(iter->second);
@@ -1155,13 +1145,7 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
}
// If a transaction changes 'conflicted' state, that changes the balance
// available of the outputs it spends. So force those to be recomputed
- for (const CTxIn& txin : wtx.tx->vin)
- {
- auto it = mapWallet.find(txin.prevout.hash);
- if (it != mapWallet.end()) {
- it->second.MarkDirty();
- }
- }
+ MarkInputsDirty(wtx.tx);
}
}
@@ -1217,31 +1201,19 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx)
}
// If a transaction changes 'conflicted' state, that changes the balance
// available of the outputs it spends. So force those to be recomputed
- for (const CTxIn& txin : wtx.tx->vin) {
- auto it = mapWallet.find(txin.prevout.hash);
- if (it != mapWallet.end()) {
- it->second.MarkDirty();
- }
- }
+ MarkInputsDirty(wtx.tx);
}
}
}
-void CWallet::SyncTransaction(const CTransactionRef& ptx, const CBlockIndex *pindex, int posInBlock) {
- const CTransaction& tx = *ptx;
-
- if (!AddToWalletIfInvolvingMe(ptx, pindex, posInBlock, true))
+void CWallet::SyncTransaction(const CTransactionRef& ptx, const CBlockIndex *pindex, int posInBlock, bool update_tx) {
+ if (!AddToWalletIfInvolvingMe(ptx, pindex, posInBlock, update_tx))
return; // Not one of ours
// If a transaction changes 'conflicted' state, that changes the balance
// available of the outputs it spends. So force those to be
// recomputed, also:
- for (const CTxIn& txin : tx.vin) {
- auto it = mapWallet.find(txin.prevout.hash);
- if (it != mapWallet.end()) {
- it->second.MarkDirty();
- }
- }
+ MarkInputsDirty(ptx);
}
void CWallet::TransactionAddedToMempool(const CTransactionRef& ptx) {
@@ -1465,6 +1437,7 @@ CAmount CWallet::GetChange(const CTransaction& tx) const
CPubKey CWallet::GenerateNewSeed()
{
+ assert(!IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
CKey key;
key.MakeNewKey(true);
return DeriveNewSeed(key);
@@ -1497,7 +1470,7 @@ CPubKey CWallet::DeriveNewSeed(const CKey& key)
return seed;
}
-bool CWallet::SetHDSeed(const CPubKey& seed)
+void CWallet::SetHDSeed(const CPubKey& seed)
{
LOCK(cs_wallet);
// store the keyid (hash160) together with
@@ -1507,18 +1480,15 @@ bool CWallet::SetHDSeed(const CPubKey& seed)
newHdChain.nVersion = CanSupportFeature(FEATURE_HD_SPLIT) ? CHDChain::VERSION_HD_CHAIN_SPLIT : CHDChain::VERSION_HD_BASE;
newHdChain.seed_id = seed.GetID();
SetHDChain(newHdChain, false);
-
- return true;
}
-bool CWallet::SetHDChain(const CHDChain& chain, bool memonly)
+void CWallet::SetHDChain(const CHDChain& chain, bool memonly)
{
LOCK(cs_wallet);
if (!memonly && !WalletBatch(*database).WriteHDChain(chain))
throw std::runtime_error(std::string(__func__) + ": writing chain failed");
hdChain = chain;
- return true;
}
bool CWallet::IsHDEnabled() const
@@ -1526,49 +1496,38 @@ bool CWallet::IsHDEnabled() const
return !hdChain.seed_id.IsNull();
}
-int64_t CWalletTx::GetTxTime() const
+void CWallet::SetWalletFlag(uint64_t flags)
{
- int64_t n = nTimeSmart;
- return n ? n : nTimeReceived;
+ LOCK(cs_wallet);
+ m_wallet_flags |= flags;
+ if (!WalletBatch(*database).WriteWalletFlags(m_wallet_flags))
+ throw std::runtime_error(std::string(__func__) + ": writing wallet flags failed");
}
-int CWalletTx::GetRequestCount() const
+bool CWallet::IsWalletFlagSet(uint64_t flag)
{
- // Returns -1 if it wasn't being tracked
- int nRequests = -1;
- {
- LOCK(pwallet->cs_wallet);
- if (IsCoinBase())
- {
- // Generated block
- if (!hashUnset())
- {
- std::map<uint256, int>::const_iterator mi = pwallet->mapRequestCount.find(hashBlock);
- if (mi != pwallet->mapRequestCount.end())
- nRequests = (*mi).second;
- }
- }
- else
- {
- // Did anyone request this transaction?
- std::map<uint256, int>::const_iterator mi = pwallet->mapRequestCount.find(GetHash());
- if (mi != pwallet->mapRequestCount.end())
- {
- nRequests = (*mi).second;
+ return (m_wallet_flags & flag);
+}
- // How about the block it's in?
- if (nRequests == 0 && !hashUnset())
- {
- std::map<uint256, int>::const_iterator _mi = pwallet->mapRequestCount.find(hashBlock);
- if (_mi != pwallet->mapRequestCount.end())
- nRequests = (*_mi).second;
- else
- nRequests = 1; // If it's in someone else's block it must have got out
- }
- }
- }
+bool CWallet::SetWalletFlags(uint64_t overwriteFlags, bool memonly)
+{
+ LOCK(cs_wallet);
+ m_wallet_flags = overwriteFlags;
+ if (((overwriteFlags & g_known_wallet_flags) >> 32) ^ (overwriteFlags >> 32)) {
+ // contains unknown non-tolerable wallet flags
+ return false;
+ }
+ if (!memonly && !WalletBatch(*database).WriteWalletFlags(m_wallet_flags)) {
+ throw std::runtime_error(std::string(__func__) + ": writing wallet flags failed");
}
- return nRequests;
+
+ return true;
+}
+
+int64_t CWalletTx::GetTxTime() const
+{
+ int64_t n = nTimeSmart;
+ return n ? n : nTimeReceived;
}
// Helper for producing a max-sized low-S signature (eg 72 bytes)
@@ -1797,7 +1756,7 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock
break;
}
for (size_t posInBlock = 0; posInBlock < block.vtx.size(); ++posInBlock) {
- AddToWalletIfInvolvingMe(block.vtx[posInBlock], pindex, posInBlock, fUpdate);
+ SyncTransaction(block.vtx[posInBlock], pindex, posInBlock, fUpdate);
}
} else {
ret = pindex;
@@ -1968,7 +1927,7 @@ CAmount CWalletTx::GetImmatureCredit(bool fUseCache) const
return 0;
}
-CAmount CWalletTx::GetAvailableCredit(bool fUseCache) const
+CAmount CWalletTx::GetAvailableCredit(bool fUseCache, const isminefilter& filter) const
{
if (pwallet == nullptr)
return 0;
@@ -1977,8 +1936,20 @@ CAmount CWalletTx::GetAvailableCredit(bool fUseCache) const
if (IsCoinBase() && GetBlocksToMaturity() > 0)
return 0;
- if (fUseCache && fAvailableCreditCached)
- return nAvailableCreditCached;
+ CAmount* cache = nullptr;
+ bool* cache_used = nullptr;
+
+ if (filter == ISMINE_SPENDABLE) {
+ cache = &nAvailableCreditCached;
+ cache_used = &fAvailableCreditCached;
+ } else if (filter == ISMINE_WATCH_ONLY) {
+ cache = &nAvailableWatchCreditCached;
+ cache_used = &fAvailableWatchCreditCached;
+ }
+
+ if (fUseCache && cache_used && *cache_used) {
+ return *cache;
+ }
CAmount nCredit = 0;
uint256 hashTx = GetHash();
@@ -1987,14 +1958,17 @@ CAmount CWalletTx::GetAvailableCredit(bool fUseCache) const
if (!pwallet->IsSpent(hashTx, i))
{
const CTxOut &txout = tx->vout[i];
- nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE);
+ nCredit += pwallet->GetCredit(txout, filter);
if (!MoneyRange(nCredit))
throw std::runtime_error(std::string(__func__) + " : value out of range");
}
}
- nAvailableCreditCached = nCredit;
- fAvailableCreditCached = true;
+ if (cache) {
+ *cache = nCredit;
+ assert(cache_used);
+ *cache_used = true;
+ }
return nCredit;
}
@@ -2012,35 +1986,6 @@ CAmount CWalletTx::GetImmatureWatchOnlyCredit(const bool fUseCache) const
return 0;
}
-CAmount CWalletTx::GetAvailableWatchOnlyCredit(const bool fUseCache) const
-{
- if (pwallet == nullptr)
- return 0;
-
- // Must wait until coinbase is safely deep enough in the chain before valuing it
- if (IsCoinBase() && GetBlocksToMaturity() > 0)
- return 0;
-
- if (fUseCache && fAvailableWatchCreditCached)
- return nAvailableWatchCreditCached;
-
- CAmount nCredit = 0;
- for (unsigned int i = 0; i < tx->vout.size(); i++)
- {
- if (!pwallet->IsSpent(GetHash(), i))
- {
- const CTxOut &txout = tx->vout[i];
- nCredit += pwallet->GetCredit(txout, ISMINE_WATCH_ONLY);
- if (!MoneyRange(nCredit))
- throw std::runtime_error(std::string(__func__) + ": value out of range");
- }
- }
-
- nAvailableWatchCreditCached = nCredit;
- fAvailableWatchCreditCached = true;
- return nCredit;
-}
-
CAmount CWalletTx::GetChange() const
{
if (fChangeCached)
@@ -2154,7 +2099,7 @@ void CWallet::ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman
*/
-CAmount CWallet::GetBalance() const
+CAmount CWallet::GetBalance(const isminefilter& filter, const int min_depth) const
{
CAmount nTotal = 0;
{
@@ -2162,8 +2107,9 @@ CAmount CWallet::GetBalance() const
for (const auto& entry : mapWallet)
{
const CWalletTx* pcoin = &entry.second;
- if (pcoin->IsTrusted())
- nTotal += pcoin->GetAvailableCredit();
+ if (pcoin->IsTrusted() && pcoin->GetDepthInMainChain() >= min_depth) {
+ nTotal += pcoin->GetAvailableCredit(true, filter);
+ }
}
}
@@ -2199,22 +2145,6 @@ CAmount CWallet::GetImmatureBalance() const
return nTotal;
}
-CAmount CWallet::GetWatchOnlyBalance() const
-{
- CAmount nTotal = 0;
- {
- LOCK2(cs_main, cs_wallet);
- for (const auto& entry : mapWallet)
- {
- const CWalletTx* pcoin = &entry.second;
- if (pcoin->IsTrusted())
- nTotal += pcoin->GetAvailableWatchOnlyCredit();
- }
- }
-
- return nTotal;
-}
-
CAmount CWallet::GetUnconfirmedWatchOnlyBalance() const
{
CAmount nTotal = 0;
@@ -2224,7 +2154,7 @@ CAmount CWallet::GetUnconfirmedWatchOnlyBalance() const
{
const CWalletTx* pcoin = &entry.second;
if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 && pcoin->InMempool())
- nTotal += pcoin->GetAvailableWatchOnlyCredit();
+ nTotal += pcoin->GetAvailableCredit(true, ISMINE_WATCH_ONLY);
}
}
return nTotal;
@@ -2474,32 +2404,14 @@ const CTxOut& CWallet::FindNonChangeParentOutput(const CTransaction& tx, int out
return ptx->vout[n];
}
-bool CWallet::OutputEligibleForSpending(const COutput& output, const CoinEligibilityFilter& eligibility_filter) const
-{
- if (!output.fSpendable)
- return false;
-
- if (output.nDepth < (output.tx->IsFromMe(ISMINE_ALL) ? eligibility_filter.conf_mine : eligibility_filter.conf_theirs))
- return false;
-
- size_t ancestors, descendants;
- mempool.GetTransactionAncestry(output.tx->GetHash(), ancestors, descendants);
- if (ancestors > eligibility_filter.max_ancestors || descendants > eligibility_filter.max_descendants) {
- return false;
- }
-
- return true;
-}
-
-bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<COutput> vCoins,
+bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<OutputGroup> groups,
std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params, bool& bnb_used) const
{
setCoinsRet.clear();
nValueRet = 0;
- std::vector<CInputCoin> utxo_pool;
+ std::vector<OutputGroup> utxo_pool;
if (coin_selection_params.use_bnb) {
-
// Get long term estimate
FeeCalculation feeCalc;
CCoinControl temp;
@@ -2510,19 +2422,26 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibil
CAmount cost_of_change = GetDiscardRate(*this, ::feeEstimator).GetFee(coin_selection_params.change_spend_size) + coin_selection_params.effective_fee.GetFee(coin_selection_params.change_output_size);
// Filter by the min conf specs and add to utxo_pool and calculate effective value
- for (const COutput &output : vCoins)
- {
- if (!OutputEligibleForSpending(output, eligibility_filter))
- continue;
-
- CInputCoin coin(output.tx->tx, output.i);
- coin.effective_value = coin.txout.nValue - (output.nInputBytes < 0 ? 0 : coin_selection_params.effective_fee.GetFee(output.nInputBytes));
- // Only include outputs that are positive effective value (i.e. not dust)
- if (coin.effective_value > 0) {
- coin.fee = output.nInputBytes < 0 ? 0 : coin_selection_params.effective_fee.GetFee(output.nInputBytes);
- coin.long_term_fee = output.nInputBytes < 0 ? 0 : long_term_feerate.GetFee(output.nInputBytes);
- utxo_pool.push_back(coin);
+ for (OutputGroup& group : groups) {
+ if (!group.EligibleForSpending(eligibility_filter)) continue;
+
+ group.fee = 0;
+ group.long_term_fee = 0;
+ group.effective_value = 0;
+ for (auto it = group.m_outputs.begin(); it != group.m_outputs.end(); ) {
+ const CInputCoin& coin = *it;
+ CAmount effective_value = coin.txout.nValue - (coin.m_input_bytes < 0 ? 0 : coin_selection_params.effective_fee.GetFee(coin.m_input_bytes));
+ // Only include outputs that are positive effective value (i.e. not dust)
+ if (effective_value > 0) {
+ group.fee += coin.m_input_bytes < 0 ? 0 : coin_selection_params.effective_fee.GetFee(coin.m_input_bytes);
+ group.long_term_fee += coin.m_input_bytes < 0 ? 0 : long_term_feerate.GetFee(coin.m_input_bytes);
+ group.effective_value += effective_value;
+ ++it;
+ } else {
+ it = group.Discard(coin);
+ }
}
+ if (group.effective_value > 0) utxo_pool.push_back(group);
}
// Calculate the fees for things that aren't inputs
CAmount not_input_fees = coin_selection_params.effective_fee.GetFee(coin_selection_params.tx_noinputs_size);
@@ -2530,13 +2449,9 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibil
return SelectCoinsBnB(utxo_pool, nTargetValue, cost_of_change, setCoinsRet, nValueRet, not_input_fees);
} else {
// Filter by the min conf specs and add to utxo_pool
- for (const COutput &output : vCoins)
- {
- if (!OutputEligibleForSpending(output, eligibility_filter))
- continue;
-
- CInputCoin coin = CInputCoin(output.tx->tx, output.i);
- utxo_pool.push_back(coin);
+ for (const OutputGroup& group : groups) {
+ if (!group.EligibleForSpending(eligibility_filter)) continue;
+ utxo_pool.push_back(group);
}
bnb_used = false;
return KnapsackSolver(nTargetValue, utxo_pool, setCoinsRet, nValueRet);
@@ -2558,7 +2473,7 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
if (!out.fSpendable)
continue;
nValueRet += out.tx->tx->vout[out.i].nValue;
- setCoinsRet.insert(CInputCoin(out.tx->tx, out.i));
+ setCoinsRet.insert(out.GetInputCoin());
}
return (nValueRet >= nTargetValue);
}
@@ -2592,27 +2507,31 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
// remove preset inputs from vCoins
for (std::vector<COutput>::iterator it = vCoins.begin(); it != vCoins.end() && coin_control.HasSelected();)
{
- if (setPresetCoins.count(CInputCoin(it->tx->tx, it->i)))
+ if (setPresetCoins.count(it->GetInputCoin()))
it = vCoins.erase(it);
else
++it;
}
+ // form groups from remaining coins; note that preset coins will not
+ // automatically have their associated (same address) coins included
+ std::vector<OutputGroup> groups = GroupOutputs(vCoins, !coin_control.m_avoid_partial_spends);
+
size_t max_ancestors = (size_t)std::max<int64_t>(1, gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT));
size_t max_descendants = (size_t)std::max<int64_t>(1, gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT));
bool fRejectLongChains = gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS);
bool res = nTargetValue <= nValueFromPresetInputs ||
- SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(1, 6, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used) ||
- SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(1, 1, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used) ||
- (m_spend_zero_conf_change && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, 2), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
- (m_spend_zero_conf_change && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, std::min((size_t)4, max_ancestors/3), std::min((size_t)4, max_descendants/3)), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
- (m_spend_zero_conf_change && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, max_ancestors/2, max_descendants/2), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
- (m_spend_zero_conf_change && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, max_ancestors-1, max_descendants-1), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
- (m_spend_zero_conf_change && !fRejectLongChains && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, std::numeric_limits<uint64_t>::max()), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(1, 6, 0), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used) ||
+ SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(1, 1, 0), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used) ||
+ (m_spend_zero_conf_change && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, 2), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
+ (m_spend_zero_conf_change && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, std::min((size_t)4, max_ancestors/3), std::min((size_t)4, max_descendants/3)), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
+ (m_spend_zero_conf_change && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, max_ancestors/2, max_descendants/2), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
+ (m_spend_zero_conf_change && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, max_ancestors-1, max_descendants-1), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
+ (m_spend_zero_conf_change && !fRejectLongChains && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, std::numeric_limits<uint64_t>::max()), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
// because SelectCoinsMinConf clears the setCoinsRet, we now add the possible inputs to the coinset
- setCoinsRet.insert(setPresetCoins.begin(), setPresetCoins.end());
+ util::insert(setCoinsRet, setPresetCoins);
// add preset inputs to the total value selected
nValueRet += nValueFromPresetInputs;
@@ -2726,7 +2645,7 @@ OutputType CWallet::TransactionChangeType(OutputType change_type, const std::vec
}
bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CReserveKey& reservekey, CAmount& nFeeRet,
- int& nChangePosInOut, std::string& strFailReason, const CCoinControl& coin_control, bool sign)
+ int& nChangePosInOut, std::string& strFailReason, const CCoinControl& coin_control, bool sign)
{
CAmount nValue = 0;
int nChangePosRequest = nChangePosInOut;
@@ -2810,6 +2729,10 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransac
// post-backup change.
// Reserve a new key pair from key pool
+ if (IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
+ strFailReason = _("Can't generate a change-address key. Private keys are disabled for this wallet.");
+ return false;
+ }
CPubKey vchPubKey;
bool ret;
ret = reservekey.GetReservedKey(vchPubKey, true);
@@ -3145,9 +3068,6 @@ bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve
}
}
- // Track how many getdata requests our transaction gets
- mapRequestCount[wtxNew.GetHash()] = 0;
-
// Get the inserted-CWalletTx from mapWallet so that the
// fInMempool flag is cached properly
CWalletTx& wtx = mapWallet.at(wtxNew.GetHash());
@@ -3210,8 +3130,11 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
}
}
- // This wallet is in its first run if all of these are empty
- fFirstRunRet = mapKeys.empty() && mapCryptedKeys.empty() && mapWatchKeys.empty() && setWatchOnly.empty() && mapScripts.empty();
+ {
+ LOCK(cs_KeyStore);
+ // This wallet is in its first run if all of these are empty
+ fFirstRunRet = mapKeys.empty() && mapCryptedKeys.empty() && mapWatchKeys.empty() && setWatchOnly.empty() && mapScripts.empty() && !IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
+ }
if (nLoadWalletRet != DBErrors::LOAD_OK)
return nLoadWalletRet;
@@ -3334,6 +3257,9 @@ const std::string& CWallet::GetLabelName(const CScript& scriptPubKey) const
*/
bool CWallet::NewKeyPool()
{
+ if (IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
+ return false;
+ }
{
LOCK(cs_wallet);
WalletBatch batch(*database);
@@ -3392,6 +3318,9 @@ void CWallet::LoadKeyPool(int64_t nIndex, const CKeyPool &keypool)
bool CWallet::TopUpKeyPool(unsigned int kpSize)
{
+ if (IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
+ return false;
+ }
{
LOCK(cs_wallet);
@@ -3516,6 +3445,10 @@ void CWallet::ReturnKey(int64_t nIndex, bool fInternal, const CPubKey& pubkey)
bool CWallet::GetKeyFromPool(CPubKey& result, bool internal)
{
+ if (IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
+ return false;
+ }
+
CKeyPool keypool;
{
LOCK(cs_wallet);
@@ -3955,10 +3888,9 @@ bool CWallet::EraseDestData(const CTxDestination &dest, const std::string &key)
return WalletBatch(*database).EraseDestData(EncodeDestination(dest), key);
}
-bool CWallet::LoadDestData(const CTxDestination &dest, const std::string &key, const std::string &value)
+void CWallet::LoadDestData(const CTxDestination &dest, const std::string &key, const std::string &value)
{
mapAddressBook[dest].destdata.insert(std::make_pair(key, value));
- return true;
}
bool CWallet::GetDestData(const CTxDestination &dest, const std::string &key, std::string *value) const
@@ -4055,7 +3987,7 @@ bool CWallet::Verify(std::string wallet_file, bool salvage_wallet, std::string&
return WalletBatch::VerifyDatabaseFile(wallet_path, warning_string, error_string);
}
-std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name, const fs::path& path)
+std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name, const fs::path& path, uint64_t wallet_creation_flags)
{
const std::string& walletFile = name;
@@ -4115,7 +4047,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name,
if (nMaxVersion == 0) // the -upgradewallet without argument case
{
LogPrintf("Performing wallet upgrade to %i\n", FEATURE_LATEST);
- nMaxVersion = CLIENT_VERSION;
+ nMaxVersion = FEATURE_LATEST;
walletInstance->SetMinVersion(FEATURE_LATEST); // permanently upgrade the wallet immediately
}
else
@@ -4147,9 +4079,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name,
// generate a new master key
CPubKey masterPubKey = walletInstance->GenerateNewSeed();
- if (!walletInstance->SetHDSeed(masterPubKey)) {
- throw std::runtime_error(std::string(__func__) + ": Storing master key failed");
- }
+ walletInstance->SetHDSeed(masterPubKey);
hd_upgrade = true;
}
// Upgrade to HD chain split if necessary
@@ -4165,7 +4095,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name,
// Regenerate the keypool if upgraded to HD
if (hd_upgrade) {
if (!walletInstance->TopUpKeyPool()) {
- InitError(_("Unable to generate keys") += "\n");
+ InitError(_("Unable to generate keys"));
return nullptr;
}
}
@@ -4180,18 +4110,31 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name,
}
walletInstance->SetMinVersion(FEATURE_LATEST);
- // generate a new seed
- CPubKey seed = walletInstance->GenerateNewSeed();
- if (!walletInstance->SetHDSeed(seed))
- throw std::runtime_error(std::string(__func__) + ": Storing HD seed failed");
+ if ((wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
+ //selective allow to set flags
+ walletInstance->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
+ } else {
+ // generate a new seed
+ CPubKey seed = walletInstance->GenerateNewSeed();
+ walletInstance->SetHDSeed(seed);
+ }
// Top up the keypool
- if (!walletInstance->TopUpKeyPool()) {
- InitError(_("Unable to generate initial keys") += "\n");
+ if (!walletInstance->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !walletInstance->TopUpKeyPool()) {
+ InitError(_("Unable to generate initial keys"));
return nullptr;
}
walletInstance->ChainStateFlushed(chainActive.GetLocator());
+ } else if (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS) {
+ // Make it impossible to disable private keys after creation
+ InitError(strprintf(_("Error loading %s: Private keys can only be disabled during creation"), walletFile));
+ return NULL;
+ } else if (walletInstance->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
+ LOCK(walletInstance->cs_KeyStore);
+ if (!walletInstance->mapKeys.empty() || !walletInstance->mapCryptedKeys.empty()) {
+ InitWarning(strprintf(_("Warning: Private keys detected in wallet {%s} with disabled private keys"), walletFile));
+ }
} else if (gArgs.IsArgSet("-usehd")) {
bool useHD = gArgs.GetBoolArg("-usehd", true);
if (walletInstance->IsHDEnabled() && !useHD) {
@@ -4416,7 +4359,7 @@ void CMerkleTx::SetMerkleBranch(const CBlockIndex* pindex, int posInBlock)
nIndex = posInBlock;
}
-int CMerkleTx::GetDepthInMainChain(const CBlockIndex* &pindexRet) const
+int CMerkleTx::GetDepthInMainChain() const
{
if (hashUnset())
return 0;
@@ -4428,7 +4371,6 @@ int CMerkleTx::GetDepthInMainChain(const CBlockIndex* &pindexRet) const
if (!pindex || !chainActive.Contains(pindex))
return 0;
- pindexRet = pindex;
return ((nIndex == -1) ? (-1) : 1) * (chainActive.Height() - pindex->nHeight + 1);
}
@@ -4453,35 +4395,6 @@ bool CWalletTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState&
return ret;
}
-static const std::string OUTPUT_TYPE_STRING_LEGACY = "legacy";
-static const std::string OUTPUT_TYPE_STRING_P2SH_SEGWIT = "p2sh-segwit";
-static const std::string OUTPUT_TYPE_STRING_BECH32 = "bech32";
-
-bool ParseOutputType(const std::string& type, OutputType& output_type)
-{
- if (type == OUTPUT_TYPE_STRING_LEGACY) {
- output_type = OutputType::LEGACY;
- return true;
- } else if (type == OUTPUT_TYPE_STRING_P2SH_SEGWIT) {
- output_type = OutputType::P2SH_SEGWIT;
- return true;
- } else if (type == OUTPUT_TYPE_STRING_BECH32) {
- output_type = OutputType::BECH32;
- return true;
- }
- return false;
-}
-
-const std::string& FormatOutputType(OutputType type)
-{
- switch (type) {
- case OutputType::LEGACY: return OUTPUT_TYPE_STRING_LEGACY;
- case OutputType::P2SH_SEGWIT: return OUTPUT_TYPE_STRING_P2SH_SEGWIT;
- case OutputType::BECH32: return OUTPUT_TYPE_STRING_BECH32;
- default: assert(false);
- }
-}
-
void CWallet::LearnRelatedScripts(const CPubKey& key, OutputType type)
{
if (key.IsCompressed() && (type == OutputType::P2SH_SEGWIT || type == OutputType::BECH32)) {
@@ -4499,57 +4412,29 @@ void CWallet::LearnAllRelatedScripts(const CPubKey& key)
LearnRelatedScripts(key, OutputType::P2SH_SEGWIT);
}
-CTxDestination GetDestinationForKey(const CPubKey& key, OutputType type)
-{
- switch (type) {
- case OutputType::LEGACY: return key.GetID();
- case OutputType::P2SH_SEGWIT:
- case OutputType::BECH32: {
- if (!key.IsCompressed()) return key.GetID();
- CTxDestination witdest = WitnessV0KeyHash(key.GetID());
- CScript witprog = GetScriptForDestination(witdest);
- if (type == OutputType::P2SH_SEGWIT) {
- return CScriptID(witprog);
- } else {
- return witdest;
- }
- }
- default: assert(false);
- }
-}
-
-std::vector<CTxDestination> GetAllDestinationsForKey(const CPubKey& key)
-{
- CKeyID keyid = key.GetID();
- if (key.IsCompressed()) {
- CTxDestination segwit = WitnessV0KeyHash(keyid);
- CTxDestination p2sh = CScriptID(GetScriptForDestination(segwit));
- return std::vector<CTxDestination>{std::move(keyid), std::move(p2sh), std::move(segwit)};
- } else {
- return std::vector<CTxDestination>{std::move(keyid)};
- }
-}
-
-CTxDestination CWallet::AddAndGetDestinationForScript(const CScript& script, OutputType type)
-{
- // Note that scripts over 520 bytes are not yet supported.
- switch (type) {
- case OutputType::LEGACY:
- return CScriptID(script);
- case OutputType::P2SH_SEGWIT:
- case OutputType::BECH32: {
- CTxDestination witdest = WitnessV0ScriptHash(script);
- CScript witprog = GetScriptForDestination(witdest);
- // Check if the resulting program is solvable (i.e. doesn't use an uncompressed key)
- if (!IsSolvable(*this, witprog)) return CScriptID(script);
- // Add the redeemscript, so that P2WSH and P2SH-P2WSH outputs are recognized as ours.
- AddCScript(witprog);
- if (type == OutputType::BECH32) {
- return witdest;
- } else {
- return CScriptID(witprog);
+std::vector<OutputGroup> CWallet::GroupOutputs(const std::vector<COutput>& outputs, bool single_coin) const {
+ std::vector<OutputGroup> groups;
+ std::map<CTxDestination, OutputGroup> gmap;
+ CTxDestination dst;
+ for (const auto& output : outputs) {
+ if (output.fSpendable) {
+ CInputCoin input_coin = output.GetInputCoin();
+
+ size_t ancestors, descendants;
+ mempool.GetTransactionAncestry(output.tx->GetHash(), ancestors, descendants);
+ if (!single_coin && ExtractDestination(output.tx->tx->vout[output.i].scriptPubKey, dst)) {
+ if (gmap.count(dst) == 10) {
+ groups.push_back(gmap[dst]);
+ gmap.erase(dst);
+ }
+ gmap[dst].Insert(input_coin, output.nDepth, output.tx->IsFromMe(ISMINE_ALL), ancestors, descendants);
+ } else {
+ groups.emplace_back(input_coin, output.nDepth, output.tx->IsFromMe(ISMINE_ALL), ancestors, descendants);
+ }
}
}
- default: assert(false);
+ if (!single_coin) {
+ for (const auto& it : gmap) groups.push_back(it.second);
}
+ return groups;
}
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index b829394847..9f9501cf72 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -7,6 +7,7 @@
#define BITCOIN_WALLET_WALLET_H
#include <amount.h>
+#include <outputtype.h>
#include <policy/feerate.h>
#include <streams.h>
#include <tinyformat.h>
@@ -54,6 +55,8 @@ static const CAmount WALLET_INCREMENTAL_RELAY_FEE = 5000;
static const bool DEFAULT_SPEND_ZEROCONF_CHANGE = true;
//! Default for -walletrejectlongchains
static const bool DEFAULT_WALLET_REJECT_LONG_CHAINS = false;
+//! Default for -avoidpartialspends
+static const bool DEFAULT_AVOIDPARTIALSPENDS = false;
//! -txconfirmtarget default
static const unsigned int DEFAULT_TX_CONFIRM_TARGET = 6;
//! -walletrbf default
@@ -61,8 +64,6 @@ static const bool DEFAULT_WALLET_RBF = false;
static const bool DEFAULT_WALLETBROADCAST = true;
static const bool DEFAULT_DISABLE_WALLET = false;
-static const int64_t TIMESTAMP_MIN = 0;
-
class CBlockIndex;
class CCoinControl;
class COutput;
@@ -93,26 +94,22 @@ enum WalletFeature
FEATURE_LATEST = FEATURE_PRE_SPLIT_KEYPOOL
};
-enum class OutputType {
- LEGACY,
- P2SH_SEGWIT,
- BECH32,
-
- /**
- * Special output type for change outputs only. Automatically choose type
- * based on address type setting and the types other of non-change outputs
- * (see -changetype option documentation and implementation in
- * CWallet::TransactionChangeType for details).
- */
- CHANGE_AUTO,
-};
-
//! Default for -addresstype
constexpr OutputType DEFAULT_ADDRESS_TYPE{OutputType::P2SH_SEGWIT};
//! Default for -changetype
constexpr OutputType DEFAULT_CHANGE_TYPE{OutputType::CHANGE_AUTO};
+enum WalletFlags : uint64_t {
+ // wallet flags in the upper section (> 1 << 31) will lead to not opening the wallet if flag is unknown
+ // unkown wallet flags in the lower section <= (1 << 31) will be tolerated
+
+ // will enforce the rule that the wallet can't contain any private keys (only watch-only/pubkeys)
+ WALLET_FLAG_DISABLE_PRIVATE_KEYS = (1ULL << 32),
+};
+
+static constexpr uint64_t g_known_wallet_flags = WALLET_FLAG_DISABLE_PRIVATE_KEYS;
+
/** A key pool entry */
class CKeyPool
{
@@ -267,9 +264,8 @@ public:
* 0 : in memory pool, waiting to be included in a block
* >=1 : this many blocks deep in the main chain
*/
- int GetDepthInMainChain(const CBlockIndex* &pindexRet) const;
- int GetDepthInMainChain() const { const CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet); }
- bool IsInMainChain() const { const CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet) > 0; }
+ int GetDepthInMainChain() const;
+ bool IsInMainChain() const { return GetDepthInMainChain() > 0; }
int GetBlocksToMaturity() const;
bool hashUnset() const { return (hashBlock.IsNull() || hashBlock == ABANDON_HASH); }
bool isAbandoned() const { return (hashBlock == ABANDON_HASH); }
@@ -460,9 +456,8 @@ public:
CAmount GetDebit(const isminefilter& filter) const;
CAmount GetCredit(const isminefilter& filter) const;
CAmount GetImmatureCredit(bool fUseCache=true) const;
- CAmount GetAvailableCredit(bool fUseCache=true) const;
+ CAmount GetAvailableCredit(bool fUseCache=true, const isminefilter& filter=ISMINE_SPENDABLE) const;
CAmount GetImmatureWatchOnlyCredit(const bool fUseCache=true) const;
- CAmount GetAvailableWatchOnlyCredit(const bool fUseCache=true) const;
CAmount GetChange() const;
// Get the marginal bytes if spending the specified output from this transaction
@@ -486,7 +481,6 @@ public:
bool IsTrusted() const;
int64_t GetTxTime() const;
- int GetRequestCount() const;
// RelayWalletTransaction may only be called if fBroadcastTransactions!
bool RelayWalletTransaction(CConnman* connman);
@@ -531,10 +525,12 @@ public:
}
std::string ToString() const;
-};
-
-
+ inline CInputCoin GetInputCoin() const
+ {
+ return CInputCoin(tx->tx, i, nInputBytes);
+ }
+};
/** Private key that includes an expiration date in case it never gets used. */
class CWalletKey
@@ -658,17 +654,6 @@ struct CoinSelectionParams
CoinSelectionParams() {}
};
-struct CoinEligibilityFilter
-{
- const int conf_mine;
- const int conf_theirs;
- const uint64_t max_ancestors;
- const uint64_t max_descendants;
-
- CoinEligibilityFilter(int conf_mine, int conf_theirs, uint64_t max_ancestors) : conf_mine(conf_mine), conf_theirs(conf_theirs), max_ancestors(max_ancestors), max_descendants(max_ancestors) {}
- CoinEligibilityFilter(int conf_mine, int conf_theirs, uint64_t max_ancestors, uint64_t max_descendants) : conf_mine(conf_mine), conf_theirs(conf_theirs), max_ancestors(max_ancestors), max_descendants(max_descendants) {}
-};
-
class WalletRescanReserver; //forward declarations for ScanForWalletTransactions/RescanFromTime
/**
* A CWallet is an extension of a keystore, which also maintains a set of transactions and balances,
@@ -704,14 +689,32 @@ private:
void AddToSpends(const COutPoint& outpoint, const uint256& wtxid);
void AddToSpends(const uint256& wtxid);
+ /**
+ * Add a transaction to the wallet, or update it. pIndex and posInBlock should
+ * be set when the transaction was known to be included in a block. When
+ * pIndex == nullptr, then wallet state is not updated in AddToWallet, but
+ * notifications happen and cached balances are marked dirty.
+ *
+ * If fUpdate is true, existing transactions will be updated.
+ * TODO: One exception to this is that the abandoned state is cleared under the
+ * assumption that any further notification of a transaction that was considered
+ * abandoned is an indication that it is not safe to be considered abandoned.
+ * Abandoned state should probably be more carefully tracked via different
+ * posInBlock signals or by checking mempool presence when necessary.
+ */
+ bool AddToWalletIfInvolvingMe(const CTransactionRef& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+
/* Mark a transaction (and its in-wallet descendants) as conflicting with a particular block. */
void MarkConflicted(const uint256& hashBlock, const uint256& hashTx);
+ /* Mark a transaction's inputs dirty, thus forcing the outputs to be recomputed */
+ void MarkInputsDirty(const CTransactionRef& tx);
+
void SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator>);
- /* Used by TransactionAddedToMemorypool/BlockConnected/Disconnected.
+ /* Used by TransactionAddedToMemorypool/BlockConnected/Disconnected/ScanForWalletTransactions.
* Should be called with pindexBlock and posInBlock if this is for a transaction that is included in a block. */
- void SyncTransaction(const CTransactionRef& tx, const CBlockIndex *pindex = nullptr, int posInBlock = 0) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ void SyncTransaction(const CTransactionRef& tx, const CBlockIndex *pindex = nullptr, int posInBlock = 0, bool update_tx = true) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/* the HD chain data model (external chain counters) */
CHDChain hdChain;
@@ -724,6 +727,7 @@ private:
std::set<int64_t> set_pre_split_keypool;
int64_t m_max_keypool_index = 0;
std::map<CKeyID, int64_t> m_pool_key_to_index;
+ std::atomic<uint64_t> m_wallet_flags{0};
int64_t nTimeFirstKey = 0;
@@ -820,7 +824,6 @@ public:
int64_t nOrderPosNext = 0;
uint64_t nAccountingEntryNumber = 0;
- std::map<uint256, int> mapRequestCount;
std::map<CTxDestination, CAddressBookData> mapAddressBook;
@@ -852,10 +855,11 @@ public:
* completion the coin set and corresponding actual target value is
* assembled
*/
- bool SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<COutput> vCoins,
+ bool SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<OutputGroup> groups,
std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params, bool& bnb_used) const;
bool IsSpent(const uint256& hash, unsigned int n) const;
+ std::vector<OutputGroup> GroupOutputs(const std::vector<COutput>& outputs, bool single_coin) const;
bool IsLockedCoin(uint256 hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void LockCoin(const COutPoint& output) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
@@ -881,8 +885,8 @@ public:
//! Adds a key to the store, without saving it to disk (used by LoadWallet)
bool LoadKey(const CKey& key, const CPubKey &pubkey) { return CCryptoKeyStore::AddKeyPubKey(key, pubkey); }
//! Load metadata (used by LoadWallet)
- bool LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata &metadata) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- bool LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata &metadata) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ void LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata &metadata) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ void LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata &metadata) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool LoadMinVersion(int nVersion) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); nWalletVersion = nVersion; nWalletMaxVersion = std::max(nWalletMaxVersion, nVersion); return true; }
void UpdateTimeFirstKey(int64_t nCreateTime) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
@@ -899,7 +903,7 @@ public:
//! Erases a destination data tuple in the store and on disk
bool EraseDestData(const CTxDestination &dest, const std::string &key);
//! Adds a destination data tuple to the store, without saving it to disk
- bool LoadDestData(const CTxDestination &dest, const std::string &key, const std::string &value);
+ void LoadDestData(const CTxDestination &dest, const std::string &key, const std::string &value);
//! Look up a destination data tuple in the store, return true if found false otherwise
bool GetDestData(const CTxDestination &dest, const std::string &key, std::string *value) const;
//! Get all destination values matching a prefix.
@@ -932,11 +936,10 @@ public:
void MarkDirty();
bool AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose=true);
- bool LoadToWallet(const CWalletTx& wtxIn);
+ void LoadToWallet(const CWalletTx& wtxIn);
void TransactionAddedToMempool(const CTransactionRef& tx) override;
void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex *pindex, const std::vector<CTransactionRef>& vtxConflicted) override;
void BlockDisconnected(const std::shared_ptr<const CBlock>& pblock) override;
- bool AddToWalletIfInvolvingMe(const CTransactionRef& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
int64_t RescanFromTime(int64_t startTime, const WalletRescanReserver& reserver, bool update);
CBlockIndex* ScanForWalletTransactions(CBlockIndex* pindexStart, CBlockIndex* pindexStop, const WalletRescanReserver& reserver, bool fUpdate = false);
void TransactionRemovedFromMempool(const CTransactionRef &ptx) override;
@@ -944,10 +947,9 @@ public:
void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) override;
// ResendWalletTransactionsBefore may only be called if fBroadcastTransactions!
std::vector<uint256> ResendWalletTransactionsBefore(int64_t nTime, CConnman* connman);
- CAmount GetBalance() const;
+ CAmount GetBalance(const isminefilter& filter=ISMINE_SPENDABLE, const int min_depth=0) const;
CAmount GetUnconfirmedBalance() const;
CAmount GetImmatureBalance() const;
- CAmount GetWatchOnlyBalance() const;
CAmount GetUnconfirmedWatchOnlyBalance() const;
CAmount GetImmatureWatchOnlyBalance() const;
CAmount GetLegacyBalance(const isminefilter& filter, int minDepth, const std::string* account) const;
@@ -1064,16 +1066,6 @@ public:
const std::string& GetLabelName(const CScript& scriptPubKey) const;
- void Inventory(const uint256 &hash) override
- {
- {
- LOCK(cs_wallet);
- std::map<uint256, int>::iterator mi = mapRequestCount.find(hash);
- if (mi != mapRequestCount.end())
- (*mi).second++;
- }
- }
-
void GetScriptForMining(std::shared_ptr<CReserveScript> &script);
unsigned int GetKeyPoolSize() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet)
@@ -1083,7 +1075,7 @@ public:
}
//! signify that a particular wallet feature is now used. this may change nWalletVersion and nWalletMaxVersion if those are lower
- bool SetMinVersion(enum WalletFeature, WalletBatch* batch_in = nullptr, bool fExplicit = false);
+ void SetMinVersion(enum WalletFeature, WalletBatch* batch_in = nullptr, bool fExplicit = false);
//! change which version we're allowed to upgrade to (note that this does not immediately imply upgrading to that format)
bool SetMaxVersion(int nVersion);
@@ -1143,7 +1135,7 @@ public:
static bool Verify(std::string wallet_file, bool salvage_wallet, std::string& error_string, std::string& warning_string);
/* Initializes the wallet, returns a new CWallet instance or a null pointer in case of an error */
- static std::shared_ptr<CWallet> CreateWalletFromFile(const std::string& name, const fs::path& path);
+ static std::shared_ptr<CWallet> CreateWalletFromFile(const std::string& name, const fs::path& path, uint64_t wallet_creation_flags = 0);
/**
* Wallet post-init setup
@@ -1154,7 +1146,7 @@ public:
bool BackupWallet(const std::string& strDest);
/* Set the HD chain model (chain child index counters) */
- bool SetHDChain(const CHDChain& chain, bool memonly);
+ void SetHDChain(const CHDChain& chain, bool memonly);
const CHDChain& GetHDChain() const { return hdChain; }
/* Returns true if HD is enabled */
@@ -1170,7 +1162,7 @@ public:
Sets the seed's version based on the current wallet version (so the
caller must ensure the current wallet version is correct before calling
this function). */
- bool SetHDSeed(const CPubKey& key);
+ void SetHDSeed(const CPubKey& key);
/**
* Blocks until the wallet state is up-to-date to /at least/ the current
@@ -1194,14 +1186,15 @@ public:
*/
void LearnAllRelatedScripts(const CPubKey& key);
- /**
- * Get a destination of the requested type (if possible) to the specified script.
- * This function will automatically add the necessary scripts to the wallet.
- */
- CTxDestination AddAndGetDestinationForScript(const CScript& script, OutputType);
+ /** set a single wallet flag */
+ void SetWalletFlag(uint64_t flags);
- /** Whether a given output is spendable by this wallet */
- bool OutputEligibleForSpending(const COutput& output, const CoinEligibilityFilter& eligibility_filter) const;
+ /** check if a certain wallet flag is set */
+ bool IsWalletFlagSet(uint64_t flag);
+
+ /** overwrite all flags by the given uint64_t
+ returns false if unknown, non-tolerable flags are present */
+ bool SetWalletFlags(uint64_t overwriteFlags, bool memOnly);
};
/** A key allocated from the key pool. */
@@ -1266,18 +1259,6 @@ public:
}
};
-bool ParseOutputType(const std::string& str, OutputType& output_type);
-const std::string& FormatOutputType(OutputType type);
-
-/**
- * Get a destination of the requested type (if possible) to the specified key.
- * The caller must make sure LearnRelatedScripts has been called beforehand.
- */
-CTxDestination GetDestinationForKey(const CPubKey& key, OutputType);
-
-/** Get all destinations (potentially) supported by the wallet for the given key. */
-std::vector<CTxDestination> GetAllDestinationsForKey(const CPubKey& key);
-
/** RAII object to check and reserve a wallet rescan */
class WalletRescanReserver
{
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index 4b4460a794..43e4747317 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -495,22 +495,21 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
ssKey >> strAddress;
ssKey >> strKey;
ssValue >> strValue;
- if (!pwallet->LoadDestData(DecodeDestination(strAddress), strKey, strValue))
- {
- strErr = "Error reading wallet database: LoadDestData failed";
- return false;
- }
+ pwallet->LoadDestData(DecodeDestination(strAddress), strKey, strValue);
}
else if (strType == "hdchain")
{
CHDChain chain;
ssValue >> chain;
- if (!pwallet->SetHDChain(chain, true))
- {
- strErr = "Error reading wallet database: SetHDChain failed";
+ pwallet->SetHDChain(chain, true);
+ } else if (strType == "flags") {
+ uint64_t flags;
+ ssValue >> flags;
+ if (!pwallet->SetWalletFlags(flags, true)) {
+ strErr = "Error reading wallet database: Unknown non-tolerable wallet flags found";
return false;
}
- } else if (strType != "bestblock" && strType != "bestblock_nomerkle"){
+ } else if (strType != "bestblock" && strType != "bestblock_nomerkle") {
wss.m_unknown_records++;
}
} catch (...)
@@ -537,7 +536,7 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
int nMinVersion = 0;
if (m_batch.Read((std::string)"minversion", nMinVersion))
{
- if (nMinVersion > CLIENT_VERSION)
+ if (nMinVersion > FEATURE_LATEST)
return DBErrors::TOO_NEW;
pwallet->LoadMinVersion(nMinVersion);
}
@@ -570,10 +569,12 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
{
// losing keys is considered a catastrophic error, anything else
// we assume the user can live with:
- if (IsKeyType(strType) || strType == "defaultkey")
+ if (IsKeyType(strType) || strType == "defaultkey") {
result = DBErrors::CORRUPT;
- else
- {
+ } else if(strType == "flags") {
+ // reading the wallet flags can only fail if unknown flags are present
+ result = DBErrors::TOO_NEW;
+ } else {
// Leave other errors alone, if we try to fix them we might make things worse.
fNoncriticalErrors = true; // ... but do warn the user there is something wrong.
if (strType == "tx")
@@ -640,7 +641,7 @@ DBErrors WalletBatch::FindWalletTx(std::vector<uint256>& vTxHash, std::vector<CW
int nMinVersion = 0;
if (m_batch.Read((std::string)"minversion", nMinVersion))
{
- if (nMinVersion > CLIENT_VERSION)
+ if (nMinVersion > FEATURE_LATEST)
return DBErrors::TOO_NEW;
}
@@ -840,6 +841,11 @@ bool WalletBatch::WriteHDChain(const CHDChain& chain)
return WriteIC(std::string("hdchain"), chain);
}
+bool WalletBatch::WriteWalletFlags(const uint64_t flags)
+{
+ return WriteIC(std::string("flags"), flags);
+}
+
bool WalletBatch::TxnBegin()
{
return m_batch.TxnBegin();
diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h
index 3237376f63..674d1c2201 100644
--- a/src/wallet/walletdb.h
+++ b/src/wallet/walletdb.h
@@ -234,6 +234,7 @@ public:
//! write the hdchain model (external chain child index counter)
bool WriteHDChain(const CHDChain& chain);
+ bool WriteWalletFlags(const uint64_t flags);
//! Begin a new transaction
bool TxnBegin();
//! Commit current transaction
diff --git a/test/README.md b/test/README.md
index b522c4469a..a5ba732bc0 100644
--- a/test/README.md
+++ b/test/README.md
@@ -5,7 +5,7 @@ etc.
There are currently two sets of tests in this directory:
-- [functional](/test/functional) which test the functionality of
+- [functional](/test/functional) which test the functionality of
bitcoind and bitcoin-qt by interacting with them through the RPC and P2P
interfaces.
- [util](/test/util) which tests the bitcoin utilities, currently only
@@ -177,11 +177,11 @@ Note: gdb attach step may require `sudo`
### Util tests
-Util tests can be run locally by running `test/util/bitcoin-util-test.py`.
+Util tests can be run locally by running `test/util/bitcoin-util-test.py`.
Use the `-v` option for verbose output.
# Writing functional tests
You are encouraged to write functional tests for new or existing features.
-Further information about the functional test framework and individual
+Further information about the functional test framework and individual
tests is found in [test/functional](/test/functional).
diff --git a/test/functional/README.md b/test/functional/README.md
index e6365222ff..6929ab5991 100644
--- a/test/functional/README.md
+++ b/test/functional/README.md
@@ -28,6 +28,7 @@ don't have test cases for.
- When subclassing the BitcoinTestFramwork, place overrides for the
`set_test_params()`, `add_options()` and `setup_xxxx()` methods at the top of
the subclass, then locally-defined helper methods, then the `run_test()` method.
+- Use `'{}'.format(x)` for string formatting, not `'%s' % x`.
#### Naming guidelines
diff --git a/test/functional/data/rpc_psbt.json b/test/functional/data/rpc_psbt.json
new file mode 100644
index 0000000000..4e2f08f274
--- /dev/null
+++ b/test/functional/data/rpc_psbt.json
@@ -0,0 +1,98 @@
+{
+ "invalid" : [
+ "AgAAAAEmgXE3Ht/yhek3re6ks3t4AAwFZsuzrWRkFxPKQhcb9gAAAABqRzBEAiBwsiRRI+a/R01gxbUMBD1MaRpdJDXwmjSnZiqdwlF5CgIgATKcqdrPKAvfMHQOwDkEIkIsgctFg5RXrrdvwS7dlbMBIQJlfRGNM1e44PTCzUbbezn22cONmnCry5st5dyNv+TOMf7///8C09/1BQAAAAAZdqkU0MWZA8W6woaHYOkP1SGkZlqnZSCIrADh9QUAAAAAF6kUNUXm4zuDLEcFDyTT7rk8nAOUi8eHsy4TAA==",
+ "cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAA==",
+ "cHNidP8BAP0KAQIAAAACqwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QAAAAAakcwRAIgR1lmF5fAGwNrJZKJSGhiGDR9iYZLcZ4ff89X0eURZYcCIFMJ6r9Wqk2Ikf/REf3xM286KdqGbX+EhtdVRs7tr5MZASEDXNxh/HupccC1AaZGoqg7ECy0OIEhfKaC3Ibi1z+ogpL+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAABASAA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHhwEEFgAUhdE1N/LiZUBaNNuvqePdoB+4IwgAAAA=",
+ "cHNidP8AAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAA==",
+ "cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAQA/AgAAAAH//////////////////////////////////////////wAAAAAA/////wEAAAAAAAAAAANqAQAAAAAAAAAA",
+ "cHNidP8CAAFVAgAAAAEnmiMjpd+1H8RfIg+liw/BPh4zQnkqhdfjbNYzO1y8OQAAAAAA/////wGgWuoLAAAAABl2qRT/6cAGEJfMO2NvLLBGD6T8Qn0rRYisAAAAAAABASCVXuoLAAAAABepFGNFIA9o0YnhrcDfHE0W6o8UwNvrhyICA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb1GRjBDAiAEJLWO/6qmlOFVnqXJO7/UqJBkIkBVzfBwtncUaUQtBwIfXI6w/qZRbWC4rLM61k7eYOh4W/s6qUuZvfhhUduamgEBBCIAIHcf0YrUWWZt1J89Vk49vEL0yEd042CtoWgWqO1IjVaBAQVHUiEDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYhA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9Uq4iBgOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RhC0prpnAAAAgAAAAIAEAACAIgYD3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg70QtKa6ZwAAAIAAAACABQAAgAAA",
+ "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAIBACCVXuoLAAAAABepFGNFIA9o0YnhrcDfHE0W6o8UwNvrhyICA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb1GRjBDAiAEJLWO/6qmlOFVnqXJO7/UqJBkIkBVzfBwtncUaUQtBwIfXI6w/qZRbWC4rLM61k7eYOh4W/s6qUuZvfhhUduamgEBBCIAIHcf0YrUWWZt1J89Vk49vEL0yEd042CtoWgWqO1IjVaBAQVHUiEDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYhA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9Uq4iBgOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RhC0prpnAAAAgAAAAIAEAACAIgYD3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg70QtKa6ZwAAAIAAAACABQAAgAAA",
+ "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIQIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYwQwIgBCS1jv+qppThVZ6lyTu/1KiQZCJAVc3wcLZ3FGlELQcCH1yOsP6mUW1guKyzOtZO3mDoeFv7OqlLmb34YVHbmpoBAQQiACB3H9GK1FlmbdSfPVZOPbxC9MhHdONgraFoFqjtSI1WgQEFR1IhA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb1GIQPeVdHh2sgF4/iljB+/m5TALz26r+En/vykmV8m+CCDvVKuIgYDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYQtKa6ZwAAAIAAAACABAAAgCIGA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9ELSmumcAAACAAAAAgAUAAIAAAA==",
+ "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIgIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUZGMEMCIAQktY7/qqaU4VWepck7v9SokGQiQFXN8HC2dxRpRC0HAh9cjrD+plFtYLisszrWTt5g6Hhb+zqpS5m9+GFR25qaAQIEACIAIHcf0YrUWWZt1J89Vk49vEL0yEd042CtoWgWqO1IjVaBAQVHUiEDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYhA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9Uq4iBgOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RhC0prpnAAAAgAAAAIAEAACAIgYD3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg70QtKa6ZwAAAIAAAACABQAAgAAA",
+ "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIgIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUZGMEMCIAQktY7/qqaU4VWepck7v9SokGQiQFXN8HC2dxRpRC0HAh9cjrD+plFtYLisszrWTt5g6Hhb+zqpS5m9+GFR25qaAQEEIgAgdx/RitRZZm3Unz1WTj28QvTIR3TjYK2haBao7UiNVoECBQBHUiEDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYhA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9Uq4iBgOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RhC0prpnAAAAgAAAAIAEAACAIgYD3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg70QtKa6ZwAAAIAAAACABQAAgAAA",
+ "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIgIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUZGMEMCIAQktY7/qqaU4VWepck7v9SokGQiQFXN8HC2dxRpRC0HAh9cjrD+plFtYLisszrWTt5g6Hhb+zqpS5m9+GFR25qaAQEEIgAgdx/RitRZZm3Unz1WTj28QvTIR3TjYK2haBao7UiNVoEBBUdSIQOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RiED3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg71SriEGA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb0QtKa6ZwAAAIAAAACABAAAgCIGA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9ELSmumcAAACAAAAAgAUAAIAAAA==",
+ "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAIAALsCAAAAAarXOTEBi9JfhK5AC2iEi+CdtwbqwqwYKYur7nGrZW+LAAAAAEhHMEQCIFj2/HxqM+GzFUjUgcgmwBW9MBNarULNZ3kNq2bSrSQ7AiBKHO0mBMZzW2OT5bQWkd14sA8MWUL7n3UYVvqpOBV9ugH+////AoDw+gIAAAAAF6kUD7lGNCFpa4LIM68kHHjBfdveSTSH0PIKJwEAAAAXqRQpynT4oI+BmZQoGFyXtdhS5AY/YYdlAAAAAQfaAEcwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMAUgwRQIhAPYQOLMI3B2oZaNIUnRvAVdyk0IIxtJEVDk82ZvfIhd3AiAFbmdaZ1ptCgK4WxTl4pB02KJam1dgvqKBb2YZEKAG6gFHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4AAQEgAMLrCwAAAAAXqRS39fr0Dj1ApaRZsds1NfK3L6kh6IcBByMiACCMI1MXN0O1ld+0oHtyuo5C43l9p06H/n2ddJfjsgKJAwEI2gQARzBEAiBi63pVYQenxz9FrEq1od3fb3B1+xJ1lpp/OD7/94S8sgIgDAXbt0cNvy8IVX3TVscyXB7TCRPpls04QJRdsSIo2l8BRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA=",
+ "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAACBwDaAEcwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMAUgwRQIhAPYQOLMI3B2oZaNIUnRvAVdyk0IIxtJEVDk82ZvfIhd3AiAFbmdaZ1ptCgK4WxTl4pB02KJam1dgvqKBb2YZEKAG6gFHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4AAQEgAMLrCwAAAAAXqRS39fr0Dj1ApaRZsds1NfK3L6kh6IcBByMiACCMI1MXN0O1ld+0oHtyuo5C43l9p06H/n2ddJfjsgKJAwEI2gQARzBEAiBi63pVYQenxz9FrEq1od3fb3B1+xJ1lpp/OD7/94S8sgIgDAXbt0cNvy8IVX3TVscyXB7TCRPpls04QJRdsSIo2l8BRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA=",
+ "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABB9oARzBEAiB0AYrUGACXuHMyPAAVcgs2hMyBI4kQSOfbzZtVrWecmQIgc9Npt0Dj61Pc76M4I8gHBRTKVafdlUTxV8FnkTJhEYwBSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAUdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSrgABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEHIyIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAggA2gQARzBEAiBi63pVYQenxz9FrEq1od3fb3B1+xJ1lpp/OD7/94S8sgIgDAXbt0cNvy8IVX3TVscyXB7TCRPpls04QJRdsSIo2l8BRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA=",
+ "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABB9oARzBEAiB0AYrUGACXuHMyPAAVcgs2hMyBI4kQSOfbzZtVrWecmQIgc9Npt0Dj61Pc76M4I8gHBRTKVafdlUTxV8FnkTJhEYwBSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAUdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSrgABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEHIyIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQjaBABHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwFHMEQCIGX0W6WZi1mif/4ae+0BavHx+Q1Us6qPdFCqX1aiUQO9AiB/ckcDrR7blmgLKEtW1P/LiPf7dZ6rvgiqMPKbhROD0gFHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4AIQIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1PtnuylhxDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA",
+ "cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wCAwABAAAAAAEAFgAUYunpgv/zTdgjlhAxawkM0qO3R8sAAQAiACCHa62DLx0WgBXtQSMqnqZaGBXZ7xPA74dZ9ktbKyeKZQEBJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnSvVf4qHUa4A",
+ "cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wAAgAAFgAUYunpgv/zTdgjlhAxawkM0qO3R8sAAQAiACCHa62DLx0WgBXtQSMqnqZaGBXZ7xPA74dZ9ktbKyeKZQEBJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnSvVf4qHUa4A",
+ "cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wAAQAWABRi6emC//NN2COWEDFrCQzSo7dHywABACIAIIdrrYMvHRaAFe1BIyqeploYFdnvE8Dvh1n2S1srJ4plIQEAJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnSvVf4qHUa4A"
+ ],
+ "valid" : [
+ "cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAAAA",
+ "cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEHakcwRAIgR1lmF5fAGwNrJZKJSGhiGDR9iYZLcZ4ff89X0eURZYcCIFMJ6r9Wqk2Ikf/REf3xM286KdqGbX+EhtdVRs7tr5MZASEDXNxh/HupccC1AaZGoqg7ECy0OIEhfKaC3Ibi1z+ogpIAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIAAAA",
+ "cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAQMEAQAAAAAAAA==",
+ "cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEA3wIAAAABJoFxNx7f8oXpN63upLN7eAAMBWbLs61kZBcTykIXG/YAAAAAakcwRAIgcLIkUSPmv0dNYMW1DAQ9TGkaXSQ18Jo0p2YqncJReQoCIAEynKnazygL3zB0DsA5BCJCLIHLRYOUV663b8Eu3ZWzASECZX0RjTNXuOD0ws1G23s59tnDjZpwq8ubLeXcjb/kzjH+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIACICAurVlmh8qAYEPtw94RbN8p1eklfBls0FXPaYyNAr8k6ZELSmumcAAACAAAAAgAIAAIAAIgIDlPYr6d8ZlSxVh3aK63aYBhrSxKJciU9H2MFitNchPQUQtKa6ZwAAAIABAACAAgAAgAA=",
+ "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIgIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUZGMEMCIAQktY7/qqaU4VWepck7v9SokGQiQFXN8HC2dxRpRC0HAh9cjrD+plFtYLisszrWTt5g6Hhb+zqpS5m9+GFR25qaAQEEIgAgdx/RitRZZm3Unz1WTj28QvTIR3TjYK2haBao7UiNVoEBBUdSIQOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RiED3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg71SriIGA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb1GELSmumcAAACAAAAAgAQAAIAiBgPeVdHh2sgF4/iljB+/m5TALz26r+En/vykmV8m+CCDvRC0prpnAAAAgAAAAIAFAACAAAA="
+ ],
+ "creator" : [
+ {
+ "inputs" : [
+ {
+ "txid":"75ddabb27b8845f5247975c8a5ba7c6f336c4570708ebe230caf6db5217ae858",
+ "vout":0
+ },
+ {
+ "txid":"1dea7cd05979072a3578cab271c02244ea8a090bbb46aa680a65ecd027048d83",
+ "vout":1
+ }
+ ],
+ "outputs" : [
+ {
+ "bcrt1qmpwzkuwsqc9snjvgdt4czhjsnywa5yjdqpxskv":1.49990000
+ },
+ {
+ "bcrt1qqzh2ngh97ru8dfvgma25d6r595wcwqy0cee4cc": 1
+ }
+ ],
+ "result" : "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAAAAAA="
+ }
+ ],
+ "signer" : [
+ {
+ "privkeys" : [
+ "cP53pDbR5WtAD8dYAW9hhTjuvvTVaEiQBdrz9XPrgLBeRFiyCbQr",
+ "cR6SXDoyfQrcp4piaiHE97Rsgta9mNhGTen9XeonVgwsh4iSgw6d"
+ ],
+ "psbt" : "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAQMEAQAAAAABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEEIgAgjCNTFzdDtZXftKB7crqOQuN5fadOh/59nXSX47ICiQMBBUdSIQMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3CECOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnNSriIGAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zENkMak8AAACAAAAAgAMAAIAiBgMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3BDZDGpPAAAAgAAAAIACAACAAQMEAQAAAAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA",
+ "result" : "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgf0cwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMAQEDBAEAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHIgIDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtxHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwEBAwQBAAAAAQQiACCMI1MXN0O1ld+0oHtyuo5C43l9p06H/n2ddJfjsgKJAwEFR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuIgYCOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnMQ2QxqTwAAAIAAAACAAwAAgCIGAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcENkMak8AAACAAAAAgAIAAIAAIgIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1Ptnuylh3EQ2QxqTwAAAIAAAACABAAAgAAiAgJ/Y5l1fS7/VaE2rQLGhLGDi2VW5fG2s0KCqUtrUAUQlhDZDGpPAAAAgAAAAIAFAACAAA=="
+ },
+ {
+ "privkeys" : [
+ "cT7J9YpCwY3AVRFSjN6ukeEeWY6mhpbJPxRaDaP5QTdygQRxP9Au",
+ "cNBc3SWUip9PPm1GjRoLEJT6T41iNzCYtD7qro84FMnM5zEqeJsE"
+ ],
+ "psbt" : "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAQMEAQAAAAABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEEIgAgjCNTFzdDtZXftKB7crqOQuN5fadOh/59nXSX47ICiQMBBUdSIQMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3CECOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnNSriIGAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zENkMak8AAACAAAAAgAMAAIAiBgMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3BDZDGpPAAAAgAAAAIACAACAAQMEAQAAAAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA",
+ "result" : "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU210gwRQIhAPYQOLMI3B2oZaNIUnRvAVdyk0IIxtJEVDk82ZvfIhd3AiAFbmdaZ1ptCgK4WxTl4pB02KJam1dgvqKBb2YZEKAG6gEBAwQBAAAAAQRHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4iBgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfxDZDGpPAAAAgAAAAIAAAACAIgYC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtcQ2QxqTwAAAIAAAACAAQAAgAABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohyICAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBAQMEAQAAAAEEIgAgjCNTFzdDtZXftKB7crqOQuN5fadOh/59nXSX47ICiQMBBUdSIQMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3CECOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnNSriIGAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zENkMak8AAACAAAAAgAMAAIAiBgMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3BDZDGpPAAAAgAAAAIACAACAACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA="
+ }
+ ],
+ "combiner" : [
+ {
+ "combine" : [
+ "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgf0cwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMAQEDBAEAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHIgIDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtxHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwEBAwQBAAAAAQQiACCMI1MXN0O1ld+0oHtyuo5C43l9p06H/n2ddJfjsgKJAwEFR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuIgYCOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnMQ2QxqTwAAAIAAAACAAwAAgCIGAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcENkMak8AAACAAAAAgAIAAIAAIgIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1Ptnuylh3EQ2QxqTwAAAIAAAACABAAAgAAiAgJ/Y5l1fS7/VaE2rQLGhLGDi2VW5fG2s0KCqUtrUAUQlhDZDGpPAAAAgAAAAIAFAACAAA==",
+ "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU210gwRQIhAPYQOLMI3B2oZaNIUnRvAVdyk0IIxtJEVDk82ZvfIhd3AiAFbmdaZ1ptCgK4WxTl4pB02KJam1dgvqKBb2YZEKAG6gEBAwQBAAAAAQRHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4iBgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfxDZDGpPAAAAgAAAAIAAAACAIgYC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtcQ2QxqTwAAAIAAAACAAQAAgAABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohyICAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBAQMEAQAAAAEEIgAgjCNTFzdDtZXftKB7crqOQuN5fadOh/59nXSX47ICiQMBBUdSIQMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3CECOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnNSriIGAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zENkMak8AAACAAAAAgAMAAIAiBgMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3BDZDGpPAAAAgAAAAIACAACAACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA="
+ ],
+ "result" : "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgf0cwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMASICAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAQEDBAEAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHIgIDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtxHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwEiAgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc0cwRAIgZfRbpZmLWaJ//hp77QFq8fH5DVSzqo90UKpfVqJRA70CIH9yRwOtHtuWaAsoS1bU/8uI9/t1nqu+CKow8puFE4PSAQEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA"
+ },
+ {
+ "combine" : [
+ "cHNidP8BAD8CAAAAAf//////////////////////////////////////////AAAAAAD/////AQAAAAAAAAAAA2oBAAAAAAAKDwECAwQFBgcICQ8BAgMEBQYHCAkKCwwNDg8ACg8BAgMEBQYHCAkPAQIDBAUGBwgJCgsMDQ4PAAoPAQIDBAUGBwgJDwECAwQFBgcICQoLDA0ODwA=",
+ "cHNidP8BAD8CAAAAAf//////////////////////////////////////////AAAAAAD/////AQAAAAAAAAAAA2oBAAAAAAAKDwECAwQFBgcIEA8BAgMEBQYHCAkKCwwNDg8ACg8BAgMEBQYHCBAPAQIDBAUGBwgJCgsMDQ4PAAoPAQIDBAUGBwgQDwECAwQFBgcICQoLDA0ODwA="
+ ],
+ "result" : "cHNidP8BAD8CAAAAAf//////////////////////////////////////////AAAAAAD/////AQAAAAAAAAAAA2oBAAAAAAAKDwECAwQFBgcICQ8BAgMEBQYHCAkKCwwNDg8KDwECAwQFBgcIEA8BAgMEBQYHCAkKCwwNDg8ACg8BAgMEBQYHCAkPAQIDBAUGBwgJCgsMDQ4PCg8BAgMEBQYHCBAPAQIDBAUGBwgJCgsMDQ4PAAoPAQIDBAUGBwgJDwECAwQFBgcICQoLDA0ODwoPAQIDBAUGBwgQDwECAwQFBgcICQoLDA0ODwA="
+ }
+ ],
+ "finalizer" : [
+ {
+ "finalize" : "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgf0cwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMASICAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAQEDBAEAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHIgIDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtxHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwEiAgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc0cwRAIgZfRbpZmLWaJ//hp77QFq8fH5DVSzqo90UKpfVqJRA70CIH9yRwOtHtuWaAsoS1bU/8uI9/t1nqu+CKow8puFE4PSAQEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA",
+ "result" : "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABB9oARzBEAiB0AYrUGACXuHMyPAAVcgs2hMyBI4kQSOfbzZtVrWecmQIgc9Npt0Dj61Pc76M4I8gHBRTKVafdlUTxV8FnkTJhEYwBSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAUdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSrgABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEHIyIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQjaBABHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwFHMEQCIGX0W6WZi1mif/4ae+0BavHx+Q1Us6qPdFCqX1aiUQO9AiB/ckcDrR7blmgLKEtW1P/LiPf7dZ6rvgiqMPKbhROD0gFHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4AIgIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1Ptnuylh3EQ2QxqTwAAAIAAAACABAAAgAAiAgJ/Y5l1fS7/VaE2rQLGhLGDi2VW5fG2s0KCqUtrUAUQlhDZDGpPAAAAgAAAAIAFAACAAA=="
+ }
+ ],
+ "extractor" : [
+ {
+ "extract" : "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABB9oARzBEAiB0AYrUGACXuHMyPAAVcgs2hMyBI4kQSOfbzZtVrWecmQIgc9Npt0Dj61Pc76M4I8gHBRTKVafdlUTxV8FnkTJhEYwBSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAUdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSrgABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEHIyIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQjaBABHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwFHMEQCIGX0W6WZi1mif/4ae+0BavHx+Q1Us6qPdFCqX1aiUQO9AiB/ckcDrR7blmgLKEtW1P/LiPf7dZ6rvgiqMPKbhROD0gFHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4AIgIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1Ptnuylh3EQ2QxqTwAAAIAAAACABAAAgAAiAgJ/Y5l1fS7/VaE2rQLGhLGDi2VW5fG2s0KCqUtrUAUQlhDZDGpPAAAAgAAAAIAFAACAAA==",
+ "result" : "0200000000010258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd7500000000da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752aeffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d01000000232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f000400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00000000"
+ }
+ ]
+} \ No newline at end of file
diff --git a/test/functional/feature_config_args.py b/test/functional/feature_config_args.py
index e9924451d1..3abc1f4c68 100755
--- a/test/functional/feature_config_args.py
+++ b/test/functional/feature_config_args.py
@@ -36,13 +36,15 @@ class ConfArgsTest(BitcoinTestFramework):
f.write("datadir=" + new_data_dir + "\n")
f.write(conf_file_contents)
- self.nodes[0].assert_start_raises_init_error(['-conf=' + conf_file], 'Error reading configuration file: specified data directory "' + new_data_dir + '" does not exist.')
+ # Temporarily disabled, because this test would access the user's home dir (~/.bitcoin)
+ #self.nodes[0].assert_start_raises_init_error(['-conf=' + conf_file], 'Error reading configuration file: specified data directory "' + new_data_dir + '" does not exist.')
# Create the directory and ensure the config file now works
os.mkdir(new_data_dir)
- self.start_node(0, ['-conf='+conf_file, '-wallet=w1'])
- self.stop_node(0)
- assert os.path.exists(os.path.join(new_data_dir, 'regtest', 'wallets', 'w1'))
+ # Temporarily disabled, because this test would access the user's home dir (~/.bitcoin)
+ #self.start_node(0, ['-conf='+conf_file, '-wallet=w1'])
+ #self.stop_node(0)
+ #assert os.path.exists(os.path.join(new_data_dir, 'regtest', 'wallets', 'w1'))
# Ensure command line argument overrides datadir in conf
os.mkdir(new_data_dir_2)
diff --git a/test/functional/interface_bitcoin_cli.py b/test/functional/interface_bitcoin_cli.py
index e29fdc84e7..b097c64b13 100755
--- a/test/functional/interface_bitcoin_cli.py
+++ b/test/functional/interface_bitcoin_cli.py
@@ -15,6 +15,9 @@ class TestBitcoinCli(BitcoinTestFramework):
def run_test(self):
"""Main test logic"""
+ cli_response = self.nodes[0].cli("-version").send_cli()
+ assert("Bitcoin Core RPC client version" in cli_response)
+
self.log.info("Compare responses from gewalletinfo RPC and `bitcoin-cli getwalletinfo`")
cli_response = self.nodes[0].cli.getwalletinfo()
rpc_response = self.nodes[0].getwalletinfo()
diff --git a/test/functional/interface_zmq.py b/test/functional/interface_zmq.py
index af2e752b7a..def71c5f0f 100755
--- a/test/functional/interface_zmq.py
+++ b/test/functional/interface_zmq.py
@@ -3,10 +3,10 @@
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test the ZMQ notification interface."""
-import configparser
import struct
-from test_framework.test_framework import BitcoinTestFramework, SkipTest
+from test_framework.test_framework import (
+ BitcoinTestFramework, skip_if_no_bitcoind_zmq, skip_if_no_py3_zmq)
from test_framework.mininode import CTransaction
from test_framework.util import (assert_equal,
bytes_to_hex_str,
@@ -38,18 +38,9 @@ class ZMQTest (BitcoinTestFramework):
self.num_nodes = 2
def setup_nodes(self):
- # Try to import python3-zmq. Skip this test if the import fails.
- try:
- import zmq
- except ImportError:
- raise SkipTest("python3-zmq module not available.")
-
- # Check that bitcoin has been built with ZMQ enabled.
- config = configparser.ConfigParser()
- config.read_file(open(self.options.configfile))
-
- if not config["components"].getboolean("ENABLE_ZMQ"):
- raise SkipTest("bitcoind has not been built with zmq enabled.")
+ skip_if_no_py3_zmq()
+ skip_if_no_bitcoind_zmq(self)
+ import zmq
# Initialize ZMQ context and socket.
# All messages are received in the same socket which means
diff --git a/test/functional/p2p_leak.py b/test/functional/p2p_leak.py
index ecb9a56fe1..186c35e0e1 100755
--- a/test/functional/p2p_leak.py
+++ b/test/functional/p2p_leak.py
@@ -8,11 +8,7 @@ A node should never send anything other than VERSION/VERACK/REJECT until it's
received a VERACK.
This test connects to a node and sends it a few messages, trying to entice it
-into sending us something it shouldn't.
-
-Also test that nodes that send unsupported service bits to bitcoind are disconnected
-and don't receive a VERACK. Unsupported service bits are currently 1 << 5 and
-1 << 7 (until August 1st 2018)."""
+into sending us something it shouldn't."""
from test_framework.mininode import *
from test_framework.test_framework import BitcoinTestFramework
@@ -95,19 +91,13 @@ class P2PLeakTest(BitcoinTestFramework):
self.extra_args = [['-banscore='+str(banscore)]]
def run_test(self):
- self.nodes[0].setmocktime(1501545600) # August 1st 2017
-
no_version_bannode = self.nodes[0].add_p2p_connection(CNodeNoVersionBan(), send_version=False)
no_version_idlenode = self.nodes[0].add_p2p_connection(CNodeNoVersionIdle(), send_version=False)
no_verack_idlenode = self.nodes[0].add_p2p_connection(CNodeNoVerackIdle())
- unsupported_service_bit5_node = self.nodes[0].add_p2p_connection(CLazyNode(), services=NODE_NETWORK|NODE_UNSUPPORTED_SERVICE_BIT_5)
- unsupported_service_bit7_node = self.nodes[0].add_p2p_connection(CLazyNode(), services=NODE_NETWORK|NODE_UNSUPPORTED_SERVICE_BIT_7)
wait_until(lambda: no_version_bannode.ever_connected, timeout=10, lock=mininode_lock)
wait_until(lambda: no_version_idlenode.ever_connected, timeout=10, lock=mininode_lock)
wait_until(lambda: no_verack_idlenode.version_received, timeout=10, lock=mininode_lock)
- wait_until(lambda: unsupported_service_bit5_node.ever_connected, timeout=10, lock=mininode_lock)
- wait_until(lambda: unsupported_service_bit7_node.ever_connected, timeout=10, lock=mininode_lock)
# Mine a block and make sure that it's not sent to the connected nodes
self.nodes[0].generate(1)
@@ -118,10 +108,6 @@ class P2PLeakTest(BitcoinTestFramework):
#This node should have been banned
assert not no_version_bannode.is_connected
- # These nodes should have been disconnected
- assert not unsupported_service_bit5_node.is_connected
- assert not unsupported_service_bit7_node.is_connected
-
self.nodes[0].disconnect_p2ps()
# Wait until all connections are closed
@@ -131,17 +117,6 @@ class P2PLeakTest(BitcoinTestFramework):
assert(no_version_bannode.unexpected_msg == False)
assert(no_version_idlenode.unexpected_msg == False)
assert(no_verack_idlenode.unexpected_msg == False)
- assert not unsupported_service_bit5_node.unexpected_msg
- assert not unsupported_service_bit7_node.unexpected_msg
-
- self.log.info("Service bits 5 and 7 are allowed after August 1st 2018")
- self.nodes[0].setmocktime(1533168000) # August 2nd 2018
-
- allowed_service_bit5_node = self.nodes[0].add_p2p_connection(P2PInterface(), services=NODE_NETWORK|NODE_UNSUPPORTED_SERVICE_BIT_5)
- allowed_service_bit7_node = self.nodes[0].add_p2p_connection(P2PInterface(), services=NODE_NETWORK|NODE_UNSUPPORTED_SERVICE_BIT_7)
-
- wait_until(lambda: allowed_service_bit5_node.message_count["verack"], lock=mininode_lock)
- wait_until(lambda: allowed_service_bit7_node.message_count["verack"], lock=mininode_lock)
if __name__ == '__main__':
diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py
index 727f2d1c6e..52f6482d56 100755
--- a/test/functional/p2p_segwit.py
+++ b/test/functional/p2p_segwit.py
@@ -41,11 +41,13 @@ from test_framework.messages import (
from test_framework.mininode import (
P2PInterface,
mininode_lock,
+ wait_until,
)
from test_framework.script import (
CScript,
CScriptNum,
CScriptOp,
+ MAX_SCRIPT_ELEMENT_SIZE,
OP_0,
OP_1,
OP_16,
@@ -221,7 +223,7 @@ class SegWitTest(BitcoinTestFramework):
block.solve()
def run_test(self):
- # Setup the p2p connections and start up the network thread.
+ # Setup the p2p connections
# self.test_node sets NODE_WITNESS|NODE_NETWORK
self.test_node = self.nodes[0].add_p2p_connection(TestP2PConn(), services=NODE_NETWORK | NODE_WITNESS)
# self.old_node sets only NODE_NETWORK
@@ -351,10 +353,7 @@ class SegWitTest(BitcoinTestFramework):
# Sending witness data before activation is not allowed (anti-spam
# rule).
test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False)
- # TODO: fix synchronization so we can test reject reason
- # Right now, bitcoind delays sending reject messages for blocks
- # until the future, making synchronization here difficult.
- # assert_equal(self.test_node.last_message["reject"].reason, "unexpected-witness")
+ wait_until(lambda: 'reject' in self.test_node.last_message and self.test_node.last_message["reject"].reason == b"unexpected-witness")
# But it should not be permanently marked bad...
# Resend without witness information.
@@ -605,9 +604,6 @@ class SegWitTest(BitcoinTestFramework):
@subtest
def advance_to_segwit_lockin(self):
"""Mine enough blocks to lock in segwit, but don't activate."""
- # TODO: we could verify that lockin only happens at the right threshold of
- # signalling blocks, rather than just at the right period boundary.
-
height = self.nodes[0].getblockcount()
# Advance to end of period, and verify lock-in happens at the end
self.nodes[0].generate(VB_PERIOD - 1)
@@ -741,9 +737,6 @@ class SegWitTest(BitcoinTestFramework):
@subtest
def advance_to_segwit_active(self):
"""Mine enough blocks to activate segwit."""
- # TODO: we could verify that activation only happens at the right threshold
- # of signalling blocks, rather than just at the right period boundary.
-
height = self.nodes[0].getblockcount()
self.nodes[0].generate(VB_PERIOD - (height % VB_PERIOD) - 2)
assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], 'locked_in')
@@ -1137,8 +1130,6 @@ class SegWitTest(BitcoinTestFramework):
def test_max_witness_push_length(self):
"""Test that witness stack can only allow up to 520 byte pushes."""
- MAX_SCRIPT_ELEMENT_SIZE = 520
-
block = self.build_next_block()
witness_program = CScript([OP_DROP, OP_TRUE])
@@ -1402,30 +1393,28 @@ class SegWitTest(BitcoinTestFramework):
Future segwit version transactions are non-standard, but valid in blocks.
Can run this before and after segwit activation."""
- num_tests = 17 # will test OP_0, OP1, ..., OP_16
- if (len(self.utxo) < num_tests):
+ NUM_SEGWIT_VERSIONS = 17 # will test OP_0, OP1, ..., OP_16
+ if len(self.utxo) < NUM_SEGWIT_VERSIONS:
tx = CTransaction()
tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b""))
- split_value = (self.utxo[0].nValue - 4000) // num_tests
- for i in range(num_tests):
+ split_value = (self.utxo[0].nValue - 4000) // NUM_SEGWIT_VERSIONS
+ for i in range(NUM_SEGWIT_VERSIONS):
tx.vout.append(CTxOut(split_value, CScript([OP_TRUE])))
tx.rehash()
block = self.build_next_block()
self.update_witness_block_with_transactions(block, [tx])
test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
self.utxo.pop(0)
- for i in range(num_tests):
+ for i in range(NUM_SEGWIT_VERSIONS):
self.utxo.append(UTXO(tx.sha256, i, split_value))
sync_blocks(self.nodes)
temp_utxo = []
tx = CTransaction()
- count = 0
witness_program = CScript([OP_TRUE])
witness_hash = sha256(witness_program)
assert_equal(len(self.nodes[1].getrawmempool()), 0)
for version in list(range(OP_1, OP_16 + 1)) + [OP_0]:
- count += 1
# First try to spend to a future version segwit script_pubkey.
script_pubkey = CScript([CScriptOp(version), witness_hash])
tx.vin = [CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")]
@@ -1680,19 +1669,19 @@ class SegWitTest(BitcoinTestFramework):
# Test combinations of signature hashes.
# Split the utxo into a lot of outputs.
# Randomly choose up to 10 to spend, sign with different hashtypes, and
- # output to a random number of outputs. Repeat num_tests times.
+ # output to a random number of outputs. Repeat NUM_SIGHASH_TESTS times.
# Ensure that we've tested a situation where we use SIGHASH_SINGLE with
# an input index > number of outputs.
- num_tests = 500
+ NUM_SIGHASH_TESTS = 500
temp_utxos = []
tx = CTransaction()
tx.vin.append(CTxIn(COutPoint(prev_utxo.sha256, prev_utxo.n), b""))
- split_value = prev_utxo.nValue // num_tests
- for i in range(num_tests):
+ split_value = prev_utxo.nValue // NUM_SIGHASH_TESTS
+ for i in range(NUM_SIGHASH_TESTS):
tx.vout.append(CTxOut(split_value, script_pubkey))
tx.wit.vtxinwit.append(CTxInWitness())
sign_p2pk_witness_input(witness_program, tx, 0, SIGHASH_ALL, prev_utxo.nValue, key)
- for i in range(num_tests):
+ for i in range(NUM_SIGHASH_TESTS):
temp_utxos.append(UTXO(tx.sha256, i, split_value))
block = self.build_next_block()
@@ -1701,7 +1690,7 @@ class SegWitTest(BitcoinTestFramework):
block = self.build_next_block()
used_sighash_single_out_of_bounds = False
- for i in range(num_tests):
+ for i in range(NUM_SIGHASH_TESTS):
# Ping regularly to keep the connection alive
if (not i % 100):
self.test_node.sync_with_ping()
diff --git a/test/functional/rpc_createmultisig.py b/test/functional/rpc_createmultisig.py
new file mode 100755
index 0000000000..97e614c888
--- /dev/null
+++ b/test/functional/rpc_createmultisig.py
@@ -0,0 +1,98 @@
+#!/usr/bin/env python3
+# Copyright (c) 2015-2017 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Test transaction signing using the signrawtransaction* RPCs."""
+
+from test_framework.test_framework import BitcoinTestFramework
+import decimal
+
+class RpcCreateMultiSigTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.num_nodes = 3
+
+ def get_keys(self):
+ node0,node1,node2 = self.nodes
+ self.add = [node1.getnewaddress() for _ in range(self.nkeys)]
+ self.pub = [node1.getaddressinfo(a)["pubkey"] for a in self.add]
+ self.priv = [node1.dumpprivkey(a) for a in self.add]
+ self.final = node2.getnewaddress()
+
+ def run_test(self):
+ node0,node1,node2 = self.nodes
+
+ # 50 BTC each, rest will be 25 BTC each
+ node0.generate(149)
+ self.sync_all()
+
+ self.moved = 0
+ for self.nkeys in [3,5]:
+ for self.nsigs in [2,3]:
+ for self.output_type in ["bech32", "p2sh-segwit", "legacy"]:
+ self.get_keys()
+ self.do_multisig()
+
+ self.checkbalances()
+
+ def checkbalances(self):
+ node0,node1,node2 = self.nodes
+ node0.generate(100)
+ self.sync_all()
+
+ bal0 = node0.getbalance()
+ bal1 = node1.getbalance()
+ bal2 = node2.getbalance()
+
+ height = node0.getblockchaininfo()["blocks"]
+ assert 150 < height < 350
+ total = 149*50 + (height-149-100)*25
+ assert bal1 == 0
+ assert bal2 == self.moved
+ assert bal0+bal1+bal2 == total
+
+ def do_multisig(self):
+ node0,node1,node2 = self.nodes
+
+ msig = node2.createmultisig(self.nsigs, self.pub, self.output_type)
+ madd = msig["address"]
+ mredeem = msig["redeemScript"]
+ if self.output_type == 'bech32':
+ assert madd[0:4] == "bcrt" # actually a bech32 address
+
+ # compare against addmultisigaddress
+ msigw = node1.addmultisigaddress(self.nsigs, self.pub, None, self.output_type)
+ maddw = msigw["address"]
+ mredeemw = msigw["redeemScript"]
+ # addmultisigiaddress and createmultisig work the same
+ assert maddw == madd
+ assert mredeemw == mredeem
+
+ txid = node0.sendtoaddress(madd, 40)
+
+ tx = node0.getrawtransaction(txid, True)
+ vout = [v["n"] for v in tx["vout"] if madd in v["scriptPubKey"].get("addresses",[])]
+ assert len(vout) == 1
+ vout = vout[0]
+ scriptPubKey = tx["vout"][vout]["scriptPubKey"]["hex"]
+ value = tx["vout"][vout]["value"]
+ prevtxs = [{"txid": txid, "vout": vout, "scriptPubKey": scriptPubKey, "redeemScript": mredeem, "amount": value}]
+
+ node0.generate(1)
+
+ outval = value - decimal.Decimal("0.00001000")
+ rawtx = node2.createrawtransaction([{"txid": txid, "vout": vout}], [{self.final: outval}])
+
+ rawtx2 = node2.signrawtransactionwithkey(rawtx, self.priv[0:self.nsigs-1], prevtxs)
+ rawtx3 = node2.signrawtransactionwithkey(rawtx2["hex"], [self.priv[-1]], prevtxs)
+
+ self.moved += outval
+ tx = node0.sendrawtransaction(rawtx3["hex"], True)
+ blk = node0.generate(1)[0]
+ assert tx in node0.getblock(blk)["tx"]
+
+ txinfo = node0.getrawtransaction(tx, True, blk)
+ self.log.info("n/m=%d/%d %s size=%d vsize=%d weight=%d" % (self.nsigs, self.nkeys, self.output_type, txinfo["size"], txinfo["vsize"], txinfo["weight"]))
+
+if __name__ == '__main__':
+ RpcCreateMultiSigTest().main()
diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py
new file mode 100755
index 0000000000..a68327496f
--- /dev/null
+++ b/test/functional/rpc_psbt.py
@@ -0,0 +1,190 @@
+#!/usr/bin/env python3
+# Copyright (c) 2018 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Test the Partially Signed Transaction RPCs.
+"""
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import *
+
+# Create one-input, one-output, no-fee transaction:
+class PSBTTest(BitcoinTestFramework):
+
+ def set_test_params(self):
+ self.setup_clean_chain = False
+ self.num_nodes = 3
+
+ def run_test(self):
+ # Create and fund a raw tx for sending 10 BTC
+ psbtx1 = self.nodes[0].walletcreatefundedpsbt([], {self.nodes[2].getnewaddress():10})['psbt']
+
+ # Node 1 should not be able to add anything to it but still return the psbtx same as before
+ psbtx = self.nodes[1].walletprocesspsbt(psbtx1)['psbt']
+ assert_equal(psbtx1, psbtx)
+
+ # Sign the transaction and send
+ signed_tx = self.nodes[0].walletprocesspsbt(psbtx)['psbt']
+ final_tx = self.nodes[0].finalizepsbt(signed_tx)['hex']
+ self.nodes[0].sendrawtransaction(final_tx)
+
+ # Create p2sh, p2wpkh, and p2wsh addresses
+ pubkey0 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())['pubkey']
+ pubkey1 = self.nodes[1].getaddressinfo(self.nodes[1].getnewaddress())['pubkey']
+ pubkey2 = self.nodes[2].getaddressinfo(self.nodes[2].getnewaddress())['pubkey']
+ p2sh = self.nodes[1].addmultisigaddress(2, [pubkey0, pubkey1, pubkey2], "", "legacy")['address']
+ p2wsh = self.nodes[1].addmultisigaddress(2, [pubkey0, pubkey1, pubkey2], "", "bech32")['address']
+ p2sh_p2wsh = self.nodes[1].addmultisigaddress(2, [pubkey0, pubkey1, pubkey2], "", "p2sh-segwit")['address']
+ p2wpkh = self.nodes[1].getnewaddress("", "bech32")
+ p2pkh = self.nodes[1].getnewaddress("", "legacy")
+ p2sh_p2wpkh = self.nodes[1].getnewaddress("", "p2sh-segwit")
+
+ # fund those addresses
+ rawtx = self.nodes[0].createrawtransaction([], {p2sh:10, p2wsh:10, p2wpkh:10, p2sh_p2wsh:10, p2sh_p2wpkh:10, p2pkh:10})
+ rawtx = self.nodes[0].fundrawtransaction(rawtx, {"changePosition":3})
+ signed_tx = self.nodes[0].signrawtransactionwithwallet(rawtx['hex'])['hex']
+ txid = self.nodes[0].sendrawtransaction(signed_tx)
+ self.nodes[0].generate(6)
+ self.sync_all()
+
+ # Find the output pos
+ p2sh_pos = -1
+ p2wsh_pos = -1
+ p2wpkh_pos = -1
+ p2pkh_pos = -1
+ p2sh_p2wsh_pos = -1
+ p2sh_p2wpkh_pos = -1
+ decoded = self.nodes[0].decoderawtransaction(signed_tx)
+ for out in decoded['vout']:
+ if out['scriptPubKey']['addresses'][0] == p2sh:
+ p2sh_pos = out['n']
+ elif out['scriptPubKey']['addresses'][0] == p2wsh:
+ p2wsh_pos = out['n']
+ elif out['scriptPubKey']['addresses'][0] == p2wpkh:
+ p2wpkh_pos = out['n']
+ elif out['scriptPubKey']['addresses'][0] == p2sh_p2wsh:
+ p2sh_p2wsh_pos = out['n']
+ elif out['scriptPubKey']['addresses'][0] == p2sh_p2wpkh:
+ p2sh_p2wpkh_pos = out['n']
+ elif out['scriptPubKey']['addresses'][0] == p2pkh:
+ p2pkh_pos = out['n']
+
+ # spend single key from node 1
+ rawtx = self.nodes[1].walletcreatefundedpsbt([{"txid":txid,"vout":p2wpkh_pos},{"txid":txid,"vout":p2sh_p2wpkh_pos},{"txid":txid,"vout":p2pkh_pos}], {self.nodes[1].getnewaddress():29.99})['psbt']
+ walletprocesspsbt_out = self.nodes[1].walletprocesspsbt(rawtx)
+ assert_equal(walletprocesspsbt_out['complete'], True)
+ self.nodes[1].sendrawtransaction(self.nodes[1].finalizepsbt(walletprocesspsbt_out['psbt'])['hex'])
+
+ # partially sign multisig things with node 1
+ psbtx = self.nodes[1].walletcreatefundedpsbt([{"txid":txid,"vout":p2wsh_pos},{"txid":txid,"vout":p2sh_pos},{"txid":txid,"vout":p2sh_p2wsh_pos}], {self.nodes[1].getnewaddress():29.99})['psbt']
+ walletprocesspsbt_out = self.nodes[1].walletprocesspsbt(psbtx)
+ psbtx = walletprocesspsbt_out['psbt']
+ assert_equal(walletprocesspsbt_out['complete'], False)
+
+ # partially sign with node 2. This should be complete and sendable
+ walletprocesspsbt_out = self.nodes[2].walletprocesspsbt(psbtx)
+ assert_equal(walletprocesspsbt_out['complete'], True)
+ self.nodes[2].sendrawtransaction(self.nodes[2].finalizepsbt(walletprocesspsbt_out['psbt'])['hex'])
+
+ # check that walletprocesspsbt fails to decode a non-psbt
+ rawtx = self.nodes[1].createrawtransaction([{"txid":txid,"vout":p2wpkh_pos}], {self.nodes[1].getnewaddress():9.99})
+ assert_raises_rpc_error(-22, "TX decode failed", self.nodes[1].walletprocesspsbt, rawtx)
+
+ # Convert a non-psbt to psbt and make sure we can decode it
+ rawtx = self.nodes[0].createrawtransaction([], {self.nodes[1].getnewaddress():10})
+ rawtx = self.nodes[0].fundrawtransaction(rawtx)
+ new_psbt = self.nodes[0].converttopsbt(rawtx['hex'])
+ self.nodes[0].decodepsbt(new_psbt)
+
+ # Make sure that a psbt with signatures cannot be converted
+ signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx['hex'])
+ assert_raises_rpc_error(-22, "TX decode failed", self.nodes[0].converttopsbt, signedtx['hex'])
+
+ # Explicilty allow converting non-empty txs
+ new_psbt = self.nodes[0].converttopsbt(rawtx['hex'])
+ self.nodes[0].decodepsbt(new_psbt)
+
+ # Create outputs to nodes 1 and 2
+ node1_addr = self.nodes[1].getnewaddress()
+ node2_addr = self.nodes[2].getnewaddress()
+ txid1 = self.nodes[0].sendtoaddress(node1_addr, 13)
+ txid2 =self.nodes[0].sendtoaddress(node2_addr, 13)
+ self.nodes[0].generate(6)
+ self.sync_all()
+ vout1 = find_output(self.nodes[1], txid1, 13)
+ vout2 = find_output(self.nodes[2], txid2, 13)
+
+ # Create a psbt spending outputs from nodes 1 and 2
+ psbt_orig = self.nodes[0].createpsbt([{"txid":txid1, "vout":vout1}, {"txid":txid2, "vout":vout2}], {self.nodes[0].getnewaddress():25.999})
+
+ # Update psbts, should only have data for one input and not the other
+ psbt1 = self.nodes[1].walletprocesspsbt(psbt_orig)['psbt']
+ psbt1_decoded = self.nodes[0].decodepsbt(psbt1)
+ assert psbt1_decoded['inputs'][0] and not psbt1_decoded['inputs'][1]
+ psbt2 = self.nodes[2].walletprocesspsbt(psbt_orig)['psbt']
+ psbt2_decoded = self.nodes[0].decodepsbt(psbt2)
+ assert not psbt2_decoded['inputs'][0] and psbt2_decoded['inputs'][1]
+
+ # Combine, finalize, and send the psbts
+ combined = self.nodes[0].combinepsbt([psbt1, psbt2])
+ finalized = self.nodes[0].finalizepsbt(combined)['hex']
+ self.nodes[0].sendrawtransaction(finalized)
+ self.nodes[0].generate(6)
+ self.sync_all()
+
+ # BIP 174 Test Vectors
+
+ # Check that unknown values are just passed through
+ unknown_psbt = "cHNidP8BAD8CAAAAAf//////////////////////////////////////////AAAAAAD/////AQAAAAAAAAAAA2oBAAAAAAAACg8BAgMEBQYHCAkPAQIDBAUGBwgJCgsMDQ4PAAA="
+ unknown_out = self.nodes[0].walletprocesspsbt(unknown_psbt)['psbt']
+ assert_equal(unknown_psbt, unknown_out)
+
+ # Open the data file
+ with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data/rpc_psbt.json'), encoding='utf-8') as f:
+ d = json.load(f)
+ invalids = d['invalid']
+ valids = d['valid']
+ creators = d['creator']
+ signers = d['signer']
+ combiners = d['combiner']
+ finalizers = d['finalizer']
+ extractors = d['extractor']
+
+ # Invalid PSBTs
+ for invalid in invalids:
+ assert_raises_rpc_error(-22, "TX decode failed", self.nodes[0].decodepsbt, invalid)
+
+ # Valid PSBTs
+ for valid in valids:
+ self.nodes[0].decodepsbt(valid)
+
+ # Creator Tests
+ for creator in creators:
+ created_tx = self.nodes[0].createpsbt(creator['inputs'], creator['outputs'])
+ assert_equal(created_tx, creator['result'])
+
+ # Signer tests
+ for i, signer in enumerate(signers):
+ for key in signer['privkeys']:
+ self.nodes[i].importprivkey(key)
+ signed_tx = self.nodes[i].walletprocesspsbt(signer['psbt'])['psbt']
+ assert_equal(signed_tx, signer['result'])
+
+ # Combiner test
+ for combiner in combiners:
+ combined = self.nodes[2].combinepsbt(combiner['combine'])
+ assert_equal(combined, combiner['result'])
+
+ # Finalizer test
+ for finalizer in finalizers:
+ finalized = self.nodes[2].finalizepsbt(finalizer['finalize'], False)['psbt']
+ assert_equal(finalized, finalizer['result'])
+
+ # Extractor test
+ for extractor in extractors:
+ extracted = self.nodes[2].finalizepsbt(extractor['extract'], True)['hex']
+ assert_equal(extracted, extractor['result'])
+
+
+if __name__ == '__main__':
+ PSBTTest().main()
diff --git a/test/functional/rpc_scantxoutset.py b/test/functional/rpc_scantxoutset.py
new file mode 100755
index 0000000000..ce5d4da9e7
--- /dev/null
+++ b/test/functional/rpc_scantxoutset.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python3
+# Copyright (c) 2018 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Test the scantxoutset rpc call."""
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import *
+
+import shutil
+import os
+
+class ScantxoutsetTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 1
+ self.setup_clean_chain = True
+ def run_test(self):
+ self.log.info("Mining blocks...")
+ self.nodes[0].generate(110)
+
+ addr_P2SH_SEGWIT = self.nodes[0].getnewaddress("", "p2sh-segwit")
+ pubk1 = self.nodes[0].getaddressinfo(addr_P2SH_SEGWIT)['pubkey']
+ addr_LEGACY = self.nodes[0].getnewaddress("", "legacy")
+ pubk2 = self.nodes[0].getaddressinfo(addr_LEGACY)['pubkey']
+ addr_BECH32 = self.nodes[0].getnewaddress("", "bech32")
+ pubk3 = self.nodes[0].getaddressinfo(addr_BECH32)['pubkey']
+ self.nodes[0].sendtoaddress(addr_P2SH_SEGWIT, 1)
+ self.nodes[0].sendtoaddress(addr_LEGACY, 2)
+ self.nodes[0].sendtoaddress(addr_BECH32, 3)
+ self.nodes[0].generate(1)
+
+ self.log.info("Stop node, remove wallet, mine again some blocks...")
+ self.stop_node(0)
+ shutil.rmtree(os.path.join(self.nodes[0].datadir, "regtest", 'wallets'))
+ self.start_node(0)
+ self.nodes[0].generate(110)
+
+ self.restart_node(0, ['-nowallet'])
+ self.log.info("Test if we have found the non HD unspent outputs.")
+ assert_equal(self.nodes[0].scantxoutset("start", [ {"pubkey": {"pubkey": pubk1}}, {"pubkey": {"pubkey": pubk2}}, {"pubkey": {"pubkey": pubk3}}])['total_amount'], 6)
+ assert_equal(self.nodes[0].scantxoutset("start", [ {"address": addr_P2SH_SEGWIT}, {"address": addr_LEGACY}, {"address": addr_BECH32}])['total_amount'], 6)
+ assert_equal(self.nodes[0].scantxoutset("start", [ {"address": addr_P2SH_SEGWIT}, {"address": addr_LEGACY}, {"pubkey": {"pubkey": pubk3}} ])['total_amount'], 6)
+
+ self.log.info("Test invalid parameters.")
+ assert_raises_rpc_error(-8, 'Scanobject "pubkey" must contain an object as value', self.nodes[0].scantxoutset, "start", [ {"pubkey": pubk1}]) #missing pubkey object
+ assert_raises_rpc_error(-8, 'Scanobject "address" must contain a single string as value', self.nodes[0].scantxoutset, "start", [ {"address": {"address": addr_P2SH_SEGWIT}}]) #invalid object for address object
+
+if __name__ == '__main__':
+ ScantxoutsetTest().main()
diff --git a/test/functional/rpc_users.py b/test/functional/rpc_users.py
index 1ef59da5ad..62d86467fc 100755
--- a/test/functional/rpc_users.py
+++ b/test/functional/rpc_users.py
@@ -59,7 +59,7 @@ class HTTPBasicsTest(BitcoinTestFramework):
#Old authpair
authpair = url.username + ':' + url.password
- #New authpair generated via share/rpcuser tool
+ #New authpair generated via share/rpcauth tool
password = "cA773lm788buwYe4g4WT+05pKyNruVKjQ25x3n0DQcM="
#Second authpair with different username
diff --git a/test/functional/rpc_zmq.py b/test/functional/rpc_zmq.py
index eb789face2..6dbc726d5e 100755
--- a/test/functional/rpc_zmq.py
+++ b/test/functional/rpc_zmq.py
@@ -4,7 +4,8 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test for the ZMQ RPC methods."""
-from test_framework.test_framework import BitcoinTestFramework
+from test_framework.test_framework import (
+ BitcoinTestFramework, skip_if_no_py3_zmq, skip_if_no_bitcoind_zmq)
from test_framework.util import assert_equal
@@ -17,6 +18,8 @@ class RPCZMQTest(BitcoinTestFramework):
self.setup_clean_chain = True
def run_test(self):
+ skip_if_no_py3_zmq()
+ skip_if_no_bitcoind_zmq(self)
self._test_getzmqnotifications()
def _test_getzmqnotifications(self):
diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py
index df8d424d01..af57e61f2f 100755
--- a/test/functional/test_framework/messages.py
+++ b/test/functional/test_framework/messages.py
@@ -42,8 +42,6 @@ NODE_NETWORK = (1 << 0)
# NODE_GETUTXO = (1 << 1)
NODE_BLOOM = (1 << 2)
NODE_WITNESS = (1 << 3)
-NODE_UNSUPPORTED_SERVICE_BIT_5 = (1 << 5)
-NODE_UNSUPPORTED_SERVICE_BIT_7 = (1 << 7)
NODE_NETWORK_LIMITED = (1 << 10)
MSG_TX = 1
diff --git a/test/functional/test_framework/mininode.py b/test/functional/test_framework/mininode.py
index 29bf33fa5b..ccf767d357 100755
--- a/test/functional/test_framework/mininode.py
+++ b/test/functional/test_framework/mininode.py
@@ -179,7 +179,17 @@ class P2PConnection(asyncio.Protocol):
raise IOError('Not connected')
self._log_message("send", message)
tmsg = self._build_message(message)
- NetworkThread.network_event_loop.call_soon_threadsafe(lambda: self._transport and self._transport.write(tmsg))
+
+ def maybe_write():
+ if not self._transport:
+ return
+ # Python <3.4.4 does not have is_closing, so we have to check for
+ # its existence explicitly as long as Bitcoin Core supports all
+ # Python 3.4 versions.
+ if hasattr(self._transport, 'is_closing') and self._transport.is_closing():
+ return
+ self._transport.write(tmsg)
+ NetworkThread.network_event_loop.call_soon_threadsafe(maybe_write)
# Class utility methods
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index abe8d12e59..c2fb2077ac 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -475,3 +475,20 @@ class SkipTest(Exception):
"""This exception is raised to skip a test"""
def __init__(self, message):
self.message = message
+
+
+def skip_if_no_py3_zmq():
+ """Attempt to import the zmq package and skip the test if the import fails."""
+ try:
+ import zmq # noqa
+ except ImportError:
+ raise SkipTest("python3-zmq module not available.")
+
+
+def skip_if_no_bitcoind_zmq(test_instance):
+ """Skip the running test if bitcoind has not been compiled with zmq support."""
+ config = configparser.ConfigParser()
+ config.read_file(open(test_instance.options.configfile))
+
+ if not config["components"].getboolean("ENABLE_ZMQ"):
+ raise SkipTest("bitcoind has not been built with zmq enabled.")
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index 287dc0e53e..50942aec40 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -351,8 +351,7 @@ class TestNodeCLI():
def send_cli(self, command=None, *args, **kwargs):
"""Run bitcoin-cli command. Deserializes returned string as python object."""
-
- pos_args = [str(arg) for arg in args]
+ pos_args = [str(arg).lower() if type(arg) is bool else str(arg) for arg in args]
named_args = [str(key) + "=" + str(value) for (key, value) in kwargs.items()]
assert not (pos_args and named_args), "Cannot use positional arguments and named arguments in the same bitcoin-cli call"
p_args = [self.binary, "-datadir=" + self.datadir] + self.options
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index c3a5468296..d324cd9bba 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -98,10 +98,14 @@ BASE_SCRIPTS = [
'mempool_persist.py',
'wallet_multiwallet.py',
'wallet_multiwallet.py --usecli',
+ 'wallet_disableprivatekeys.py',
+ 'wallet_disableprivatekeys.py --usecli',
'interface_http.py',
+ 'rpc_psbt.py',
'rpc_users.py',
'feature_proxy.py',
'rpc_signrawtransaction.py',
+ 'wallet_groups.py',
'p2p_disconnect_ban.py',
'rpc_decodescript.py',
'rpc_blockchain.py',
@@ -113,6 +117,7 @@ BASE_SCRIPTS = [
'mining_prioritisetransaction.py',
'p2p_invalid_block.py',
'p2p_invalid_tx.py',
+ 'rpc_createmultisig.py',
'feature_versionbits_warning.py',
'rpc_preciousblock.py',
'wallet_importprunedfunds.py',
@@ -141,6 +146,7 @@ BASE_SCRIPTS = [
'feature_uacomment.py',
'p2p_unrequested_blocks.py',
'feature_includeconf.py',
+ 'rpc_scantxoutset.py',
'feature_logging.py',
'p2p_node_network_limited.py',
'feature_blocksdir.py',
diff --git a/test/functional/wallet_abandonconflict.py b/test/functional/wallet_abandonconflict.py
index d5ef08d782..cf2120d4eb 100755
--- a/test/functional/wallet_abandonconflict.py
+++ b/test/functional/wallet_abandonconflict.py
@@ -70,9 +70,17 @@ class AbandonConflictTest(BitcoinTestFramework):
signed2 = self.nodes[0].signrawtransactionwithwallet(self.nodes[0].createrawtransaction(inputs, outputs))
txABC2 = self.nodes[0].sendrawtransaction(signed2["hex"])
+ # Create a child tx spending ABC2
+ signed3_change = Decimal("24.999")
+ inputs = [ {"txid":txABC2, "vout":0} ]
+ outputs = { self.nodes[0].getnewaddress(): signed3_change }
+ signed3 = self.nodes[0].signrawtransactionwithwallet(self.nodes[0].createrawtransaction(inputs, outputs))
+ # note tx is never directly referenced, only abandoned as a child of the above
+ self.nodes[0].sendrawtransaction(signed3["hex"])
+
# In mempool txs from self should increase balance from change
newbalance = self.nodes[0].getbalance()
- assert_equal(newbalance, balance - Decimal("30") + Decimal("24.9996"))
+ assert_equal(newbalance, balance - Decimal("30") + signed3_change)
balance = newbalance
# Restart the node with a higher min relay fee so the parent tx is no longer in mempool
@@ -87,7 +95,7 @@ class AbandonConflictTest(BitcoinTestFramework):
# Not in mempool txs from self should only reduce balance
# inputs are still spent, but change not received
newbalance = self.nodes[0].getbalance()
- assert_equal(newbalance, balance - Decimal("24.9996"))
+ assert_equal(newbalance, balance - signed3_change)
# Unconfirmed received funds that are not in mempool, also shouldn't show
# up in unconfirmed balance
unconfbalance = self.nodes[0].getunconfirmedbalance() + self.nodes[0].getbalance()
diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py
index 0353905142..431fec3738 100755
--- a/test/functional/wallet_basic.py
+++ b/test/functional/wallet_basic.py
@@ -64,6 +64,15 @@ class WalletTest(BitcoinTestFramework):
assert_equal(self.nodes[1].getbalance(), 50)
assert_equal(self.nodes[2].getbalance(), 0)
+ # Check getbalance with different arguments
+ assert_equal(self.nodes[0].getbalance("*"), 50)
+ assert_equal(self.nodes[0].getbalance("*", 1), 50)
+ assert_equal(self.nodes[0].getbalance("*", 1, True), 50)
+ assert_equal(self.nodes[0].getbalance(minconf=1), 50)
+
+ # first argument of getbalance must be excluded or set to "*"
+ assert_raises_rpc_error(-32, "dummy first argument must be excluded or set to \"*\"", self.nodes[0].getbalance, "")
+
# Check that only first and second nodes have UTXOs
utxos = self.nodes[0].listunspent()
assert_equal(len(utxos), 1)
diff --git a/test/functional/wallet_disableprivatekeys.py b/test/functional/wallet_disableprivatekeys.py
new file mode 100755
index 0000000000..0ba2cfe9be
--- /dev/null
+++ b/test/functional/wallet_disableprivatekeys.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env python3
+# Copyright (c) 2018 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Test disable-privatekeys mode.
+"""
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_raises_rpc_error,
+)
+
+
+class DisablePrivateKeysTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = False
+ self.num_nodes = 1
+ self.supports_cli = True
+
+ def run_test(self):
+ node = self.nodes[0]
+ self.log.info("Test disableprivatekeys creation.")
+ self.nodes[0].createwallet('w1', True)
+ self.nodes[0].createwallet('w2')
+ w1 = node.get_wallet_rpc('w1')
+ w2 = node.get_wallet_rpc('w2')
+ assert_raises_rpc_error(-4,"Error: Private keys are disabled for this wallet", w1.getnewaddress)
+ assert_raises_rpc_error(-4,"Error: Private keys are disabled for this wallet", w1.getrawchangeaddress)
+ w1.importpubkey(w2.getaddressinfo(w2.getnewaddress())['pubkey'])
+
+if __name__ == '__main__':
+ DisablePrivateKeysTest().main()
diff --git a/test/functional/wallet_groups.py b/test/functional/wallet_groups.py
new file mode 100755
index 0000000000..0d27815da0
--- /dev/null
+++ b/test/functional/wallet_groups.py
@@ -0,0 +1,67 @@
+#!/usr/bin/env python3
+# Copyright (c) 2018 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Test wallet group functionality."""
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+)
+
+def assert_approx(v, vexp, vspan=0.00001):
+ if v < vexp - vspan:
+ raise AssertionError("%s < [%s..%s]" % (str(v), str(vexp - vspan), str(vexp + vspan)))
+ if v > vexp + vspan:
+ raise AssertionError("%s > [%s..%s]" % (str(v), str(vexp - vspan), str(vexp + vspan)))
+
+class WalletGroupTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.num_nodes = 3
+ self.extra_args = [[], [], ['-avoidpartialspends']]
+
+ def run_test (self):
+ # Mine some coins
+ self.nodes[0].generate(110)
+
+ # Get some addresses from the two nodes
+ addr1 = [self.nodes[1].getnewaddress() for i in range(3)]
+ addr2 = [self.nodes[2].getnewaddress() for i in range(3)]
+ addrs = addr1 + addr2
+
+ # Send 1 + 0.5 coin to each address
+ [self.nodes[0].sendtoaddress(addr, 1.0) for addr in addrs]
+ [self.nodes[0].sendtoaddress(addr, 0.5) for addr in addrs]
+
+ self.nodes[0].generate(1)
+ self.sync_all()
+
+ # For each node, send 0.2 coins back to 0;
+ # - node[1] should pick one 0.5 UTXO and leave the rest
+ # - node[2] should pick one (1.0 + 0.5) UTXO group corresponding to a
+ # given address, and leave the rest
+ txid1 = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 0.2)
+ tx1 = self.nodes[1].getrawtransaction(txid1, True)
+ # txid1 should have 1 input and 2 outputs
+ assert_equal(1, len(tx1["vin"]))
+ assert_equal(2, len(tx1["vout"]))
+ # one output should be 0.2, the other should be ~0.3
+ v = [vout["value"] for vout in tx1["vout"]]
+ v.sort()
+ assert_approx(v[0], 0.2)
+ assert_approx(v[1], 0.3, 0.0001)
+
+ txid2 = self.nodes[2].sendtoaddress(self.nodes[0].getnewaddress(), 0.2)
+ tx2 = self.nodes[2].getrawtransaction(txid2, True)
+ # txid2 should have 2 inputs and 2 outputs
+ assert_equal(2, len(tx2["vin"]))
+ assert_equal(2, len(tx2["vout"]))
+ # one output should be 0.2, the other should be ~1.3
+ v = [vout["value"] for vout in tx2["vout"]]
+ v.sort()
+ assert_approx(v[0], 0.2)
+ assert_approx(v[1], 1.3, 0.0001)
+
+if __name__ == '__main__':
+ WalletGroupTest().main ()
diff --git a/test/functional/wallet_importprunedfunds.py b/test/functional/wallet_importprunedfunds.py
index 9cee9aa49a..256901884b 100755
--- a/test/functional/wallet_importprunedfunds.py
+++ b/test/functional/wallet_importprunedfunds.py
@@ -15,7 +15,6 @@ class ImportPrunedFundsTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 2
- self.extra_args = [['-deprecatedrpc=accounts']] * 2
def run_test(self):
self.log.info("Mining blocks...")
@@ -74,22 +73,20 @@ class ImportPrunedFundsTest(BitcoinTestFramework):
# Import with no affiliated address
assert_raises_rpc_error(-5, "No addresses", self.nodes[1].importprunedfunds, rawtxn1, proof1)
- balance1 = self.nodes[1].getbalance("", 0, True)
+ balance1 = self.nodes[1].getbalance()
assert_equal(balance1, Decimal(0))
# Import with affiliated address with no rescan
- self.nodes[1].importaddress(address2, "add2", False)
+ self.nodes[1].importaddress(address=address2, rescan=False)
self.nodes[1].importprunedfunds(rawtxn2, proof2)
- balance2 = self.nodes[1].getbalance("add2", 0, True)
- assert_equal(balance2, Decimal('0.05'))
+ assert [tx for tx in self.nodes[1].listtransactions(include_watchonly=True) if tx['txid'] == txnid2]
# Import with private key with no rescan
- self.nodes[1].importprivkey(privkey=address3_privkey, label="add3", rescan=False)
+ self.nodes[1].importprivkey(privkey=address3_privkey, rescan=False)
self.nodes[1].importprunedfunds(rawtxn3, proof3)
- balance3 = self.nodes[1].getbalance("add3", 0, False)
+ assert [tx for tx in self.nodes[1].listtransactions() if tx['txid'] == txnid3]
+ balance3 = self.nodes[1].getbalance()
assert_equal(balance3, Decimal('0.025'))
- balance3 = self.nodes[1].getbalance("*", 0, True)
- assert_equal(balance3, Decimal('0.075'))
# Addresses Test - after import
address_info = self.nodes[1].getaddressinfo(address1)
@@ -104,17 +101,13 @@ class ImportPrunedFundsTest(BitcoinTestFramework):
# Remove transactions
assert_raises_rpc_error(-8, "Transaction does not exist in wallet.", self.nodes[1].removeprunedfunds, txnid1)
-
- balance1 = self.nodes[1].getbalance("*", 0, True)
- assert_equal(balance1, Decimal('0.075'))
+ assert not [tx for tx in self.nodes[1].listtransactions(include_watchonly=True) if tx['txid'] == txnid1]
self.nodes[1].removeprunedfunds(txnid2)
- balance2 = self.nodes[1].getbalance("*", 0, True)
- assert_equal(balance2, Decimal('0.025'))
+ assert not [tx for tx in self.nodes[1].listtransactions(include_watchonly=True) if tx['txid'] == txnid2]
self.nodes[1].removeprunedfunds(txnid3)
- balance3 = self.nodes[1].getbalance("*", 0, True)
- assert_equal(balance3, Decimal('0.0'))
+ assert not [tx for tx in self.nodes[1].listtransactions(include_watchonly=True) if tx['txid'] == txnid3]
if __name__ == '__main__':
ImportPrunedFundsTest().main()
diff --git a/test/lint/lint-circular-dependencies.sh b/test/lint/lint-circular-dependencies.sh
new file mode 100755
index 0000000000..b8d105b49b
--- /dev/null
+++ b/test/lint/lint-circular-dependencies.sh
@@ -0,0 +1,84 @@
+#!/usr/bin/env bash
+#
+# Copyright (c) 2018 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#
+# Check for circular dependencies
+
+export LC_ALL=C
+
+EXPECTED_CIRCULAR_DEPENDENCIES=(
+ "chainparamsbase -> util -> chainparamsbase"
+ "checkpoints -> validation -> checkpoints"
+ "index/txindex -> validation -> index/txindex"
+ "policy/fees -> txmempool -> policy/fees"
+ "policy/policy -> validation -> policy/policy"
+ "qt/addresstablemodel -> qt/walletmodel -> qt/addresstablemodel"
+ "qt/bantablemodel -> qt/clientmodel -> qt/bantablemodel"
+ "qt/bitcoingui -> qt/utilitydialog -> qt/bitcoingui"
+ "qt/bitcoingui -> qt/walletframe -> qt/bitcoingui"
+ "qt/bitcoingui -> qt/walletview -> qt/bitcoingui"
+ "qt/clientmodel -> qt/peertablemodel -> qt/clientmodel"
+ "qt/paymentserver -> qt/walletmodel -> qt/paymentserver"
+ "qt/recentrequeststablemodel -> qt/walletmodel -> qt/recentrequeststablemodel"
+ "qt/sendcoinsdialog -> qt/walletmodel -> qt/sendcoinsdialog"
+ "qt/transactiontablemodel -> qt/walletmodel -> qt/transactiontablemodel"
+ "qt/walletmodel -> qt/walletmodeltransaction -> qt/walletmodel"
+ "rpc/rawtransaction -> wallet/rpcwallet -> rpc/rawtransaction"
+ "txmempool -> validation -> txmempool"
+ "validation -> validationinterface -> validation"
+ "wallet/coincontrol -> wallet/wallet -> wallet/coincontrol"
+ "wallet/fees -> wallet/wallet -> wallet/fees"
+ "wallet/rpcwallet -> wallet/wallet -> wallet/rpcwallet"
+ "wallet/wallet -> wallet/walletdb -> wallet/wallet"
+ "policy/fees -> policy/policy -> validation -> policy/fees"
+ "policy/rbf -> txmempool -> validation -> policy/rbf"
+ "qt/addressbookpage -> qt/bitcoingui -> qt/walletview -> qt/addressbookpage"
+ "qt/guiutil -> qt/walletmodel -> qt/optionsmodel -> qt/guiutil"
+ "txmempool -> validation -> validationinterface -> txmempool"
+ "qt/addressbookpage -> qt/bitcoingui -> qt/walletview -> qt/receivecoinsdialog -> qt/addressbookpage"
+ "qt/addressbookpage -> qt/bitcoingui -> qt/walletview -> qt/signverifymessagedialog -> qt/addressbookpage"
+ "qt/guiutil -> qt/walletmodel -> qt/optionsmodel -> qt/intro -> qt/guiutil"
+ "qt/addressbookpage -> qt/bitcoingui -> qt/walletview -> qt/sendcoinsdialog -> qt/sendcoinsentry -> qt/addressbookpage"
+)
+
+EXIT_CODE=0
+
+CIRCULAR_DEPENDENCIES=()
+
+IFS=$'\n'
+for CIRC in $(cd src && ../contrib/devtools/circular-dependencies.py {*,*/*,*/*/*}.{h,cpp} | sed -e 's/^Circular dependency: //'); do
+ CIRCULAR_DEPENDENCIES+=($CIRC)
+ IS_EXPECTED_CIRC=0
+ for EXPECTED_CIRC in "${EXPECTED_CIRCULAR_DEPENDENCIES[@]}"; do
+ if [[ "${CIRC}" == "${EXPECTED_CIRC}" ]]; then
+ IS_EXPECTED_CIRC=1
+ break
+ fi
+ done
+ if [[ ${IS_EXPECTED_CIRC} == 0 ]]; then
+ echo "A new circular dependency in the form of \"${CIRC}\" appears to have been introduced."
+ echo
+ EXIT_CODE=1
+ fi
+done
+
+for EXPECTED_CIRC in "${EXPECTED_CIRCULAR_DEPENDENCIES[@]}"; do
+ IS_PRESENT_EXPECTED_CIRC=0
+ for CIRC in "${CIRCULAR_DEPENDENCIES[@]}"; do
+ if [[ "${CIRC}" == "${EXPECTED_CIRC}" ]]; then
+ IS_PRESENT_EXPECTED_CIRC=1
+ break
+ fi
+ done
+ if [[ ${IS_PRESENT_EXPECTED_CIRC} == 0 ]]; then
+ echo "Good job! The circular dependency \"${EXPECTED_CIRC}\" is no longer present."
+ echo "Please remove it from EXPECTED_CIRCULAR_DEPENDENCIES in $0"
+ echo "to make sure this circular dependency is not accidentally reintroduced."
+ echo
+ EXIT_CODE=1
+ fi
+done
+
+exit ${EXIT_CODE}
diff --git a/test/lint/lint-includes.sh b/test/lint/lint-includes.sh
index 40d28ed3e0..8f7a1fd76b 100755
--- a/test/lint/lint-includes.sh
+++ b/test/lint/lint-includes.sh
@@ -49,8 +49,6 @@ EXPECTED_BOOST_INCLUDES=(
boost/algorithm/string.hpp
boost/algorithm/string/case_conv.hpp
boost/algorithm/string/classification.hpp
- boost/algorithm/string/join.hpp
- boost/algorithm/string/predicate.hpp
boost/algorithm/string/replace.hpp
boost/algorithm/string/split.hpp
boost/bind.hpp
@@ -67,7 +65,6 @@ EXPECTED_BOOST_INCLUDES=(
boost/optional.hpp
boost/preprocessor/cat.hpp
boost/preprocessor/stringize.hpp
- boost/program_options/detail/config_file.hpp
boost/scoped_array.hpp
boost/signals2/connection.hpp
boost/signals2/last_value.hpp