aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.appveyor.yml38
-rw-r--r--.travis.yml20
-rwxr-xr-x.travis/lint_04_install.sh4
-rw-r--r--CONTRIBUTING.md6
-rw-r--r--Makefile.am2
-rw-r--r--build_msvc/common.vcxproj5
-rw-r--r--build_msvc/test_bitcoin/test_bitcoin.vcxproj2
-rw-r--r--configure.ac26
-rwxr-xr-xcontrib/macdeploy/custom_dsstore.py2
-rw-r--r--depends/README.md27
-rw-r--r--depends/packages/freetype.mk2
-rw-r--r--depends/packages/qt.mk1
-rw-r--r--doc/JSON-RPC-interface.md38
-rw-r--r--doc/README.md10
-rw-r--r--doc/REST-interface.md6
-rw-r--r--doc/bitcoin-conf.md37
-rw-r--r--doc/build-freebsd.md20
-rw-r--r--doc/build-openbsd.md4
-rw-r--r--doc/build-osx.md12
-rw-r--r--doc/build-unix.md30
-rw-r--r--doc/build-windows.md20
-rw-r--r--doc/descriptors.md91
-rw-r--r--doc/developer-notes.md6
-rw-r--r--doc/init.md2
-rw-r--r--doc/release-notes-13152.md3
-rw-r--r--doc/release-notes-14296.md5
-rw-r--r--doc/release-notes-14454.md6
-rw-r--r--doc/release-notes-14468.md15
-rw-r--r--doc/release-notes/release-notes-0.17.0.md1105
-rw-r--r--doc/release-process.md2
-rw-r--r--share/qt/Info.plist.in3
-rw-r--r--src/Makefile.am2
-rw-r--r--src/Makefile.qt.include24
-rw-r--r--src/Makefile.qttest.include14
-rw-r--r--src/Makefile.test.include10
-rw-r--r--src/addrman.h53
-rw-r--r--src/arith_uint256.cpp2
-rw-r--r--src/base58.cpp7
-rw-r--r--src/bench/checkblock.cpp4
-rw-r--r--src/bitcoin-cli.cpp5
-rw-r--r--src/bitcoin-tx.cpp2
-rw-r--r--src/bitcoind.cpp4
-rw-r--r--src/dummywallet.cpp10
-rw-r--r--src/fs.cpp105
-rw-r--r--src/fs.h51
-rw-r--r--src/index/base.cpp2
-rw-r--r--src/interfaces/node.cpp14
-rw-r--r--src/interfaces/node.h6
-rw-r--r--src/interfaces/wallet.cpp2
-rw-r--r--src/net.cpp491
-rw-r--r--src/net.h5
-rw-r--r--src/primitives/transaction.h2
-rw-r--r--src/qt/README.md4
-rw-r--r--src/qt/bitcoin.cpp18
-rw-r--r--src/qt/bitcoingui.cpp16
-rw-r--r--src/qt/callback.h30
-rw-r--r--src/qt/clientmodel.cpp5
-rw-r--r--src/qt/clientmodel.h1
-rw-r--r--src/qt/coincontroldialog.cpp5
-rw-r--r--src/qt/forms/debugwindow.ui68
-rw-r--r--src/qt/guiutil.cpp6
-rw-r--r--src/qt/paymentserver.cpp299
-rw-r--r--src/qt/paymentserver.h44
-rw-r--r--src/qt/rpcconsole.cpp5
-rw-r--r--src/qt/sendcoinsdialog.cpp8
-rw-r--r--src/qt/sendcoinsentry.cpp10
-rw-r--r--src/qt/test/addressbooktests.cpp15
-rw-r--r--src/qt/test/compattests.cpp6
-rw-r--r--src/qt/test/test_main.cpp6
-rw-r--r--src/qt/test/util.cpp11
-rw-r--r--src/qt/test/wallettests.cpp18
-rw-r--r--src/qt/transactiondesc.cpp6
-rw-r--r--src/qt/transactionview.cpp6
-rw-r--r--src/qt/utilitydialog.cpp2
-rw-r--r--src/qt/walletmodel.cpp13
-rw-r--r--src/qt/walletmodel.h16
-rw-r--r--src/qt/walletmodeltransaction.cpp6
-rw-r--r--src/qt/walletmodeltransaction.h1
-rw-r--r--src/rpc/blockchain.cpp28
-rw-r--r--src/rpc/client.cpp2
-rw-r--r--src/rpc/mining.cpp8
-rw-r--r--src/rpc/misc.cpp10
-rw-r--r--src/rpc/protocol.cpp10
-rw-r--r--src/rpc/rawtransaction.cpp16
-rw-r--r--src/script/descriptor.cpp122
-rw-r--r--src/script/sign.cpp23
-rw-r--r--src/script/sign.h4
-rw-r--r--src/test/addrman_tests.cpp4
-rw-r--r--src/test/amount_tests.cpp57
-rw-r--r--src/test/descriptor_tests.cpp40
-rw-r--r--src/test/fs_tests.cpp56
-rw-r--r--src/test/miner_tests.cpp4
-rw-r--r--src/test/script_tests.cpp8
-rw-r--r--src/txmempool.cpp2
-rw-r--r--src/uint256.cpp2
-rw-r--r--src/util.cpp36
-rw-r--r--src/util.h16
-rw-r--r--src/utilmoneystr.cpp6
-rw-r--r--src/utilstrencodings.cpp4
-rw-r--r--src/utilstrencodings.h15
-rw-r--r--src/validation.cpp13
-rw-r--r--src/validation.h6
-rw-r--r--src/wallet/db.cpp31
-rw-r--r--src/wallet/db.h7
-rw-r--r--src/wallet/init.cpp7
-rw-r--r--src/wallet/rpcdump.cpp278
-rw-r--r--src/wallet/rpcwallet.cpp219
-rw-r--r--src/wallet/test/init_test_fixture.cpp42
-rw-r--r--src/wallet/test/init_test_fixture.h21
-rw-r--r--src/wallet/test/init_tests.cpp78
-rw-r--r--src/wallet/test/psbt_wallet_tests.cpp2
-rw-r--r--src/wallet/wallet.cpp3
-rw-r--r--src/wallet/wallet.h58
-rw-r--r--src/wallet/walletutil.cpp55
-rw-r--r--src/wallet/walletutil.h8
-rw-r--r--test/README.md29
-rwxr-xr-xtest/functional/feature_config_args.py10
-rwxr-xr-xtest/functional/feature_filelock.py36
-rwxr-xr-xtest/functional/feature_notifications.py47
-rwxr-xr-xtest/functional/feature_nulldummy.py16
-rwxr-xr-xtest/functional/feature_segwit.py150
-rwxr-xr-xtest/functional/feature_uacomment.py2
-rwxr-xr-xtest/functional/interface_bitcoin_cli.py34
-rwxr-xr-xtest/functional/interface_zmq.py40
-rwxr-xr-xtest/functional/p2p_compactblocks.py36
-rwxr-xr-xtest/functional/p2p_leak_tx.py57
-rwxr-xr-xtest/functional/rpc_deprecated.py12
-rwxr-xr-xtest/functional/rpc_getblockstats.py2
-rwxr-xr-xtest/functional/rpc_psbt.py47
-rwxr-xr-xtest/functional/rpc_scantxoutset.py4
-rwxr-xr-xtest/functional/rpc_zmq.py37
-rw-r--r--test/functional/test_framework/address.py3
-rw-r--r--test/functional/test_framework/blocktools.py2
-rwxr-xr-xtest/functional/test_framework/messages.py17
-rwxr-xr-xtest/functional/test_framework/mininode.py39
-rwxr-xr-xtest/functional/test_framework/test_framework.py6
-rwxr-xr-xtest/functional/test_framework/test_node.py23
-rwxr-xr-xtest/functional/test_runner.py25
-rwxr-xr-xtest/functional/wallet_basic.py5
-rwxr-xr-xtest/functional/wallet_bumpfee.py19
-rwxr-xr-xtest/functional/wallet_dump.py83
-rwxr-xr-xtest/functional/wallet_importmulti.py174
-rwxr-xr-xtest/functional/wallet_keypool.py9
-rwxr-xr-xtest/functional/wallet_labels.py9
-rwxr-xr-xtest/functional/wallet_listreceivedby.py4
-rwxr-xr-xtest/functional/wallet_multiwallet.py40
-rwxr-xr-xtest/lint/lint-locale-dependence.sh4
-rwxr-xr-xtest/lint/lint-python.sh2
-rw-r--r--test/lint/lint-spelling.ignore-words.txt1
-rwxr-xr-xtest/lint/lint-spelling.sh3
150 files changed, 3864 insertions, 1454 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
index d7ce2224e9..8dad9c4f9d 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -6,12 +6,11 @@ platform: x64
environment:
APPVEYOR_SAVE_CACHE_ON_ERROR: true
CLCACHE_SERVER: 1
- PACKAGES: boost-filesystem boost-signals2 boost-interprocess boost-test libevent openssl zeromq berkeleydb secp256k1 leveldb
+ PACKAGES: boost-filesystem boost-signals2 boost-test libevent openssl zeromq berkeleydb secp256k1 leveldb
PYTHONIOENCODING: utf-8
cache:
- C:\tools\vcpkg\installed
- C:\Users\appveyor\clcache
-- build_msvc\cache
init:
- cmd: set PATH=C:\Python36-x64;C:\Python36-x64\Scripts;%PATH%
install:
@@ -23,37 +22,44 @@ install:
$env:ALL_PACKAGES += $packages[$i] + ":" + $env:PLATFORM + "-windows-static "
}
- cmd: git -C C:\Tools\vcpkg pull # This is a temporary fix, can be removed after appveyor update its image to include Microsoft/vcpkg#4046
+- cmd: C:\Tools\vcpkg\bootstrap-vcpkg.bat
+- cmd: vcpkg remove --recurse --outdated
- cmd: vcpkg install %ALL_PACKAGES%
-- cmd: vcpkg upgrade --no-dry-run
- cmd: del /s /q C:\Tools\vcpkg\installed\%PLATFORM%-windows-static\debug # Remove unused debug library
before_build:
-- cmd: if not exist build_msvc\cache\ (del build_msvc\cache & mkdir build_msvc\cache)
-- cmd: if not exist build_msvc\%PLATFORM%\%CONFIGURATION%\ (mkdir build_msvc\%PLATFORM%\%CONFIGURATION%)
-- cmd: if exist build_msvc\cache\*.iobj (move build_msvc\cache\* build_msvc\%PLATFORM%\%CONFIGURATION%\)
-- cmd: clcache -M 2147483648
+- ps: clcache -M 536870912
- cmd: python build_msvc\msvc-autogen.py
- ps: $files = (Get-ChildItem -Recurse | where {$_.extension -eq ".vcxproj"}).FullName
-- ps: for ($i = 0; $i -lt $files.length; $i++) {
- (Get-Content $files[$i]).Replace("</RuntimeLibrary>", "</RuntimeLibrary><DebugInformationFormat>None</DebugInformationFormat>").Replace("NDEBUG;", "") | Set-Content $files[$i]
+- ps: for (${i} = 0; ${i} -lt ${files}.length; ${i}++) {
+ ${content} = (Get-Content ${files}[${i}]);
+ ${content} = ${content}.Replace("</RuntimeLibrary>", "</RuntimeLibrary><DebugInformationFormat>None</DebugInformationFormat>");
+ ${content} = ${content}.Replace("<WholeProgramOptimization>true", "<WholeProgramOptimization>false");
+ ${content} = ${content}.Replace("NDEBUG;", "");
+ Set-Content ${files}[${i}] ${content};
}
- ps: Start-Process clcache-server
+- ps: fsutil behavior set disablelastaccess 0 # Enable Access time feature on Windows (for clcache)
build_script:
- cmd: msbuild /p:TrackFileAccess=false /p:CLToolExe=clcache.exe build_msvc\bitcoin.sln /m /v:q /nowarn:C4244;C4267;C4715 /nologo
after_build:
-- cmd: move build_msvc\%PLATFORM%\%CONFIGURATION%\*.iobj build_msvc\cache\
-- cmd: move build_msvc\%PLATFORM%\%CONFIGURATION%\*.ipdb build_msvc\cache\
-- cmd: del C:\Users\appveyor\clcache\stats.txt
+- ps: fsutil behavior set disablelastaccess 0 # Disable Access time feature on Windows (better performance)
+- ps: clcache -z
before_test:
- ps: ${conf_ini} = (Get-Content([IO.Path]::Combine(${env:APPVEYOR_BUILD_FOLDER}, "test", "config.ini.in")))
-- ps: ${conf_ini} = $conf_ini.Replace("@abs_top_srcdir@", ${env:APPVEYOR_BUILD_FOLDER}).Replace("@abs_top_builddir@", ${env:APPVEYOR_BUILD_FOLDER}).Replace("@EXEEXT@", ".exe")
-- ps: ${conf_ini} = $conf_ini.Replace("@ENABLE_WALLET_TRUE@", "").Replace("@BUILD_BITCOIN_UTILS_TRUE@", "").Replace("@BUILD_BITCOIND_TRUE@", "").Replace("@ENABLE_ZMQ_TRUE@", "")
+- ps: ${conf_ini} = ${conf_ini}.Replace("@abs_top_srcdir@", ${env:APPVEYOR_BUILD_FOLDER})
+- ps: ${conf_ini} = ${conf_ini}.Replace("@abs_top_builddir@", ${env:APPVEYOR_BUILD_FOLDER})
+- ps: ${conf_ini} = ${conf_ini}.Replace("@EXEEXT@", ".exe")
+- ps: ${conf_ini} = ${conf_ini}.Replace("@ENABLE_WALLET_TRUE@", "")
+- ps: ${conf_ini} = ${conf_ini}.Replace("@BUILD_BITCOIN_CLI_TRUE@", "")
+- ps: ${conf_ini} = ${conf_ini}.Replace("@BUILD_BITCOIND_TRUE@", "")
+- ps: ${conf_ini} = ${conf_ini}.Replace("@ENABLE_ZMQ_TRUE@", "")
- ps: ${utf8} = New-Object System.Text.UTF8Encoding ${false}
-- ps: '[IO.File]::WriteAllLines([IO.Path]::Combine(${env:APPVEYOR_BUILD_FOLDER}, "test", "config.ini"), $conf_ini, ${utf8})'
+- ps: '[IO.File]::WriteAllLines([IO.Path]::Combine(${env:APPVEYOR_BUILD_FOLDER}, "test", "config.ini"), ${conf_ini}, ${utf8})'
- ps: move "build_msvc\${env:PLATFORM}\${env:CONFIGURATION}\*.exe" src
test_script:
- cmd: src\test_bitcoin.exe
- ps: src\bench_bitcoin.exe -evals=1 -scaling=0
- ps: python test\util\bitcoin-util-test.py
- cmd: python test\util\rpcauth-test.py
-- cmd: python test\functional\test_runner.py --force --quiet --combinedlogslen=4000 --exclude wallet_multiwallet
+- cmd: python test\functional\test_runner.py --force --quiet --combinedlogslen=4000
deploy: off
diff --git a/.travis.yml b/.travis.yml
index 8819d38914..c1d264e25f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -34,7 +34,7 @@ install:
before_script:
- set -o errexit; source .travis/test_05_before_script.sh
script:
- - set -o errexit; source .travis/test_06_script.sh
+ - if [ $SECONDS -gt 1200 ]; then set +o errexit; echo "Travis early exit to cache current state"; false; else set -o errexit; source .travis/test_06_script.sh; fi
after_script:
- echo $TRAVIS_COMMIT_RANGE
- echo $TRAVIS_COMMIT_LOG
@@ -57,8 +57,7 @@ jobs:
- stage: test
env: >-
HOST=arm-linux-gnueabihf
- PACKAGES="g++-arm-linux-gnueabihf"
- DEP_OPTS="NO_QT=1"
+ PACKAGES="python3 g++-arm-linux-gnueabihf"
RUN_UNIT_TESTS=false
RUN_FUNCTIONAL_TESTS=false
GOAL="install"
@@ -70,24 +69,21 @@ jobs:
env: >-
HOST=i686-w64-mingw32
DPKG_ADD_ARCH="i386"
- DEP_OPTS="NO_QT=1"
PACKAGES="python3 nsis g++-mingw-w64-i686 wine-binfmt wine32"
- GOAL="install"
- BITCOIN_CONFIG="--enable-reduce-exports"
+ GOAL="deploy"
+ BITCOIN_CONFIG="--enable-reduce-exports --disable-gui-tests"
# Win64
- stage: test
env: >-
HOST=x86_64-w64-mingw32
- DEP_OPTS="NO_QT=1"
PACKAGES="python3 nsis g++-mingw-w64-x86-64 wine-binfmt wine64"
- GOAL="install"
- BITCOIN_CONFIG="--enable-reduce-exports"
+ GOAL="deploy"
+ BITCOIN_CONFIG="--enable-reduce-exports --disable-gui-tests"
# 32-bit + dash
- stage: test
env: >-
HOST=i686-pc-linux-gnu
PACKAGES="g++-multilib python3-zmq"
- DEP_OPTS="NO_QT=1"
GOAL="install"
BITCOIN_CONFIG="--enable-zmq --enable-glibc-back-compat --enable-reduce-exports LDFLAGS=-static-libstdc++"
CONFIG_SHELL="/bin/dash"
@@ -121,7 +117,7 @@ jobs:
- stage: test
env: >-
HOST=x86_64-unknown-linux-gnu
- PACKAGES="python3"
+ PACKAGES="python3-zmq"
DEP_OPTS="NO_WALLET=1"
GOAL="install"
BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports"
@@ -133,5 +129,5 @@ jobs:
OSX_SDK=10.11
RUN_UNIT_TESTS=false
RUN_FUNCTIONAL_TESTS=false
- GOAL="all deploy"
+ GOAL="deploy"
BITCOIN_CONFIG="--enable-gui --enable-reduce-exports --enable-werror"
diff --git a/.travis/lint_04_install.sh b/.travis/lint_04_install.sh
index 3a6ff73d79..8c3a9124b9 100755
--- a/.travis/lint_04_install.sh
+++ b/.travis/lint_04_install.sh
@@ -6,5 +6,5 @@
export LC_ALL=C
-travis_retry pip install codespell
-travis_retry pip install flake8
+travis_retry pip install codespell==1.13.0
+travis_retry pip install flake8==3.5.0
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 3d5dc3221b..08b2fa609e 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -67,6 +67,8 @@ If a particular commit references another issue, please add the reference. For
example: `refs #1234` or `fixes #4321`. Using the `fixes` or `closes` keywords
will cause the corresponding issue to be closed when the pull request is merged.
+Commit messages should never contain any `@` mentions.
+
Please refer to the [Git manual](https://git-scm.com/doc) for more information
about Git.
@@ -135,6 +137,10 @@ before it will be merged. The basic squashing workflow is shown below.
# Save and quit.
git push -f # (force push to GitHub)
+Please update the resulting commit message if needed, it should read as a
+coherent message. In most cases this means that you should not just list the
+interim commits.
+
If you have problems with squashing (or other workflows with `git`), you can
alternatively enable "Allow edits from maintainers" in the right GitHub
sidebar and ask for help in the pull request.
diff --git a/Makefile.am b/Makefile.am
index eec9f72215..7eb4ea2f52 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -294,5 +294,5 @@ clean-docs:
clean-local: clean-docs
rm -rf coverage_percent.txt test_bitcoin.coverage/ total.coverage/ test/tmp/ cache/ $(OSX_APP)
- rm -rf test/functional/__pycache__ test/functional/test_framework/__pycache__ test/cache
+ rm -rf test/functional/__pycache__ test/functional/test_framework/__pycache__ test/cache share/rpcauth/__pycache__
diff --git a/build_msvc/common.vcxproj b/build_msvc/common.vcxproj
index 3a53f3bad3..5c87026efe 100644
--- a/build_msvc/common.vcxproj
+++ b/build_msvc/common.vcxproj
@@ -12,4 +12,9 @@
Outputs="$(MSBuildThisFileDirectory)..\src\config\bitcoin-config.h">
<Copy SourceFiles="$(MSBuildThisFileDirectory)bitcoin_config.h" DestinationFiles="$(MSBuildThisFileDirectory)..\src\config\bitcoin-config.h" />
</Target>
+ <ItemDefinitionGroup>
+ <ClCompile>
+ <AdditionalOptions>/utf-8 %(AdditionalOptions)</AdditionalOptions>
+ </ClCompile>
+ </ItemDefinitionGroup>
</Project> \ No newline at end of file
diff --git a/build_msvc/test_bitcoin/test_bitcoin.vcxproj b/build_msvc/test_bitcoin/test_bitcoin.vcxproj
index 444a2ed725..2316e473aa 100644
--- a/build_msvc/test_bitcoin/test_bitcoin.vcxproj
+++ b/build_msvc/test_bitcoin/test_bitcoin.vcxproj
@@ -24,7 +24,7 @@
<ClCompile Include="..\..\src\wallet\test\*_tests.cpp" />
<ClCompile Include="..\..\src\test\test_bitcoin.cpp" />
<ClCompile Include="..\..\src\test\test_bitcoin_main.cpp" />
- <ClCompile Include="..\..\src\wallet\test\wallet_test_fixture.cpp" />
+ <ClCompile Include="..\..\src\wallet\test\*_fixture.cpp" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\libbitcoinconsensus\libbitcoinconsensus.vcxproj">
diff --git a/configure.ac b/configure.ac
index 72bd785e2e..7141a9a03a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -209,6 +209,11 @@ AC_ARG_ENABLE([zmq],
[disable ZMQ notifications])],
[use_zmq=$enableval],
[use_zmq=yes])
+AC_ARG_ENABLE([bip70],
+ [AS_HELP_STRING([--disable-bip70],
+ [disable BIP70 (payment protocol) support in GUI (enabled by default)])],
+ [enable_bip70=$enableval],
+ [enable_bip70=yes])
AC_ARG_WITH([protoc-bindir],[AS_HELP_STRING([--with-protoc-bindir=BIN_DIR],[specify protoc bin path])], [protoc_bin_path=$withval], [])
@@ -1082,7 +1087,9 @@ if test x$use_pkgconfig = xyes; then
[
PKG_CHECK_MODULES([SSL], [libssl],, [AC_MSG_ERROR(openssl not found.)])
PKG_CHECK_MODULES([CRYPTO], [libcrypto],,[AC_MSG_ERROR(libcrypto not found.)])
- BITCOIN_QT_CHECK([PKG_CHECK_MODULES([PROTOBUF], [protobuf], [have_protobuf=yes], [BITCOIN_QT_FAIL(libprotobuf not found)])])
+ if test x$enable_bip70 != xno; then
+ BITCOIN_QT_CHECK([PKG_CHECK_MODULES([PROTOBUF], [protobuf], [have_protobuf=yes], [BITCOIN_QT_FAIL(libprotobuf not found)])])
+ fi
if test x$use_qr != xno; then
BITCOIN_QT_CHECK([PKG_CHECK_MODULES([QR], [libqrencode], [have_qrencode=yes], [have_qrencode=no])])
fi
@@ -1142,7 +1149,9 @@ else
esac
fi
- BITCOIN_QT_CHECK(AC_CHECK_LIB([protobuf] ,[main],[PROTOBUF_LIBS=-lprotobuf], BITCOIN_QT_FAIL(libprotobuf not found)))
+ if test x$enable_bip70 != xno; then
+ BITCOIN_QT_CHECK(AC_CHECK_LIB([protobuf] ,[main],[PROTOBUF_LIBS=-lprotobuf], BITCOIN_QT_FAIL(libprotobuf not found)))
+ fi
if test x$use_qr != xno; then
BITCOIN_QT_CHECK([AC_CHECK_LIB([qrencode], [main],[QR_LIBS=-lqrencode], [have_qrencode=no])])
BITCOIN_QT_CHECK([AC_CHECK_HEADER([qrencode.h],, have_qrencode=no)])
@@ -1220,7 +1229,9 @@ AM_CONDITIONAL([EMBEDDED_UNIVALUE],[test x$need_bundled_univalue = xyes])
AC_SUBST(UNIVALUE_CFLAGS)
AC_SUBST(UNIVALUE_LIBS)
+if test x$enable_bip70 != xno; then
BITCOIN_QT_PATH_PROGS([PROTOC], [protoc],$protoc_bin_path)
+fi
AC_MSG_CHECKING([whether to build bitcoind])
AM_CONDITIONAL([BUILD_BITCOIND], [test x$build_bitcoind = xyes])
@@ -1338,6 +1349,15 @@ if test x$bitcoin_enable_qt != xno; then
else
AC_MSG_RESULT([no])
fi
+
+ AC_MSG_CHECKING([whether to build BIP70 support])
+ if test x$enable_bip70 != xno; then
+ AC_DEFINE([ENABLE_BIP70],[1],[Define if BIP70 support should be compiled in])
+ enable_bip70=yes
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ fi
fi
AM_CONDITIONAL([ENABLE_ZMQ], [test "x$use_zmq" = "xyes"])
@@ -1369,6 +1389,7 @@ AM_CONDITIONAL([ENABLE_WALLET],[test x$enable_wallet = xyes])
AM_CONDITIONAL([ENABLE_TESTS],[test x$BUILD_TEST = xyes])
AM_CONDITIONAL([ENABLE_QT],[test x$bitcoin_enable_qt = xyes])
AM_CONDITIONAL([ENABLE_QT_TESTS],[test x$BUILD_TEST_QT = xyes])
+AM_CONDITIONAL([ENABLE_BIP70],[test x$enable_bip70 = xyes])
AM_CONDITIONAL([ENABLE_BENCH],[test x$use_bench = xyes])
AM_CONDITIONAL([USE_QRCODE], [test x$use_qr = xyes])
AM_CONDITIONAL([USE_LCOV],[test x$use_lcov = xyes])
@@ -1503,6 +1524,7 @@ echo "Options used to compile and link:"
echo " with wallet = $enable_wallet"
echo " with gui / qt = $bitcoin_enable_qt"
if test x$bitcoin_enable_qt != xno; then
+ echo " with bip70 = $enable_bip70"
echo " with qr = $use_qr"
fi
echo " with zmq = $use_zmq"
diff --git a/contrib/macdeploy/custom_dsstore.py b/contrib/macdeploy/custom_dsstore.py
index 6fa134972a..c29f83a91e 100755
--- a/contrib/macdeploy/custom_dsstore.py
+++ b/contrib/macdeploy/custom_dsstore.py
@@ -13,7 +13,7 @@ package_name_ns = sys.argv[2]
ds = DSStore.open(output_file, 'w+')
ds['.']['bwsp'] = {
'ShowStatusBar': False,
- 'WindowBounds': b'{{300, 280}, {500, 343}}',
+ 'WindowBounds': '{{300, 280}, {500, 343}}',
'ContainerShowSidebar': False,
'SidebarWidth': 0,
'ShowTabView': False,
diff --git a/depends/README.md b/depends/README.md
index 73bfc8b726..693bc36197 100644
--- a/depends/README.md
+++ b/depends/README.md
@@ -30,29 +30,38 @@ Common `host-platform-triplets` for cross compilation are:
No other options are needed, the paths are automatically configured.
-Install the required dependencies: Ubuntu & Debian
---------------------------------------------------
+### Install the required dependencies: Ubuntu & Debian
-For macOS cross compilation:
+#### For macOS cross compilation
sudo apt-get install curl librsvg2-bin libtiff-tools bsdmainutils cmake imagemagick libcap-dev libz-dev libbz2-dev python-setuptools
-For Win32/Win64 cross compilation:
+#### For Win32/Win64 cross compilation
- see [build-windows.md](../doc/build-windows.md#cross-compilation-for-ubuntu-and-windows-subsystem-for-linux)
-For linux (including i386, ARM) cross compilation:
+#### For linux (including i386, ARM) cross compilation
- sudo apt-get install curl g++-aarch64-linux-gnu g++-4.8-aarch64-linux-gnu gcc-4.8-aarch64-linux-gnu binutils-aarch64-linux-gnu g++-arm-linux-gnueabihf g++-4.8-arm-linux-gnueabihf gcc-4.8-arm-linux-gnueabihf binutils-arm-linux-gnueabihf g++-4.8-multilib gcc-4.8-multilib binutils-gold bsdmainutils
+Common linux dependencies:
+
+ sudo apt-get install make automake cmake curl g++-multilib libtool binutils-gold bsdmainutils pkg-config python3
+
+For linux ARM cross compilation:
+
+ sudo apt-get install g++-arm-linux-gnueabihf binutils-arm-linux-gnueabihf
+
+For linux AARCH64 cross compilation:
+
+ sudo apt-get install g++-aarch64-linux-gnu binutils-aarch64-linux-gnu
For linux RISC-V 64-bit cross compilation (there are no packages for 32-bit):
- sudo apt-get install curl g++-riscv64-linux-gnu binutils-riscv64-linux-gnu
+ sudo apt-get install g++-riscv64-linux-gnu binutils-riscv64-linux-gnu
RISC-V known issue: gcc-7.3.0 and gcc-7.3.1 result in a broken `test_bitcoin` executable (see https://github.com/bitcoin/bitcoin/pull/13543),
this is apparently fixed in gcc-8.1.0.
-Dependency Options:
+### Dependency Options
The following can be set when running make: make FOO=bar
SOURCES_PATH: downloaded sources will be placed here
@@ -70,7 +79,7 @@ The following can be set when running make: make FOO=bar
If some packages are not built, for example `make NO_WALLET=1`, the appropriate
options will be passed to bitcoin's configure. In this case, `--disable-wallet`.
-Additional targets:
+### Additional targets
download: run 'make download' to fetch all sources without building them
download-osx: run 'make download-osx' to fetch all sources needed for macOS builds
diff --git a/depends/packages/freetype.mk b/depends/packages/freetype.mk
index 76b025c463..41e02e2030 100644
--- a/depends/packages/freetype.mk
+++ b/depends/packages/freetype.mk
@@ -5,7 +5,7 @@ $(package)_file_name=$(package)-$($(package)_version).tar.bz2
$(package)_sha256_hash=3a3bb2c4e15ffb433f2032f50a5b5a92558206822e22bfe8cbe339af4aa82f88
define $(package)_set_vars
- $(package)_config_opts=--without-zlib --without-png --disable-static
+ $(package)_config_opts=--without-zlib --without-png --without-harfbuzz --without-bzip2 --disable-static
$(package)_config_opts_linux=--with-pic
endef
diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk
index d15f147cd7..dc1d17cd57 100644
--- a/depends/packages/qt.mk
+++ b/depends/packages/qt.mk
@@ -64,6 +64,7 @@ $(package)_config_opts += -prefix $(host_prefix)
$(package)_config_opts += -qt-libpng
$(package)_config_opts += -qt-libjpeg
$(package)_config_opts += -qt-pcre
+$(package)_config_opts += -qt-harfbuzz
$(package)_config_opts += -system-zlib
$(package)_config_opts += -static
$(package)_config_opts += -silent
diff --git a/doc/JSON-RPC-interface.md b/doc/JSON-RPC-interface.md
new file mode 100644
index 0000000000..59df541567
--- /dev/null
+++ b/doc/JSON-RPC-interface.md
@@ -0,0 +1,38 @@
+# JSON-RPC Interface
+
+The headless daemon `bitcoind` has the JSON-RPC API enabled by default, the GUI
+`bitcoin-qt` has it disabled by default. This can be changed with the `-server`
+option. In the GUI it is possible to execute RPC methods in the Debug Console
+Dialog.
+
+## RPC consistency guarantees
+
+State that can be queried via RPCs is guaranteed to be at least up-to-date with
+the chain state immediately prior to the call's execution. However, the state
+returned by RPCs that reflect the mempool may not be up-to-date with the
+current mempool state.
+
+### Transaction Pool
+
+The mempool state returned via an RPC is consistent with itself and with the
+chain state at the time of the call. Thus, the mempool state only encompasses
+transactions that are considered mine-able by the node at the time of the RPC.
+
+The mempool state returned via an RPC reflects all effects of mempool and chain
+state related RPCs that returned prior to this call.
+
+### Wallet
+
+The wallet state returned via an RPC is consistent with itself and with the
+chain state at the time of the call.
+
+Wallet RPCs will return the latest chain state consistent with prior non-wallet
+RPCs. The effects of all blocks (and transactions in blocks) at the time of the
+call is reflected in the state of all wallet transactions. For example, if a
+block contains transactions that conflicted with mempool transactions, the
+wallet would reflect the removal of these mempool transactions in the state.
+
+However, the wallet may not be up-to-date with the current state of the mempool
+or the state of the mempool by an RPC that returned before this RPC. For
+example, a wallet transaction that was BIP-125-replaced in the mempool prior to
+this RPC may not yet be reflected as such in this RPC response.
diff --git a/doc/README.md b/doc/README.md
index b3f875c4a4..344b1be5c4 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -3,7 +3,7 @@ Bitcoin Core
Setup
---------------------
-Bitcoin Core is the original Bitcoin client and it builds the backbone of the network. It downloads and, by default, stores the entire history of Bitcoin transactions (which is currently more than 100 GBs); depending on the speed of your computer and network connection, the synchronization process can take anywhere from a few hours to a day or more.
+Bitcoin Core is the original Bitcoin client and it builds the backbone of the network. It downloads and, by default, stores the entire history of Bitcoin transactions, which requires a few hundred gigabytes of disk space. Depending on the speed of your computer and network connection, the synchronization process can take anywhere from a few hours to a day or more.
To download Bitcoin Core, visit [bitcoincore.org](https://bitcoincore.org/en/releases/).
@@ -30,7 +30,7 @@ Drag Bitcoin Core to your applications folder, and then run Bitcoin Core.
* See the documentation at the [Bitcoin Wiki](https://en.bitcoin.it/wiki/Main_Page)
for help and more information.
-* Ask for help on [#bitcoin](http://webchat.freenode.net?channels=bitcoin) on Freenode. If you don't have an IRC client use [webchat here](http://webchat.freenode.net?channels=bitcoin).
+* Ask for help on [#bitcoin](http://webchat.freenode.net?channels=bitcoin) on Freenode. If you don't have an IRC client, use [webchat here](http://webchat.freenode.net?channels=bitcoin).
* Ask for help on the [BitcoinTalk](https://bitcointalk.org/) forums, in the [Technical Support board](https://bitcointalk.org/index.php?board=4.0).
Building
@@ -56,6 +56,7 @@ The Bitcoin repo's [root README](/README.md) contains relevant information on th
- [Translation Process](translation_process.md)
- [Translation Strings Policy](translation_strings_policy.md)
- [Travis CI](travis-ci.md)
+- [JSON-RPC Interface](JSON-RPC-interface.md)
- [Unauthenticated REST Interface](REST-interface.md)
- [Shared Libraries](shared-libraries.md)
- [BIPS](bips.md)
@@ -64,11 +65,12 @@ The Bitcoin repo's [root README](/README.md) contains relevant information on th
### Resources
* Discuss on the [BitcoinTalk](https://bitcointalk.org/) forums, in the [Development & Technical Discussion board](https://bitcointalk.org/index.php?board=6.0).
-* Discuss project-specific development on #bitcoin-core-dev on Freenode. If you don't have an IRC client use [webchat here](http://webchat.freenode.net/?channels=bitcoin-core-dev).
-* Discuss general Bitcoin development on #bitcoin-dev on Freenode. If you don't have an IRC client use [webchat here](http://webchat.freenode.net/?channels=bitcoin-dev).
+* Discuss project-specific development on #bitcoin-core-dev on Freenode. If you don't have an IRC client, use [webchat here](http://webchat.freenode.net/?channels=bitcoin-core-dev).
+* Discuss general Bitcoin development on #bitcoin-dev on Freenode. If you don't have an IRC client, use [webchat here](http://webchat.freenode.net/?channels=bitcoin-dev).
### Miscellaneous
- [Assets Attribution](assets-attribution.md)
+- [bitcoin.conf Configuration File](bitcoin-conf.md)
- [Files](files.md)
- [Fuzz-testing](fuzzing.md)
- [Reduce Traffic](reduce-traffic.md)
diff --git a/doc/REST-interface.md b/doc/REST-interface.md
index 7010edfcd3..44df698382 100644
--- a/doc/REST-interface.md
+++ b/doc/REST-interface.md
@@ -6,6 +6,12 @@ The REST API can be enabled with the `-rest` option.
The interface runs on the same port as the JSON-RPC interface, by default port 8332 for mainnet, port 18332 for testnet,
and port 18443 for regtest.
+REST Interface consistency guarantees
+-------------------------------------
+
+The [same guarantees as for the RPC Interface](/doc/JSON-RPC-interface.md#rpc-consistency-guarantees)
+apply.
+
Supported API
-------------
diff --git a/doc/bitcoin-conf.md b/doc/bitcoin-conf.md
new file mode 100644
index 0000000000..88ecb8fe65
--- /dev/null
+++ b/doc/bitcoin-conf.md
@@ -0,0 +1,37 @@
+# `bitcoin.conf` Configuration File
+
+The configuration file is used by `bitcoind`, `bitcoin-qt` and `bitcoin-cli`.
+
+All command-line options (except for `-?`, `-help`, `-version` and `-conf`) may be specified in a configuration file, and all configuration file options (except for `includeconf`) may also be specified on the command line. Command-line options override values set in the configuration file and configuration file options override values set in the GUI.
+
+## Configuration File Format
+
+The configuration file is a plain text file and consists of `option=value` entries, one per line. Leading and trailing whitespaces are removed.
+
+In contrast to the command-line usage:
+- an option must be specified without leading `-`;
+- a value of the given option is mandatory; e.g., `testnet=1` (for chain selection options), `noconnect=1` (for negated options).
+
+### Blank lines
+
+Blank lines are allowed and ignored by the parser.
+
+### Comments
+
+A comment starts with a number sign (`#`) and extends to the end of the line. All comments are ignored by the parser.
+
+Comments may appear in two ways:
+- on their own on an otherwise empty line (_preferable_);
+- after an `option=value` entry.
+
+### Network specific options
+
+Network specific options can be:
+- placed into sections with headers `[main]` (not `[mainnet]`), `[test]` (not `[testnet]`) or `[regtest]`;
+- prefixed with a chain name; e.g., `regtest.maxmempool=100`.
+
+## Configuration File Path
+
+The configuration file is not automatically created; you can create it using your favorite text editor. By default, the configuration file name is `bitcoin.conf` and it is located in the Bitcoin data directory, but both the Bitcoin data directory and the configuration file path may be changed using the `-datadir` and `-conf` command-line options.
+
+The `includeconf=<file>` option in the `bitcoin.conf` file can be used to include additional configuration files.
diff --git a/doc/build-freebsd.md b/doc/build-freebsd.md
index 48746ce0c2..70f5dfc882 100644
--- a/doc/build-freebsd.md
+++ b/doc/build-freebsd.md
@@ -14,6 +14,12 @@ You will need the following dependencies, which can be installed as root via pkg
pkg install autoconf automake boost-libs git gmake libevent libtool openssl pkgconf
```
+In order to run the test suite (recommended), you will need to have Python 3 installed:
+
+```
+pkg install python3
+```
+
For the wallet (optional):
```
./contrib/install_db4.sh `pwd`
@@ -29,17 +35,29 @@ git clone https://github.com/bitcoin/bitcoin
## Building Bitcoin Core
-**Important**: Use `gmake` (the non-GNU `make` will exit with an error).
+**Important**: Use `gmake` (the non-GNU `make` will exit with an error):
```
./autogen.sh
./configure # to build with wallet OR
./configure --disable-wallet # to build without wallet
+```
+
+followed by either:
+```
gmake
```
+to build without testing, or
+
+```
+gmake check
+```
+
+to also run the test suite (recommended, if Python 3 is installed).
+
*Note on debugging*: The version of `gdb` installed by default is [ancient and considered harmful](https://wiki.freebsd.org/GdbRetirement).
It is not suitable for debugging a multi-threaded C++ program, not even for getting backtraces. Please install the package `gdb` and
use the versioned gdb command (e.g. `gdb7111`).
diff --git a/doc/build-openbsd.md b/doc/build-openbsd.md
index 63288acf16..48ac0525b9 100644
--- a/doc/build-openbsd.md
+++ b/doc/build-openbsd.md
@@ -36,7 +36,7 @@ BerkeleyDB is only necessary for the wallet functionality. To skip this, pass
It is recommended to use Berkeley DB 4.8. You cannot use the BerkeleyDB library
from ports, for the same reason as boost above (g++/libstd++ incompatibility).
If you have to build it yourself, you can use [the installation script included
-in contrib/](/contrib/install_db4.sh) like so
+in contrib/](/contrib/install_db4.sh) like so:
```shell
./contrib/install_db4.sh `pwd` CC=cc CXX=c++
@@ -94,7 +94,7 @@ The standard ulimit restrictions in OpenBSD are very strict:
data(kbytes) 1572864
-This, unfortunately, in some cases not enough to compile some `.cpp` files in the project,
+This is, unfortunately, in some cases not enough to compile some `.cpp` files in the project,
(see issue [#6658](https://github.com/bitcoin/bitcoin/issues/6658)).
If your user is in the `staff` group the limit can be raised with:
diff --git a/doc/build-osx.md b/doc/build-osx.md
index 1fa01d0d6e..7be21f7921 100644
--- a/doc/build-osx.md
+++ b/doc/build-osx.md
@@ -20,7 +20,7 @@ Dependencies
See [dependencies.md](dependencies.md) for a complete overview.
-If you want to build the disk image with `make deploy` (.dmg / optional), you need RSVG
+If you want to build the disk image with `make deploy` (.dmg / optional), you need RSVG:
brew install librsvg
@@ -28,7 +28,7 @@ Berkeley DB
-----------
It is recommended to use Berkeley DB 4.8. If you have to build it yourself,
you can use [the installation script included in contrib/](/contrib/install_db4.sh)
-like so
+like so:
```shell
./contrib/install_db4.sh .
@@ -36,12 +36,12 @@ like so
from the root of the repository.
-**Note**: You only need Berkeley DB if the wallet is enabled (see the section *Disable-Wallet mode* below).
+**Note**: You only need Berkeley DB if the wallet is enabled (see [*Disable-wallet mode*](/doc/build-osx.md#disable-wallet-mode)).
Build Bitcoin Core
------------------------
-1. Clone the Bitcoin Core source code and cd into `bitcoin`
+1. Clone the Bitcoin Core source code:
git clone https://github.com/bitcoin/bitcoin
cd bitcoin
@@ -80,13 +80,13 @@ Running
Bitcoin Core is now available at `./src/bitcoind`
-Before running, it's recommended that you create an RPC configuration file.
+Before running, it's recommended that you create an RPC configuration file:
echo -e "rpcuser=bitcoinrpc\nrpcpassword=$(xxd -l 16 -p /dev/urandom)" > "/Users/${USER}/Library/Application Support/Bitcoin/bitcoin.conf"
chmod 600 "/Users/${USER}/Library/Application Support/Bitcoin/bitcoin.conf"
-The first time you run bitcoind, it will start downloading the blockchain. This process could take several hours.
+The first time you run bitcoind, it will start downloading the blockchain. This process could take many hours, or even days on slower than average systems.
You can monitor the download process by looking at the debug.log file:
diff --git a/doc/build-unix.md b/doc/build-unix.md
index 3f8848abf2..522e3069ce 100644
--- a/doc/build-unix.md
+++ b/doc/build-unix.md
@@ -6,8 +6,8 @@ Some notes on how to build Bitcoin Core in Unix.
Note
---------------------
-Always use absolute paths to configure and compile Bitcoin Core and the dependencies,
-for example, when specifying the path of the dependency:
+Always use absolute paths to configure and compile Bitcoin Core and the dependencies.
+For example, when specifying the path of the dependency:
../dist/configure --enable-cxx --disable-shared --with-pic --prefix=$BDB_PREFIX
@@ -24,7 +24,7 @@ make
make install # optional
```
-This will build bitcoin-qt as well if the dependencies are met.
+This will build bitcoin-qt as well, if the dependencies are met.
Dependencies
---------------------
@@ -70,7 +70,11 @@ 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-test-dev libboost-thread-dev
+ sudo apt-get install build-essential libtool autotools-dev automake pkg-config bsdmainutils python3
+
+Now, you can either build from self-compiled [depends](/depends/README.md) or install the required dependencies:
+
+ sudo apt-get install libssl-dev libevent-dev libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-test-dev libboost-thread-dev
BerkeleyDB is required for the wallet.
@@ -83,11 +87,12 @@ You can add the repository and install using the following commands:
sudo apt-get install libdb4.8-dev libdb4.8++-dev
Ubuntu and Debian have their own libdb-dev and libdb++-dev packages, but these will install
-BerkeleyDB 5.1 or later, which break binary wallet compatibility with the distributed executables which
+BerkeleyDB 5.1 or later. This will break binary wallet compatibility with the distributed executables, which
are based on BerkeleyDB 4.8. If you do not care about wallet compatibility,
pass `--with-incompatible-bdb` to configure.
-See the section "Disable-wallet mode" to build Bitcoin Core without wallet.
+To build Bitcoin Core without wallet, see [*Disable-wallet mode*](/doc/build-unix.md#disable-wallet-mode)
+
Optional (see --with-miniupnpc and --enable-upnp-default):
@@ -97,7 +102,7 @@ ZMQ dependencies (provides ZMQ API):
sudo apt-get install libzmq3-dev
-#### Dependencies for the GUI
+GUI dependencies:
If you want to build bitcoin-qt, make sure that the required packages for Qt development
are installed. Qt 5 is necessary to build the GUI.
@@ -157,7 +162,7 @@ Berkeley DB
-----------
It is recommended to use Berkeley DB 4.8. If you have to build it yourself,
you can use [the installation script included in contrib/](/contrib/install_db4.sh)
-like so
+like so:
```shell
./contrib/install_db4.sh `pwd`
@@ -165,7 +170,7 @@ like so
from the root of the repository.
-**Note**: You only need Berkeley DB if the wallet is enabled (see the section *Disable-Wallet mode* below).
+**Note**: You only need Berkeley DB if the wallet is enabled (see [*Disable-wallet mode*](/doc/build-unix.md#disable-wallet-mode)).
Boost
-----
@@ -189,9 +194,7 @@ Hardening Flags:
Hardening enables the following features:
-
-* Position Independent Executable
- Build position independent code to take advantage of Address Space Layout Randomization
+* _Position Independent Executable_: Build position independent code to take advantage of Address Space Layout Randomization
offered by some kernels. Attackers who can cause execution of code at an arbitrary memory
location are thwarted if they don't know where anything useful is located.
The stack and heap are randomly located by default, but this allows the code section to be
@@ -209,8 +212,7 @@ Hardening enables the following features:
TYPE
ET_DYN
-* Non-executable Stack
- If the stack is executable then trivial stack-based buffer overflow exploits are possible if
+* _Non-executable Stack_: If the stack is executable then trivial stack-based buffer overflow exploits are possible if
vulnerable buffers are found. By default, Bitcoin Core should be built with a non-executable stack,
but if one of the libraries it uses asks for an executable stack or someone makes a mistake
and uses a compiler extension which requires an executable stack, it will silently build an
diff --git a/doc/build-windows.md b/doc/build-windows.md
index 12adadacdc..fc93a0c6e4 100644
--- a/doc/build-windows.md
+++ b/doc/build-windows.md
@@ -5,15 +5,15 @@ Below are some notes on how to build Bitcoin Core for Windows.
The options known to work for building Bitcoin Core on Windows are:
-* On Linux using the [Mingw-w64](https://mingw-w64.org/doku.php) cross compiler tool chain. Ubuntu Bionic 18.04 is required
+* On Linux, using the [Mingw-w64](https://mingw-w64.org/doku.php) cross compiler tool chain. Ubuntu Bionic 18.04 is required
and is the platform used to build the Bitcoin Core Windows release binaries.
-* On Windows using [Windows
+* On Windows, using [Windows
Subsystem for Linux (WSL)](https://msdn.microsoft.com/commandline/wsl/about) and the Mingw-w64 cross compiler tool chain.
Other options which may work, but which have not been extensively tested are (please contribute instructions):
-* On Windows using a POSIX compatibility layer application such as [cygwin](http://www.cygwin.com/) or [msys2](http://www.msys2.org/).
-* On Windows using a native compiler tool chain such as [Visual Studio](https://www.visualstudio.com).
+* On Windows, using a POSIX compatibility layer application such as [cygwin](http://www.cygwin.com/) or [msys2](http://www.msys2.org/).
+* On Windows, using a native compiler tool chain such as [Visual Studio](https://www.visualstudio.com).
Installing Windows Subsystem for Linux
---------------------------------------
@@ -69,7 +69,7 @@ See also: [dependencies.md](dependencies.md).
## Building for 64-bit Windows
-The first step is to install the mingw-w64 cross-compilation tool chain.
+The first step is to install the mingw-w64 cross-compilation tool chain:
sudo apt install g++-mingw-w64-x86-64
@@ -81,13 +81,13 @@ Once the toolchain is installed the build steps are common:
Note that for WSL the Bitcoin Core source path MUST be somewhere in the default mount file system, for
example /usr/src/bitcoin, AND not under /mnt/d/. If this is not the case the dependency autoconf scripts will fail.
-This means you cannot use a directory that located directly on the host Windows file system to perform the build.
+This means you cannot use a directory that is located directly on the host Windows file system to perform the build.
Acquire the source in the usual way:
git clone https://github.com/bitcoin/bitcoin.git
-Once the source code is ready the build steps are below.
+Once the source code is ready the build steps are below:
PATH=$(echo "$PATH" | sed -e 's/:\/mnt.*//g') # strip out problematic Windows %PATH% imported var
cd depends
@@ -133,7 +133,7 @@ Installation
-------------
After building using the Windows subsystem it can be useful to copy the compiled
-executables to a directory on the windows drive in the same directory structure
+executables to a directory on the Windows drive in the same directory structure
as they appear in the release `.zip` archive. This can be done in the following
way. This will install to `c:\workspace\bitcoin`, for example:
@@ -142,9 +142,9 @@ way. This will install to `c:\workspace\bitcoin`, for example:
Footnotes
---------
-<a name="footnote1">1</a>: Starting from Ubuntu Xenial 16.04 both the 32 and 64 bit Mingw-w64 packages install two different
+<a name="footnote1">1</a>: Starting from Ubuntu Xenial 16.04, both the 32 and 64 bit Mingw-w64 packages install two different
compiler options to allow a choice between either posix or win32 threads. The default option is win32 threads which is the more
efficient since it will result in binary code that links directly with the Windows kernel32.lib. Unfortunately, the headers
-required to support win32 threads conflict with some of the classes in the C++11 standard library in particular std::mutex.
+required to support win32 threads conflict with some of the classes in the C++11 standard library, in particular std::mutex.
It's not possible to build the Bitcoin Core code using the win32 version of the Mingw-w64 cross compilers (at least not without
modifying headers in the Bitcoin Core source code).
diff --git a/doc/descriptors.md b/doc/descriptors.md
index c23ac06e8f..b25678e06a 100644
--- a/doc/descriptors.md
+++ b/doc/descriptors.md
@@ -22,19 +22,20 @@ Output descriptors currently support:
## Examples
-- `pk(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)` represents a P2PK output.
-- `pkh(02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5)` represents a P2PKH output.
-- `wpkh(02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9)` represents a P2WPKH output.
-- `sh(wpkh(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))` represents a P2SH-P2WPKH output.
-- `combo(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)` represents a P2PK, P2PKH, P2WPKH, and P2SH-P2WPKH output.
-- `sh(wsh(pkh(02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13)))` represents a (overly complicated) P2SH-P2WSH-P2PKH output.
-- `multi(1,022f8bde4d1a07209355b4a7250a5c5128e88b84bddc619ab7cba8d569b240efe4,025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc)` represents a bare *1-of-2* multisig.
-- `sh(multi(2,022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01,03acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbe))` represents a P2SH *2-of-2* multisig.
-- `wsh(multi(2,03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7,03774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a))` represents a P2WSH *2-of-3* multisig.
-- `sh(wsh(multi(1,03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8,03499fdf9e895e719cfd64e67f07d38e3226aa7b63678949e6e49b241a60e823e4,02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e)))` represents a P2SH-P2WSH *1-of-3* multisig.
-- `pk(xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8)` refers to a single P2PK output, using the public key part from the specified xpub.
-- `pkh(xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw/1'/2)` refers to a single P2PKH output, using child key *1'/2* of the specified xpub.
-- `wsh(multi(1,xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/1/0/*,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/0/*))` refers to a chain of *1-of-2* P2WSH multisig outputs, using public keys taken from two HD chains with corresponding derivation paths.
+- `pk(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)` describes a P2PK output with the specified public key.
+- `pkh(02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5)` describes a P2PKH output with the specified public key.
+- `wpkh(02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9)` describes a P2WPKH output with the specified public key.
+- `sh(wpkh(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))` describes a P2SH-P2WPKH output with the specified public key.
+- `combo(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)` describes any P2PK, P2PKH, P2WPKH, or P2SH-P2WPKH output with the specified public key.
+- `sh(wsh(pkh(02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13)))` describes an (overly complicated) P2SH-P2WSH-P2PKH output with the specified public key.
+- `multi(1,022f8bde4d1a07209355b4a7250a5c5128e88b84bddc619ab7cba8d569b240efe4,025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc)` describes a bare *1-of-2* multisig output with keys in the specified order.
+- `sh(multi(2,022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01,03acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbe))` describes a P2SH *2-of-2* multisig output with keys in the specified order.
+- `wsh(multi(2,03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7,03774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a))` describes a P2WSH *2-of-3* multisig output with keys in the specified order.
+- `sh(wsh(multi(1,03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8,03499fdf9e895e719cfd64e67f07d38e3226aa7b63678949e6e49b241a60e823e4,02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e)))` describes a P2SH-P2WSH *1-of-3* multisig output with keys in the specified order.
+- `pk(xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8)` describes a P2PK output with the public key of the specified xpub.
+- `pkh(xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw/1'/2)` describes a P2PKH output with child key *1'/2* of the specified xpub.
+- `pkh([d34db33f/44'/0'/0']xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*)` describes a set of P2PKH outputs, but additionally specifies that the specified xpub is a child of a master with fingerprint `d34db33f`, and derived using path `44'/0'/0'`.
+- `wsh(multi(1,xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/1/0/*,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/0/0/*))` describes a set of *1-of-2* P2WSH multisig outputs where the first multisig key is the *1/0/`i`* child of the first specified xpub and the second multisig key is the *0/0/`i`* child of the second specified xpub, and `i` is any number in a configurable range (`0-1000` by default).
## Reference
@@ -52,14 +53,20 @@ Descriptors consist of several types of expressions. The top level expression is
- `raw(HEX)` (top level only): the script whose hex encoding is HEX.
`KEY` expressions:
-- Hex encoded public keys (66 characters starting with `02` or `03`, or 130 characters starting with `04`).
- - Inside `wpkh` and `wsh`, only compressed public keys are permitted.
-- [WIF](https://en.bitcoin.it/wiki/Wallet_import_format) encoded private keys may be specified instead of the corresponding public key, with the same meaning.
--`xpub` encoded extended public key or `xprv` encoded private key (as defined in [BIP 32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki)).
- - Followed by zero or more `/NUM` unhardened and `/NUM'` hardened BIP32 derivation steps.
- - Optionally followed by a single `/*` or `/*'` final step to denote all (direct) unhardened or hardened children.
- - The usage of hardened derivation steps requires providing the private key.
- - Instead of a `'`, the suffix `h` can be used to denote hardened derivation.
+- Optionally, key origin information, consisting of:
+ - An open bracket `[`
+ - Exactly 8 hex characters for the fingerprint of the key where the derivation starts (see BIP32 for details)
+ - Followed by zero or more `/NUM` or `/NUM'` path elements to indicate unhardened or hardened derivation steps between the fingerprint and the key or xpub/xprv root that follows
+ - A closing bracket `]`
+- Followed by the actual key, which is either:
+ - Hex encoded public keys (66 characters starting with `02` or `03`, or 130 characters starting with `04`).
+ - Inside `wpkh` and `wsh`, only compressed public keys are permitted.
+ - [WIF](https://en.bitcoin.it/wiki/Wallet_import_format) encoded private keys may be specified instead of the corresponding public key, with the same meaning.
+ -`xpub` encoded extended public key or `xprv` encoded private key (as defined in [BIP 32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki)).
+ - Followed by zero or more `/NUM` unhardened and `/NUM'` hardened BIP32 derivation steps.
+ - Optionally followed by a single `/*` or `/*'` final step to denote all (direct) unhardened or hardened children.
+ - The usage of hardened derivation steps requires providing the private key.
+- Anywhere a `'` suffix is permitted to denote hardened derivation, the suffix `h` can be used instead.
`ADDR` expressions are any type of supported address:
- P2PKH addresses (base58, of the form `1...`). Note that P2PKH addresses in descriptors cannot be used for P2PK outputs (use the `pk` function instead).
@@ -91,6 +98,15 @@ on Bitcoin's OP_CHECKMULTISIG opcode. To support these, we introduce the
multisig policy, where any *k* out of the *n* provided public keys must
sign.
+Key order is significant. A `multi()` expression describes a multisig script
+with keys in the specified order, and in a search for TXOs, it will not match
+outputs with multisig scriptPubKeys that have the same keys in a different
+order. Also, to prevent a combinatorial explosion of the search space, if more
+than one of the `multi()` key arguments is a BIP32 wildcard path ending in `/*`
+or `*'`, the `multi()` expression only matches multisig scripts with the `i`th
+child key from each wildcard path in lockstep, rather than scripts with any
+combination of child keys from each wildcard path.
+
### BIP32 derived keys and chains
Most modern wallet software and hardware uses keys that are derived using
@@ -101,12 +117,39 @@ path consists of a sequence of 0 or more integers (in the range
*0..2<sup>31</sup>-1*) each optionally followed by `'` or `h`, and
separated by `/` characters. The string may optionally end with the
literal `/*` or `/*'` (or `/*h`) to refer to all unhardened or hardened
-child keys instead.
+child keys in a configurable range (by default `0-1000`, inclusive).
Whenever a public key is described using a hardened derivation step, the
script cannot be computed without access to the corresponding private
key.
+### Key origin identification
+
+In order to describe scripts whose signing keys reside on another device,
+it may be necessary to identify the master key and derivation path an
+xpub was derived with.
+
+For example, when following BIP44, it would be useful to describe a
+change chain directly as `xpub.../44'/0'/0'/1/*` where `xpub...`
+corresponds with the master key `m`. Unfortunately, since there are
+hardened derivation steps that follow the xpub, this descriptor does not
+let you compute scripts without access to the corresponding private keys.
+Instead, it should be written as `xpub.../1/*`, where xpub corresponds to
+`m/44'/0'/0'`.
+
+When interacting with a hardware device, it may be necessary to include
+the entire path from the master down. BIP174 standardizes this by
+providing the master key *fingerprint* (first 32 bit of the Hash160 of
+the master pubkey), plus all derivation steps. To support constructing
+these, we permit providing this key origin information inside the
+descriptor language, even though it does not affect the actual
+scriptPubKeys it refers to.
+
+Every public key can be prefixed by an 8-character hexadecimal
+fingerprint plus optional derivation steps (hardened and unhardened)
+surrounded by brackets, identifying the master and derivation path the key or xpub
+that follows was derived with.
+
### Including private keys
Often it is useful to communicate a description of scripts along with the
@@ -119,6 +162,6 @@ steps, or for dumping wallet descriptors including private key material.
In order to easily represent the sets of scripts currently supported by
existing Bitcoin Core wallets, a convenience function `combo` is
-provided, which takes as input a public key, and constructs the P2PK,
+provided, which takes as input a public key, and describes a set of P2PK,
P2PKH, P2WPKH, and P2SH-P2WPH scripts for that key. In case the key is
-uncompressed, it only constructs P2PK and P2PKH.
+uncompressed, the set only includes P2PK and P2PKH scripts.
diff --git a/doc/developer-notes.md b/doc/developer-notes.md
index 1d103d481b..fd5cc78297 100644
--- a/doc/developer-notes.md
+++ b/doc/developer-notes.md
@@ -69,7 +69,7 @@ tool to clean up patches automatically before submission.
- **Symbol naming conventions**. These are preferred in new code, but are not
required when doing so would need changes to significant pieces of existing
code.
- - Variable and namespace names are all lowercase, and may use `_` to
+ - Variable (including function arguments) and namespace names are all lowercase, and may use `_` to
separate words (snake_case).
- Class member variables have a `m_` prefix.
- Global variables have a `g_` prefix.
@@ -705,10 +705,10 @@ Current subtrees include:
- Upstream at https://github.com/google/leveldb ; Maintained by Google, but
open important PRs to Core to avoid delay.
- **Note**: Follow the instructions in [Upgrading LevelDB](#upgrading-leveldb) when
- merging upstream changes to the leveldb subtree.
+ merging upstream changes to the LevelDB subtree.
- src/libsecp256k1
- - Upstream at https://github.com/bitcoin-core/secp256k1/ ; actively maintaned by Core contributors.
+ - Upstream at https://github.com/bitcoin-core/secp256k1/ ; actively maintained by Core contributors.
- src/crypto/ctaes
- Upstream at https://github.com/bitcoin-core/ctaes ; actively maintained by Core contributors.
diff --git a/doc/init.md b/doc/init.md
index 239b74e4e1..5778b09d05 100644
--- a/doc/init.md
+++ b/doc/init.md
@@ -22,7 +22,7 @@ Configuration
At a bare minimum, bitcoind requires that the rpcpassword setting be set
when running as a daemon. If the configuration file does not exist or this
-setting is not set, bitcoind will shutdown promptly after startup.
+setting is not set, bitcoind will shut down promptly after startup.
This password does not have to be remembered or typed as it is mostly used
as a fixed token that bitcoind and client programs read from the configuration
diff --git a/doc/release-notes-13152.md b/doc/release-notes-13152.md
index 72526f5355..ace56f4d1b 100644
--- a/doc/release-notes-13152.md
+++ b/doc/release-notes-13152.md
@@ -1,4 +1,5 @@
New RPC methods
------------
-- `getnodeaddresses` returns peer addresses known to this node. It may be used to connect to nodes over TCP without using the DNS seeds. \ No newline at end of file
+- `getnodeaddresses` returns peer addresses known to this node. It may be used to connect to nodes over TCP without using the DNS seeds.
+- `listwalletdir` returns a list of wallets in the wallet directory which is configured with `-walletdir` parameter.
diff --git a/doc/release-notes-14296.md b/doc/release-notes-14296.md
new file mode 100644
index 0000000000..f7c52baace
--- /dev/null
+++ b/doc/release-notes-14296.md
@@ -0,0 +1,5 @@
+addwitnessaddress RPC method removed
+------------------------------------
+
+The `addwitnessaddress` RPC was added for segwit testing in version 0.13.0. It
+was deprecated in version 0.16.0. This version fully removes the RPC method.
diff --git a/doc/release-notes-14454.md b/doc/release-notes-14454.md
new file mode 100644
index 0000000000..dd2c6c7ced
--- /dev/null
+++ b/doc/release-notes-14454.md
@@ -0,0 +1,6 @@
+Low-level RPC changes
+----------------------
+
+The `importmulti` RPC has been updated to support P2WSH, P2WPKH, P2SH-P2WPKH,
+P2SH-P2WSH. Each request now accepts an additional `witnessscript` to be used
+for P2WSH or P2SH-P2WSH.
diff --git a/doc/release-notes-14468.md b/doc/release-notes-14468.md
new file mode 100644
index 0000000000..fb0243aba8
--- /dev/null
+++ b/doc/release-notes-14468.md
@@ -0,0 +1,15 @@
+Wallet `generate` RPC method deprecated
+---------------------------------------
+
+The wallet's `generate` RPC method has been deprecated and will be fully
+removed in v0.19.
+
+`generate` is only used for testing. The RPC call reaches across multiple
+subsystems (wallet and mining), so is deprecated to simplify the wallet-node
+interface. Projects that are using `generate` for testing purposes should
+transition to using the `generatetoaddress` call, which does not require or use
+the wallet component. Calling `generatetoaddress` with an address returned by
+`getnewaddress` gives the same functionality as the old `generate` method.
+
+To continue using `generate` in v0.18, restart bitcoind with the
+`-deprecatedrpc=generate` configuration.
diff --git a/doc/release-notes/release-notes-0.17.0.md b/doc/release-notes/release-notes-0.17.0.md
new file mode 100644
index 0000000000..418d7ba5f9
--- /dev/null
+++ b/doc/release-notes/release-notes-0.17.0.md
@@ -0,0 +1,1105 @@
+Bitcoin Core version 0.17.0 is now available from:
+
+ <https://bitcoincore.org/bin/bitcoin-core-0.17.0/>
+
+This is a new major version release, including new features, various bugfixes
+and performance improvements, 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).
+
+If your node has a txindex, the txindex db will be migrated the first time you run 0.17.0 or newer, which may take up to a few hours. Your node will not be functional until this migration completes.
+
+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. Upgrading
+directly from 0.7.x and earlier without redownloading the blockchain is not supported.
+However, as usual, old wallet versions are still supported.
+
+Downgrading warning
+-------------------
+
+The chainstate database for this release is not compatible with previous
+releases, so if you run 0.15 and then decide to switch back to any
+older version, you will need to run the old release with the `-reindex-chainstate`
+option to rebuild the chainstate data structures in the old format.
+
+If your node has pruning enabled, this will entail re-downloading and
+processing the entire blockchain.
+
+Compatibility
+==============
+
+Bitcoin Core is extensively tested on multiple operating systems using
+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.
+
+Known issues
+============
+
+- Upgrading from 0.13.0 or older currently results in memory blow-up during the roll-back of blocks to the SegWit activation point. In these cases, a full `-reindex` is necessary.
+
+- The GUI suffers from visual glitches in the new MacOS dark mode. This has to do with our Qt theme handling and is not a new problem in 0.17.0, but is expected to be resolved in 0.17.1.
+
+Notable changes
+===============
+
+Changed configuration options
+-----------------------------
+
+- `-includeconf=<file>` can be used to include additional configuration files.
+ Only works inside the `bitcoin.conf` file, not inside included files or from
+ command-line. Multiple files may be included. Can be disabled from command-
+ line via `-noincludeconf`. Note that multi-argument commands like
+ `-includeconf` will override preceding `-noincludeconf`, i.e.
+ ```
+ noincludeconf=1
+ includeconf=relative.conf
+ ```
+
+ as bitcoin.conf will still include `relative.conf`.
+
+GUI changes
+-----------
+
+- Block storage can be limited under Preferences, in the Main tab. Undoing this setting requires downloading the full blockchain again. This mode is incompatible with -txindex and -rescan.
+
+External wallet files
+---------------------
+
+The `-wallet=<path>` option now accepts full paths instead of requiring wallets
+to be located in the -walletdir directory.
+
+Newly created wallet format
+---------------------------
+
+If `-wallet=<path>` is specified with a path that does not exist, it will now
+create a wallet directory at the specified location (containing a wallet.dat
+data file, a db.log file, and database/log.?????????? files) instead of just
+creating a data file at the path and storing log files in the parent
+directory. This should make backing up wallets more straightforward than
+before because the specified wallet path can just be directly archived without
+having to look in the parent directory for transaction log files.
+
+For backwards compatibility, wallet paths that are names of existing data files
+in the `-walletdir` directory will continue to be accepted and interpreted the
+same as before.
+
+Dynamic loading and creation of wallets
+---------------------------------------
+
+Previously, wallets could only be loaded or created at startup, by specifying `-wallet` parameters on the command line or in the bitcoin.conf file. It is now possible to load, create and unload wallets dynamically at runtime:
+
+- Existing wallets can be loaded by calling the `loadwallet` RPC. The wallet can be specified as file/directory basename (which must be located in the `walletdir` directory), or as an absolute path to a file/directory.
+- New wallets can be created (and loaded) by calling the `createwallet` RPC. The provided name must not match a wallet file in the `walletdir` directory or the name of a wallet that is currently loaded.
+- Loaded wallets can be unloaded by calling the `unloadwallet` RPC.
+
+This feature is currently only available through the RPC interface.
+
+Coin selection
+--------------
+
+### Partial spend avoidance
+
+When an address is paid multiple times the coins from those separate payments can be spent separately which hurts privacy due to linking otherwise separate addresses. A new `-avoidpartialspends` flag has been added (default=false). If enabled, the wallet will always spend existing UTXO to the same address together even if it results in higher fees. If someone were to send coins to an address after it was used, those coins will still be included in future coin selections.
+
+Configuration sections for testnet and regtest
+----------------------------------------------
+
+It is now possible for a single configuration file to set different
+options for different networks. This is done by using sections or by
+prefixing the option with the network, such as:
+
+ main.uacomment=bitcoin
+ test.uacomment=bitcoin-testnet
+ regtest.uacomment=regtest
+ [main]
+ mempoolsize=300
+ [test]
+ mempoolsize=100
+ [regtest]
+ mempoolsize=20
+
+If the following options are not in a section, they will only apply to mainnet:
+`addnode=`, `connect=`, `port=`, `bind=`, `rpcport=`, `rpcbind=` and `wallet=`.
+The options to choose a network (`regtest=` and `testnet=`) must be specified
+outside of sections.
+
+'label' and 'account' APIs for wallet
+-------------------------------------
+
+A new 'label' API has been introduced for the wallet. This is intended as a
+replacement for the deprecated 'account' API. The 'account' can continue to
+be used in V0.17 by starting bitcoind with the '-deprecatedrpc=accounts'
+argument, and will be fully removed in V0.18.
+
+The label RPC methods mirror the account functionality, with the following functional differences:
+
+- Labels can be set on any address, not just receiving addresses. This functionality was previously only available through the GUI.
+- Labels can be deleted by reassigning all addresses using the `setlabel` RPC method.
+- There isn't support for sending transactions _from_ a label, or for determining which label a transaction was sent from.
+- Labels do not have a balance.
+
+Here are the changes to RPC methods:
+
+| Deprecated Method | New Method | Notes |
+| :---------------------- | :-------------------- | :-----------|
+| `getaccount` | `getaddressinfo` | `getaddressinfo` returns a json object with address information instead of just the name of the account as a string. |
+| `getaccountaddress` | n/a | There is no replacement for `getaccountaddress` since labels do not have an associated receive address. |
+| `getaddressesbyaccount` | `getaddressesbylabel` | `getaddressesbylabel` returns a json object with the addresses as keys, instead of a list of strings. |
+| `getreceivedbyaccount` | `getreceivedbylabel` | _no change in behavior_ |
+| `listaccounts` | `listlabels` | `listlabels` does not return a balance or accept `minconf` and `watchonly` arguments. |
+| `listreceivedbyaccount` | `listreceivedbylabel` | Both methods return new `label` fields, along with `account` fields for backward compatibility. |
+| `move` | n/a | _no replacement_ |
+| `sendfrom` | n/a | _no replacement_ |
+| `setaccount` | `setlabel` | Both methods now: <ul><li>allow assigning labels to any address, instead of raising an error if the address is not receiving address.<li>delete the previous label associated with an address when the final address using that label is reassigned to a different label, instead of making an implicit `getaccountaddress` call to ensure the previous label still has a receiving address. |
+
+| Changed Method | Notes |
+| :--------------------- | :------ |
+| `addmultisigaddress` | Renamed `account` named parameter to `label`. Still accepts `account` for backward compatibility if running with '-deprecatedrpc=accounts'. |
+| `getnewaddress` | Renamed `account` named parameter to `label`. Still accepts `account` for backward compatibility. if running with '-deprecatedrpc=accounts' |
+| `listunspent` | Returns new `label` fields. `account` field will be returned for backward compatibility if running with '-deprecatedrpc=accounts' |
+| `sendmany` | The `account` named parameter has been renamed to `dummy`. If provided, the `dummy` parameter must be set to the empty string, unless running with the `-deprecatedrpc=accounts` argument (in which case functionality is unchanged). |
+| `listtransactions` | The `account` named parameter has been renamed to `dummy`. If provided, the `dummy` parameter must be set to the string `*`, unless running with the `-deprecatedrpc=accounts` argument (in which case functionality is unchanged). |
+| `getbalance` | `account`, `minconf` and `include_watchonly` parameters are deprecated, and can only be used if running with '-deprecatedrpc=accounts' |
+
+BIP 174 Partially Signed Bitcoin Transactions support
+-----------------------------------------------------
+
+[BIP 174 PSBT](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki) is an interchange format for Bitcoin transactions that are not fully signed
+yet, together with relevant metadata to help entities work towards signing it.
+It is intended to simplify workflows where multiple parties need to cooperate to
+produce a transaction. Examples include hardware wallets, multisig setups, and
+[CoinJoin](https://bitcointalk.org/?topic=279249) transactions.
+
+### Overall workflow
+
+Overall, the construction of a fully signed Bitcoin transaction goes through the
+following steps:
+
+- A **Creator** proposes a particular transaction to be created. He constructs
+ a PSBT that contains certain inputs and outputs, but no additional metadata.
+- For each input, an **Updater** adds information about the UTXOs being spent by
+ the transaction to the PSBT.
+- A potentially other Updater adds information about the scripts and public keys
+ involved in each of the inputs (and possibly outputs) of the PSBT.
+- **Signers** inspect the transaction and its metadata to decide whether they
+ agree with the transaction. They can use amount information from the UTXOs
+ to assess the values and fees involved. If they agree, they produce a
+ partial signature for the inputs for which they have relevant key(s).
+- A **Finalizer** is run for each input to convert the partial signatures and
+ possibly script information into a final `scriptSig` and/or `scriptWitness`.
+- An **Extractor** produces a valid Bitcoin transaction (in network format)
+ from a PSBT for which all inputs are finalized.
+
+Generally, each of the above (excluding Creator and Extractor) will simply
+add more and more data to a particular PSBT. In a naive workflow, they all have
+to operate sequentially, passing the PSBT from one to the next, until the
+Extractor can convert it to a real transaction. In order to permit parallel
+operation, **Combiners** can be employed which merge metadata from different
+PSBTs for the same unsigned transaction.
+
+The names above in bold are the names of the roles defined in BIP174. They're
+useful in understanding the underlying steps, but in practice, software and
+hardware implementations will typically implement multiple roles simultaneously.
+
+### RPCs
+
+- **`converttopsbt` (Creator)** is a utility RPC that converts an
+ unsigned raw transaction to PSBT format. It ignores existing signatures.
+- **`createpsbt` (Creator)** is a utility RPC that takes a list of inputs and
+ outputs and converts them to a PSBT with no additional information. It is
+ equivalent to calling `createrawtransaction` followed by `converttopsbt`.
+- **`walletcreatefundedpsbt` (Creator, Updater)** is a wallet RPC that creates a
+ PSBT with the specified inputs and outputs, adds additional inputs and change
+ to it to balance it out, and adds relevant metadata. In particular, for inputs
+ that the wallet knows about (counting towards its normal or watch-only
+ balance), UTXO information will be added. For outputs and inputs with UTXO
+ information present, key and script information will be added which the wallet
+ knows about. It is equivalent to running `createrawtransaction`, followed by
+ `fundrawtransaction`, and `converttopsbt`.
+- **`walletprocesspsbt` (Updater, Signer, Finalizer)** is a wallet RPC that takes as
+ input a PSBT, adds UTXO, key, and script data to inputs and outputs that miss
+ it, and optionally signs inputs. Where possible it also finalizes the partial
+ signatures.
+- **`finalizepsbt` (Finalizer, Extractor)** is a utility RPC that finalizes any
+ partial signatures, and if all inputs are finalized, converts the result to a
+ fully signed transaction which can be broadcast with `sendrawtransaction`.
+- **`combinepsbt` (Combiner)** is a utility RPC that implements a Combiner. It
+ can be used at any point in the workflow to merge information added to
+ different versions of the same PSBT. In particular it is useful to combine the
+ output of multiple Updaters or Signers.
+- **`decodepsbt`** is a diagnostic utility RPC which will show all information in
+ a PSBT in human-readable form, as well as compute its eventual fee if known.
+
+Upgrading non-HD wallets to HD wallets
+--------------------------------------
+
+Since Bitcoin Core 0.13.0, creating new BIP 32 Hierarchical Deterministic wallets has been supported by Bitcoin Core but old non-HD wallets could not be upgraded to HD. Now non-HD wallets can be upgraded to HD using the `-upgradewallet` command line option. This upgrade will result in the all keys in the keypool being marked as used and a new keypool generated. **A new backup must be made when this upgrade is performed.**
+
+Additionally, `-upgradewallet` can be used to upgraded from a non-split HD chain (all keys generated with `m/0'/0'/i'`) to a split HD chain (receiving keys generated with `'m/0'/0'/i'` and change keys generated with `m'/0'/1'/i'`). When this upgrade occurs, all keys already in the keypool will remain in the keypool to be used until all keys from before the upgrade are exhausted. This is to avoid issues with backups and downgrades when some keys may come from the change key keypool. Users can begin using the new split HD chain keypools by using the `newkeypool` RPC to mark all keys in the keypool as used and begin using a new keypool generated from the split HD chain.
+
+HD Master key rotation
+----------------------
+
+A new RPC, `sethdseed`, has been introduced which allows users to set a new HD seed or set their own HD seed. This allows for a new HD seed to be used. **A new backup must be made when a new HD seed is set.**
+
+Low-level RPC changes
+---------------------
+
+- The new RPC `scantxoutset` can be used to scan the UTXO set for entries
+ that match certain output descriptors. Refer to the [output descriptors
+ reference documentation](/doc/descriptors.md) for more details. This call
+ is similar to `listunspent` but does not use a wallet, meaning that the
+ wallet can be disabled at compile or run time. This call is experimental,
+ as such, is subject to changes or removal in future releases.
+
+- The `createrawtransaction` RPC will now accept an array or dictionary (kept for compatibility) for the `outputs` parameter. This means the order of transaction outputs can be specified by the client.
+- The `fundrawtransaction` RPC will reject the previously deprecated `reserveChangeKey` option.
+- `sendmany` now shuffles outputs to improve privacy, so any previously expected behavior with regards to output ordering can no longer be relied upon.
+- The new RPC `testmempoolaccept` can be used to test acceptance of a transaction to the mempool without adding it.
+- JSON transaction decomposition now includes a `weight` field which provides
+ the transaction's exact weight. This is included in REST /rest/tx/ and
+ /rest/block/ endpoints when in json mode. This is also included in `getblock`
+ (with verbosity=2), `listsinceblock`, `listtransactions`, and
+ `getrawtransaction` RPC commands.
+- New `fees` field introduced in `getrawmempool`, `getmempoolancestors`, `getmempooldescendants` and
+ `getmempoolentry` when verbosity is set to `true` with sub-fields `ancestor`, `base`, `modified`
+ and `descendant` denominated in BTC. This new field deprecates previous fee fields, such as
+ `fee`, `modifiedfee`, `ancestorfee` and `descendantfee`.
+- The new RPC `getzmqnotifications` returns information about active ZMQ
+ notifications.
+- When bitcoin is not started with any `-wallet=<path>` options, the name of
+ the default wallet returned by `getwalletinfo` and `listwallets` RPCs is
+ now the empty string `""` instead of `"wallet.dat"`. If bitcoin is started
+ with any `-wallet=<path>` options, there is no change in behavior, and the
+ name of any wallet is just its `<path>` string.
+- Passing an empty string (`""`) as the `address_type` parameter to
+ `getnewaddress`, `getrawchangeaddress`, `addmultisigaddress`,
+ `fundrawtransaction` RPCs is now an error. Previously, this would fall back
+ to using the default address type. It is still possible to pass null or leave
+ the parameter unset to use the default address type.
+
+- Bare multisig outputs to our keys are no longer automatically treated as
+ incoming payments. As this feature was only available for multisig outputs for
+ which you had all private keys in your wallet, there was generally no use for
+ them compared to single-key schemes. Furthermore, no address format for such
+ outputs is defined, and wallet software can't easily send to it. These outputs
+ will no longer show up in `listtransactions`, `listunspent`, or contribute to
+ your balance, unless they are explicitly watched (using `importaddress` or
+ `importmulti` with hex script argument). `signrawtransaction*` also still
+ works for them.
+
+- The `getwalletinfo` RPC method now returns an `hdseedid` value, which is always the same as the incorrectly-named `hdmasterkeyid` value. `hdmasterkeyid` will be removed in V0.18.
+- The `getaddressinfo` RPC method now returns an `hdseedid` value, which is always the same as the incorrectly-named `hdmasterkeyid` value. `hdmasterkeyid` will be removed in V0.18.
+
+- Parts of the `validateaddress` RPC method have been deprecated and moved to
+ `getaddressinfo`. Clients must transition to using `getaddressinfo` to access
+ this information before upgrading to v0.18. The following deprecated fields
+ have moved to `getaddressinfo` and will only be shown with
+ `-deprecatedrpc=validateaddress`: `ismine`, `iswatchonly`, `script`, `hex`,
+ `pubkeys`, `sigsrequired`, `pubkey`, `addresses`, `embedded`, `iscompressed`,
+ `account`, `timestamp`, `hdkeypath`, `hdmasterkeyid`.
+- `signrawtransaction` is deprecated and will be fully removed in v0.18. To use
+ `signrawtransaction` in v0.17, restart bitcoind with
+ `-deprecatedrpc=signrawtransaction`. Projects should transition to using
+ `signrawtransactionwithkey` and `signrawtransactionwithwallet` before
+ upgrading to v0.18.
+
+Other API changes
+-----------------
+
+- The `inactivehdmaster` property in the `dumpwallet` output has been corrected to `inactivehdseed`
+
+### Logging
+
+- 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`.
+
+Transaction index changes
+-------------------------
+
+The transaction index is now built separately from the main node procedure,
+meaning the `-txindex` flag can be toggled without a full reindex. If bitcoind
+is run with `-txindex` on a node that is already partially or fully synced
+without one, the transaction index will be built in the background and become
+available once caught up. When switching from running `-txindex` to running
+without the flag, the transaction index database will *not* be deleted
+automatically, meaning it could be turned back on at a later time without a full
+resync.
+
+Miner block size removed
+------------------------
+
+The `-blockmaxsize` option for miners to limit their blocks' sizes was
+deprecated in V0.15.1, and has now been removed. Miners should use the
+`-blockmaxweight` option if they want to limit the weight of their blocks.
+
+Python Support
+--------------
+
+Support for Python 2 has been discontinued for all test files and tools.
+
+0.17.0 change log
+=================
+
+### Consensus
+- #12204 `3fa24bb` Fix overly eager BIP30 bypass (morcos)
+
+### Policy
+- #12568 `ed6ae80` Allow dustrelayfee to be set to zero (luke-jr)
+- #13120 `ca2a233` Treat segwit as always active (MarcoFalke)
+- #13096 `062738c` Fix `MAX_STANDARD_TX_WEIGHT` check (jl2012)
+
+### Mining
+- #12693 `df529dc` Remove unused variable in SortForBlock (drewx2)
+- #12448 `84efa9a` Interrupt block generation on shutdown request (promag)
+
+### Block and transaction handling
+- #12225 `67447ba` Mempool cleanups (sdaftuar)
+- #12356 `fd65937` Fix 'mempool min fee not met' debug output (Empact)
+- #12287 `bf3353d` Optimise lock behaviour for GuessVerificationProgress() (jonasschnelli)
+- #11889 `47a7666` Drop extra script variable in ProduceSignature (ryanofsky)
+- #11880 `d59b8d6` Stop special-casing phashBlock handling in validation for TBV (TheBlueMatt)
+- #12431 `947c25e` Only call NotifyBlockTip when chainActive changes (jamesob)
+- #12653 `534b8fa` Allow to optional specify the directory for the blocks storage (jonasschnelli)
+- #12172 `3b62a91` Bugfix: RPC: savemempool: Don't save until LoadMempool() is finished (jtimon)
+- #12167 `88430cb` Make segwit failure due to `CLEANSTACK` violation return a `SCRIPT_ERR_CLEANSTACK` error code (maaku)
+- #12561 `24133b1` Check for block corruption in ConnectBlock() (sdaftuar)
+- #11617 `1b5723e` Avoid lock: Call FlushStateToDisk(…) regardless of fCheckForPruning (practicalswift)
+- #11739 `0a8b7b4` Enforce `SCRIPT_VERIFY_P2SH` and `SCRIPT_VERIFY_WITNESS` from genesis (sdaftuar)
+- #12885 `a49381d` Reduce implementation code inside CScript (sipa)
+- #13032 `34dd1a6` Output values for "min relay fee not met" error (kristapsk)
+- #13033 `a07e8ca` Build txindex in parallel with validation (jimpo)
+- #13080 `66cc47b` Add compile time checking for ::mempool.cs runtime locking assertions (practicalswift)
+- #13185 `08c1caf` Bugfix: the end of a reorged chain is invalid when connect fails (sipa)
+- #11689 `0264836` Fix missing locking in CTxMemPool::check(…) and CTxMemPool::setSanityCheck(…) (practicalswift)
+- #13011 `3c2a41a` Cache witness hash in CTransaction (MarcoFalke)
+- #13191 `0de7cc8` Specialized double-SHA256 with 64 byte inputs with SSE4.1 and AVX2 (sipa)
+- #13243 `ea263e1` Make reusable base class for auxiliary indices (jimpo)
+- #13393 `a607d23` Enable double-SHA256-for-64-byte code on 32-bit x86 (sipa)
+- #13428 `caabdea` validation: check the specified number of blocks (off-by-one) (kallewoof)
+- #13438 `450055b` Improve coverage of SHA256 SelfTest code (sipa)
+- #13431 `954f4a9` validation: count blocks correctly for check level < 3 (kallewoof)
+- #13386 `3a3eabe` SHA256 implementations based on Intel SHA Extensions (sipa)
+- #11658 `9a1ad2c` During IBD, when doing pruning, prune 10% extra to avoid pruning again soon after (luke-jr)
+- #13794 `8ce55df` chainparams: Update with data from assumed valid chain (MarcoFalke)
+- #13527 `e7ea858` Remove promiscuousmempoolflags (MarcoFalke)
+
+### P2P protocol and network code
+- #12342 `eaeaa2d` Extend #11583 ("Do not make it trivial for inbound peers to generate log entries") to include "version handshake timeout" message (clemtaylor)
+- #12218 `9a32114` Move misbehaving logging to net logging category (laanwj)
+- #10387 `5c2aff8` Eventually connect to `NODE_NETWORK_LIMITED` peers (jonasschnelli)
+- #9037 `a36834f` Add test-before-evict discipline to addrman (EthanHeilman)
+- #12622 `e1d6e2a` Correct addrman logging (laanwj)
+- #11962 `0a01843` add seed.bitcoin.sprovoost.nl to DNS seeds (Sjors)
+- #12569 `23e7fe8` Increase signal-to-noise ratio in debug.log by adjusting log level when logging failed non-manual connect():s (practicalswift)
+- #12855 `c199869` Minor accumulated cleanups (tjps)
+- #13153 `ef46c99` Add missing newlines to debug logging (laanwj)
+- #13162 `a174702` Don't incorrectly log that REJECT messages are unknown (jnewbery)
+- #13151 `7f4db9a` Serve blocks directly from disk when possible (laanwj)
+- #13134 `70d3541` Add option `-enablebip61` to configure sending of BIP61 notifications (laanwj)
+- #13532 `7209fec` Log warning when deprecated network name 'tor' is used (wodry)
+- #13615 `172f984` Remove unused interrupt from SendMessages (fanquake)
+- #13417 `1e90862` Tighten scope in `net_processing` (skeees)
+- #13298 `f8d470e` Bucketing INV delays (1 bucket) for incoming connections to hide tx time (naumenkogs)
+- #13672 `0d8d6be` Modified `in_addr6` cast in CConman class to work with msvc (sipsorcery)
+- #11637 `c575260` Remove dead service bits code (MarcoFalke)
+- #13212 `a6f00ce` Fixed a race condition when disabling the network (lmanners)
+- #13656 `1211b15` Remove the boost/algorithm/string/predicate.hpp dependency (251Labs)
+- #13423 `f58674a` Thread safety annotations in `net_processing` (skeees)
+- #13776 `7d36237` Add missing verification of IPv6 address in CNetAddr::GetIn6Addr(…) (practicalswift)
+- #13907 `48bf8ff` Introduce a maximum size for locators (gmaxwell)
+- #13951 `8a9ffec` Hardcoded seeds update pre-0.17 branch (laanwj)
+
+### Wallet
+- #12330 `2a30e67` Reduce scope of `cs_main` and `cs_wallet` locks in listtransactions (promag)
+- #12298 `a1ffddb` Refactor HaveKeys to early return on false result (promag)
+- #12282 `663911e` Disallow abandon of conflicted txes (MarcoFalke)
+- #12333 `d405bee` Make CWallet::ListCoins atomic (promag)
+- #12296 `8e6f9f4` Only fee-bump non-conflicted/non-confirmed txes (MarcoFalke)
+- #11866 `6bb9c13` Do not un-mark fInMempool on wallet txn if ATMP fails (TheBlueMatt)
+- #11882 `987a809` Disable default fallbackfee on mainnet (jonasschnelli)
+- #9991 `4ca7c1e` listreceivedbyaddress Filter Address (NicolasDorier)
+- #11687 `98bc27f` External wallet files (ryanofsky)
+- #12658 `af88094` Sanitize some wallet serialization (sipa)
+- #9680 `6acd870` Unify CWalletTx construction (ryanofsky)
+- #10637 `e057589` Coin Selection with Murch's algorithm (achow101, Xekyo)
+- #12408 `c39dd2e` Change output type globals to members (MarcoFalke)
+- #12694 `9552dfb` Actually disable BnB when there are preset inputs (achow101)
+- #11536 `cead84b` Rename account to label where appropriate (ryanofsky)
+- #12709 `02b7e83` shuffle sendmany recipients ordering (instagibbs)
+- #12699 `c948dc8` Shuffle transaction inputs before signing (instagibbs)
+- #10762 `6d53663` Remove Wallet dependencies from init.cpp (jnewbery)
+- #12857 `821980c` Avoid travis lint-include-guards error (ken2812221)
+- #12702 `dab0d68` importprivkey: hint about importmulti (kallewoof)
+- #12836 `9abdb7c` Make WalletInitInterface and DummyWalletInit private, fix nullptr deref (promag)
+- #12785 `215158a` Initialize `m_last_block_processed` to nullptr (practicalswift)
+- #12932 `8d651ae` Remove redundant lambda function arg in handleTransactionChanged (laanwj)
+- #12749 `a84b056` feebumper: discard change outputs below discard rate (instagibbs)
+- #12892 `9b3370d` introduce 'label' API for wallet (jnewbery)
+- #12925 `6d3de17` Logprint the start of a rescan (jonasschnelli)
+- #12888 `39439e5` debug log number of unknown wallet records on load (instagibbs)
+- #12977 `434150a` Refactor `g_wallet_init_interface` to const reference (promag)
+- #13017 `65d7083` Add wallets management functions (promag)
+- #12953 `d1d54ae` Deprecate accounts (jnewbery)
+- #12909 `476cb35` Make fee settings to be non-static members (MarcoFalke)
+- #13002 `487dcbe` Do not treat bare multisig outputs as IsMine unless watched (sipa)
+- #13028 `783bb64` Make vpwallets usage thread safe (promag)
+- #12507 `2afdc29` Interrupt rescan on shutdown request (promag)
+- #12729 `979150b` Get rid of ambiguous OutputType::NONE value (ryanofsky)
+- #13079 `5778d44` Fix rescanblockchain rpc to properly report progress (Empact)
+- #12560 `e03c0db` Upgrade path for non-HD wallets to HD (achow101)
+- #13161 `7cc1bd3` Reset BerkeleyDB handle after connection fails (real-or-random)
+- #13081 `0dec5b5` Add compile time checking for `cs_wallet` runtime locking assertions (practicalswift)
+- #13127 `19a3a9e` Add Clang thread safety annotations for variables guarded by `cs_db` (practicalswift)
+- #10740 `4cfe17c` `loadwallet` RPC - load wallet at runtime (jnewbery)
+- #12924 `6738813` Fix hdmaster-key / seed-key confusion (scripted diff) (jnewbery)
+- #13297 `d82c5d1` Fix incorrect comment for DeriveNewSeed (jnewbery)
+- #13063 `6378eef` Use shared pointer to retain wallet instance (promag)
+- #13142 `56fe3dc` Separate IsMine from solvability (sipa)
+- #13194 `fd96d54` Remove template matching and pseudo opcodes (sipa)
+- #13252 `c4cc8d9` Refactor ReserveKeyFromKeyPool for safety (Empact)
+- #13058 `343d4e4` `createwallet` RPC - create new wallet at runtime (jnewbery)
+- #13351 `2140f6c` Prevent segfault when sending to unspendable witness (MarcoFalke)
+- #13060 `3f0f394` Remove getlabeladdress RPC (jnewbery)
+- #13111 `000abbb` Add unloadwallet RPC (promag)
+- #13160 `868cf43` Unlock spent outputs (promag)
+- #13498 `f54f373` Fixups from account API deprecation (jnewbery)
+- #13491 `61a044a` Improve handling of INVALID in IsMine (sipa)
+- #13425 `028b0d9` Moving final scriptSig construction from CombineSignatures to ProduceSignature (PSBT signer logic) (achow101)
+- #13564 `88a15eb` loadwallet shouldn't create new wallets (jnewbery)
+- #12944 `619cd29` ScanforWalletTransactions should mark input txns as dirty (instagibbs)
+- #13630 `d6b2235` Drop unused pindexRet arg to CMerkleTx::GetDepthInMainChain (Empact)
+- #13566 `ad552a5` Fix get balance (jnewbery)
+- #13500 `4a3e8c5` Decouple wallet version from client version (achow101)
+- #13712 `aba2e66` Fix non-determinism in ParseHDKeypath(…). Avoid using an uninitialized variable in path calculation (practicalswift)
+- #9662 `6b6e854` Add createwallet "disableprivatekeys" option: a sane mode for watchonly-wallets (jonasschnelli)
+- #13683 `e8c7434` Introduce assertion to document the assumption that cache and cache_used are always set in tandem (practicalswift)
+- #12257 `5f7575e` Use destination groups instead of coins in coin select (kallewoof)
+- #13773 `89a116d` Fix accidental use of the comma operator (practicalswift)
+- #13805 `c88529a` Correctly limit output group size (sdaftuar)
+- #12992 `26f59f5` Add wallet name to log messages (PierreRochard)
+- #13667 `b81a8a5` Fix backupwallet for multiwallets (domob1812)
+- #13657 `51c693d` assert to ensure accuracy of CMerkleTx::GetBlocksToMaturity (Empact)
+- #13812 `9d86aad` sum ancestors rather than taking max in output groups (kallewoof)
+- #13876 `8eb9870` Catch `filesystem_error` and raise `InitError` (MarcoFalke)
+- #13808 `13d51a2` shuffle coins before grouping, where warranted (kallewoof)
+- #13666 `2115cba` Always create signatures with Low R values (achow101)
+- #13917 `0333914` Additional safety checks in PSBT signer (sipa)
+- #13968 `65e7a8b` couple of walletcreatefundedpsbt fixes (instagibbs)
+- #14055 `2307a6e` fix walletcreatefundedpsbt deriv paths, add test (instagibbs)
+
+### RPC and other APIs
+- #12336 `3843780` Remove deprecated rpc options (jnewbery)
+- #12193 `5dc00f6` Consistently use UniValue.pushKV instead of push_back(Pair()) (karel-3d) (MarcoFalke)
+- #12409 `0cc45ed` Reject deprecated reserveChangeKey in fundrawtransaction (MarcoFalke)
+- #10583 `8a98dfe` Split part of validateaddress into getaddressinfo (achow101)
+- #10579 `ffc6e48` Split signrawtransaction into wallet and non-wallet RPC command (achow101)
+- #12494 `e4ffcac` Declare CMutableTransaction a struct in rawtransaction.h (Empact)
+- #12503 `0e26591` createmultisig no longer takes addresses (instagibbs)
+- #12083 `228b086` Improve getchaintxstats test coverage (promag)
+- #12479 `cd5e438` Add child transactions to getrawmempool verbose output (conscott)
+- #11872 `702e8b7` createrawtransaction: Accept sorted outputs (MarcoFalke)
+- #12700 `ebdf84c` Document RPC method aliasing (ryanofsky)
+- #12727 `8ee5c7b` Remove unreachable help conditions in rpcwallet.cpp (lutangar)
+- #12778 `b648974` Add username and ip logging for RPC method requests (GabrielDav)
+- #12717 `ac898b6` rest: Handle utxo retrieval when ignoring the mempool (romanz)
+- #12787 `cd99e5b` Adjust ifdef to avoid unreachable code (practicalswift)
+- #11742 `18815b4` Add testmempoolaccept (MarcoFalke)
+- #12942 `fefb817` Drop redundant testing of signrawtransaction prevtxs args (Empact)
+- #11200 `5f2a399` Allow for aborting rescans in the GUI (achow101)
+- #12791 `3a8a4dc` Expose a transaction's weight via RPC (TheBlueMatt)
+- #12436 `6e67754` Adds a functional test to validate the transaction version number in the RPC output (251Labs)
+- #12240 `6f8b345` Introduced a new `fees` structure that aggregates all sub-field fee types denominated in BTC (mryandao)
+- #12321 `eac067a` p2wsh and p2sh-p2wsh address in decodescript (fivepiece)
+- #13090 `17266a1` Remove Safe mode (achow101, laanwj)
+- #12639 `7eb7076` Reduce `cs_main` lock in listunspent (promag)
+- #10267 `7b966d9` New -includeconf argument for including external configuration files (kallewoof)
+- #10757 `b9551d3` Introduce getblockstats to plot things (jtimon)
+- #13288 `a589f53` Remove the need to include rpc/blockchain.cpp in order to put `GetDifficulty` under test (Empact)
+- #13394 `e1f8dce` cli: Ignore libevent warnings (theuni)
+- #13439 `3f398d7` Avoid "duplicate" return value for invalid submitblock (TheBlueMatt)
+- #13570 `a247594` Add new "getzmqnotifications" method (domob1812)
+- #13072 `b25a4c2` Update createmultisig RPC to support segwit (ajtowns)
+- #12196 `8fceae0` Add scantxoutset RPC method (jonasschnelli)
+- #13557 `b654723` BIP 174 PSBT Serializations and RPCs (achow101)
+- #13697 `f030410` Support output descriptors in scantxoutset (sipa)
+- #13927 `bced8ea` Use pushKV in some new PSBT RPCs (domob1812)
+- #13918 `a9c56b6` Replace median fee rate with feerate percentiles in getblockstats (marcinja)
+- #13721 `9f23c16` Bugfixes for BIP 174 combining and deserialization (achow101)
+- #13960 `517010e` Fix PSBT deserialization of 0-input transactions (achow101)
+
+### GUI
+- #12416 `c997f88` Fix Windows build errors introduced in #10498 (practicalswift)
+- #11733 `e782099` Remove redundant locks (practicalswift)
+- #12426 `bfa3911` Initialize members in WalletModel (MarcoFalke)
+- #12489 `e117cfe` Bugfix: respect user defined configuration file (-conf) in QT settings (jonasschnelli)
+- #12421 `be263fa` navigate to transaction history page after send (Sjors)
+- #12580 `ce56fdd` Show a transaction's virtual size in its details dialog (dooglus)
+- #12501 `c8ea91a` Improved "custom fee" explanation in tooltip (randolf)
+- #12616 `cff95a6` Set modal overlay hide button as default (promag)
+- #12620 `8a43bdc` Remove TransactionTableModel::TxIDRole (promag)
+- #12080 `56cc022` Add support to search the address book (promag)
+- #12621 `2bac3e4` Avoid querying unnecessary model data when filtering transactions (promag)
+- #12721 `e476826` remove "new" button during receive-mode in addressbook (jonasschnelli)
+- #12723 `310dc61` Qt5: Warning users about invalid-BIP21 URI bitcoin:// (krab)
+- #12610 `25cf18f` Multiwallet for the GUI (jonasschnelli)
+- #12779 `f4353da` Remove unused method setupAmountWidget(…) (practicalswift)
+- #12795 `68484d6` do not truncate .dat extension for wallets in gui (instagibbs)
+- #12870 `1d54004` make clean removes `src/qt/moc_` files (Sjors)
+- #13055 `bdda14d` Don't log to console by default (laanwj)
+- #13141 `57c57df` fixes broken link on readme (marcoagner)
+- #12928 `ef006d9` Initialize non-static class members that were previously neither initialized where defined nor in constructor (practicalswift)
+- #13158 `81c533c` Improve sendcoinsdialog readability (marcoagner)
+- #11491 `40c34a0` Add proxy icon in statusbar (mess110)
+- #13264 `2a7c53b` Satoshi unit (GreatSock)
+- #13097 `e545503` Support wallets loaded dynamically (promag)
+- #13284 `f8be434` fix visual "overflow" of amount input (brandonrninefive)
+- #13275 `a315b79` use `[default wallet]` as name for wallet with no name (jonasschnelli)
+- #13273 `3fd0c23` Qt/Bugfix: fix handling default wallet with no name (jonasschnelli)
+- #13341 `25d2df2` Stop translating command line options (laanwj)
+- #13043 `6e249e4` OptionsDialog: add prune setting (Sjors)
+- #13506 `6579d80` load wallet in UI after possible init aborts (jonasschnelli)
+- #13458 `dc53f7f` Drop qt4 support (laanwj)
+- #13528 `b877c39` Move BitcoinGUI initializers to class, fix initializer order warning (laanwj)
+- #13536 `baf3a3a` coincontrol: Remove unused qt4 workaround (MarcoFalke)
+- #13537 `10ffca7` Peer table: Visualize inbound/outbound state for every row (wodry)
+- #13791 `2c14c1f` Reject dialogs if key escape is pressed (promag)
+
+### Build system
+- #12371 `c9ca4f6` Add gitian PGP key: akx20000 (ghost)
+- #11966 `f4f4f51` clientversion: Use full commit hash for commit-based version descriptions (luke-jr)
+- #12417 `ae0fbf0` Upgrade `mac_alias` to 2.0.7 (droark)
+- #12444 `1f055ef` gitian: Bump descriptors for (0.)17 (theuni)
+- #12402 `59e032b` expat 2.2.5, ccache 3.4.1, miniupnpc 2.0.20180203 (fanquake)
+- #12029 `daa84b3` Add a makefile target for Doxygen documentation (Ov3rlo4d)
+- #12466 `6645eaf` Only use `D_DARWIN_C_SOURCE` when building miniupnpc on darwin (fanquake)
+- #11986 `765a3eb` zeromq 4.2.3 (fanquake)
+- #12373 `f13d756` Add build support for profiling (murrayn)
+- #12631 `a312e20` gitian: Alphabetize signing keys & add kallewoof key (kallewoof)
+- #12607 `29fad97` Remove ccache (fanquake)
+- #12625 `c4219ff` biplist 1.0.3 (fanquake)
+- #12666 `05042d3` configure: UniValue 1.0.4 is required for pushKV(, bool) (luke-jr)
+- #12678 `6324c68` Fix a few compilation issues with Clang 7 and -Werror (vasild)
+- #12692 `de6bdfd` Add configure options for various -fsanitize flags (eklitzke)
+- #12901 `7e23972` Show enabled sanitizers in configure output (practicalswift)
+- #12899 `3076993` macOS: Prevent Xcode 9.3 build warnings (AkioNak)
+- #12715 `8fd6243` Add 'make clean' rule (hkjn)
+- #13133 `a024a18` Remove python2 from configure.ac (ken2812221)
+- #13005 `cb088b1` Make --enable-debug to pick better options (practicalswift)
+- #13254 `092b366` Remove improper `qt/moc_*` cleaning glob from the general Makefile (Empact)
+- #13306 `f5a7733` split warnings out of CXXFLAGS (theuni)
+- #13385 `7c7508c` Guard against accidental introduction of new Boost dependencies (practicalswift)
+- #13041 `5779dc4` Add linter checking for accidental introduction of locale dependence (practicalswift)
+- #13408 `70a03c6` crypto: cleanup sha256 build (theuni)
+- #13435 `cf7ca60` When build fails due to lib missing, indicate which one (Empact)
+- #13445 `8eb76f3` Reset default -g -O2 flags when enable debug (ken2812221)
+- #13465 `81069a7` Avoid concurrency issue when make multiple target (ken2812221)
+- #13454 `45c00f8` Make sure `LC_ALL=C` is set in all shell scripts (practicalswift)
+- #13480 `31145a3` Avoid copies in range-for loops and add a warning to detect them (theuni)
+- #13486 `66e1a08` Move rpc/util.cpp from libbitcoin-util to libbitcoin-server (ken2812221)
+- #13580 `40334c7` Detect if char equals `int8_t` (ken2812221)
+- #12788 `287e4ed` Tune wildcards for LIBSECP256K1 target (kallewoof)
+- #13611 `b55f0c3` bugfix: Use `__cpuid_count` for gnu C to avoid gitian build fail (ken2812221)
+- #12971 `a6d14b1` Upgrade Qt to 5.9.6 (TheCharlatan)
+- #13543 `6c6a300` Add RISC-V support (laanwj)
+- #13177 `dcb154e` GCC-7 and glibc-2.27 back compat code (ken2812221)
+- #13659 `90b1c7e` add missing leveldb defines (theuni)
+- #13368 `c0f1569` Update gitian-build.sh for docker (achow101)
+- #13171 `19d8ca5` Change gitian-descriptors to use bionic instead (ken2812221)
+- #13604 `75bea05` Add depends 32-bit arm support for bitcoin-qt (TheCharlatan)
+- #13623 `9cdb19f` Migrate gitian-build.sh to python (ken2812221)
+- #13689 `8c36432` disable Werror when building zmq (greenaddress)
+- #13617 `cf7f9ae` release: Require macos 10.10+ (fanquake)
+- #13750 `c883653` use MacOS friendly sed syntax in qt.mk (Sjors)
+- #13095 `415f2bf` update `ax_boost_chrono`/`unit_test_framework` (fanquake)
+- #13732 `e8ffec6` Fix Qt's rcc determinism (Fuzzbawls)
+- #13782 `8284f1d` Fix osslsigncode compile issue in gitian-build (ken2812221)
+- #13696 `2ab7208` Add aarch64 qt depends support for cross compiling bitcoin-qt (TheCharlatan)
+- #13705 `b413ba0` Add format string linter (practicalswift)
+- #14000 `48c8459` fix qt determinism (theuni)
+- #14018 `3e4829a` Bugfix: NSIS: Exclude `Makefile*` from docs (luke-jr)
+- #12906 `048ac83` Avoid `interface` keyword to fix windows gitian build (ryanofsky)
+- #13314 `a9b6957` Fix FreeBSD build by including utilstrencodings.h (laanwj)
+
+### Tests and QA
+- #12252 `8d57319` Require all tests to follow naming convention (ajtowns)
+- #12295 `935eb8d` Enable flake8 warnings for all currently non-violated rules (practicalswift)
+- #11858 `b4d8549` Prepare tests for Windows (MarcoFalke)
+- #11771 `2dbc4a4` Change invalidtxrequest to use BitcoinTestFramework (jnewbery)
+- #12200 `d09968f` Bind functional test nodes to 127.0.0.1 (Sjors)
+- #12425 `26dc2da` Add some script tests (richardkiss)
+- #12455 `23481fa` Fix bip68 sequence test to reflect updated rpc error message (Empact)
+- #12477 `acd1e61` Plug memory leaks and stack-use-after-scope (MarcoFalke)
+- #12443 `07090c5` Move common args to bitcoin.conf (MarcoFalke)
+- #12570 `39dcac2` Add test cases for HexStr (`std::reverse_iterator` and corner cases) (kostaz)
+- #12582 `6012f1c` Fix ListCoins test failure due to unset `g_wallet_allow_fallback_fee` (ryanofsky)
+- #12516 `7f99964` Avoid unintentional unsigned integer wraparounds in tests (practicalswift)
+- #12512 `955fd23` Don't test against the mempool min fee information in mempool_limit.py (Empact)
+- #12600 `29088b1` Add a test for large tx output scripts with segwit input (richardkiss)
+- #12627 `791c3ea` Fix some tests to work on native windows (MarcoFalke)
+- #12405 `0f58d7f` travis: Full clone for git subtree check (MarcoFalke)
+- #11772 `0630974` Change invalidblockrequest to use BitcoinTestFramework (jnewbery)
+- #12681 `1846296` Fix ComputeTimeSmart test failure with `-DDEBUG_LOCKORDER` (ryanofsky)
+- #12682 `9f04c8e` travis: Clone depth 1 unless `$check_doc` (MarcoFalke)
+- #12710 `00d1680` Append scripts to new `test_list` array to fix bad assignment (jeffrade)
+- #12720 `872c921` Avoiding 'file' function name from python2 (jeffrade)
+- #12728 `4ba3d4f` rename TestNode to TestP2PConn in tests (jnewbery)
+- #12746 `2405ce1` Remove unused argument `max_invalid` from `check_estimates(…)` (practicalswift)
+- #12718 `185d484` Require exact match in `assert_start_raises_init_eror` (jnewbery, MarcoFalke)
+- #12076 `6d36f59` Use node.datadir instead of tmpdir in test framework (MarcoFalke)
+- #12772 `b43aba8` ci: Bump travis timeout for make check to 50m (jnewbery)
+- #12806 `18606eb` Fix function names in `feature_blocksdir` (MarcoFalke)
+- #12811 `0d8fc8d` Make summary row bold-red if any test failed and show failed tests at end of table (laanwj)
+- #12790 `490644d` Use blockmaxweight where tests previously had blockmaxsize (conscott)
+- #11773 `f0f9732` Change `feature_block.py` to use BitcoinTestFramework (jnewbery)
+- #12839 `40f4baf` Remove travis checkout depth (laanwj)
+- #11817 `2a09a78` Change `feature_csv_activation.py` to use BitcoinTestFramework (jnewbery)
+- #12284 `fa5825d` Remove assigned but never used local variables. Enable Travis checking for unused local variables (practicalswift)
+- #12719 `9beded5` Add note about test suite naming convention in developer-notes.md (practicalswift)
+- #12861 `c564424` Stop `feature_block.py` from blowing up memory (jnewbery)
+- #12851 `648252e` travis: Run verify-commits only on cron jobs (MarcoFalke)
+- #12853 `2106c4c` Match full plain text by default (MarcoFalke)
+- #11818 `9a2db3b` I accidentally (deliberately) killed it (the ComparisonTestFramework) (jnewbery)
+- #12766 `69310a3` Tidy up REST interface functional tests (romanz)
+- #12849 `83c7533` Add logging in loops in `p2p_sendhears.py` (ccdle12)
+- #12895 `d6f10b2` Add note about test suite name uniqueness requirement to developer notes (practicalswift)
+- #12856 `27278df` Add Metaclass for BitcoinTestFramework (WillAyd)
+- #12918 `6fc5a05` Assert on correct variable (kallewoof)
+- #11878 `a04440f` Add Travis check for duplicate includes (practicalswift)
+- #12917 `cf8073f` Windows fixups for functional tests (MarcoFalke)
+- #12926 `dd1ca9e` Run unit tests in parallel (sipa)
+- #12920 `b1fdfc1` Fix sign for expected values (kallewoof)
+- #12947 `979f598` Wallet hd functional test speedup and clarification (instagibbs)
+- #12993 `0d69921` Remove compatibility code not needed now when we're on Python 3 (practicalswift)
+- #12996 `6a278e0` Remove redundant bytes(…) calls (practicalswift)
+- #12949 `6b46288` Avoid copies of CTransaction (MarcoFalke)
+- #13007 `0d12570` Fix dangling wallet pointer in vpwallets (promag)
+- #13048 `cac6d11` Fix `feature_block` flakiness (jnewbery)
+- #12510 `d5b2e98` Add `rpc_bind` test to default-run tests (laanwj)
+- #13022 `896a9d0` Attach node index to `test_node` AssertionError and print messages (jamesob)
+- #13024 `018c7e5` Add rpcauth pair that generated by rpcauth.py (ken2812221)
+- #13013 `a0079d4` bench: Amend `mempool_eviction` test for witness txs (MarcoFalke)
+- #13051 `e074097` Normalize executable location (MarcoFalke)
+- #13056 `106d929` Make rpcauth.py testable and add unit tests (nixbox)
+- #13073 `a785bc3` add rpcauth-test to `AC_CONFIG_LINKS` to fix out-of-tree make check (laanwj)
+- #12830 `25ad2f7` Clarify address book error messages, add tests (jamesob)
+- #13082 `24106a8` don't test against min relay fee information in `mining_prioritisetransaction.py` (kristapsk)
+- #13003 `8d045a0` Add test for orphan handling (MarcoFalke)
+- #13105 `9e9b48d` Add --failfast option to functional test runner (jamesob)
+- #13130 `3186ad4` Fix race in `rpc_deprecated.py` (jnewbery)
+- #13136 `baf6b4e` Fix flake8 warnings in several wallet functional tests (jnewbery)
+- #13094 `bf9b03d` Add test for 64-bit Windows PE, modify 32-bit test results (ken2812221)
+- #13183 `9458b05` travis: New travis job for `check_docs` steps (glaksmono)
+- #12265 `1834d4d` fundrawtransaction: lock watch-only shared address (kallewoof)
+- #13188 `4a50ec0` Remove unused option --srcdir (MarcoFalke)
+- #12755 `612ba35` Better stderr testing (jnewbery)
+- #13198 `196c5a9` Avoid printing to console during cache creation (sdaftuar)
+- #13075 `cb9bbf7` Remove 'account' API from wallet functional tests (jnewbery)
+- #13221 `ffa86af` travis: Rename the build stage `check_doc` to `lint` (practicalswift)
+- #13205 `3cbd25f` Remove spurious error log in `p2p_segwit.py` (jnewbery)
+- #13291 `536120e` Don't include torcontrol.cpp into the test file (Empact)
+- #13281 `2ac6315` Move linters to test/lint, add readme (MarcoFalke)
+- #13215 `f8a29ca` travis: Build tests on ubuntu 18.04 with docker (ken2812221)
+- #13349 `24f7011` bench: Don't return a bool from main (laanwj)
+- #13347 `87a9d03` travis: Skip cache for lint stage (MarcoFalke)
+- #13355 `0b1c0c4` Fix "gmake check" under OpenBSD 6.3 (probably `*BSD`): Avoid using GNU grep specific regexp handling (practicalswift)
+- #13353 `d4f6dac` Fixup setting of PATH env var (MarcoFalke)
+- #13352 `e24bf1c` Avoid checking reject code for now (MarcoFalke)
+- #13383 `2722a1f` bench: Use non-throwing parsedouble(…) instead of throwing boost::lexical_cast<double>(…) (practicalswift)
+- #13367 `264efdc` Increase includeconf test coverage (MarcoFalke)
+- #13404 `3d3d8ae` speed up of `tx_validationcache_tests` by reusing of CTransaction (lucash-dev)
+- #13421 `531a033` Remove `portseed_offset` from test runner (MarcoFalke)
+- #13440 `5315660` Log as utf-8 (MarcoFalke)
+- #13066 `fa4b906` Migrate verify-commits script to python, run in travis (ken2812221)
+- #13447 `4b1edd3` travis: Increase `travis_wait` time while verifying commits (ken2812221)
+- #13350 `f532d52` Add logging to provide anchor points when debugging p2p_sendheaders (lmanners)
+- #13406 `4382f19` travis: Change mac goal to all deploy (ken2812221)
+- #13457 `b222138` Drop variadic macro (MarcoFalke)
+- #13512 `3a45493` mininode: Expose connection state through `is_connected` (MarcoFalke)
+- #13496 `9ab4c2a` Harden lint-filenames.sh (wodry)
+- #13219 `08516e0` bench: Add block assemble benchmark (MarcoFalke)
+- #13530 `b1dc39d` bench: Add missing pow.h header (laanwj)
+- #12686 `2643fa5` Add -ftrapv to CFLAGS and CXXFLAGS when --enable-debug is used. Enable -ftrapv in Travis (practicalswift)
+- #12882 `d96bdd7` Make `test_bitcoin` pass under ThreadSanitzer (clang). Fix lock-order-inversion (potential deadlock) (practicalswift)
+- #13535 `2328039` `wallet_basic`: Specify minimum required amount for listunspent (MarcoFalke)
+- #13551 `c93c360` Fix incorrect documentation for test case `cuckoocache_hit_rate_ok` (practicalswift)
+- #13563 `b330f3f` bench: Simplify coinselection (promag)
+- #13517 `a6ed99a` Remove need to handle the network thread in tests (MarcoFalke)
+- #13522 `686e97a` Fix `p2p_sendheaders` race (jnewbery)
+- #13467 `3dc2dcf` Make `p2p_segwit` easier to debug (jnewbery)
+- #13598 `0212187` bench: Fix incorrect behaviour in prevector.cpp (AkioNak)
+- #13565 `b05ded1` Fix AreInputsStandard test to reference the proper scriptPubKey (Empact)
+- #13145 `d3dae3d` Use common getPath method to create temp directory in tests (winder)
+- #13645 `2ea7eb6` skip `rpc_zmq` functional test as necessary (jamesob)
+- #13626 `8f1106d` Fix some TODOs in `p2p_segwit` (MarcoFalke)
+- #13138 `8803c91` Remove accounts from `wallet_importprunedfunds.py` (jnewbery)
+- #13663 `cbc9b50` Avoid read/write to default datadir (MarcoFalke)
+- #13682 `f8a32a3` bench: Remove unused variable (practicalswift)
+- #13638 `6fcdb5e` Use `MAX_SCRIPT_ELEMENT_SIZE` from script.py (domob1812)
+- #13687 `9d26b69` travis: Check that ~/.bitcoin is never created (MarcoFalke)
+- #13715 `e1260a7` fixes mininode's P2PConnection sending messages on closing transport (marcoagner)
+- #13729 `aa9429a` travis: Avoid unnecessarily setting env variables on the lint build (Empact)
+- #13747 `ab28b5b` Skip P2PConnection's `is_closing()` check when not available (domob1812)
+- #13650 `7a9bca6` travis: Don't store debug info if --enable-debug is set (ken2812221)
+- #13711 `f98d1e0` bench: Add benchmark for unserialize prevector (AkioNak)
+- #13771 `365384f` travis: Retry to fetch docker image (MarcoFalke)
+- #13806 `4d550ff` Fix `bench/block_assemble` assert failure (jamesob)
+- #13779 `d25079a` travis: Improve readability of travis.yml and log outputs (scravy)
+- #13822 `0fb9c87` bench: Make coinselection output groups pass eligibility filter (achow101)
+- #13247 `e83d82a` Add tests to SingleThreadedSchedulerClient() and document the memory model (skeees)
+- #13811 `660abc1` travis: Run `bench_bitcoin` once (MarcoFalke)
+- #13837 `990e182` Extract `rpc_timewait` as test param (MarcoFalke)
+- #13851 `9c4324d` fix locale for lint-shell (scravy)
+- #13823 `489b51b` quote path in authproxy for external multiwallets (MarcoFalke)
+- #13849 `2b67354` travis: Use only travis jobs: instead of mix of jobs+matrix (scravy)
+- #13859 `2384323` Add emojis to `test_runner` path and wallet filename (MarcoFalke)
+- #13916 `8ac7125` `wait_for_verack` by default (MarcoFalke)
+- #13669 `f66e1c7` Cleanup `create_transaction` implementations (conscott)
+- #13924 `09ada21` Simplify comparison in `rpc_blockchain.py` (domob1812)
+- #13913 `a08533c` Remove redundant checkmempool/checkblockindex `extra_args` (MarcoFalke)
+- #13915 `a04888a` Add test for max number of entries in locator (MarcoFalke)
+- #13867 `1b04b55` Make extended tests pass on native Windows (MarcoFalke)
+- #13944 `0df7a6c` Port usage of deprecated optparse module to argparse module (Kvaciral)
+- #13928 `b8eb0df` blocktools enforce named args for amount (MarcoFalke)
+- #13054 `bffb35f` Enable automatic detection of undefined names in Python tests scripts. Remove wildcard imports (practicalswift)
+- #14069 `cf3d7f9` Use assert not `BOOST_CHECK_*` from multithreaded tests (skeees)
+- #14071 `fab0fbe` Stop txindex thread before calling destructor (MarcoFalke)
+
+### Miscellaneous
+- #11909 `8897135` contrib: Replace developer keys with list of pgp fingerprints (MarcoFalke)
+- #12394 `fe53d5f` gitian-builder.sh: fix --setup doc, since lxc is default (Sjors)
+- #12468 `294a766` Add missing newline in init.cpp log message (Aesti)
+- #12308 `dcfe218` contrib: Add support for out-of-tree builds in gen-manpages.sh (laanwj)
+- #12451 `aae64a2` Bump leveldb subtree (MarcoFalke)
+- #12527 `d77b4a7` gitian-build.sh: fix signProg being recognized as two parameters (ken2812221)
+- #12588 `d74b01d` utils: Remove deprecated pyzmq call from python zmq example (kosciej)
+- #10271 `bc67982` Use `std::thread::hardware_concurrency`, instead of Boost, to determine available cores (fanquake)
+- #12097 `14475e2` scripts: Lint-whitespace: use perl instead of grep -p (Sjors)
+- #12098 `17c44b2` scripts: Lint-whitespace: add param to check last n commits (Sjors)
+- #11900 `842f61a` script: Simplify checkminimalpush checks, add safety assert (instagibbs)
+- #12567 `bb98aec` util: Print timestamp strings in logs using iso 8601 formatting (practicalswift)
+- #12572 `d8d9162` script: Lint-whitespace: find errors more easily (AkioNak)
+- #10694 `ae5bcc7` Remove redundant code in MutateTxSign(CMutableTransaction&, const std::string&) (practicalswift)
+- #12659 `3d16f58` Improve Fatal LevelDB Log Messages (eklitzke)
+- #12643 `0f0229d` util: Remove unused `sync_chain` (MarcoFalke)
+- #12102 `7fb8fb4` Apply hardening measures in bitcoind systemd service file (Flowdalic)
+- #12652 `55f490a` bitcoin-cli: Provide a better error message when bitcoind is not running (practicalswift)
+- #12630 `c290508` Provide useful error message if datadir is not writable (murrayn)
+- #11881 `624bee9` Remove Python2 support (jnewbery)
+- #12821 `082e26c` contrib: Remove unused import string (MarcoFalke)
+- #12829 `252c1b0` Python3 fixup (jnewbery)
+- #12822 `ff48f62` Revert 7deba93bdc76616011a9f493cbc203d60084416f and fix expired-key-sigs properly (TheBlueMatt)
+- #12820 `5e53b80` contrib: Fix check-doc script regexes (MarcoFalke)
+- #12713 `4490871` Track negated options in the option parser (eklitzke)
+- #12708 `b2e5fe8` Make verify-commits.sh test that merges are clean (sipa)
+- #12891 `3190785` logging: Add lint-logs.sh to check for newline termination (jnewbery)
+- #12923 `a7cbe38` util: Pass `pthread_self()` to `pthread_setschedparam` instead of 0 (laanwj)
+- #12871 `fb17fae` Add shell script linting: Check for shellcheck warnings in shell scripts (practicalswift)
+- #12970 `5df84de` logging: Bypass timestamp formatting when not logging (theuni)
+- #12987 `fe8fa22` tests/tools: Enable additional Python flake8 rules for automatic linting via Travis (practicalswift)
+- #12972 `0782508` Add python3 script shebang lint (ken2812221)
+- #13004 `58bbc55` Print to console by default when not run with -daemon (practicalswift)
+- #13039 `8b4081a` Add logging and error handling for file syncing (laanwj)
+- #13020 `4741ca5` Consistently log CValidationState on call failure (Empact)
+- #13031 `826acc9` Fix for utiltime to compile with msvc (sipsorcery)
+- #13119 `81743b5` Remove script to clean up datadirs (MarcoFalke)
+- #12954 `5a66642` util: Refactor logging code into a global object (jimpo)
+- #12769 `35eb9d6` Add systemd service to bitcoind in debian package (ghost)
+- #13146 `0bc980b` rpcauth: Make it possible to provide a custom password (laanwj)
+- #13148 `b62b437` logging: Fix potential use-after-free in logprintstr(…) (practicalswift)
+- #13214 `0612d96` Enable Travis checking for two Python linting rules we are currently not violating (practicalswift)
+- #13197 `6826989` util: Warn about ignored recursive -includeconf calls (kallewoof)
+- #13176 `d9ebb63` Improve CRollingBloomFilter performance: replace modulus with FastMod (martinus)
+- #13228 `d792e47` Add script to detect circular dependencies between source modules (sipa)
+- #13320 `e08c130` Ensure gitian-build.sh uses bash (jhfrontz)
+- #13301 `e4082d5` lint: Add linter to error on `#include <*.cpp>` (Empact)
+- #13374 `56f6936` utils and libraries: checking for bitcoin address in translations (kaplanmaxe)
+- #13230 `7c32b41` Simplify include analysis by enforcing the developer guide's include syntax (practicalswift)
+- #13450 `32bf4c6` Add linter: Enforce the source code file naming convention described in the developer notes (practicalswift)
+- #13479 `fa2ea37` contrib: Fix cve-2018-12356 by hardening the regex (loganaden)
+- #13448 `a90ca40` Add linter: Make sure we explicitly open all text files using UTF-8 encoding in Python (practicalswift)
+- #13494 `d67eff8` Follow-up to #13454: Fix broken build by exporting `LC_ALL=C` (practicalswift)
+- #13510 `03f3925` Scripts and tools: Obsolete #!/bin/bash shebang (DesWurstes)
+- #13577 `c9eb8d1` logging: Avoid nstart may be used uninitialized in appinitmain warning (mruddy)
+- #13603 `453ae5e` bitcoin-tx: Stricter check for valid integers (domob1812)
+- #13118 `c05c93c` RPCAuth Detection in Logs (Linrono)
+- #13647 `4027ec1` Scripts and tools: Fix `BIND_NOW` check in security-check.py (conradoplg)
+- #13692 `f5d166a` contrib: Clone core repo in gitian-build (MarcoFalke)
+- #13699 `4c6d1b9` contrib: Correct version check (kallewoof)
+- #13695 `dcc0cff` lint: Add linter for circular dependencies (Empact)
+- #13733 `0d1ebf4` utils: Refactor argsmanager a little (AtsukiTak)
+- #13714 `29b4ee6` contrib: Add lxc network setup for bionic host (ken2812221)
+- #13764 `f8685f4` contrib: Fix test-security-check fail in ubuntu 18.04 (ken2812221)
+- #13809 `77168f7` contrib: Remove debian and rpm subfolder (MarcoFalke)
+- #13799 `230652c` Ignore unknown config file options; warn instead of error (sipa)
+- #13894 `df9f712` shutdown: Stop threads before resetting ptrs (MarcoFalke)
+- #13925 `71dec5c` Merge leveldb subtree (MarcoFalke)
+- #13939 `ef86f26` lint: Make format string linter understand basic template parameter syntax (practicalswift)
+- #14105 `eb202ea` util: Report parse errors in configuration file (laanwj)
+- #12604 `9903537` Add DynamicMemoryUsage() to CDBWrapper to estimate LevelDB memory use (eklitzke)
+- #12495 `047865e` Increase LevelDB `max_open_files` (eklitzke)
+- #12784 `e80716d` Fix bug in memory usage calculation (unintended integer division) (practicalswift)
+- #12618 `becd8dd` Set `SCHED_BATCH` priority on the loadblk thread (eklitzke)
+- #12854 `5ca1509` Add P2P, Network, and Qt categories to the desktop icon (luke-jr)
+- #11862 `4366f61` Network specific conf sections (ajtowns)
+- #13441 `4a7e64f` Prevent shared conf files from failing with different available options in different binaries (achow101)
+- #13471 `5eca4e8` For AVX2 code, also check for AVX, XSAVE, and OS support (sipa)
+- #13503 `c655b2c` Document FreeBSD quirk. Fix FreeBSD build: Use std::min<int>(…) to allow for compilation under certain FreeBSD versions (practicalswift)
+- #13725 `07ce278` Fix bitcoin-cli --version (Empact)
+
+### Documentation
+- #12306 `216f9a4` Improvements to UNIX documentation (axvr)
+- #12309 `895fbd7` Explain how to update chainTxData in release process (laanwj)
+- #12317 `85123be` Document method for reviewers to verify chainTxData (jnewbery)
+- #12331 `d32528e` Properly alphabetize output of CLI --help option (murrayn)
+- #12322 `c345148` Remove step making cloned repository world-writable for Windows build (murrayn)
+- #12354 `b264528` add gpg key for fivepiece (fivepiece)
+- #11761 `89005dd` initial QT documentation (Sjors)
+- #12232 `fdc2188` Improve "Turn Windows Features On or Off" step (MCFX2)
+- #12487 `4528f74` init: Remove translation for `-blockmaxsize` option help (laanwj)
+- #12546 `a4a5fc7` Minor improvements to Compatibility Notes (randolf)
+- #12434 `21e2670` dev-notes: Members should be initialized (MarcoFalke)
+- #12452 `71f56da` clarified systemd installation instructions in init.md for Ubuntu users (DaveFromBinary)
+- #12615 `1f93491` allow for SIGNER containing spaces (ken2812221)
+- #12603 `85424d7` PeerLogicValidation interface (jamesob)
+- #12581 `12ac2f0` Mention configure without wallet in FreeBSD instructions (dbolser)
+- #12619 `8a709fb` Give hint about gitian not able to download (kallewoof)
+- #12668 `de2fcaa` do update before fetching packages in WSL build guide (nvercamm)
+- #12586 `e7721e6` Update osx brew install instruction (fanquake)
+- #12760 `7466a26` Improve documentation on standard communication channels (jimpo)
+- #12797 `0415b1e` init: Fix help message for checkblockindex (MarcoFalke)
+- #12800 `2d97611` Add note about our preference for scoped enumerations ("enum class") (practicalswift)
+- #12798 `174d016` Refer to witness reserved value as spec. in the BIP (MarcoFalke)
+- #12759 `d3908e2` Improve formatting of developer notes (eklitzke)
+- #12877 `2b54155` Use bitcoind in Tor documentation (knoxcard)
+- #12896 `b15485e` Fix conflicting statements about initialization in developer notes (practicalswift)
+- #12850 `319991d` add qrencode to brew install instructions (buddilla)
+- #12007 `cd8e45b` Clarify the meaning of fee delta not being a fee rate in prioritisetransaction RPC (honzik666)
+- #12927 `06ead15` fixed link, replaced QT with Qt (trulex)
+- #12852 `ebd786b` devtools: Setup ots git integration (MarcoFalke)
+- #12933 `3cf76c2` Refine header include policy (MarcoFalke)
+- #12951 `6df0c6c` Fix comment in FindForkInGlobalIndex (jamesob)
+- #12982 `a63b4e3` Fix inconsistent namespace formatting guidelines (ryanofsky)
+- #13026 `9b3a67e` Fix include comment in src/interfaces/wallet.h (promag)
+- #13012 `d1e3c5e` Add comments for chainparams.h, validation.cpp (jamesob)
+- #13064 `569e381` List support for BIP173 in bips.md (sipa)
+- #12997 `646b7f6` build-windows: Switch to Artful, since Zesty is EOL (MarcoFalke)
+- #12384 `c5f7efe` Add version footnote to tor.md (Willtech)
+- #13165 `627c376` Mention good first issue list in CONTRIBUTING.md (fanquake)
+- #13295 `fb77310` Update OpenBSD build instructions for OpenBSD 6.3 (practicalswift)
+- #13340 `3a8e3f4` remove leftover check-doc documentation (fanquake)
+- #13346 `60f0358` update bitcoin-dot-org links in release-process.md (fanquake)
+- #13372 `f014933` split FreeBSD build instructions out of build-unix.md (steverusso)
+- #13366 `861de3b` Rename “OS X” to the newer “macOS” convention (giulio92)
+- #13369 `f8bcef3` update transifex doc link (mess110)
+- #13312 `b22115d` Add a note about the source code filename naming convention (practicalswift)
+- #13460 `1939536` Remove note to install all boost dev packages (MarcoFalke)
+- #13476 `9501938` Fix incorrect shell quoting in FreeBSD build instructions (murrayn)
+- #13402 `43fa355` Document validationinterace callback blocking deadlock potential (TheBlueMatt)
+- #13488 `d6cf4bd` Improve readability of "Squashing commits" (wodry)
+- #13531 `ee02deb` Clarify that mempool txiter is `const_iterator` (MarcoFalke)
+- #13418 `01f9098` More precise explanation of parameter onlynet (wodry)
+- #13592 `1756cb4` Modify policy to not translate command-line help (ken2812221)
+- #13588 `b77c38e` Improve doc of options addnode, connect, seednode (wodry)
+- #13614 `17e9106` Update command line help for -printtoconsole and -debuglogfile (satwo, fanquake)
+- #13605 `8cc048e` corrected text to reflect new(er) process of specifying fingerprints (jhfrontz)
+- #13481 `b641f60` Rewrite some validation docs as lock annotations (MarcoFalke)
+- #13680 `30640f8` Remove outdated comment about miner ignoring CPFP (jamesob)
+- #13625 `7146672` Add release notes for -printtoconsole and -debuglogfile changes (satwo)
+- #13718 `f7f574d` Specify preferred Python string formatting technique (masonicboom)
+- #12764 `10b9a81` Remove field in getblocktemplate help that has never been used (conscott)
+- #13742 `d2186b3` Adjust bitcoincore.org links (MarcoFalke)
+- #13706 `94dd89e` Minor improvements to release-process.md (MitchellCash)
+- #13775 `ef4fac0` Remove newlines from error message (practicalswift)
+- #13803 `feb7dd9` add note to contributor docs about warranted PR's (kallewoof)
+- #13814 `67af7ef` Add BIP174 to list of implemented BIPs (sipa)
+- #13835 `c1cba35` Fix memory consistency model in comment (skeees)
+- #13824 `aa30e4b` Remove outdated net comment (MarcoFalke)
+- #13853 `317477a` correct versions in dependencies.md (fanquake)
+- #13872 `37ab117` Reformat -help output for help2man (real-or-random)
+- #13717 `8c3c402` Link to python style guidelines from developer notes (masonicboom)
+- #13895 `1cd5f2c` fix GetWarnings docs to reflect behavior (Empact)
+- #13911 `3e3a50a` Revert translated string change, clarify wallet log messages (PierreRochard)
+- #13908 `d6faea4` upgrade rescan time warning from minutes to >1 hour (masonicboom)
+- #13905 `73a09b4` fixed bitcoin-cli -help output for help2man (hebasto)
+- #14100 `2936dbc` Change documentation for =0 for non-boolean options (laanwj)
+- #14096 `465a583` Add reference documentation for descriptors language (sipa)
+- #12757 `0c5f67b` Clarify include guard naming convention (practicalswift)
+- #13844 `d3325b0` Correct the help output for `-prune` (hebasto)
+
+Credits
+=======
+
+Thanks to everyone who directly contributed to this release:
+
+- 251
+- 532479301
+- Aaron Clauson
+- Akio Nakamura
+- Akira Takizawa
+- Alex Morcos
+- Alex Vear
+- Alexey Ivanov
+- Alin Rus
+- Andrea Comand
+- Andrew Chow
+- Anthony Towns
+- AtsukiTak
+- Ben Woosley
+- Bernhard M. Wiedemann
+- Brandon Ruggles
+- buddilla
+- ccdle12
+- Chris Moore
+- Chun Kuan Lee
+- Clem Taylor
+- Conor Scott
+- Conrado Gouvea
+- Cory Fields
+- Cristian Mircea Messel
+- ctp-tsteenholdt
+- Damian Williamson
+- Dan Bolser
+- Daniel Kraft
+- Darko Janković
+- DaveFromBinary
+- David A. Harding
+- DesWurstes
+- Dimitris Apostolou
+- donaloconnor
+- Douglas Roark
+- DrahtBot
+- Drew Rasmussen
+- e0
+- Ernest Hemingway
+- Ethan Heilman
+- Evan Klitzke
+- fanquake
+- Felix Wolfsteller
+- fivepiece
+- Florian Schmaus
+- Fuzzbawls
+- Gabriel Davidian
+- Giulio Lombardo
+- Gleb
+- Grady Laksmono
+- GreatSock
+- Gregory Maxwell
+- Gregory Sanders
+- Hennadii Stepanov
+- Henrik Jonsson
+- Indospace.io
+- James O'Beirne
+- Jan Čapek
+- Jeff Frontz
+- Jeff Rade
+- Jeremy Rubin
+- JeremyRand
+- Jesse Cohen
+- Jim Posen
+- joemphilips
+- John Bampton
+- John Newbery
+- johnlow95
+- Johnson Lau
+- Jonas Nick
+- Jonas Schnelli
+- João Barbosa
+- Jorge Timón
+- Josh Hartshorn
+- Julian Fleischer
+- kallewoof
+- Karel Bilek
+- Karl-Johan Alm
+- Ken Lee
+- Kevin Pan
+- Kosta Zertsekel
+- Kristaps Kaupe
+- Kvaciral
+- Lawrence Nahum
+- Linrono
+- lmanners
+- Loganaden Velvindron
+- Lowell Manners
+- lucash.dev@gmail.com
+- Luke Dashjr
+- lutangar
+- Marcin Jachymiak
+- marcoagner
+- MarcoFalke
+- Mark Erhardt
+- Mark Friedenbach
+- Martin Ankerl
+- Mason Simon
+- Matt Corallo
+- Matteo Sumberaz
+- Max Kaplan
+- MeshCollider
+- Michał Zabielski
+- Mitchell Cash
+- mruddy
+- mryandao
+- murrayn
+- Nick Vercammen
+- Nicolas Dorier
+- Nikolay Mitev
+- okayplanet
+- Pierre Rochard
+- Pieter Wuille
+- practicalswift
+- Qasim Javed
+- Randolf Richardson
+- Richard Kiss
+- Roman Zeyde
+- Russell Yanofsky
+- Samuel B. Atwood
+- Sebastian Kung
+- Sjors Provoost
+- Steve Lee
+- steverusso
+- Suhas Daftuar
+- Tamas Blummer
+- TheCharlatan
+- Thomas Kerin
+- Thomas Snider
+- Tim Ruffing
+- Varunram
+- Vasil Dimov
+- Will Ayd
+- William Robinson
+- winder
+- Wladimir J. van der Laan
+- wodry
+
+And to those that reported security issues:
+
+- awemany (for CVE-2018-17144, previously credited as "anonymous reporter")
+
+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 3ba622ee6d..f2fe44c8bf 100644
--- a/doc/release-process.md
+++ b/doc/release-process.md
@@ -296,6 +296,8 @@ bitcoin.org (see below for bitcoin.org update instructions).
- bitcoincore.org blog post
+ - bitcoincore.org RPC documentation update
+
- Update title of #bitcoin on Freenode IRC
- Optionally twitter, reddit /r/Bitcoin, ... but this will usually sort out itself
diff --git a/share/qt/Info.plist.in b/share/qt/Info.plist.in
index 17b4ee47de..0c0335a1e8 100644
--- a/share/qt/Info.plist.in
+++ b/share/qt/Info.plist.in
@@ -99,6 +99,9 @@
<key>LSAppNapIsDisabled</key>
<string>True</string>
+
+ <key>NSRequiresAquaSystemAppearance</key>
+ <string>True</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.finance</string>
diff --git a/src/Makefile.am b/src/Makefile.am
index 7a2e9fa5e8..6141919007 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -587,9 +587,11 @@ if HARDEN
$(AM_V_at) READELF=$(READELF) OBJDUMP=$(OBJDUMP) $(top_srcdir)/contrib/devtools/security-check.py < $(bin_PROGRAMS)
endif
+if ENABLE_BIP70
%.pb.cc %.pb.h: %.proto
@test -f $(PROTOC)
$(AM_V_GEN) $(PROTOC) --cpp_out=$(@D) --proto_path=$(<D) $<
+endif
if EMBEDDED_LEVELDB
include Makefile.leveldb.include
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index c7a1963135..3ca2a7451d 100644
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -122,7 +122,6 @@ QT_MOC_CPP = \
qt/moc_bitcoinamountfield.cpp \
qt/moc_bitcoingui.cpp \
qt/moc_bitcoinunits.cpp \
- qt/moc_callback.cpp \
qt/moc_clientmodel.cpp \
qt/moc_coincontroldialog.cpp \
qt/moc_coincontroltreewidget.cpp \
@@ -168,7 +167,6 @@ BITCOIN_MM = \
QT_MOC = \
qt/bitcoin.moc \
qt/bitcoinamountfield.moc \
- qt/callback.moc \
qt/intro.moc \
qt/overviewpage.moc \
qt/rpcconsole.moc
@@ -178,9 +176,15 @@ QT_QRC = qt/bitcoin.qrc
QT_QRC_LOCALE_CPP = qt/qrc_bitcoin_locale.cpp
QT_QRC_LOCALE = qt/bitcoin_locale.qrc
+if ENABLE_BIP70
PROTOBUF_CC = qt/paymentrequest.pb.cc
PROTOBUF_H = qt/paymentrequest.pb.h
PROTOBUF_PROTO = qt/paymentrequest.proto
+else
+PROTOBUF_CC =
+PROTOBUF_H =
+PROTOBUF_PROTO =
+endif
BITCOIN_QT_H = \
qt/addressbookpage.h \
@@ -191,7 +195,6 @@ BITCOIN_QT_H = \
qt/bitcoinamountfield.h \
qt/bitcoingui.h \
qt/bitcoinunits.h \
- qt/callback.h \
qt/clientmodel.h \
qt/coincontroldialog.h \
qt/coincontroltreewidget.h \
@@ -330,7 +333,6 @@ BITCOIN_QT_WALLET_CPP = \
qt/editaddressdialog.cpp \
qt/openuridialog.cpp \
qt/overviewpage.cpp \
- qt/paymentrequestplus.cpp \
qt/paymentserver.cpp \
qt/receivecoinsdialog.cpp \
qt/receiverequestdialog.cpp \
@@ -349,13 +351,19 @@ BITCOIN_QT_WALLET_CPP = \
qt/walletmodeltransaction.cpp \
qt/walletview.cpp
+BITCOIN_QT_WALLET_BIP70_CPP = \
+ qt/paymentrequestplus.cpp
+
BITCOIN_QT_CPP = $(BITCOIN_QT_BASE_CPP)
if TARGET_WINDOWS
BITCOIN_QT_CPP += $(BITCOIN_QT_WINDOWS_CPP)
endif
if ENABLE_WALLET
BITCOIN_QT_CPP += $(BITCOIN_QT_WALLET_CPP)
-endif
+if ENABLE_BIP70
+BITCOIN_QT_CPP += $(BITCOIN_QT_WALLET_BIP70_CPP)
+endif # ENABLE_BIP70
+endif # ENABLE_WALLET
RES_IMAGES =
@@ -409,8 +417,12 @@ if ENABLE_ZMQ
qt_bitcoin_qt_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
endif
qt_bitcoin_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) $(LIBLEVELDB_SSE42) $(LIBMEMENV) \
- $(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QR_LIBS) $(PROTOBUF_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) \
+ $(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QR_LIBS) $(PROTOBUF_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) \
$(EVENT_PTHREADS_LIBS) $(EVENT_LIBS)
+if ENABLE_BIP70
+qt_bitcoin_qt_LDADD += $(SSL_LIBS)
+endif
+qt_bitcoin_qt_LDADD += $(CRYPTO_LIBS)
qt_bitcoin_qt_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
qt_bitcoin_qt_LIBTOOLFLAGS = $(AM_LIBTOOLFLAGS) --tag CXX
diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include
index 4b14212b2e..db7873e8b7 100644
--- a/src/Makefile.qttest.include
+++ b/src/Makefile.qttest.include
@@ -13,9 +13,12 @@ TEST_QT_MOC_CPP = \
if ENABLE_WALLET
TEST_QT_MOC_CPP += \
qt/test/moc_addressbooktests.cpp \
- qt/test/moc_paymentservertests.cpp \
qt/test/moc_wallettests.cpp
-endif
+if ENABLE_BIP70
+TEST_QT_MOC_CPP += \
+ qt/test/moc_paymentservertests.cpp
+endif # ENABLE_BIP70
+endif # ENABLE_WALLET
TEST_QT_H = \
qt/test/addressbooktests.h \
@@ -48,10 +51,13 @@ qt_test_test_bitcoin_qt_SOURCES = \
if ENABLE_WALLET
qt_test_test_bitcoin_qt_SOURCES += \
qt/test/addressbooktests.cpp \
- qt/test/paymentservertests.cpp \
qt/test/wallettests.cpp \
wallet/test/wallet_test_fixture.cpp
-endif
+if ENABLE_BIP70
+qt_test_test_bitcoin_qt_SOURCES += \
+ qt/test/paymentservertests.cpp
+endif # ENABLE_BIP70
+endif # ENABLE_WALLET
nodist_qt_test_test_bitcoin_qt_SOURCES = $(TEST_QT_MOC_CPP)
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 8a537ed4f6..4506d5dd6a 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -51,6 +51,7 @@ BITCOIN_TESTS =\
test/cuckoocache_tests.cpp \
test/denialofservice_tests.cpp \
test/descriptor_tests.cpp \
+ test/fs_tests.cpp \
test/getarg_tests.cpp \
test/hash_tests.cpp \
test/key_io_tests.cpp \
@@ -110,11 +111,14 @@ BITCOIN_TESTS += \
wallet/test/psbt_wallet_tests.cpp \
wallet/test/wallet_tests.cpp \
wallet/test/wallet_crypto_tests.cpp \
- wallet/test/coinselector_tests.cpp
+ wallet/test/coinselector_tests.cpp \
+ wallet/test/init_tests.cpp
BITCOIN_TEST_SUITE += \
wallet/test/wallet_test_fixture.cpp \
- wallet/test/wallet_test_fixture.h
+ wallet/test/wallet_test_fixture.h \
+ wallet/test/init_test_fixture.cpp \
+ wallet/test/init_test_fixture.h
endif
test_test_bitcoin_SOURCES = $(BITCOIN_TEST_SUITE) $(BITCOIN_TESTS) $(JSON_TEST_FILES) $(RAW_TEST_FILES)
@@ -161,7 +165,7 @@ nodist_test_test_bitcoin_SOURCES = $(GENERATED_TEST_FILES)
$(BITCOIN_TESTS): $(GENERATED_TEST_FILES)
-CLEAN_BITCOIN_TEST = test/*.gcda test/*.gcno $(GENERATED_TEST_FILES)
+CLEAN_BITCOIN_TEST = test/*.gcda test/*.gcno $(GENERATED_TEST_FILES) $(BITCOIN_TESTS:=.log)
CLEANFILES += $(CLEAN_BITCOIN_TEST)
diff --git a/src/addrman.h b/src/addrman.h
index cf1949c28c..6d5780afa8 100644
--- a/src/addrman.h
+++ b/src/addrman.h
@@ -187,36 +187,37 @@ public:
*/
class CAddrMan
{
-private:
+protected:
//! critical section to protect the inner data structures
mutable CCriticalSection cs;
+private:
//! last used nId
- int nIdCount;
+ int nIdCount GUARDED_BY(cs);
//! table with information about all nIds
- std::map<int, CAddrInfo> mapInfo;
+ std::map<int, CAddrInfo> mapInfo GUARDED_BY(cs);
//! find an nId based on its network address
- std::map<CNetAddr, int> mapAddr;
+ std::map<CNetAddr, int> mapAddr GUARDED_BY(cs);
//! randomly-ordered vector of all nIds
- std::vector<int> vRandom;
+ std::vector<int> vRandom GUARDED_BY(cs);
// number of "tried" entries
- int nTried;
+ int nTried GUARDED_BY(cs);
//! list of "tried" buckets
- int vvTried[ADDRMAN_TRIED_BUCKET_COUNT][ADDRMAN_BUCKET_SIZE];
+ int vvTried[ADDRMAN_TRIED_BUCKET_COUNT][ADDRMAN_BUCKET_SIZE] GUARDED_BY(cs);
//! number of (unique) "new" entries
- int nNew;
+ int nNew GUARDED_BY(cs);
//! list of "new" buckets
- int vvNew[ADDRMAN_NEW_BUCKET_COUNT][ADDRMAN_BUCKET_SIZE];
+ int vvNew[ADDRMAN_NEW_BUCKET_COUNT][ADDRMAN_BUCKET_SIZE] GUARDED_BY(cs);
//! last time Good was called (memory only)
- int64_t nLastGood;
+ int64_t nLastGood GUARDED_BY(cs);
//! Holds addrs inserted into tried table that collide with existing entries. Test-before-evict discipline used to resolve these collisions.
std::set<int> m_tried_collisions;
@@ -229,58 +230,58 @@ protected:
FastRandomContext insecure_rand;
//! Find an entry.
- CAddrInfo* Find(const CNetAddr& addr, int *pnId = nullptr);
+ CAddrInfo* Find(const CNetAddr& addr, int *pnId = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs);
//! find an entry, creating it if necessary.
//! nTime and nServices of the found node are updated, if necessary.
- CAddrInfo* Create(const CAddress &addr, const CNetAddr &addrSource, int *pnId = nullptr);
+ CAddrInfo* Create(const CAddress &addr, const CNetAddr &addrSource, int *pnId = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs);
//! Swap two elements in vRandom.
- void SwapRandom(unsigned int nRandomPos1, unsigned int nRandomPos2);
+ void SwapRandom(unsigned int nRandomPos1, unsigned int nRandomPos2) EXCLUSIVE_LOCKS_REQUIRED(cs);
//! Move an entry from the "new" table(s) to the "tried" table
- void MakeTried(CAddrInfo& info, int nId);
+ void MakeTried(CAddrInfo& info, int nId) EXCLUSIVE_LOCKS_REQUIRED(cs);
//! Delete an entry. It must not be in tried, and have refcount 0.
- void Delete(int nId);
+ void Delete(int nId) EXCLUSIVE_LOCKS_REQUIRED(cs);
//! Clear a position in a "new" table. This is the only place where entries are actually deleted.
- void ClearNew(int nUBucket, int nUBucketPos);
+ void ClearNew(int nUBucket, int nUBucketPos) EXCLUSIVE_LOCKS_REQUIRED(cs);
//! Mark an entry "good", possibly moving it from "new" to "tried".
- void Good_(const CService &addr, bool test_before_evict, int64_t time);
+ void Good_(const CService &addr, bool test_before_evict, int64_t time) EXCLUSIVE_LOCKS_REQUIRED(cs);
//! Add an entry to the "new" table.
- bool Add_(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty);
+ bool Add_(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty) EXCLUSIVE_LOCKS_REQUIRED(cs);
//! Mark an entry as attempted to connect.
- void Attempt_(const CService &addr, bool fCountFailure, int64_t nTime);
+ void Attempt_(const CService &addr, bool fCountFailure, int64_t nTime) EXCLUSIVE_LOCKS_REQUIRED(cs);
//! Select an address to connect to, if newOnly is set to true, only the new table is selected from.
- CAddrInfo Select_(bool newOnly);
+ CAddrInfo Select_(bool newOnly) EXCLUSIVE_LOCKS_REQUIRED(cs);
//! See if any to-be-evicted tried table entries have been tested and if so resolve the collisions.
- void ResolveCollisions_();
+ void ResolveCollisions_() EXCLUSIVE_LOCKS_REQUIRED(cs);
//! Return a random to-be-evicted tried table address.
- CAddrInfo SelectTriedCollision_();
+ CAddrInfo SelectTriedCollision_() EXCLUSIVE_LOCKS_REQUIRED(cs);
//! Wraps GetRandInt to allow tests to override RandomInt and make it determinismistic.
virtual int RandomInt(int nMax);
#ifdef DEBUG_ADDRMAN
//! Perform consistency check. Returns an error code or zero.
- int Check_();
+ int Check_() EXCLUSIVE_LOCKS_REQUIRED(cs);
#endif
//! Select several addresses at once.
- void GetAddr_(std::vector<CAddress> &vAddr);
+ void GetAddr_(std::vector<CAddress> &vAddr) EXCLUSIVE_LOCKS_REQUIRED(cs);
//! Mark an entry as currently-connected-to.
- void Connected_(const CService &addr, int64_t nTime);
+ void Connected_(const CService &addr, int64_t nTime) EXCLUSIVE_LOCKS_REQUIRED(cs);
//! Update an entry's service bits.
- void SetServices_(const CService &addr, ServiceFlags nServices);
+ void SetServices_(const CService &addr, ServiceFlags nServices) EXCLUSIVE_LOCKS_REQUIRED(cs);
public:
/**
diff --git a/src/arith_uint256.cpp b/src/arith_uint256.cpp
index 13fa176f8b..791dad7a60 100644
--- a/src/arith_uint256.cpp
+++ b/src/arith_uint256.cpp
@@ -176,7 +176,7 @@ unsigned int base_uint<BITS>::bits() const
for (int pos = WIDTH - 1; pos >= 0; pos--) {
if (pn[pos]) {
for (int nbits = 31; nbits > 0; nbits--) {
- if (pn[pos] & 1 << nbits)
+ if (pn[pos] & 1U << nbits)
return 32 * pos + nbits + 1;
}
return 32 * pos + 1;
diff --git a/src/base58.cpp b/src/base58.cpp
index 7020c24055..eac763394b 100644
--- a/src/base58.cpp
+++ b/src/base58.cpp
@@ -6,6 +6,7 @@
#include <hash.h>
#include <uint256.h>
+#include <utilstrencodings.h>
#include <assert.h>
#include <string.h>
@@ -34,7 +35,7 @@ static const int8_t mapBase58[256] = {
bool DecodeBase58(const char* psz, std::vector<unsigned char>& vch)
{
// Skip leading spaces.
- while (*psz && isspace(*psz))
+ while (*psz && IsSpace(*psz))
psz++;
// Skip and count leading '1's.
int zeroes = 0;
@@ -48,7 +49,7 @@ bool DecodeBase58(const char* psz, std::vector<unsigned char>& vch)
std::vector<unsigned char> b256(size);
// Process the characters.
static_assert(sizeof(mapBase58)/sizeof(mapBase58[0]) == 256, "mapBase58.size() should be 256"); // guarantee not out of range
- while (*psz && !isspace(*psz)) {
+ while (*psz && !IsSpace(*psz)) {
// Decode base58 character
int carry = mapBase58[(uint8_t)*psz];
if (carry == -1) // Invalid b58 character
@@ -64,7 +65,7 @@ bool DecodeBase58(const char* psz, std::vector<unsigned char>& vch)
psz++;
}
// Skip trailing spaces.
- while (isspace(*psz))
+ while (IsSpace(*psz))
psz++;
if (*psz != 0)
return false;
diff --git a/src/bench/checkblock.cpp b/src/bench/checkblock.cpp
index 6f03581c4b..e325333c01 100644
--- a/src/bench/checkblock.cpp
+++ b/src/bench/checkblock.cpp
@@ -20,7 +20,7 @@ namespace block_bench {
static void DeserializeBlockTest(benchmark::State& state)
{
CDataStream stream((const char*)block_bench::block413567,
- (const char*)&block_bench::block413567[sizeof(block_bench::block413567)],
+ (const char*)block_bench::block413567 + sizeof(block_bench::block413567),
SER_NETWORK, PROTOCOL_VERSION);
char a = '\0';
stream.write(&a, 1); // Prevent compaction
@@ -36,7 +36,7 @@ static void DeserializeBlockTest(benchmark::State& state)
static void DeserializeAndCheckBlockTest(benchmark::State& state)
{
CDataStream stream((const char*)block_bench::block413567,
- (const char*)&block_bench::block413567[sizeof(block_bench::block413567)],
+ (const char*)block_bench::block413567 + sizeof(block_bench::block413567),
SER_NETWORK, PROTOCOL_VERSION);
char a = '\0';
stream.write(&a, 1); // Prevent compaction
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index 09507fd249..f466505114 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -17,6 +17,7 @@
#include <memory>
#include <stdio.h>
+#include <tuple>
#include <event2/buffer.h>
#include <event2/keyvalq_struct.h>
@@ -511,6 +512,10 @@ static int CommandLineRPC(int argc, char *argv[])
int main(int argc, char* argv[])
{
+#ifdef WIN32
+ util::WinCmdLineArgs winArgs;
+ std::tie(argc, argv) = winArgs.get();
+#endif
SetupEnvironment();
if (!SetupNetworking()) {
fprintf(stderr, "Error: Initializing networking failed\n");
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index a3fcb87675..6d86581ac6 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -356,7 +356,7 @@ static void MutateTxAddOutMultiSig(CMutableTransaction& tx, const std::string& s
if (vStrInputParts.size() < numkeys + 3)
throw std::runtime_error("incorrect number of multisig pubkeys");
- if (required < 1 || required > 20 || numkeys < 1 || numkeys > 20 || numkeys < required)
+ if (required < 1 || required > MAX_PUBKEYS_PER_MULTISIG || numkeys < 1 || numkeys > MAX_PUBKEYS_PER_MULTISIG || numkeys < required)
throw std::runtime_error("multisig parameter mismatch. Required " \
+ std::to_string(required) + " of " + std::to_string(numkeys) + "signatures.");
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index bf04d95b50..18fcd9bc2a 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -185,6 +185,10 @@ static bool AppInit(int argc, char* argv[])
int main(int argc, char* argv[])
{
+#ifdef WIN32
+ util::WinCmdLineArgs winArgs;
+ std::tie(argc, argv) = winArgs.get();
+#endif
SetupEnvironment();
// Connect bitcoind signal handlers
diff --git a/src/dummywallet.cpp b/src/dummywallet.cpp
index 3714187a96..3eb77354c1 100644
--- a/src/dummywallet.cpp
+++ b/src/dummywallet.cpp
@@ -34,6 +34,16 @@ void DummyWalletInit::AddWalletOptions() const
const WalletInitInterface& g_wallet_init_interface = DummyWalletInit();
+fs::path GetWalletDir()
+{
+ throw std::logic_error("Wallet function called in non-wallet build.");
+}
+
+std::vector<fs::path> ListWalletDir()
+{
+ throw std::logic_error("Wallet function called in non-wallet build.");
+}
+
std::vector<std::shared_ptr<CWallet>> GetWallets()
{
throw std::logic_error("Wallet function called in non-wallet build.");
diff --git a/src/fs.cpp b/src/fs.cpp
index df79b5e3df..3c8f4c0247 100644
--- a/src/fs.cpp
+++ b/src/fs.cpp
@@ -3,6 +3,7 @@
#ifndef WIN32
#include <fcntl.h>
#else
+#define NOMINMAX
#include <codecvt>
#include <windows.h>
#endif
@@ -89,7 +90,7 @@ bool FileLock::TryLock()
return false;
}
_OVERLAPPED overlapped = {0};
- if (!LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY, 0, 0, 0, &overlapped)) {
+ if (!LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY, 0, std::numeric_limits<DWORD>::max(), std::numeric_limits<DWORD>::max(), &overlapped)) {
reason = GetErrorReason();
return false;
}
@@ -113,4 +114,106 @@ std::string get_filesystem_error_message(const fs::filesystem_error& e)
#endif
}
+#ifdef WIN32
+#ifdef __GLIBCXX__
+
+// reference: https://github.com/gcc-mirror/gcc/blob/gcc-7_3_0-release/libstdc%2B%2B-v3/include/std/fstream#L270
+
+static std::string openmodeToStr(std::ios_base::openmode mode)
+{
+ switch (mode & ~std::ios_base::ate) {
+ case std::ios_base::out:
+ case std::ios_base::out | std::ios_base::trunc:
+ return "w";
+ case std::ios_base::out | std::ios_base::app:
+ case std::ios_base::app:
+ return "a";
+ case std::ios_base::in:
+ return "r";
+ case std::ios_base::in | std::ios_base::out:
+ return "r+";
+ case std::ios_base::in | std::ios_base::out | std::ios_base::trunc:
+ return "w+";
+ case std::ios_base::in | std::ios_base::out | std::ios_base::app:
+ case std::ios_base::in | std::ios_base::app:
+ return "a+";
+ case std::ios_base::out | std::ios_base::binary:
+ case std::ios_base::out | std::ios_base::trunc | std::ios_base::binary:
+ return "wb";
+ case std::ios_base::out | std::ios_base::app | std::ios_base::binary:
+ case std::ios_base::app | std::ios_base::binary:
+ return "ab";
+ case std::ios_base::in | std::ios_base::binary:
+ return "rb";
+ case std::ios_base::in | std::ios_base::out | std::ios_base::binary:
+ return "r+b";
+ case std::ios_base::in | std::ios_base::out | std::ios_base::trunc | std::ios_base::binary:
+ return "w+b";
+ case std::ios_base::in | std::ios_base::out | std::ios_base::app | std::ios_base::binary:
+ case std::ios_base::in | std::ios_base::app | std::ios_base::binary:
+ return "a+b";
+ default:
+ return std::string();
+ }
+}
+
+void ifstream::open(const fs::path& p, std::ios_base::openmode mode)
+{
+ close();
+ m_file = fsbridge::fopen(p, openmodeToStr(mode).c_str());
+ if (m_file == nullptr) {
+ return;
+ }
+ m_filebuf = __gnu_cxx::stdio_filebuf<char>(m_file, mode);
+ rdbuf(&m_filebuf);
+ if (mode & std::ios_base::ate) {
+ seekg(0, std::ios_base::end);
+ }
+}
+
+void ifstream::close()
+{
+ if (m_file != nullptr) {
+ m_filebuf.close();
+ fclose(m_file);
+ }
+ m_file = nullptr;
+}
+
+void ofstream::open(const fs::path& p, std::ios_base::openmode mode)
+{
+ close();
+ m_file = fsbridge::fopen(p, openmodeToStr(mode).c_str());
+ if (m_file == nullptr) {
+ return;
+ }
+ m_filebuf = __gnu_cxx::stdio_filebuf<char>(m_file, mode);
+ rdbuf(&m_filebuf);
+ if (mode & std::ios_base::ate) {
+ seekp(0, std::ios_base::end);
+ }
+}
+
+void ofstream::close()
+{
+ if (m_file != nullptr) {
+ m_filebuf.close();
+ fclose(m_file);
+ }
+ m_file = nullptr;
+}
+#else // __GLIBCXX__
+
+static_assert(sizeof(*fs::path().BOOST_FILESYSTEM_C_STR) == sizeof(wchar_t),
+ "Warning: This build is using boost::filesystem ofstream and ifstream "
+ "implementations which will fail to open paths containing multibyte "
+ "characters. You should delete this static_assert to ignore this warning, "
+ "or switch to a different C++ standard library like the Microsoft C++ "
+ "Standard Library (where boost uses non-standard extensions to construct "
+ "stream objects with wide filenames), or the GNU libstdc++ library (where "
+ "a more complicated workaround has been implemented above).");
+
+#endif // __GLIBCXX__
+#endif // WIN32
+
} // fsbridge
diff --git a/src/fs.h b/src/fs.h
index a7074f446a..bdccb15232 100644
--- a/src/fs.h
+++ b/src/fs.h
@@ -7,6 +7,9 @@
#include <stdio.h>
#include <string>
+#if defined WIN32 && defined __GLIBCXX__
+#include <ext/stdio_filebuf.h>
+#endif
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
@@ -39,6 +42,54 @@ namespace fsbridge {
};
std::string get_filesystem_error_message(const fs::filesystem_error& e);
+
+ // GNU libstdc++ specific workaround for opening UTF-8 paths on Windows.
+ //
+ // On Windows, it is only possible to reliably access multibyte file paths through
+ // `wchar_t` APIs, not `char` APIs. But because the C++ standard doesn't
+ // require ifstream/ofstream `wchar_t` constructors, and the GNU library doesn't
+ // provide them (in contrast to the Microsoft C++ library, see
+ // https://stackoverflow.com/questions/821873/how-to-open-an-stdfstream-ofstream-or-ifstream-with-a-unicode-filename/822032#822032),
+ // Boost is forced to fall back to `char` constructors which may not work properly.
+ //
+ // Work around this issue by creating stream objects with `_wfopen` in
+ // combination with `__gnu_cxx::stdio_filebuf`. This workaround can be removed
+ // with an upgrade to C++17, where streams can be constructed directly from
+ // `std::filesystem::path` objects.
+
+#if defined WIN32 && defined __GLIBCXX__
+ class ifstream : public std::istream
+ {
+ public:
+ ifstream() = default;
+ explicit ifstream(const fs::path& p, std::ios_base::openmode mode = std::ios_base::in) { open(p, mode); }
+ ~ifstream() { close(); }
+ void open(const fs::path& p, std::ios_base::openmode mode = std::ios_base::in);
+ bool is_open() { return m_filebuf.is_open(); }
+ void close();
+
+ private:
+ __gnu_cxx::stdio_filebuf<char> m_filebuf;
+ FILE* m_file = nullptr;
+ };
+ class ofstream : public std::ostream
+ {
+ public:
+ ofstream() = default;
+ explicit ofstream(const fs::path& p, std::ios_base::openmode mode = std::ios_base::out) { open(p, mode); }
+ ~ofstream() { close(); }
+ void open(const fs::path& p, std::ios_base::openmode mode = std::ios_base::out);
+ bool is_open() { return m_filebuf.is_open(); }
+ void close();
+
+ private:
+ __gnu_cxx::stdio_filebuf<char> m_filebuf;
+ FILE* m_file = nullptr;
+ };
+#else // !(WIN32 && __GLIBCXX__)
+ typedef fs::ifstream ifstream;
+ typedef fs::ofstream ofstream;
+#endif // WIN32 && __GLIBCXX__
};
#endif // BITCOIN_FS_H
diff --git a/src/index/base.cpp b/src/index/base.cpp
index 788f7adccd..42c6b0373d 100644
--- a/src/index/base.cpp
+++ b/src/index/base.cpp
@@ -65,7 +65,7 @@ bool BaseIndex::Init()
return true;
}
-static const CBlockIndex* NextSyncBlock(const CBlockIndex* pindex_prev)
+static const CBlockIndex* NextSyncBlock(const CBlockIndex* pindex_prev) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
AssertLockHeld(cs_main);
diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp
index d95b41657c..2b19e11f08 100644
--- a/src/interfaces/node.cpp
+++ b/src/interfaces/node.cpp
@@ -38,6 +38,8 @@
#include <univalue.h>
class CWallet;
+fs::path GetWalletDir();
+std::vector<fs::path> ListWalletDir();
std::vector<std::shared_ptr<CWallet>> GetWallets();
namespace interfaces {
@@ -218,6 +220,18 @@ class NodeImpl : public Node
LOCK(::cs_main);
return ::pcoinsTip->GetCoin(output, coin);
}
+ std::string getWalletDir() override
+ {
+ return GetWalletDir().string();
+ }
+ std::vector<std::string> listWalletDir() override
+ {
+ std::vector<std::string> paths;
+ for (auto& path : ListWalletDir()) {
+ paths.push_back(path.string());
+ }
+ return paths;
+ }
std::vector<std::unique_ptr<Wallet>> getWallets() override
{
std::vector<std::unique_ptr<Wallet>> wallets;
diff --git a/src/interfaces/node.h b/src/interfaces/node.h
index 8185c015a9..1f8bbbff7a 100644
--- a/src/interfaces/node.h
+++ b/src/interfaces/node.h
@@ -173,6 +173,12 @@ public:
//! Get unspent outputs associated with a transaction.
virtual bool getUnspentOutput(const COutPoint& output, Coin& coin) = 0;
+ //! Return default wallet directory.
+ virtual std::string getWalletDir() = 0;
+
+ //! Return available wallets in wallet directory.
+ virtual std::vector<std::string> listWalletDir() = 0;
+
//! Return interfaces for accessing wallets (if any).
virtual std::vector<std::unique_ptr<Wallet>> getWallets() = 0;
diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp
index 566ae37509..84079fdc3c 100644
--- a/src/interfaces/wallet.cpp
+++ b/src/interfaces/wallet.cpp
@@ -103,7 +103,7 @@ static WalletTxStatus MakeWalletTxStatus(const CWalletTx& wtx) EXCLUSIVE_LOCKS_R
}
//! Construct wallet TxOut struct.
-static WalletTxOut MakeWalletTxOut(CWallet& wallet, const CWalletTx& wtx, int n, int depth) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+static WalletTxOut MakeWalletTxOut(CWallet& wallet, const CWalletTx& wtx, int n, int depth) EXCLUSIVE_LOCKS_REQUIRED(cs_main, wallet.cs_wallet)
{
WalletTxOut result;
result.txout = wtx.tx->vout[n];
diff --git a/src/net.cpp b/src/net.cpp
index f83f39a67d..c8d3efceed 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -1153,310 +1153,322 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
}
}
-void CConnman::ThreadSocketHandler()
+void CConnman::DisconnectNodes()
{
- unsigned int nPrevNodeCount = 0;
- while (!interruptNet)
{
- //
- // Disconnect nodes
- //
- {
- LOCK(cs_vNodes);
+ 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;
- }
+ 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)
+ // Disconnect unused nodes
+ std::vector<CNode*> vNodesCopy = vNodes;
+ for (CNode* pnode : vNodesCopy)
+ {
+ if (pnode->fDisconnect)
{
- if (pnode->fDisconnect)
- {
- // remove from vNodes
- vNodes.erase(remove(vNodes.begin(), vNodes.end(), pnode), vNodes.end());
+ // remove from vNodes
+ vNodes.erase(remove(vNodes.begin(), vNodes.end(), pnode), vNodes.end());
- // release outbound grant (if any)
- pnode->grantOutbound.Release();
+ // release outbound grant (if any)
+ pnode->grantOutbound.Release();
- // close socket and cleanup
- pnode->CloseSocketDisconnect();
+ // close socket and cleanup
+ pnode->CloseSocketDisconnect();
- // hold in disconnected pool until all refs are released
- pnode->Release();
- vNodesDisconnected.push_back(pnode);
- }
+ // hold in disconnected pool until all refs are released
+ pnode->Release();
+ vNodesDisconnected.push_back(pnode);
}
}
+ }
+ {
+ // Delete disconnected nodes
+ std::list<CNode*> vNodesDisconnectedCopy = vNodesDisconnected;
+ for (CNode* pnode : vNodesDisconnectedCopy)
{
- // Delete disconnected nodes
- std::list<CNode*> vNodesDisconnectedCopy = vNodesDisconnected;
- for (CNode* pnode : vNodesDisconnectedCopy)
- {
- // wait until threads are done using it
- if (pnode->GetRefCount() <= 0) {
- bool fDelete = false;
- {
- TRY_LOCK(pnode->cs_inventory, lockInv);
- if (lockInv) {
- TRY_LOCK(pnode->cs_vSend, lockSend);
- if (lockSend) {
- fDelete = true;
- }
+ // wait until threads are done using it
+ if (pnode->GetRefCount() <= 0) {
+ bool fDelete = false;
+ {
+ TRY_LOCK(pnode->cs_inventory, lockInv);
+ if (lockInv) {
+ TRY_LOCK(pnode->cs_vSend, lockSend);
+ if (lockSend) {
+ fDelete = true;
}
}
- if (fDelete) {
- vNodesDisconnected.remove(pnode);
- DeleteNode(pnode);
- }
+ }
+ if (fDelete) {
+ vNodesDisconnected.remove(pnode);
+ DeleteNode(pnode);
}
}
}
- size_t vNodesSize;
+ }
+}
+
+void CConnman::NotifyNumConnectionsChanged()
+{
+ size_t vNodesSize;
+ {
+ LOCK(cs_vNodes);
+ vNodesSize = vNodes.size();
+ }
+ if(vNodesSize != nPrevNodeCount) {
+ nPrevNodeCount = vNodesSize;
+ if(clientInterface)
+ clientInterface->NotifyNumConnectionsChanged(vNodesSize);
+ }
+}
+
+void CConnman::InactivityCheck(CNode *pnode)
+{
+ int64_t nTime = GetSystemTimeInSeconds();
+ if (nTime - pnode->nTimeConnected > 60)
+ {
+ if (pnode->nLastRecv == 0 || pnode->nLastSend == 0)
+ {
+ LogPrint(BCLog::NET, "socket no message in first 60 seconds, %d %d from %d\n", pnode->nLastRecv != 0, pnode->nLastSend != 0, pnode->GetId());
+ pnode->fDisconnect = true;
+ }
+ else if (nTime - pnode->nLastSend > TIMEOUT_INTERVAL)
{
- LOCK(cs_vNodes);
- vNodesSize = vNodes.size();
+ LogPrintf("socket sending timeout: %is\n", nTime - pnode->nLastSend);
+ pnode->fDisconnect = true;
+ }
+ else if (nTime - pnode->nLastRecv > (pnode->nVersion > BIP0031_VERSION ? TIMEOUT_INTERVAL : 90*60))
+ {
+ LogPrintf("socket receive timeout: %is\n", nTime - pnode->nLastRecv);
+ pnode->fDisconnect = true;
+ }
+ else if (pnode->nPingNonceSent && pnode->nPingUsecStart + TIMEOUT_INTERVAL * 1000000 < GetTimeMicros())
+ {
+ LogPrintf("ping timeout: %fs\n", 0.000001 * (GetTimeMicros() - pnode->nPingUsecStart));
+ pnode->fDisconnect = true;
}
- if(vNodesSize != nPrevNodeCount) {
- nPrevNodeCount = vNodesSize;
- if(clientInterface)
- clientInterface->NotifyNumConnectionsChanged(vNodesSize);
+ else if (!pnode->fSuccessfullyConnected)
+ {
+ LogPrint(BCLog::NET, "version handshake timeout from %d\n", pnode->GetId());
+ pnode->fDisconnect = true;
}
+ }
+}
- //
- // Find which sockets have data to receive
- //
- struct timeval timeout;
- timeout.tv_sec = 0;
- timeout.tv_usec = 50000; // frequency to poll pnode->vSend
-
- fd_set fdsetRecv;
- fd_set fdsetSend;
- fd_set fdsetError;
- FD_ZERO(&fdsetRecv);
- FD_ZERO(&fdsetSend);
- FD_ZERO(&fdsetError);
- SOCKET hSocketMax = 0;
- bool have_fds = false;
+void CConnman::SocketHandler()
+{
+ //
+ // Find which sockets have data to receive
+ //
+ struct timeval timeout;
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 50000; // frequency to poll pnode->vSend
- for (const ListenSocket& hListenSocket : vhListenSocket) {
- FD_SET(hListenSocket.socket, &fdsetRecv);
- hSocketMax = std::max(hSocketMax, hListenSocket.socket);
- have_fds = true;
- }
+ fd_set fdsetRecv;
+ fd_set fdsetSend;
+ fd_set fdsetError;
+ FD_ZERO(&fdsetRecv);
+ FD_ZERO(&fdsetSend);
+ FD_ZERO(&fdsetError);
+ SOCKET hSocketMax = 0;
+ bool have_fds = false;
+ for (const ListenSocket& hListenSocket : vhListenSocket) {
+ FD_SET(hListenSocket.socket, &fdsetRecv);
+ hSocketMax = std::max(hSocketMax, hListenSocket.socket);
+ have_fds = true;
+ }
+
+ {
+ LOCK(cs_vNodes);
+ for (CNode* pnode : vNodes)
{
- LOCK(cs_vNodes);
- for (CNode* pnode : vNodes)
+ // Implement the following logic:
+ // * If there is data to send, select() for sending data. As this only
+ // happens when optimistic write failed, we choose to first drain the
+ // write buffer in this case before receiving more. This avoids
+ // needlessly queueing received data, if the remote peer is not themselves
+ // receiving data. This means properly utilizing TCP flow control signalling.
+ // * Otherwise, if there is space left in the receive buffer, select() for
+ // receiving data.
+ // * Hand off all complete messages to the processor, to be handled without
+ // blocking here.
+
+ bool select_recv = !pnode->fPauseRecv;
+ bool select_send;
{
- // Implement the following logic:
- // * If there is data to send, select() for sending data. As this only
- // happens when optimistic write failed, we choose to first drain the
- // write buffer in this case before receiving more. This avoids
- // needlessly queueing received data, if the remote peer is not themselves
- // receiving data. This means properly utilizing TCP flow control signalling.
- // * Otherwise, if there is space left in the receive buffer, select() for
- // receiving data.
- // * Hand off all complete messages to the processor, to be handled without
- // blocking here.
-
- bool select_recv = !pnode->fPauseRecv;
- bool select_send;
- {
- LOCK(pnode->cs_vSend);
- select_send = !pnode->vSendMsg.empty();
- }
+ LOCK(pnode->cs_vSend);
+ select_send = !pnode->vSendMsg.empty();
+ }
- LOCK(pnode->cs_hSocket);
- if (pnode->hSocket == INVALID_SOCKET)
- continue;
+ LOCK(pnode->cs_hSocket);
+ if (pnode->hSocket == INVALID_SOCKET)
+ continue;
- FD_SET(pnode->hSocket, &fdsetError);
- hSocketMax = std::max(hSocketMax, pnode->hSocket);
- have_fds = true;
+ FD_SET(pnode->hSocket, &fdsetError);
+ hSocketMax = std::max(hSocketMax, pnode->hSocket);
+ have_fds = true;
- if (select_send) {
- FD_SET(pnode->hSocket, &fdsetSend);
- continue;
- }
- if (select_recv) {
- FD_SET(pnode->hSocket, &fdsetRecv);
- }
+ if (select_send) {
+ FD_SET(pnode->hSocket, &fdsetSend);
+ continue;
+ }
+ if (select_recv) {
+ FD_SET(pnode->hSocket, &fdsetRecv);
}
}
+ }
- int nSelect = select(have_fds ? hSocketMax + 1 : 0,
- &fdsetRecv, &fdsetSend, &fdsetError, &timeout);
- if (interruptNet)
- return;
+ int nSelect = select(have_fds ? hSocketMax + 1 : 0,
+ &fdsetRecv, &fdsetSend, &fdsetError, &timeout);
+ if (interruptNet)
+ return;
- if (nSelect == SOCKET_ERROR)
+ if (nSelect == SOCKET_ERROR)
+ {
+ if (have_fds)
{
- if (have_fds)
- {
- int nErr = WSAGetLastError();
- LogPrintf("socket select error %s\n", NetworkErrorString(nErr));
- for (unsigned int i = 0; i <= hSocketMax; i++)
- FD_SET(i, &fdsetRecv);
- }
- FD_ZERO(&fdsetSend);
- FD_ZERO(&fdsetError);
- if (!interruptNet.sleep_for(std::chrono::milliseconds(timeout.tv_usec/1000)))
- return;
+ int nErr = WSAGetLastError();
+ LogPrintf("socket select error %s\n", NetworkErrorString(nErr));
+ for (unsigned int i = 0; i <= hSocketMax; i++)
+ FD_SET(i, &fdsetRecv);
}
+ FD_ZERO(&fdsetSend);
+ FD_ZERO(&fdsetError);
+ if (!interruptNet.sleep_for(std::chrono::milliseconds(timeout.tv_usec/1000)))
+ return;
+ }
- //
- // Accept new connections
- //
- for (const ListenSocket& hListenSocket : vhListenSocket)
+ //
+ // Accept new connections
+ //
+ for (const ListenSocket& hListenSocket : vhListenSocket)
+ {
+ if (hListenSocket.socket != INVALID_SOCKET && FD_ISSET(hListenSocket.socket, &fdsetRecv))
{
- if (hListenSocket.socket != INVALID_SOCKET && FD_ISSET(hListenSocket.socket, &fdsetRecv))
- {
- AcceptConnection(hListenSocket);
- }
+ AcceptConnection(hListenSocket);
}
+ }
+
+ //
+ // Service each socket
+ //
+ std::vector<CNode*> vNodesCopy;
+ {
+ LOCK(cs_vNodes);
+ vNodesCopy = vNodes;
+ for (CNode* pnode : vNodesCopy)
+ pnode->AddRef();
+ }
+ for (CNode* pnode : vNodesCopy)
+ {
+ if (interruptNet)
+ return;
//
- // Service each socket
+ // Receive
//
- std::vector<CNode*> vNodesCopy;
+ bool recvSet = false;
+ bool sendSet = false;
+ bool errorSet = false;
{
- LOCK(cs_vNodes);
- vNodesCopy = vNodes;
- for (CNode* pnode : vNodesCopy)
- pnode->AddRef();
+ LOCK(pnode->cs_hSocket);
+ if (pnode->hSocket == INVALID_SOCKET)
+ continue;
+ recvSet = FD_ISSET(pnode->hSocket, &fdsetRecv);
+ sendSet = FD_ISSET(pnode->hSocket, &fdsetSend);
+ errorSet = FD_ISSET(pnode->hSocket, &fdsetError);
}
- for (CNode* pnode : vNodesCopy)
+ if (recvSet || errorSet)
{
- if (interruptNet)
- return;
-
- //
- // Receive
- //
- bool recvSet = false;
- bool sendSet = false;
- bool errorSet = false;
+ // typical socket buffer is 8K-64K
+ char pchBuf[0x10000];
+ int nBytes = 0;
{
LOCK(pnode->cs_hSocket);
if (pnode->hSocket == INVALID_SOCKET)
continue;
- recvSet = FD_ISSET(pnode->hSocket, &fdsetRecv);
- sendSet = FD_ISSET(pnode->hSocket, &fdsetSend);
- errorSet = FD_ISSET(pnode->hSocket, &fdsetError);
+ nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT);
}
- if (recvSet || errorSet)
+ if (nBytes > 0)
{
- // typical socket buffer is 8K-64K
- char pchBuf[0x10000];
- int nBytes = 0;
- {
- LOCK(pnode->cs_hSocket);
- if (pnode->hSocket == INVALID_SOCKET)
- continue;
- nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT);
- }
- if (nBytes > 0)
- {
- bool notify = false;
- if (!pnode->ReceiveMsgBytes(pchBuf, nBytes, notify))
- pnode->CloseSocketDisconnect();
- RecordBytesRecv(nBytes);
- if (notify) {
- size_t nSizeAdded = 0;
- auto it(pnode->vRecvMsg.begin());
- for (; it != pnode->vRecvMsg.end(); ++it) {
- if (!it->complete())
- break;
- nSizeAdded += it->vRecv.size() + CMessageHeader::HEADER_SIZE;
- }
- {
- LOCK(pnode->cs_vProcessMsg);
- pnode->vProcessMsg.splice(pnode->vProcessMsg.end(), pnode->vRecvMsg, pnode->vRecvMsg.begin(), it);
- pnode->nProcessQueueSize += nSizeAdded;
- pnode->fPauseRecv = pnode->nProcessQueueSize > nReceiveFloodSize;
- }
- WakeMessageHandler();
- }
- }
- else if (nBytes == 0)
- {
- // socket closed gracefully
- if (!pnode->fDisconnect) {
- LogPrint(BCLog::NET, "socket closed\n");
- }
+ bool notify = false;
+ if (!pnode->ReceiveMsgBytes(pchBuf, nBytes, notify))
pnode->CloseSocketDisconnect();
- }
- else if (nBytes < 0)
- {
- // error
- int nErr = WSAGetLastError();
- if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS)
+ RecordBytesRecv(nBytes);
+ if (notify) {
+ size_t nSizeAdded = 0;
+ auto it(pnode->vRecvMsg.begin());
+ for (; it != pnode->vRecvMsg.end(); ++it) {
+ if (!it->complete())
+ break;
+ nSizeAdded += it->vRecv.size() + CMessageHeader::HEADER_SIZE;
+ }
{
- if (!pnode->fDisconnect)
- LogPrintf("socket recv error %s\n", NetworkErrorString(nErr));
- pnode->CloseSocketDisconnect();
+ LOCK(pnode->cs_vProcessMsg);
+ pnode->vProcessMsg.splice(pnode->vProcessMsg.end(), pnode->vRecvMsg, pnode->vRecvMsg.begin(), it);
+ pnode->nProcessQueueSize += nSizeAdded;
+ pnode->fPauseRecv = pnode->nProcessQueueSize > nReceiveFloodSize;
}
+ WakeMessageHandler();
}
}
-
- //
- // Send
- //
- if (sendSet)
+ else if (nBytes == 0)
{
- LOCK(pnode->cs_vSend);
- size_t nBytes = SocketSendData(pnode);
- if (nBytes) {
- RecordBytesSent(nBytes);
+ // socket closed gracefully
+ if (!pnode->fDisconnect) {
+ LogPrint(BCLog::NET, "socket closed\n");
}
+ pnode->CloseSocketDisconnect();
}
-
- //
- // Inactivity checking
- //
- int64_t nTime = GetSystemTimeInSeconds();
- if (nTime - pnode->nTimeConnected > 60)
+ else if (nBytes < 0)
{
- if (pnode->nLastRecv == 0 || pnode->nLastSend == 0)
- {
- LogPrint(BCLog::NET, "socket no message in first 60 seconds, %d %d from %d\n", pnode->nLastRecv != 0, pnode->nLastSend != 0, pnode->GetId());
- pnode->fDisconnect = true;
- }
- else if (nTime - pnode->nLastSend > TIMEOUT_INTERVAL)
- {
- LogPrintf("socket sending timeout: %is\n", nTime - pnode->nLastSend);
- pnode->fDisconnect = true;
- }
- else if (nTime - pnode->nLastRecv > (pnode->nVersion > BIP0031_VERSION ? TIMEOUT_INTERVAL : 90*60))
- {
- LogPrintf("socket receive timeout: %is\n", nTime - pnode->nLastRecv);
- pnode->fDisconnect = true;
- }
- else if (pnode->nPingNonceSent && pnode->nPingUsecStart + TIMEOUT_INTERVAL * 1000000 < GetTimeMicros())
- {
- LogPrintf("ping timeout: %fs\n", 0.000001 * (GetTimeMicros() - pnode->nPingUsecStart));
- pnode->fDisconnect = true;
- }
- else if (!pnode->fSuccessfullyConnected)
+ // error
+ int nErr = WSAGetLastError();
+ if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS)
{
- LogPrint(BCLog::NET, "version handshake timeout from %d\n", pnode->GetId());
- pnode->fDisconnect = true;
+ if (!pnode->fDisconnect)
+ LogPrintf("socket recv error %s\n", NetworkErrorString(nErr));
+ pnode->CloseSocketDisconnect();
}
}
}
+
+ //
+ // Send
+ //
+ if (sendSet)
{
- LOCK(cs_vNodes);
- for (CNode* pnode : vNodesCopy)
- pnode->Release();
+ LOCK(pnode->cs_vSend);
+ size_t nBytes = SocketSendData(pnode);
+ if (nBytes) {
+ RecordBytesSent(nBytes);
+ }
}
+
+ InactivityCheck(pnode);
+ }
+ {
+ LOCK(cs_vNodes);
+ for (CNode* pnode : vNodesCopy)
+ pnode->Release();
+ }
+}
+
+void CConnman::ThreadSocketHandler()
+{
+ while (!interruptNet)
+ {
+ DisconnectNodes();
+ NotifyNumConnectionsChanged();
+ SocketHandler();
}
}
@@ -2217,6 +2229,7 @@ CConnman::CConnman(uint64_t nSeed0In, uint64_t nSeed1In) : nSeed0(nSeed0In), nSe
setBannedIsDirty = false;
fAddressesInitialized = false;
nLastNodeId = 0;
+ nPrevNodeCount = 0;
nSendBufferMaxSize = 0;
nReceiveFloodSize = 0;
flagInterruptMsgProc = false;
diff --git a/src/net.h b/src/net.h
index c43429c7f3..49360a7206 100644
--- a/src/net.h
+++ b/src/net.h
@@ -338,6 +338,10 @@ private:
void ThreadOpenConnections(std::vector<std::string> connect);
void ThreadMessageHandler();
void AcceptConnection(const ListenSocket& hListenSocket);
+ void DisconnectNodes();
+ void NotifyNumConnectionsChanged();
+ void InactivityCheck(CNode *pnode);
+ void SocketHandler();
void ThreadSocketHandler();
void ThreadDNSAddressSeed();
@@ -408,6 +412,7 @@ private:
std::list<CNode*> vNodesDisconnected;
mutable CCriticalSection cs_vNodes;
std::atomic<NodeId> nLastNodeId;
+ unsigned int nPrevNodeCount;
/** Services this instance offers */
ServiceFlags nLocalServices;
diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h
index 6d8b530f69..0f834eb8c1 100644
--- a/src/primitives/transaction.h
+++ b/src/primitives/transaction.h
@@ -73,7 +73,7 @@ public:
/* Below flags apply in the context of BIP 68*/
/* If this flag set, CTxIn::nSequence is NOT interpreted as a
* relative lock-time. */
- static const uint32_t SEQUENCE_LOCKTIME_DISABLE_FLAG = (1 << 31);
+ static const uint32_t SEQUENCE_LOCKTIME_DISABLE_FLAG = (1U << 31);
/* If CTxIn::nSequence encodes a relative lock-time and this flag
* is set, the relative lock-time has units of 512 seconds,
diff --git a/src/qt/README.md b/src/qt/README.md
index 3ec538b4f4..0eb18f7cd5 100644
--- a/src/qt/README.md
+++ b/src/qt/README.md
@@ -64,8 +64,8 @@ Represents the view to a single wallet.
* `callback.h`
* `guiconstants.h`: UI colors, app name, etc
* `guiutil.h`: several helper functions
-* `macdockiconhandler.(h/cpp)`
-* `macdockiconhandler.(h/cpp)`: display notifications in macOS
+* `macdockiconhandler.(h/mm)`: macOS dock icon handler
+* `macnotificationhandler.(h/mm)`: display notifications in macOS
## Contribute
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 1e950e2686..c0f9b3d5d3 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -51,7 +51,6 @@
#include <QThread>
#include <QTimer>
#include <QTranslator>
-#include <QSslConfiguration>
#if defined(QT_STATICPLUGIN)
#include <QtPlugin>
@@ -440,8 +439,10 @@ void BitcoinApplication::addWallet(WalletModel* walletModel)
window->setCurrentWallet(walletModel->getWalletName());
}
+#ifdef ENABLE_BIP70
connect(walletModel, &WalletModel::coinsSent,
paymentServer, &PaymentServer::fetchPaymentACK);
+#endif
connect(walletModel, &WalletModel::unload, this, &BitcoinApplication::removeWallet);
m_wallet_models.push_back(walletModel);
@@ -468,7 +469,9 @@ void BitcoinApplication::initializeResult(bool success)
// Log this only after AppInitMain finishes, as then logging setup is guaranteed complete
qWarning() << "Platform customization:" << platformStyle->getName();
#ifdef ENABLE_WALLET
+#ifdef ENABLE_BIP70
PaymentServer::LoadRootCAs();
+#endif
paymentServer->setOptionsModel(optionsModel);
#endif
@@ -537,7 +540,7 @@ WId BitcoinApplication::getMainWinId() const
static void SetupUIArgs()
{
-#ifdef ENABLE_WALLET
+#if defined(ENABLE_WALLET) && defined(ENABLE_BIP70)
gArgs.AddArg("-allowselfsignedrootcertificates", strprintf("Allow self signed root certificates (default: %u)", DEFAULT_SELFSIGNED_ROOTCERTS), true, OptionsCategory::GUI);
#endif
gArgs.AddArg("-choosedatadir", strprintf("Choose data directory on startup (default: %u)", DEFAULT_CHOOSE_DATADIR), false, OptionsCategory::GUI);
@@ -552,6 +555,10 @@ static void SetupUIArgs()
#ifndef BITCOIN_QT_TEST
int main(int argc, char *argv[])
{
+#ifdef WIN32
+ util::WinCmdLineArgs winArgs;
+ std::tie(argc, argv) = winArgs.get();
+#endif
SetupEnvironment();
std::unique_ptr<interfaces::Node> node = interfaces::MakeNode();
@@ -573,13 +580,6 @@ int main(int argc, char *argv[])
#ifdef Q_OS_MAC
QApplication::setAttribute(Qt::AA_DontShowIconsInMenus);
#endif
-#if QT_VERSION >= 0x050500
- // Because of the POODLE attack it is recommended to disable SSLv3 (https://disablessl3.com/),
- // so set SSL protocols to TLS1.0+.
- QSslConfiguration sslconf = QSslConfiguration::defaultConfiguration();
- sslconf.setProtocol(QSsl::TlsV1_0OrLater);
- QSslConfiguration::setDefaultConfiguration(sslconf);
-#endif
// Register meta types used for QMetaObject::invokeMethod
qRegisterMetaType< bool* >();
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 51aff08c42..311841017f 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -620,14 +620,16 @@ void BitcoinGUI::createTrayIconMenu()
trayIconMenu->addAction(toggleHideAction);
trayIconMenu->addSeparator();
#endif
- trayIconMenu->addAction(sendCoinsMenuAction);
- trayIconMenu->addAction(receiveCoinsMenuAction);
- trayIconMenu->addSeparator();
- trayIconMenu->addAction(signMessageAction);
- trayIconMenu->addAction(verifyMessageAction);
- trayIconMenu->addSeparator();
+ if (enableWallet) {
+ trayIconMenu->addAction(sendCoinsMenuAction);
+ trayIconMenu->addAction(receiveCoinsMenuAction);
+ trayIconMenu->addSeparator();
+ trayIconMenu->addAction(signMessageAction);
+ trayIconMenu->addAction(verifyMessageAction);
+ trayIconMenu->addSeparator();
+ trayIconMenu->addAction(openRPCConsoleAction);
+ }
trayIconMenu->addAction(optionsAction);
- trayIconMenu->addAction(openRPCConsoleAction);
#ifndef Q_OS_MAC // This is built-in on Mac
trayIconMenu->addSeparator();
trayIconMenu->addAction(quitAction);
diff --git a/src/qt/callback.h b/src/qt/callback.h
deleted file mode 100644
index da6b0c4c2e..0000000000
--- a/src/qt/callback.h
+++ /dev/null
@@ -1,30 +0,0 @@
-#ifndef BITCOIN_QT_CALLBACK_H
-#define BITCOIN_QT_CALLBACK_H
-
-#include <QObject>
-
-class Callback : public QObject
-{
- Q_OBJECT
-public Q_SLOTS:
- virtual void call() = 0;
-};
-
-template <typename F>
-class FunctionCallback : public Callback
-{
- F f;
-
-public:
- explicit FunctionCallback(F f_) : f(std::move(f_)) {}
- ~FunctionCallback() override {}
- void call() override { f(this); }
-};
-
-template <typename F>
-FunctionCallback<F>* makeCallback(F f)
-{
- return new FunctionCallback<F>(std::move(f));
-}
-
-#endif // BITCOIN_QT_CALLBACK_H
diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp
index b2cf4b6399..183444efab 100644
--- a/src/qt/clientmodel.cpp
+++ b/src/qt/clientmodel.cpp
@@ -177,6 +177,11 @@ QString ClientModel::dataDir() const
return GUIUtil::boostPathToQString(GetDataDir());
}
+QString ClientModel::blocksDir() const
+{
+ return GUIUtil::boostPathToQString(GetBlocksDir());
+}
+
void ClientModel::updateBanlist()
{
banTableModel->refresh();
diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h
index ed7ecbf73b..79e7074cca 100644
--- a/src/qt/clientmodel.h
+++ b/src/qt/clientmodel.h
@@ -69,6 +69,7 @@ public:
bool isReleaseVersion() const;
QString formatClientStartupTime() const;
QString dataDir() const;
+ QString blocksDir() const;
bool getProxyInfo(std::string& ip_port) const;
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index 68330c51fa..ea970c0bc9 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -2,10 +2,15 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#if defined(HAVE_CONFIG_H)
+#include <config/bitcoin-config.h>
+#endif
+
#include <qt/coincontroldialog.h>
#include <qt/forms/ui_coincontroldialog.h>
#include <qt/addresstablemodel.h>
+#include <base58.h>
#include <qt/bitcoinunits.h>
#include <qt/guiutil.h>
#include <qt/optionsmodel.h>
diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui
index 695ed61228..dca16d6f78 100644
--- a/src/qt/forms/debugwindow.ui
+++ b/src/qt/forms/debugwindow.ui
@@ -127,6 +127,9 @@
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
+ <property name="toolTip">
+ <string>To specify a non-default location of the data directory use the '%1' option.</string>
+ </property>
<property name="text">
<string>N/A</string>
</property>
@@ -142,13 +145,42 @@
</widget>
</item>
<item row="5" column="0">
+ <widget class="QLabel" name="label_11">
+ <property name="text">
+ <string>Blocksdir</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1" colspan="2">
+ <widget class="QLabel" name="blocksDir">
+ <property name="cursor">
+ <cursorShape>IBeamCursor</cursorShape>
+ </property>
+ <property name="toolTip">
+ <string>To specify a non-default location of the blocks directory use the '%1' option.</string>
+ </property>
+ <property name="text">
+ <string>N/A</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="0">
<widget class="QLabel" name="label_13">
<property name="text">
<string>Startup time</string>
</property>
</widget>
</item>
- <item row="5" column="1" colspan="2">
+ <item row="6" column="1" colspan="2">
<widget class="QLabel" name="startupTime">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -164,7 +196,7 @@
</property>
</widget>
</item>
- <item row="6" column="0">
+ <item row="7" column="0">
<widget class="QLabel" name="labelNetwork">
<property name="font">
<font>
@@ -177,14 +209,14 @@
</property>
</widget>
</item>
- <item row="7" column="0">
+ <item row="8" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
- <item row="7" column="1" colspan="2">
+ <item row="8" column="1" colspan="2">
<widget class="QLabel" name="networkName">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -200,14 +232,14 @@
</property>
</widget>
</item>
- <item row="8" column="0">
+ <item row="9" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Number of connections</string>
</property>
</widget>
</item>
- <item row="8" column="1" colspan="2">
+ <item row="9" column="1" colspan="2">
<widget class="QLabel" name="numberOfConnections">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -223,7 +255,7 @@
</property>
</widget>
</item>
- <item row="9" column="0">
+ <item row="10" column="0">
<widget class="QLabel" name="label_10">
<property name="font">
<font>
@@ -236,14 +268,14 @@
</property>
</widget>
</item>
- <item row="10" column="0">
+ <item row="11" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Current number of blocks</string>
</property>
</widget>
</item>
- <item row="10" column="1" colspan="2">
+ <item row="11" column="1" colspan="2">
<widget class="QLabel" name="numberOfBlocks">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -259,14 +291,14 @@
</property>
</widget>
</item>
- <item row="11" column="0">
+ <item row="12" column="0">
<widget class="QLabel" name="labelLastBlockTime">
<property name="text">
<string>Last block time</string>
</property>
</widget>
</item>
- <item row="11" column="1" colspan="2">
+ <item row="12" column="1" colspan="2">
<widget class="QLabel" name="lastBlockTime">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -282,7 +314,7 @@
</property>
</widget>
</item>
- <item row="12" column="0">
+ <item row="13" column="0">
<widget class="QLabel" name="labelMempoolTitle">
<property name="font">
<font>
@@ -295,14 +327,14 @@
</property>
</widget>
</item>
- <item row="13" column="0">
+ <item row="14" column="0">
<widget class="QLabel" name="labelNumberOfTransactions">
<property name="text">
<string>Current number of transactions</string>
</property>
</widget>
</item>
- <item row="13" column="1">
+ <item row="14" column="1">
<widget class="QLabel" name="mempoolNumberTxs">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -318,14 +350,14 @@
</property>
</widget>
</item>
- <item row="14" column="0">
+ <item row="15" column="0">
<widget class="QLabel" name="labelMemoryUsage">
<property name="text">
<string>Memory usage</string>
</property>
</widget>
</item>
- <item row="14" column="1">
+ <item row="15" column="1">
<widget class="QLabel" name="mempoolSize">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -341,7 +373,7 @@
</property>
</widget>
</item>
- <item row="12" column="2" rowspan="3">
+ <item row="13" column="2" rowspan="3">
<layout class="QVBoxLayout" name="verticalLayoutDebugButton">
<property name="spacing">
<number>3</number>
@@ -381,7 +413,7 @@
</item>
</layout>
</item>
- <item row="15" column="0">
+ <item row="16" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index b894fc8166..5f6af61a70 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -367,7 +367,7 @@ bool openBitcoinConf()
fs::path pathConfig = GetConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME));
/* Create the file */
- fs::ofstream configFile(pathConfig, std::ios_base::app);
+ fsbridge::ofstream configFile(pathConfig, std::ios_base::app);
if (!configFile.good())
return false;
@@ -611,7 +611,7 @@ fs::path static GetAutostartFilePath()
bool GetStartOnSystemStartup()
{
- fs::ifstream optionFile(GetAutostartFilePath());
+ fsbridge::ifstream optionFile(GetAutostartFilePath());
if (!optionFile.good())
return false;
// Scan through file for "Hidden=true":
@@ -642,7 +642,7 @@ bool SetStartOnSystemStartup(bool fAutoStart)
fs::create_directories(GetAutostartDir());
- fs::ofstream optionFile(GetAutostartFilePath(), std::ios_base::out|std::ios_base::trunc);
+ fsbridge::ofstream optionFile(GetAutostartFilePath(), std::ios_base::out | std::ios_base::trunc);
if (!optionFile.good())
return false;
std::string chain = gArgs.GetChainName();
diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp
index bcafc8f859..3f118e37f1 100644
--- a/src/qt/paymentserver.cpp
+++ b/src/qt/paymentserver.cpp
@@ -2,6 +2,10 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#if defined(HAVE_CONFIG_H)
+#include <config/bitcoin-config.h>
+#endif
+
#include <qt/paymentserver.h>
#include <qt/bitcoinunits.h>
@@ -45,6 +49,7 @@
const int BITCOIN_IPC_CONNECT_TIMEOUT = 1000; // milliseconds
const QString BITCOIN_IPC_PREFIX("bitcoin:");
+#ifdef ENABLE_BIP70
// BIP70 payment protocol messages
const char* BIP70_MESSAGE_PAYMENTACK = "PaymentACK";
const char* BIP70_MESSAGE_PAYMENTREQUEST = "PaymentRequest";
@@ -52,21 +57,7 @@ const char* BIP70_MESSAGE_PAYMENTREQUEST = "PaymentRequest";
const char* BIP71_MIMETYPE_PAYMENT = "application/bitcoin-payment";
const char* BIP71_MIMETYPE_PAYMENTACK = "application/bitcoin-paymentack";
const char* BIP71_MIMETYPE_PAYMENTREQUEST = "application/bitcoin-paymentrequest";
-
-struct X509StoreDeleter {
- void operator()(X509_STORE* b) {
- X509_STORE_free(b);
- }
-};
-
-struct X509Deleter {
- void operator()(X509* b) { X509_free(b); }
-};
-
-namespace // Anon namespace
-{
- std::unique_ptr<X509_STORE, X509StoreDeleter> certStore;
-}
+#endif
//
// Create a name that is unique for:
@@ -93,94 +84,6 @@ static QString ipcServerName()
static QList<QString> savedPaymentRequests;
-static void ReportInvalidCertificate(const QSslCertificate& cert)
-{
- qDebug() << QString("%1: Payment server found an invalid certificate: ").arg(__func__) << cert.serialNumber() << cert.subjectInfo(QSslCertificate::CommonName) << cert.subjectInfo(QSslCertificate::DistinguishedNameQualifier) << cert.subjectInfo(QSslCertificate::OrganizationalUnitName);
-}
-
-//
-// Load OpenSSL's list of root certificate authorities
-//
-void PaymentServer::LoadRootCAs(X509_STORE* _store)
-{
- // Unit tests mostly use this, to pass in fake root CAs:
- if (_store)
- {
- certStore.reset(_store);
- return;
- }
-
- // Normal execution, use either -rootcertificates or system certs:
- certStore.reset(X509_STORE_new());
-
- // Note: use "-system-" default here so that users can pass -rootcertificates=""
- // and get 'I don't like X.509 certificates, don't trust anybody' behavior:
- QString certFile = QString::fromStdString(gArgs.GetArg("-rootcertificates", "-system-"));
-
- // Empty store
- if (certFile.isEmpty()) {
- qDebug() << QString("PaymentServer::%1: Payment request authentication via X.509 certificates disabled.").arg(__func__);
- return;
- }
-
- QList<QSslCertificate> certList;
-
- if (certFile != "-system-") {
- qDebug() << QString("PaymentServer::%1: Using \"%2\" as trusted root certificate.").arg(__func__).arg(certFile);
-
- certList = QSslCertificate::fromPath(certFile);
- // Use those certificates when fetching payment requests, too:
- QSslSocket::setDefaultCaCertificates(certList);
- } else
- certList = QSslSocket::systemCaCertificates();
-
- int nRootCerts = 0;
- const QDateTime currentTime = QDateTime::currentDateTime();
-
- for (const QSslCertificate& cert : certList) {
- // Don't log NULL certificates
- if (cert.isNull())
- continue;
-
- // Not yet active/valid, or expired certificate
- if (currentTime < cert.effectiveDate() || currentTime > cert.expiryDate()) {
- ReportInvalidCertificate(cert);
- continue;
- }
-
- // Blacklisted certificate
- if (cert.isBlacklisted()) {
- ReportInvalidCertificate(cert);
- continue;
- }
- QByteArray certData = cert.toDer();
- const unsigned char *data = (const unsigned char *)certData.data();
-
- std::unique_ptr<X509, X509Deleter> x509(d2i_X509(0, &data, certData.size()));
- if (x509 && X509_STORE_add_cert(certStore.get(), x509.get()))
- {
- // Note: X509_STORE increases the reference count to the X509 object,
- // we still have to release our reference to it.
- ++nRootCerts;
- }
- else
- {
- ReportInvalidCertificate(cert);
- continue;
- }
- }
- qWarning() << "PaymentServer::LoadRootCAs: Loaded " << nRootCerts << " root certificates";
-
- // Project for another day:
- // Fetch certificate revocation lists, and add them to certStore.
- // Issues to consider:
- // performance (start a thread to fetch in background?)
- // privacy (fetch through tor/proxy so IP address isn't revealed)
- // would it be easier to just use a compiled-in blacklist?
- // or use Qt's blacklist?
- // "certificate stapling" with server-side caching is more efficient
-}
-
//
// Sending to the server is done synchronously, at startup.
// If the server isn't already running, startup continues,
@@ -221,6 +124,7 @@ void PaymentServer::ipcParseCommandLine(interfaces::Node& node, int argc, char*
}
}
}
+#ifdef ENABLE_BIP70
else if (QFile::exists(arg)) // Filename
{
savedPaymentRequests.append(arg);
@@ -244,6 +148,7 @@ void PaymentServer::ipcParseCommandLine(interfaces::Node& node, int argc, char*
// GUI hasn't started yet so we can't pop up a message box.
qWarning() << "PaymentServer::ipcSendCommandLine: Payment request file does not exist: " << arg;
}
+#endif
}
}
@@ -290,12 +195,16 @@ PaymentServer::PaymentServer(QObject* parent, bool startLocalServer) :
QObject(parent),
saveURIs(true),
uriServer(0),
- netManager(0),
optionsModel(0)
+#ifdef ENABLE_BIP70
+ ,netManager(0)
+#endif
{
+#ifdef ENABLE_BIP70
// Verify that the version of the library that we linked against is
// compatible with the version of the headers we compiled against.
GOOGLE_PROTOBUF_VERIFY_VERSION;
+#endif
// Install global event filter to catch QFileOpenEvents
// on Mac: sent when you click bitcoin: links
@@ -319,14 +228,18 @@ PaymentServer::PaymentServer(QObject* parent, bool startLocalServer) :
}
else {
connect(uriServer, &QLocalServer::newConnection, this, &PaymentServer::handleURIConnection);
+#ifdef ENABLE_BIP70
connect(this, &PaymentServer::receivedPaymentACK, this, &PaymentServer::handlePaymentACK);
+#endif
}
}
}
PaymentServer::~PaymentServer()
{
+#ifdef ENABLE_BIP70
google::protobuf::ShutdownProtobufLibrary();
+#endif
}
//
@@ -349,33 +262,11 @@ bool PaymentServer::eventFilter(QObject *object, QEvent *event)
return QObject::eventFilter(object, event);
}
-void PaymentServer::initNetManager()
-{
- if (!optionsModel)
- return;
- delete netManager;
-
- // netManager is used to fetch paymentrequests given in bitcoin: URIs
- netManager = new QNetworkAccessManager(this);
-
- QNetworkProxy proxy;
-
- // Query active SOCKS5 proxy
- if (optionsModel->getProxySettings(proxy)) {
- netManager->setProxy(proxy);
-
- qDebug() << "PaymentServer::initNetManager: Using SOCKS5 proxy" << proxy.hostName() << ":" << proxy.port();
- }
- else
- qDebug() << "PaymentServer::initNetManager: No active proxy server found.";
-
- connect(netManager, &QNetworkAccessManager::finished, this, &PaymentServer::netRequestFinished);
- connect(netManager, &QNetworkAccessManager::sslErrors, this, &PaymentServer::reportSslErrors);
-}
-
void PaymentServer::uiReady()
{
+#ifdef ENABLE_BIP70
initNetManager();
+#endif
saveURIs = false;
for (const QString& s : savedPaymentRequests)
@@ -403,6 +294,10 @@ void PaymentServer::handleURIOrFile(const QString& s)
QUrlQuery uri((QUrl(s)));
if (uri.hasQueryItem("r")) // payment request URI
{
+#ifdef ENABLE_BIP70
+ Q_EMIT message(tr("URI handling"),
+ tr("You are using a BIP70 URL which will be unsupported in the future."),
+ CClientUIInterface::ICON_WARNING);
QByteArray temp;
temp.append(uri.queryItemValue("r"));
QString decoded = QUrl::fromPercentEncoding(temp);
@@ -420,7 +315,11 @@ void PaymentServer::handleURIOrFile(const QString& s)
tr("Payment request fetch URL is invalid: %1").arg(fetchUrl.toString()),
CClientUIInterface::ICON_WARNING);
}
-
+#else
+ Q_EMIT message(tr("URI handling"),
+ tr("Cannot process payment request because BIP70 support was not compiled in."),
+ CClientUIInterface::ICON_WARNING);
+#endif
return;
}
else // normal URI
@@ -444,6 +343,7 @@ void PaymentServer::handleURIOrFile(const QString& s)
}
}
+#ifdef ENABLE_BIP70
if (QFile::exists(s)) // payment request file
{
PaymentRequestPlus request;
@@ -459,6 +359,7 @@ void PaymentServer::handleURIOrFile(const QString& s)
return;
}
+#endif
}
void PaymentServer::handleURIConnection()
@@ -481,6 +382,140 @@ void PaymentServer::handleURIConnection()
handleURIOrFile(msg);
}
+void PaymentServer::setOptionsModel(OptionsModel *_optionsModel)
+{
+ this->optionsModel = _optionsModel;
+}
+
+#ifdef ENABLE_BIP70
+struct X509StoreDeleter {
+ void operator()(X509_STORE* b) {
+ X509_STORE_free(b);
+ }
+};
+
+struct X509Deleter {
+ void operator()(X509* b) { X509_free(b); }
+};
+
+namespace // Anon namespace
+{
+ std::unique_ptr<X509_STORE, X509StoreDeleter> certStore;
+}
+
+static void ReportInvalidCertificate(const QSslCertificate& cert)
+{
+ qDebug() << QString("%1: Payment server found an invalid certificate: ").arg(__func__) << cert.serialNumber() << cert.subjectInfo(QSslCertificate::CommonName) << cert.subjectInfo(QSslCertificate::DistinguishedNameQualifier) << cert.subjectInfo(QSslCertificate::OrganizationalUnitName);
+}
+
+//
+// Load OpenSSL's list of root certificate authorities
+//
+void PaymentServer::LoadRootCAs(X509_STORE* _store)
+{
+ // Unit tests mostly use this, to pass in fake root CAs:
+ if (_store)
+ {
+ certStore.reset(_store);
+ return;
+ }
+
+ // Normal execution, use either -rootcertificates or system certs:
+ certStore.reset(X509_STORE_new());
+
+ // Note: use "-system-" default here so that users can pass -rootcertificates=""
+ // and get 'I don't like X.509 certificates, don't trust anybody' behavior:
+ QString certFile = QString::fromStdString(gArgs.GetArg("-rootcertificates", "-system-"));
+
+ // Empty store
+ if (certFile.isEmpty()) {
+ qDebug() << QString("PaymentServer::%1: Payment request authentication via X.509 certificates disabled.").arg(__func__);
+ return;
+ }
+
+ QList<QSslCertificate> certList;
+
+ if (certFile != "-system-") {
+ qDebug() << QString("PaymentServer::%1: Using \"%2\" as trusted root certificate.").arg(__func__).arg(certFile);
+
+ certList = QSslCertificate::fromPath(certFile);
+ // Use those certificates when fetching payment requests, too:
+ QSslSocket::setDefaultCaCertificates(certList);
+ } else
+ certList = QSslSocket::systemCaCertificates();
+
+ int nRootCerts = 0;
+ const QDateTime currentTime = QDateTime::currentDateTime();
+
+ for (const QSslCertificate& cert : certList) {
+ // Don't log NULL certificates
+ if (cert.isNull())
+ continue;
+
+ // Not yet active/valid, or expired certificate
+ if (currentTime < cert.effectiveDate() || currentTime > cert.expiryDate()) {
+ ReportInvalidCertificate(cert);
+ continue;
+ }
+
+ // Blacklisted certificate
+ if (cert.isBlacklisted()) {
+ ReportInvalidCertificate(cert);
+ continue;
+ }
+
+ QByteArray certData = cert.toDer();
+ const unsigned char *data = (const unsigned char *)certData.data();
+
+ std::unique_ptr<X509, X509Deleter> x509(d2i_X509(0, &data, certData.size()));
+ if (x509 && X509_STORE_add_cert(certStore.get(), x509.get()))
+ {
+ // Note: X509_STORE increases the reference count to the X509 object,
+ // we still have to release our reference to it.
+ ++nRootCerts;
+ }
+ else
+ {
+ ReportInvalidCertificate(cert);
+ continue;
+ }
+ }
+ qWarning() << "PaymentServer::LoadRootCAs: Loaded " << nRootCerts << " root certificates";
+
+ // Project for another day:
+ // Fetch certificate revocation lists, and add them to certStore.
+ // Issues to consider:
+ // performance (start a thread to fetch in background?)
+ // privacy (fetch through tor/proxy so IP address isn't revealed)
+ // would it be easier to just use a compiled-in blacklist?
+ // or use Qt's blacklist?
+ // "certificate stapling" with server-side caching is more efficient
+}
+
+void PaymentServer::initNetManager()
+{
+ if (!optionsModel)
+ return;
+ delete netManager;
+
+ // netManager is used to fetch paymentrequests given in bitcoin: URIs
+ netManager = new QNetworkAccessManager(this);
+
+ QNetworkProxy proxy;
+
+ // Query active SOCKS5 proxy
+ if (optionsModel->getProxySettings(proxy)) {
+ netManager->setProxy(proxy);
+
+ qDebug() << "PaymentServer::initNetManager: Using SOCKS5 proxy" << proxy.hostName() << ":" << proxy.port();
+ }
+ else
+ qDebug() << "PaymentServer::initNetManager: No active proxy server found.";
+
+ connect(netManager, &QNetworkAccessManager::finished, this, &PaymentServer::netRequestFinished);
+ connect(netManager, &QNetworkAccessManager::sslErrors, this, &PaymentServer::reportSslErrors);
+}
+
//
// Warning: readPaymentRequestFromFile() is used in ipcSendCommandLine()
// so don't use "Q_EMIT message()", but "QMessageBox::"!
@@ -731,11 +766,6 @@ void PaymentServer::reportSslErrors(QNetworkReply* reply, const QList<QSslError>
Q_EMIT message(tr("Network request error"), errString, CClientUIInterface::MSG_ERROR);
}
-void PaymentServer::setOptionsModel(OptionsModel *_optionsModel)
-{
- this->optionsModel = _optionsModel;
-}
-
void PaymentServer::handlePaymentACK(const QString& paymentACKMsg)
{
// currently we don't further process or store the paymentACK message
@@ -794,3 +824,4 @@ X509_STORE* PaymentServer::getCertStore()
{
return certStore.get();
}
+#endif
diff --git a/src/qt/paymentserver.h b/src/qt/paymentserver.h
index d335db9c85..30b5bc3b6d 100644
--- a/src/qt/paymentserver.h
+++ b/src/qt/paymentserver.h
@@ -32,7 +32,13 @@
// sends them to the server.
//
+#if defined(HAVE_CONFIG_H)
+#include <config/bitcoin-config.h>
+#endif
+
+#ifdef ENABLE_BIP70
#include <qt/paymentrequestplus.h>
+#endif
#include <qt/walletmodel.h>
#include <QObject>
@@ -73,6 +79,10 @@ public:
explicit PaymentServer(QObject* parent, bool startLocalServer = true);
~PaymentServer();
+ // OptionsModel is used for getting proxy settings and display unit
+ void setOptionsModel(OptionsModel *optionsModel);
+
+#ifdef ENABLE_BIP70
// Load root certificate authorities. Pass nullptr (default)
// to read from the file specified in the -rootcertificates setting,
// or, if that's not set, to use the system default root certificates.
@@ -83,9 +93,6 @@ public:
// Return certificate store
static X509_STORE* getCertStore();
- // OptionsModel is used for getting proxy settings and display unit
- void setOptionsModel(OptionsModel *optionsModel);
-
// Verify that the payment request network matches the client network
static bool verifyNetwork(interfaces::Node& node, const payments::PaymentDetails& requestDetails);
// Verify if the payment request is expired
@@ -94,33 +101,40 @@ public:
static bool verifySize(qint64 requestSize);
// Verify the payment request amount is valid
static bool verifyAmount(const CAmount& requestAmount);
+#endif
Q_SIGNALS:
// Fired when a valid payment request is received
void receivedPaymentRequest(SendCoinsRecipient);
- // Fired when a valid PaymentACK is received
- void receivedPaymentACK(const QString &paymentACKMsg);
-
// Fired when a message should be reported to the user
void message(const QString &title, const QString &message, unsigned int style);
+#ifdef ENABLE_BIP70
+ // Fired when a valid PaymentACK is received
+ void receivedPaymentACK(const QString &paymentACKMsg);
+#endif
+
public Q_SLOTS:
// Signal this when the main window's UI is ready
// to display payment requests to the user
void uiReady();
- // Submit Payment message to a merchant, get back PaymentACK:
- void fetchPaymentACK(WalletModel* walletModel, const SendCoinsRecipient& recipient, QByteArray transaction);
-
// Handle an incoming URI, URI with local file scheme or file
void handleURIOrFile(const QString& s);
+#ifdef ENABLE_BIP70
+ // Submit Payment message to a merchant, get back PaymentACK:
+ void fetchPaymentACK(WalletModel* walletModel, const SendCoinsRecipient& recipient, QByteArray transaction);
+#endif
+
private Q_SLOTS:
void handleURIConnection();
+#ifdef ENABLE_BIP70
void netRequestFinished(QNetworkReply*);
void reportSslErrors(QNetworkReply*, const QList<QSslError> &);
void handlePaymentACK(const QString& paymentACKMsg);
+#endif
protected:
// Constructor registers this on the parent QApplication to
@@ -128,19 +142,19 @@ protected:
bool eventFilter(QObject *object, QEvent *event);
private:
+ bool saveURIs; // true during startup
+ QLocalServer* uriServer;
+ OptionsModel *optionsModel;
+
+#ifdef ENABLE_BIP70
static bool readPaymentRequestFromFile(const QString& filename, PaymentRequestPlus& request);
bool processPaymentRequest(const PaymentRequestPlus& request, SendCoinsRecipient& recipient);
void fetchRequest(const QUrl& url);
// Setup networking
void initNetManager();
-
- bool saveURIs; // true during startup
- QLocalServer* uriServer;
-
QNetworkAccessManager* netManager; // Used to fetch payment requests
-
- OptionsModel *optionsModel;
+#endif
};
#endif // BITCOIN_QT_PAYMENTSERVER_H
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index 3857befdf2..c004c783f2 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -459,6 +459,9 @@ RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformSty
move(QApplication::desktop()->availableGeometry().center() - frameGeometry().center());
}
+ QChar nonbreaking_hyphen(8209);
+ ui->dataDir->setToolTip(ui->dataDir->toolTip().arg(QString(nonbreaking_hyphen) + "datadir"));
+ ui->blocksDir->setToolTip(ui->blocksDir->toolTip().arg(QString(nonbreaking_hyphen) + "blocksdir"));
ui->openDebugLogfileButton->setToolTip(ui->openDebugLogfileButton->toolTip().arg(tr(PACKAGE_NAME)));
if (platformStyle->getImagesOnButtons()) {
@@ -536,6 +539,7 @@ bool RPCConsole::eventFilter(QObject* obj, QEvent *event)
// forward these events to lineEdit
if(obj == autoCompleter->popup()) {
QApplication::postEvent(ui->lineEdit, new QKeyEvent(*keyevt));
+ autoCompleter->popup()->hide();
return true;
}
break;
@@ -661,6 +665,7 @@ void RPCConsole::setClientModel(ClientModel *model)
ui->clientVersion->setText(model->formatFullVersion());
ui->clientUserAgent->setText(model->formatSubVersion());
ui->dataDir->setText(model->dataDir());
+ ui->blocksDir->setText(model->blocksDir());
ui->startupTime->setText(model->formatClientStartupTime());
ui->networkName->setText(QString::fromStdString(Params().NetworkIDString()));
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 6f66bc19e1..858128f9f9 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -2,6 +2,10 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#if defined(HAVE_CONFIG_H)
+#include <config/bitcoin-config.h>
+#endif
+
#include <qt/sendcoinsdialog.h>
#include <qt/forms/ui_sendcoinsdialog.h>
@@ -290,7 +294,9 @@ void SendCoinsDialog::on_sendButton_clicked()
QString recipientElement;
recipientElement = "<br />";
+#ifdef ENABLE_BIP70
if (!rcp.paymentRequest.IsInitialized()) // normal payment
+#endif
{
if(rcp.label.length() > 0) // label with address
{
@@ -302,6 +308,7 @@ void SendCoinsDialog::on_sendButton_clicked()
recipientElement.append(tr("%1 to %2").arg(amount, address));
}
}
+#ifdef ENABLE_BIP70
else if(!rcp.authenticatedMerchant.isEmpty()) // authenticated payment request
{
recipientElement.append(tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.authenticatedMerchant)));
@@ -310,6 +317,7 @@ void SendCoinsDialog::on_sendButton_clicked()
{
recipientElement.append(tr("%1 to %2").arg(amount, address));
}
+#endif
formatted.append(recipientElement);
}
diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp
index b394ff3150..76c942c8b9 100644
--- a/src/qt/sendcoinsentry.cpp
+++ b/src/qt/sendcoinsentry.cpp
@@ -2,6 +2,10 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#if defined(HAVE_CONFIG_H)
+#include <config/bitcoin-config.h>
+#endif
+
#include <qt/sendcoinsentry.h>
#include <qt/forms/ui_sendcoinsentry.h>
@@ -133,9 +137,11 @@ bool SendCoinsEntry::validate(interfaces::Node& node)
// Check input validity
bool retval = true;
+#ifdef ENABLE_BIP70
// Skip checks for payment request
if (recipient.paymentRequest.IsInitialized())
return retval;
+#endif
if (!model->validateAddress(ui->payTo->text()))
{
@@ -166,9 +172,11 @@ bool SendCoinsEntry::validate(interfaces::Node& node)
SendCoinsRecipient SendCoinsEntry::getValue()
{
+#ifdef ENABLE_BIP70
// Payment request
if (recipient.paymentRequest.IsInitialized())
return recipient;
+#endif
// Normal payment
recipient.address = ui->payTo->text();
@@ -196,6 +204,7 @@ void SendCoinsEntry::setValue(const SendCoinsRecipient &value)
{
recipient = value;
+#ifdef ENABLE_BIP70
if (recipient.paymentRequest.IsInitialized()) // payment request
{
if (recipient.authenticatedMerchant.isEmpty()) // unauthenticated
@@ -216,6 +225,7 @@ void SendCoinsEntry::setValue(const SendCoinsRecipient &value)
}
}
else // normal payment
+#endif
{
// message
ui->messageTextLabel->setText(recipient.message);
diff --git a/src/qt/test/addressbooktests.cpp b/src/qt/test/addressbooktests.cpp
index c3d33c76d4..e6777c068d 100644
--- a/src/qt/test/addressbooktests.cpp
+++ b/src/qt/test/addressbooktests.cpp
@@ -6,17 +6,17 @@
#include <qt/addressbookpage.h>
#include <qt/addresstablemodel.h>
#include <qt/editaddressdialog.h>
-#include <qt/callback.h>
#include <qt/optionsmodel.h>
#include <qt/platformstyle.h>
#include <qt/qvalidatedlineedit.h>
#include <qt/walletmodel.h>
#include <key.h>
-#include <pubkey.h>
#include <key_io.h>
+#include <pubkey.h>
#include <wallet/wallet.h>
+#include <QApplication>
#include <QTimer>
#include <QMessageBox>
@@ -139,5 +139,16 @@ void TestAddAddressesToSendBook()
void AddressBookTests::addressBookTests()
{
+#ifdef Q_OS_MAC
+ if (QApplication::platformName() == "minimal") {
+ // Disable for mac on "minimal" platform to avoid crashes inside the Qt
+ // framework when it tries to look up unimplemented cocoa functions,
+ // and fails to handle returned nulls
+ // (https://bugreports.qt.io/browse/QTBUG-49686).
+ QWARN("Skipping AddressBookTests on mac build with 'minimal' platform set due to Qt bugs. To run AppTests, invoke "
+ "with 'test_bitcoin-qt -platform cocoa' on mac, or else use a linux or windows build.");
+ return;
+ }
+#endif
TestAddAddressesToSendBook();
}
diff --git a/src/qt/test/compattests.cpp b/src/qt/test/compattests.cpp
index af5c69ea9a..6750c543da 100644
--- a/src/qt/test/compattests.cpp
+++ b/src/qt/test/compattests.cpp
@@ -2,7 +2,13 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#if defined(HAVE_CONFIG_H)
+#include <config/bitcoin-config.h>
+#endif
+
+#if defined(ENABLE_WALLET) && defined(ENABLE_BIP70)
#include <qt/paymentrequestplus.h> // this includes protobuf's port.h which defines its own bswap macos
+#endif
#include <qt/test/compattests.h>
diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp
index df65a85fb5..28df4ebf26 100644
--- a/src/qt/test/test_main.cpp
+++ b/src/qt/test/test_main.cpp
@@ -14,9 +14,11 @@
#ifdef ENABLE_WALLET
#include <qt/test/addressbooktests.h>
+#ifdef ENABLE_BIP70
#include <qt/test/paymentservertests.h>
+#endif // ENABLE_BIP70
#include <qt/test/wallettests.h>
-#endif
+#endif // ENABLE_WALLET
#include <QApplication>
#include <QObject>
@@ -74,7 +76,7 @@ int main(int argc, char *argv[])
if (QTest::qExec(&test1) != 0) {
fInvalid = true;
}
-#ifdef ENABLE_WALLET
+#if defined(ENABLE_WALLET) && defined(ENABLE_BIP70)
PaymentServerTests test2;
if (QTest::qExec(&test2) != 0) {
fInvalid = true;
diff --git a/src/qt/test/util.cpp b/src/qt/test/util.cpp
index 0bd719326e..ae2fb93bf7 100644
--- a/src/qt/test/util.cpp
+++ b/src/qt/test/util.cpp
@@ -1,15 +1,13 @@
-#include <qt/callback.h>
-
#include <QApplication>
#include <QMessageBox>
-#include <QTimer>
-#include <QString>
#include <QPushButton>
+#include <QString>
+#include <QTimer>
#include <QWidget>
void ConfirmMessage(QString* text, int msec)
{
- QTimer::singleShot(msec, makeCallback([text](Callback* callback) {
+ QTimer::singleShot(msec, [text]() {
for (QWidget* widget : QApplication::topLevelWidgets()) {
if (widget->inherits("QMessageBox")) {
QMessageBox* messageBox = qobject_cast<QMessageBox*>(widget);
@@ -17,6 +15,5 @@ void ConfirmMessage(QString* text, int msec)
messageBox->defaultButton()->click();
}
}
- delete callback;
- }), &Callback::call);
+ });
}
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index a0cfe8ae87..fcc5806b81 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -2,8 +2,8 @@
#include <qt/test/util.h>
#include <interfaces/node.h>
+#include <base58.h>
#include <qt/bitcoinamountfield.h>
-#include <qt/callback.h>
#include <qt/optionsmodel.h>
#include <qt/platformstyle.h>
#include <qt/qvalidatedlineedit.h>
@@ -39,7 +39,7 @@ namespace
//! Press "Yes" or "Cancel" buttons in modal send confirmation dialog.
void ConfirmSend(QString* text = nullptr, bool cancel = false)
{
- QTimer::singleShot(0, makeCallback([text, cancel](Callback* callback) {
+ QTimer::singleShot(0, [text, cancel]() {
for (QWidget* widget : QApplication::topLevelWidgets()) {
if (widget->inherits("SendConfirmationDialog")) {
SendConfirmationDialog* dialog = qobject_cast<SendConfirmationDialog*>(widget);
@@ -49,8 +49,7 @@ void ConfirmSend(QString* text = nullptr, bool cancel = false)
button->click();
}
}
- delete callback;
- }), &Callback::call);
+ });
}
//! Send coins to address and return txid.
@@ -243,5 +242,16 @@ void TestGUI()
void WalletTests::walletTests()
{
+#ifdef Q_OS_MAC
+ if (QApplication::platformName() == "minimal") {
+ // Disable for mac on "minimal" platform to avoid crashes inside the Qt
+ // framework when it tries to look up unimplemented cocoa functions,
+ // and fails to handle returned nulls
+ // (https://bugreports.qt.io/browse/QTBUG-49686).
+ QWARN("Skipping WalletTests on mac build with 'minimal' platform set due to Qt bugs. To run AppTests, invoke "
+ "with 'test_bitcoin-qt -platform cocoa' on mac, or else use a linux or windows build.");
+ return;
+ }
+#endif
TestGUI();
}
diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp
index 59332be754..3c5617bfa8 100644
--- a/src/qt/transactiondesc.cpp
+++ b/src/qt/transactiondesc.cpp
@@ -2,6 +2,10 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#ifdef HAVE_CONFIG_H
+#include <config/bitcoin-config.h>
+#endif
+
#include <qt/transactiondesc.h>
#include <qt/bitcoinunits.h>
@@ -257,6 +261,7 @@ QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wall
if (r.first == "Message")
strHTML += "<br><b>" + tr("Message") + ":</b><br>" + GUIUtil::HtmlEscape(r.second, true) + "<br>";
+#ifdef ENABLE_BIP70
//
// PaymentRequest info:
//
@@ -271,6 +276,7 @@ QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wall
strHTML += "<b>" + tr("Merchant") + ":</b> " + GUIUtil::HtmlEscape(merchant) + "<br>";
}
}
+#endif
if (wtx.is_coinbase)
{
diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp
index 6d08a3b0fb..68410c8bd6 100644
--- a/src/qt/transactionview.cpp
+++ b/src/qt/transactionview.cpp
@@ -106,7 +106,11 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
} else {
amountWidget->setFixedWidth(100);
}
- amountWidget->setValidator(new QDoubleValidator(0, 1e20, 8, this));
+ QDoubleValidator *amountValidator = new QDoubleValidator(0, 1e20, 8, this);
+ QLocale amountLocale(QLocale::C);
+ amountLocale.setNumberOptions(QLocale::RejectGroupSeparator);
+ amountValidator->setLocale(amountLocale);
+ amountWidget->setValidator(amountValidator);
hlayout->addWidget(amountWidget);
// Delay before filtering transactions in ms
diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp
index 7d903b3b1c..faeed87ec4 100644
--- a/src/qt/utilitydialog.cpp
+++ b/src/qt/utilitydialog.cpp
@@ -14,7 +14,9 @@
#include <qt/clientmodel.h>
#include <qt/guiconstants.h>
#include <qt/intro.h>
+#ifdef ENABLE_BIP70
#include <qt/paymentrequestplus.h>
+#endif
#include <qt/guiutil.h>
#include <clientversion.h>
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index f7cc94ae32..71b2d321e2 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -2,6 +2,10 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#if defined(HAVE_CONFIG_H)
+#include <config/bitcoin-config.h>
+#endif
+
#include <qt/walletmodel.h>
#include <qt/addresstablemodel.h>
@@ -142,6 +146,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
if (rcp.fSubtractFeeFromAmount)
fSubtractFeeFromAmount = true;
+#ifdef ENABLE_BIP70
if (rcp.paymentRequest.IsInitialized())
{ // PaymentRequest...
CAmount subtotal = 0;
@@ -164,6 +169,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
total += subtotal;
}
else
+#endif
{ // User-entered bitcoin address / amount:
if(!validateAddress(rcp.address))
{
@@ -235,6 +241,7 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran
std::vector<std::pair<std::string, std::string>> vOrderForm;
for (const SendCoinsRecipient &rcp : transaction.getRecipients())
{
+#ifdef ENABLE_BIP70
if (rcp.paymentRequest.IsInitialized())
{
// Make sure any payment requests involved are still valid.
@@ -247,7 +254,9 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran
rcp.paymentRequest.SerializeToString(&value);
vOrderForm.emplace_back("PaymentRequest", std::move(value));
}
- else if (!rcp.message.isEmpty()) // Message from normal bitcoin:URI (bitcoin:123...?message=example)
+ else
+#endif
+ if (!rcp.message.isEmpty()) // Message from normal bitcoin:URI (bitcoin:123...?message=example)
vOrderForm.emplace_back("Message", rcp.message.toStdString());
}
@@ -266,7 +275,9 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran
for (const SendCoinsRecipient &rcp : transaction.getRecipients())
{
// Don't touch the address book when we have a payment request
+#ifdef ENABLE_BIP70
if (!rcp.paymentRequest.IsInitialized())
+#endif
{
std::string strAddress = rcp.address.toStdString();
CTxDestination dest = DecodeDestination(strAddress);
diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h
index b22728c69b..ec4c5a2a6c 100644
--- a/src/qt/walletmodel.h
+++ b/src/qt/walletmodel.h
@@ -10,7 +10,13 @@
#include <serialize.h>
#include <script/standard.h>
+#if defined(HAVE_CONFIG_H)
+#include <config/bitcoin-config.h>
+#endif
+
+#ifdef ENABLE_BIP70
#include <qt/paymentrequestplus.h>
+#endif
#include <qt/walletmodeltransaction.h>
#include <interfaces/wallet.h>
@@ -63,8 +69,14 @@ public:
// If from a payment request, this is used for storing the memo
QString message;
+#ifdef ENABLE_BIP70
// If from a payment request, paymentRequest.IsInitialized() will be true
PaymentRequestPlus paymentRequest;
+#else
+ // If building with BIP70 is disabled, keep the payment request around as
+ // serialized string to ensure load/store is lossless
+ std::string sPaymentRequest;
+#endif
// Empty if no authentication or invalid signature/cert/etc.
QString authenticatedMerchant;
@@ -80,9 +92,11 @@ public:
std::string sAddress = address.toStdString();
std::string sLabel = label.toStdString();
std::string sMessage = message.toStdString();
+#ifdef ENABLE_BIP70
std::string sPaymentRequest;
if (!ser_action.ForRead() && paymentRequest.IsInitialized())
paymentRequest.SerializeToString(&sPaymentRequest);
+#endif
std::string sAuthenticatedMerchant = authenticatedMerchant.toStdString();
READWRITE(this->nVersion);
@@ -98,8 +112,10 @@ public:
address = QString::fromStdString(sAddress);
label = QString::fromStdString(sLabel);
message = QString::fromStdString(sMessage);
+#ifdef ENABLE_BIP70
if (!sPaymentRequest.empty())
paymentRequest.parse(QByteArray::fromRawData(sPaymentRequest.data(), sPaymentRequest.size()));
+#endif
authenticatedMerchant = QString::fromStdString(sAuthenticatedMerchant);
}
}
diff --git a/src/qt/walletmodeltransaction.cpp b/src/qt/walletmodeltransaction.cpp
index de50616499..eb3b0baf08 100644
--- a/src/qt/walletmodeltransaction.cpp
+++ b/src/qt/walletmodeltransaction.cpp
@@ -2,6 +2,10 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#ifdef HAVE_CONFIG_H
+#include <config/bitcoin-config.h>
+#endif
+
#include <qt/walletmodeltransaction.h>
#include <interfaces/node.h>
@@ -46,6 +50,7 @@ void WalletModelTransaction::reassignAmounts(int nChangePosRet)
{
SendCoinsRecipient& rcp = (*it);
+#ifdef ENABLE_BIP70
if (rcp.paymentRequest.IsInitialized())
{
CAmount subtotal = 0;
@@ -62,6 +67,7 @@ void WalletModelTransaction::reassignAmounts(int nChangePosRet)
rcp.amount = subtotal;
}
else // normal recipient (no payment request)
+#endif
{
if (i == nChangePosRet)
i++;
diff --git a/src/qt/walletmodeltransaction.h b/src/qt/walletmodeltransaction.h
index 75ede2e2a1..289aee847b 100644
--- a/src/qt/walletmodeltransaction.h
+++ b/src/qt/walletmodeltransaction.h
@@ -8,6 +8,7 @@
#include <qt/walletmodel.h>
#include <memory>
+#include <amount.h>
#include <QObject>
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 1eca0277b0..e940134fb5 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -182,7 +182,7 @@ static UniValue getbestblockhash(const JSONRPCRequest& request)
"getbestblockhash\n"
"\nReturns the hash of the best (tip) block in the longest blockchain.\n"
"\nResult:\n"
- "\"hex\" (string) the block hash hex encoded\n"
+ "\"hex\" (string) the block hash, hex-encoded\n"
"\nExamples:\n"
+ HelpExampleCli("getbestblockhash", "")
+ HelpExampleRpc("getbestblockhash", "")
@@ -509,17 +509,17 @@ static UniValue getmempoolancestors(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
throw std::runtime_error(
- "getmempoolancestors txid (verbose)\n"
+ "getmempoolancestors txid ( verbose )\n"
"\nIf txid is in the mempool, returns all in-mempool ancestors.\n"
"\nArguments:\n"
"1. \"txid\" (string, required) The transaction id (must be in mempool)\n"
"2. verbose (boolean, optional, default=false) True for a json object, false for array of transaction ids\n"
- "\nResult (for verbose=false):\n"
+ "\nResult (for verbose = false):\n"
"[ (json array of strings)\n"
" \"transactionid\" (string) The transaction id of an in-mempool ancestor transaction\n"
" ,...\n"
"]\n"
- "\nResult (for verbose=true):\n"
+ "\nResult (for verbose = true):\n"
"{ (json object)\n"
" \"transactionid\" : { (json object)\n"
+ EntryDescriptionString()
@@ -573,17 +573,17 @@ static UniValue getmempooldescendants(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
throw std::runtime_error(
- "getmempooldescendants txid (verbose)\n"
+ "getmempooldescendants txid ( verbose )\n"
"\nIf txid is in the mempool, returns all in-mempool descendants.\n"
"\nArguments:\n"
"1. \"txid\" (string, required) The transaction id (must be in mempool)\n"
"2. verbose (boolean, optional, default=false) True for a json object, false for array of transaction ids\n"
- "\nResult (for verbose=false):\n"
+ "\nResult (for verbose = false):\n"
"[ (json array of strings)\n"
" \"transactionid\" (string) The transaction id of an in-mempool descendant transaction\n"
" ,...\n"
"]\n"
- "\nResult (for verbose=true):\n"
+ "\nResult (for verbose = true):\n"
"{ (json object)\n"
" \"transactionid\" : { (json object)\n"
+ EntryDescriptionString()
@@ -700,7 +700,7 @@ static UniValue getblockheader(const JSONRPCRequest& request)
"If verbose is true, returns an Object with information about blockheader <hash>.\n"
"\nArguments:\n"
"1. \"hash\" (string, required) The block hash\n"
- "2. verbose (boolean, optional, default=true) true for a json object, false for the hex encoded data\n"
+ "2. verbose (boolean, optional, default=true) true for a json object, false for the hex-encoded data\n"
"\nResult (for verbose = true):\n"
"{\n"
" \"hash\" : \"hash\", (string) the block hash (same as provided)\n"
@@ -779,7 +779,7 @@ static UniValue getblock(const JSONRPCRequest& request)
"If verbosity is 2, returns an Object with information about block <hash> and information about each transaction. \n"
"\nArguments:\n"
"1. \"blockhash\" (string, required) The block hash\n"
- "2. verbosity (numeric, optional, default=1) 0 for hex encoded data, 1 for a json object, and 2 for json object with transaction data\n"
+ "2. verbosity (numeric, optional, default=1) 0 for hex-encoded data, 1 for a json object, and 2 for json object with transaction data\n"
"\nResult (for verbosity = 0):\n"
"\"data\" (string) A string that is serialized, hex-encoded data for block 'hash'.\n"
"\nResult (for verbosity = 1):\n"
@@ -1046,7 +1046,7 @@ UniValue gettxout(const JSONRPCRequest& request)
+ HelpExampleCli("listunspent", "") +
"\nView the details\n"
+ HelpExampleCli("gettxout", "\"txid\" 1") +
- "\nAs a json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("gettxout", "\"txid\", 1")
);
@@ -1798,6 +1798,10 @@ static UniValue getblockstats(const JSONRPCRequest& request)
const bool do_calculate_weight = do_all || SetHasKeys(stats, "total_weight", "avgfeerate", "swtotal_weight", "avgfeerate", "feerate_percentiles", "minfeerate", "maxfeerate");
const bool do_calculate_sw = do_all || SetHasKeys(stats, "swtxs", "swtotal_size", "swtotal_weight");
+ if (loop_inputs && !g_txindex) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "One or more of the selected stats requires -txindex enabled");
+ }
+
CAmount maxfee = 0;
CAmount maxfeerate = 0;
CAmount minfee = MAX_MONEY;
@@ -1861,10 +1865,6 @@ static UniValue getblockstats(const JSONRPCRequest& request)
}
if (loop_inputs) {
-
- if (!g_txindex) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "One or more of the selected stats requires -txindex enabled");
- }
CAmount tx_total_in = 0;
for (const CTxIn& in : tx->vin) {
CTransactionRef tx_in;
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index 649e222c39..a550f693e6 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -45,7 +45,6 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "listreceivedbyaddress", 0, "minconf" },
{ "listreceivedbyaddress", 1, "include_empty" },
{ "listreceivedbyaddress", 2, "include_watchonly" },
- { "listreceivedbyaddress", 3, "address_filter" },
{ "listreceivedbylabel", 0, "minconf" },
{ "listreceivedbylabel", 1, "include_empty" },
{ "listreceivedbylabel", 2, "include_watchonly" },
@@ -148,7 +147,6 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "logging", 0, "include" },
{ "logging", 1, "exclude" },
{ "disconnectnode", 1, "nodeid" },
- { "addwitnessaddress", 1, "p2sh" },
// Echo with conversion (For testing only)
{ "echojson", 0, "arg0" },
{ "echojson", 1, "arg1" },
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 1b2fc2c156..25cfc69da9 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -167,6 +167,8 @@ static UniValue generatetoaddress(const JSONRPCRequest& request)
"\nExamples:\n"
"\nGenerate 11 blocks to myaddress\n"
+ HelpExampleCli("generatetoaddress", "11 \"myaddress\"")
+ + "If you are running the bitcoin core wallet, you can get a new address to send the newly generated bitcoin to with:\n"
+ + HelpExampleCli("getnewaddress", "")
);
int nGenerate = request.params[0].get_int();
@@ -362,8 +364,8 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
"}\n"
"\nExamples:\n"
- + HelpExampleCli("getblocktemplate", "")
- + HelpExampleRpc("getblocktemplate", "")
+ + HelpExampleCli("getblocktemplate", "{\"rules\": [\"segwit\"]}")
+ + HelpExampleRpc("getblocktemplate", "{\"rules\": [\"segwit\"]}")
);
LOCK(cs_main);
@@ -813,7 +815,7 @@ static UniValue estimatesmartfee(const JSONRPCRequest& request)
" higher feerate and is more likely to be sufficient for the desired\n"
" target, but is not as responsive to short term drops in the\n"
" prevailing fee market. Must be one of:\n"
- " \"UNSET\" (defaults to CONSERVATIVE)\n"
+ " \"UNSET\"\n"
" \"ECONOMICAL\"\n"
" \"CONSERVATIVE\"\n"
"\nResult:\n"
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index 0f3b601414..b53c1a5199 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -44,7 +44,7 @@ static UniValue validateaddress(const JSONRPCRequest& request)
"{\n"
" \"isvalid\" : true|false, (boolean) If the address is valid or not. If not, this is the only property returned.\n"
" \"address\" : \"address\", (string) The bitcoin address validated\n"
- " \"scriptPubKey\" : \"hex\", (string) The hex encoded scriptPubKey generated by the address\n"
+ " \"scriptPubKey\" : \"hex\", (string) The hex-encoded scriptPubKey generated by the address\n"
" \"isscript\" : true|false, (boolean) If the key is a script\n"
" \"iswitness\" : true|false, (boolean) If the address is a witness address\n"
" \"witness_version\" : version (numeric, optional) The version number of the witness program\n"
@@ -99,7 +99,7 @@ static UniValue createmultisig(const JSONRPCRequest& request)
"\nExamples:\n"
"\nCreate a multisig address from 2 public keys\n"
+ HelpExampleCli("createmultisig", "2 \"[\\\"03789ed0bb717d88f7d321a368d905e7430207ebbd82bd342cf11ae157a7ace5fd\\\",\\\"03dbc6764b8884a92e871274b87583e6d5c2a58819473e17e107ef3f6aa5a61626\\\"]\"") +
- "\nAs a json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("createmultisig", "2, \"[\\\"03789ed0bb717d88f7d321a368d905e7430207ebbd82bd342cf11ae157a7ace5fd\\\",\\\"03dbc6764b8884a92e871274b87583e6d5c2a58819473e17e107ef3f6aa5a61626\\\"]\"")
;
throw std::runtime_error(msg);
@@ -157,7 +157,7 @@ static UniValue verifymessage(const JSONRPCRequest& request)
+ HelpExampleCli("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"my message\"") +
"\nVerify the signature\n"
+ HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"signature\" \"my message\"") +
- "\nAs json rpc\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"signature\", \"my message\"")
);
@@ -210,7 +210,7 @@ static UniValue signmessagewithprivkey(const JSONRPCRequest& request)
+ HelpExampleCli("signmessagewithprivkey", "\"privkey\" \"my message\"") +
"\nVerify the signature\n"
+ HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"signature\" \"my message\"") +
- "\nAs json rpc\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("signmessagewithprivkey", "\"privkey\", \"my message\"")
);
@@ -446,7 +446,7 @@ static const CRPCCommand commands[] =
{ "control", "getmemoryinfo", &getmemoryinfo, {"mode"} },
{ "control", "logging", &logging, {"include", "exclude"}},
{ "util", "validateaddress", &validateaddress, {"address"} },
- { "util", "createmultisig", &createmultisig, {"nrequired","keys"} },
+ { "util", "createmultisig", &createmultisig, {"nrequired","keys","address_type"} },
{ "util", "verifymessage", &verifymessage, {"address","signature","message"} },
{ "util", "signmessagewithprivkey", &signmessagewithprivkey, {"privkey","message"} },
diff --git a/src/rpc/protocol.cpp b/src/rpc/protocol.cpp
index 55bebb5662..ee178f34ce 100644
--- a/src/rpc/protocol.cpp
+++ b/src/rpc/protocol.cpp
@@ -12,8 +12,6 @@
#include <utiltime.h>
#include <version.h>
-#include <fstream>
-
/**
* JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility,
* but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
@@ -85,9 +83,9 @@ bool GenerateAuthCookie(std::string *cookie_out)
/** the umask determines what permissions are used to create this file -
* these are set to 077 in init.cpp unless overridden with -sysperms.
*/
- std::ofstream file;
+ fsbridge::ofstream file;
fs::path filepath_tmp = GetAuthCookieFile(true);
- file.open(filepath_tmp.string().c_str());
+ file.open(filepath_tmp);
if (!file.is_open()) {
LogPrintf("Unable to open cookie authentication file %s for writing\n", filepath_tmp.string());
return false;
@@ -109,10 +107,10 @@ bool GenerateAuthCookie(std::string *cookie_out)
bool GetAuthCookie(std::string *cookie_out)
{
- std::ifstream file;
+ fsbridge::ifstream file;
std::string cookie;
fs::path filepath = GetAuthCookieFile();
- file.open(filepath.string().c_str());
+ file.open(filepath);
if (!file.is_open())
return false;
std::getline(file, cookie);
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 7397216506..a2d990b51d 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -466,13 +466,13 @@ static UniValue createrawtransaction(const JSONRPCRequest& request)
" \"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"
+ " \"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"
+ "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"
@@ -581,11 +581,11 @@ static UniValue decodescript(const JSONRPCRequest& request)
"decodescript \"hexstring\"\n"
"\nDecode a hex-encoded script.\n"
"\nArguments:\n"
- "1. \"hexstring\" (string) the hex encoded script\n"
+ "1. \"hexstring\" (string) the hex-encoded script\n"
"\nResult:\n"
"{\n"
" \"asm\":\"asm\", (string) Script public key\n"
- " \"hex\":\"hex\", (string) hex encoded public key\n"
+ " \"hex\":\"hex\", (string) hex-encoded public key\n"
" \"type\":\"type\", (string) The output type\n"
" \"reqSigs\": n, (numeric) The required signatures\n"
" \"addresses\": [ (json array of string)\n"
@@ -922,7 +922,7 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
" }\n"
" ,...\n"
" ]\n"
- "4. \"sighashtype\" (string, optional, default=ALL) The signature hash type. Must be one of\n"
+ "4. \"sighashtype\" (string, optional, default=ALL) The signature hash type. Must be one of:\n"
" \"ALL\"\n"
" \"NONE\"\n"
" \"SINGLE\"\n"
@@ -999,7 +999,7 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request)
+ HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"") +
"\nSend the transaction (signed hex)\n"
+ HelpExampleCli("sendrawtransaction", "\"signedhex\"") +
- "\nAs a json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("sendrawtransaction", "\"signedhex\"")
);
@@ -1104,7 +1104,7 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
+ HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"") +
"\nTest acceptance of the transaction (signed hex)\n"
+ HelpExampleCli("testmempoolaccept", "\"signedhex\"") +
- "\nAs a json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("testmempoolaccept", "[\"signedhex\"]")
// clang-format on
);
@@ -1587,7 +1587,7 @@ UniValue createpsbt(const JSONRPCRequest& request)
" \"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"
+ " \"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"
diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp
index 45b097dde6..478797e958 100644
--- a/src/script/descriptor.cpp
+++ b/src/script/descriptor.cpp
@@ -41,7 +41,7 @@ struct PubkeyProvider
virtual ~PubkeyProvider() = default;
/** Derive a public key. */
- virtual bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& out) const = 0;
+ virtual bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& key, KeyOriginInfo& info) const = 0;
/** Whether this represent multiple public keys at different positions. */
virtual bool IsRange() const = 0;
@@ -56,6 +56,37 @@ struct PubkeyProvider
virtual bool ToPrivateString(const SigningProvider& arg, std::string& out) const = 0;
};
+class OriginPubkeyProvider final : public PubkeyProvider
+{
+ KeyOriginInfo m_origin;
+ std::unique_ptr<PubkeyProvider> m_provider;
+
+ std::string OriginString() const
+ {
+ return HexStr(std::begin(m_origin.fingerprint), std::end(m_origin.fingerprint)) + FormatKeyPath(m_origin.path);
+ }
+
+public:
+ OriginPubkeyProvider(KeyOriginInfo info, std::unique_ptr<PubkeyProvider> provider) : m_origin(std::move(info)), m_provider(std::move(provider)) {}
+ bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& key, KeyOriginInfo& info) const override
+ {
+ if (!m_provider->GetPubKey(pos, arg, key, info)) return false;
+ std::copy(std::begin(m_origin.fingerprint), std::end(m_origin.fingerprint), info.fingerprint);
+ info.path.insert(info.path.begin(), m_origin.path.begin(), m_origin.path.end());
+ return true;
+ }
+ bool IsRange() const override { return m_provider->IsRange(); }
+ size_t GetSize() const override { return m_provider->GetSize(); }
+ std::string ToString() const override { return "[" + OriginString() + "]" + m_provider->ToString(); }
+ bool ToPrivateString(const SigningProvider& arg, std::string& ret) const override
+ {
+ std::string sub;
+ if (!m_provider->ToPrivateString(arg, sub)) return false;
+ ret = "[" + OriginString() + "]" + std::move(sub);
+ return true;
+ }
+};
+
/** An object representing a parsed constant public key in a descriptor. */
class ConstPubkeyProvider final : public PubkeyProvider
{
@@ -63,9 +94,12 @@ class ConstPubkeyProvider final : public PubkeyProvider
public:
ConstPubkeyProvider(const CPubKey& pubkey) : m_pubkey(pubkey) {}
- bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& out) const override
+ bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& key, KeyOriginInfo& info) const override
{
- out = m_pubkey;
+ key = m_pubkey;
+ info.path.clear();
+ CKeyID keyid = m_pubkey.GetID();
+ std::copy(keyid.begin(), keyid.begin() + sizeof(info.fingerprint), info.fingerprint);
return true;
}
bool IsRange() const override { return false; }
@@ -98,7 +132,7 @@ class BIP32PubkeyProvider final : public PubkeyProvider
CKey key;
if (!arg.GetKey(m_extkey.pubkey.GetID(), key)) return false;
ret.nDepth = m_extkey.nDepth;
- std::copy(m_extkey.vchFingerprint, m_extkey.vchFingerprint + 4, ret.vchFingerprint);
+ std::copy(m_extkey.vchFingerprint, m_extkey.vchFingerprint + sizeof(ret.vchFingerprint), ret.vchFingerprint);
ret.nChild = m_extkey.nChild;
ret.chaincode = m_extkey.chaincode;
ret.key = key;
@@ -118,27 +152,32 @@ public:
BIP32PubkeyProvider(const CExtPubKey& extkey, KeyPath path, DeriveType derive) : m_extkey(extkey), m_path(std::move(path)), m_derive(derive) {}
bool IsRange() const override { return m_derive != DeriveType::NO; }
size_t GetSize() const override { return 33; }
- bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& out) const override
+ bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& key, KeyOriginInfo& info) const override
{
if (IsHardened()) {
- CExtKey key;
- if (!GetExtKey(arg, key)) return false;
+ CExtKey extkey;
+ if (!GetExtKey(arg, extkey)) return false;
for (auto entry : m_path) {
- key.Derive(key, entry);
+ extkey.Derive(extkey, entry);
}
- if (m_derive == DeriveType::UNHARDENED) key.Derive(key, pos);
- if (m_derive == DeriveType::HARDENED) key.Derive(key, pos | 0x80000000UL);
- out = key.Neuter().pubkey;
+ if (m_derive == DeriveType::UNHARDENED) extkey.Derive(extkey, pos);
+ if (m_derive == DeriveType::HARDENED) extkey.Derive(extkey, pos | 0x80000000UL);
+ key = extkey.Neuter().pubkey;
} else {
// TODO: optimize by caching
- CExtPubKey key = m_extkey;
+ CExtPubKey extkey = m_extkey;
for (auto entry : m_path) {
- key.Derive(key, entry);
+ extkey.Derive(extkey, entry);
}
- if (m_derive == DeriveType::UNHARDENED) key.Derive(key, pos);
+ if (m_derive == DeriveType::UNHARDENED) extkey.Derive(extkey, pos);
assert(m_derive != DeriveType::HARDENED);
- out = key.pubkey;
+ key = extkey.pubkey;
}
+ CKeyID keyid = m_extkey.pubkey.GetID();
+ std::copy(keyid.begin(), keyid.begin() + sizeof(info.fingerprint), info.fingerprint);
+ info.path = m_path;
+ if (m_derive == DeriveType::UNHARDENED) info.path.push_back((uint32_t)pos);
+ if (m_derive == DeriveType::HARDENED) info.path.push_back(((uint32_t)pos) | 0x80000000L);
return true;
}
std::string ToString() const override
@@ -221,9 +260,11 @@ public:
bool Expand(int pos, const SigningProvider& arg, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override
{
CPubKey key;
- if (!m_provider->GetPubKey(pos, arg, key)) return false;
+ KeyOriginInfo info;
+ if (!m_provider->GetPubKey(pos, arg, key, info)) return false;
output_scripts = std::vector<CScript>{m_script_fn(key)};
- out.pubkeys.emplace(key.GetID(), std::move(key));
+ out.origins.emplace(key.GetID(), std::move(info));
+ out.pubkeys.emplace(key.GetID(), key);
return true;
}
};
@@ -272,15 +313,19 @@ public:
bool Expand(int pos, const SigningProvider& arg, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override
{
- std::vector<CPubKey> pubkeys;
- pubkeys.reserve(m_providers.size());
+ std::vector<std::pair<CPubKey, KeyOriginInfo>> entries;
+ entries.reserve(m_providers.size());
+ // Construct temporary data in `entries`, to avoid producing output in case of failure.
for (const auto& p : m_providers) {
- CPubKey key;
- if (!p->GetPubKey(pos, arg, key)) return false;
- pubkeys.push_back(key);
+ entries.emplace_back();
+ if (!p->GetPubKey(pos, arg, entries.back().first, entries.back().second)) return false;
}
- for (const CPubKey& key : pubkeys) {
- out.pubkeys.emplace(key.GetID(), std::move(key));
+ std::vector<CPubKey> pubkeys;
+ pubkeys.reserve(entries.size());
+ for (auto& entry : entries) {
+ pubkeys.push_back(entry.first);
+ out.origins.emplace(entry.first.GetID(), std::move(entry.second));
+ out.pubkeys.emplace(entry.first.GetID(), entry.first);
}
output_scripts = std::vector<CScript>{GetScriptForMultisig(m_threshold, pubkeys)};
return true;
@@ -343,13 +388,15 @@ public:
bool Expand(int pos, const SigningProvider& arg, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override
{
CPubKey key;
- if (!m_provider->GetPubKey(pos, arg, key)) return false;
+ KeyOriginInfo info;
+ if (!m_provider->GetPubKey(pos, arg, key, info)) return false;
CKeyID keyid = key.GetID();
{
CScript p2pk = GetScriptForRawPubKey(key);
CScript p2pkh = GetScriptForDestination(keyid);
output_scripts = std::vector<CScript>{std::move(p2pk), std::move(p2pkh)};
out.pubkeys.emplace(keyid, key);
+ out.origins.emplace(keyid, std::move(info));
}
if (key.IsCompressed()) {
CScript p2wpkh = GetScriptForDestination(WitnessV0KeyHash(keyid));
@@ -447,7 +494,8 @@ bool ParseKeyPath(const std::vector<Span<const char>>& split, KeyPath& out)
return true;
}
-std::unique_ptr<PubkeyProvider> ParsePubkey(const Span<const char>& sp, bool permit_uncompressed, FlatSigningProvider& out)
+/** Parse a public key that excludes origin information. */
+std::unique_ptr<PubkeyProvider> ParsePubkeyInner(const Span<const char>& sp, bool permit_uncompressed, FlatSigningProvider& out)
{
auto split = Split(sp, '/');
std::string str(split[0].begin(), split[0].end());
@@ -484,6 +532,28 @@ std::unique_ptr<PubkeyProvider> ParsePubkey(const Span<const char>& sp, bool per
return MakeUnique<BIP32PubkeyProvider>(extpubkey, std::move(path), type);
}
+/** Parse a public key including origin information (if enabled). */
+std::unique_ptr<PubkeyProvider> ParsePubkey(const Span<const char>& sp, bool permit_uncompressed, FlatSigningProvider& out)
+{
+ auto origin_split = Split(sp, ']');
+ if (origin_split.size() > 2) return nullptr;
+ if (origin_split.size() == 1) return ParsePubkeyInner(origin_split[0], permit_uncompressed, out);
+ if (origin_split[0].size() < 1 || origin_split[0][0] != '[') return nullptr;
+ auto slash_split = Split(origin_split[0].subspan(1), '/');
+ if (slash_split[0].size() != 8) return nullptr;
+ std::string fpr_hex = std::string(slash_split[0].begin(), slash_split[0].end());
+ if (!IsHex(fpr_hex)) return nullptr;
+ auto fpr_bytes = ParseHex(fpr_hex);
+ KeyOriginInfo info;
+ static_assert(sizeof(info.fingerprint) == 4, "Fingerprint must be 4 bytes");
+ assert(fpr_bytes.size() == 4);
+ std::copy(fpr_bytes.begin(), fpr_bytes.end(), info.fingerprint);
+ if (!ParseKeyPath(slash_split, info.path)) return nullptr;
+ auto provider = ParsePubkeyInner(origin_split[1], permit_uncompressed, out);
+ if (!provider) return nullptr;
+ return MakeUnique<OriginPubkeyProvider>(std::move(info), std::move(provider));
+}
+
/** Parse a script in a particular context. */
std::unique_ptr<Descriptor> ParseScript(Span<const char>& sp, ParseScriptContext ctx, FlatSigningProvider& out)
{
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index d779910425..89cc7c808c 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -73,15 +73,18 @@ static bool GetPubKey(const SigningProvider& provider, SignatureData& sigdata, c
return false;
}
-static bool CreateSig(const BaseSignatureCreator& creator, SignatureData& sigdata, const SigningProvider& provider, std::vector<unsigned char>& sig_out, const CKeyID& keyid, const CScript& scriptcode, SigVersion sigversion)
+static bool CreateSig(const BaseSignatureCreator& creator, SignatureData& sigdata, const SigningProvider& provider, std::vector<unsigned char>& sig_out, const CPubKey& pubkey, const CScript& scriptcode, SigVersion sigversion)
{
+ CKeyID keyid = pubkey.GetID();
const auto it = sigdata.signatures.find(keyid);
if (it != sigdata.signatures.end()) {
sig_out = it->second.second;
return true;
}
- CPubKey pubkey;
- GetPubKey(provider, sigdata, keyid, pubkey);
+ KeyOriginInfo info;
+ if (provider.GetKeyOrigin(keyid, info)) {
+ sigdata.misc_pubkeys.emplace(keyid, std::make_pair(pubkey, std::move(info)));
+ }
if (creator.CreateSig(provider, sig_out, keyid, scriptcode, sigversion)) {
auto i = sigdata.signatures.emplace(keyid, SigPair(pubkey, sig_out));
assert(i.second);
@@ -114,15 +117,15 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
case TX_WITNESS_UNKNOWN:
return false;
case TX_PUBKEY:
- if (!CreateSig(creator, sigdata, provider, sig, CPubKey(vSolutions[0]).GetID(), scriptPubKey, sigversion)) return false;
+ if (!CreateSig(creator, sigdata, provider, sig, CPubKey(vSolutions[0]), scriptPubKey, sigversion)) return false;
ret.push_back(std::move(sig));
return true;
case TX_PUBKEYHASH: {
CKeyID keyID = CKeyID(uint160(vSolutions[0]));
- if (!CreateSig(creator, sigdata, provider, sig, keyID, scriptPubKey, sigversion)) return false;
- ret.push_back(std::move(sig));
CPubKey pubkey;
GetPubKey(provider, sigdata, keyID, pubkey);
+ if (!CreateSig(creator, sigdata, provider, sig, pubkey, scriptPubKey, sigversion)) return false;
+ ret.push_back(std::move(sig));
ret.push_back(ToByteVector(pubkey));
return true;
}
@@ -138,7 +141,7 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
ret.push_back(valtype()); // workaround CHECKMULTISIG bug
for (size_t i = 1; i < vSolutions.size() - 1; ++i) {
CPubKey pubkey = CPubKey(vSolutions[i]);
- if (ret.size() < required + 1 && CreateSig(creator, sigdata, provider, sig, pubkey.GetID(), scriptPubKey, sigversion)) {
+ if (ret.size() < required + 1 && CreateSig(creator, sigdata, provider, sig, pubkey, scriptPubKey, sigversion)) {
ret.push_back(std::move(sig));
}
}
@@ -277,6 +280,11 @@ bool SignPSBTInput(const SigningProvider& provider, const CMutableTransaction& t
if (require_witness_sig && !sigdata.witness) return false;
input.FromSignatureData(sigdata);
+ if (sigdata.witness) {
+ assert(!utxo.IsNull());
+ input.witness_utxo = utxo;
+ }
+
// If both UTXO types are present, drop the unnecessary one.
if (input.non_witness_utxo && !input.witness_utxo.IsNull()) {
if (sigdata.witness) {
@@ -683,6 +691,7 @@ bool HidingSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& inf
bool FlatSigningProvider::GetCScript(const CScriptID& scriptid, CScript& script) const { return LookupHelper(scripts, scriptid, script); }
bool FlatSigningProvider::GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const { return LookupHelper(pubkeys, keyid, pubkey); }
+bool FlatSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const { return LookupHelper(origins, keyid, info); }
bool FlatSigningProvider::GetKey(const CKeyID& keyid, CKey& key) const { return LookupHelper(keys, keyid, key); }
FlatSigningProvider Merge(const FlatSigningProvider& a, const FlatSigningProvider& b)
diff --git a/src/script/sign.h b/src/script/sign.h
index 5b45777a2d..d47aada17d 100644
--- a/src/script/sign.h
+++ b/src/script/sign.h
@@ -34,7 +34,7 @@ public:
virtual bool GetCScript(const CScriptID &scriptid, CScript& script) const { return false; }
virtual bool GetPubKey(const CKeyID &address, CPubKey& pubkey) const { return false; }
virtual bool GetKey(const CKeyID &address, CKey& key) const { return false; }
- virtual bool GetKeyOrigin(const CKeyID& id, KeyOriginInfo& info) const { return false; }
+ virtual bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const { return false; }
};
extern const SigningProvider& DUMMY_SIGNING_PROVIDER;
@@ -58,10 +58,12 @@ struct FlatSigningProvider final : public SigningProvider
{
std::map<CScriptID, CScript> scripts;
std::map<CKeyID, CPubKey> pubkeys;
+ std::map<CKeyID, KeyOriginInfo> origins;
std::map<CKeyID, CKey> keys;
bool GetCScript(const CScriptID& scriptid, CScript& script) const override;
bool GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const override;
+ bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override;
bool GetKey(const CKeyID& keyid, CKey& key) const override;
};
diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp
index f57d0c6d79..8c2873d916 100644
--- a/src/test/addrman_tests.cpp
+++ b/src/test/addrman_tests.cpp
@@ -40,22 +40,26 @@ public:
CAddrInfo* Find(const CNetAddr& addr, int* pnId = nullptr)
{
+ LOCK(cs);
return CAddrMan::Find(addr, pnId);
}
CAddrInfo* Create(const CAddress& addr, const CNetAddr& addrSource, int* pnId = nullptr)
{
+ LOCK(cs);
return CAddrMan::Create(addr, addrSource, pnId);
}
void Delete(int nId)
{
+ LOCK(cs);
CAddrMan::Delete(nId);
}
// Simulates connection failure so that we can test eviction of offline nodes
void SimConnFail(CService& addr)
{
+ LOCK(cs);
int64_t nLastSuccess = 1;
Good_(addr, true, nLastSuccess); // Set last good connection in the deep past.
diff --git a/src/test/amount_tests.cpp b/src/test/amount_tests.cpp
index adffcfeef5..1ff040b077 100644
--- a/src/test/amount_tests.cpp
+++ b/src/test/amount_tests.cpp
@@ -13,8 +13,10 @@ BOOST_FIXTURE_TEST_SUITE(amount_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(MoneyRangeTest)
{
BOOST_CHECK_EQUAL(MoneyRange(CAmount(-1)), false);
- BOOST_CHECK_EQUAL(MoneyRange(MAX_MONEY + CAmount(1)), false);
+ BOOST_CHECK_EQUAL(MoneyRange(CAmount(0)), true);
BOOST_CHECK_EQUAL(MoneyRange(CAmount(1)), true);
+ BOOST_CHECK_EQUAL(MoneyRange(MAX_MONEY), true);
+ BOOST_CHECK_EQUAL(MoneyRange(MAX_MONEY + CAmount(1)), false);
}
BOOST_AUTO_TEST_CASE(GetFeeTest)
@@ -23,43 +25,43 @@ BOOST_AUTO_TEST_CASE(GetFeeTest)
feeRate = CFeeRate(0);
// Must always return 0
- BOOST_CHECK_EQUAL(feeRate.GetFee(0), 0);
- BOOST_CHECK_EQUAL(feeRate.GetFee(1e5), 0);
+ BOOST_CHECK_EQUAL(feeRate.GetFee(0), CAmount(0));
+ BOOST_CHECK_EQUAL(feeRate.GetFee(1e5), CAmount(0));
feeRate = CFeeRate(1000);
// Must always just return the arg
- BOOST_CHECK_EQUAL(feeRate.GetFee(0), 0);
- BOOST_CHECK_EQUAL(feeRate.GetFee(1), 1);
- BOOST_CHECK_EQUAL(feeRate.GetFee(121), 121);
- BOOST_CHECK_EQUAL(feeRate.GetFee(999), 999);
- BOOST_CHECK_EQUAL(feeRate.GetFee(1e3), 1e3);
- BOOST_CHECK_EQUAL(feeRate.GetFee(9e3), 9e3);
+ BOOST_CHECK_EQUAL(feeRate.GetFee(0), CAmount(0));
+ BOOST_CHECK_EQUAL(feeRate.GetFee(1), CAmount(1));
+ BOOST_CHECK_EQUAL(feeRate.GetFee(121), CAmount(121));
+ BOOST_CHECK_EQUAL(feeRate.GetFee(999), CAmount(999));
+ BOOST_CHECK_EQUAL(feeRate.GetFee(1e3), CAmount(1e3));
+ BOOST_CHECK_EQUAL(feeRate.GetFee(9e3), CAmount(9e3));
feeRate = CFeeRate(-1000);
// Must always just return -1 * arg
- BOOST_CHECK_EQUAL(feeRate.GetFee(0), 0);
- BOOST_CHECK_EQUAL(feeRate.GetFee(1), -1);
- BOOST_CHECK_EQUAL(feeRate.GetFee(121), -121);
- BOOST_CHECK_EQUAL(feeRate.GetFee(999), -999);
- BOOST_CHECK_EQUAL(feeRate.GetFee(1e3), -1e3);
- BOOST_CHECK_EQUAL(feeRate.GetFee(9e3), -9e3);
+ BOOST_CHECK_EQUAL(feeRate.GetFee(0), CAmount(0));
+ BOOST_CHECK_EQUAL(feeRate.GetFee(1), CAmount(-1));
+ BOOST_CHECK_EQUAL(feeRate.GetFee(121), CAmount(-121));
+ BOOST_CHECK_EQUAL(feeRate.GetFee(999), CAmount(-999));
+ BOOST_CHECK_EQUAL(feeRate.GetFee(1e3), CAmount(-1e3));
+ BOOST_CHECK_EQUAL(feeRate.GetFee(9e3), CAmount(-9e3));
feeRate = CFeeRate(123);
// Truncates the result, if not integer
- BOOST_CHECK_EQUAL(feeRate.GetFee(0), 0);
- BOOST_CHECK_EQUAL(feeRate.GetFee(8), 1); // Special case: returns 1 instead of 0
- BOOST_CHECK_EQUAL(feeRate.GetFee(9), 1);
- BOOST_CHECK_EQUAL(feeRate.GetFee(121), 14);
- BOOST_CHECK_EQUAL(feeRate.GetFee(122), 15);
- BOOST_CHECK_EQUAL(feeRate.GetFee(999), 122);
- BOOST_CHECK_EQUAL(feeRate.GetFee(1e3), 123);
- BOOST_CHECK_EQUAL(feeRate.GetFee(9e3), 1107);
+ BOOST_CHECK_EQUAL(feeRate.GetFee(0), CAmount(0));
+ BOOST_CHECK_EQUAL(feeRate.GetFee(8), CAmount(1)); // Special case: returns 1 instead of 0
+ BOOST_CHECK_EQUAL(feeRate.GetFee(9), CAmount(1));
+ BOOST_CHECK_EQUAL(feeRate.GetFee(121), CAmount(14));
+ BOOST_CHECK_EQUAL(feeRate.GetFee(122), CAmount(15));
+ BOOST_CHECK_EQUAL(feeRate.GetFee(999), CAmount(122));
+ BOOST_CHECK_EQUAL(feeRate.GetFee(1e3), CAmount(123));
+ BOOST_CHECK_EQUAL(feeRate.GetFee(9e3), CAmount(1107));
feeRate = CFeeRate(-123);
// Truncates the result, if not integer
- BOOST_CHECK_EQUAL(feeRate.GetFee(0), 0);
- BOOST_CHECK_EQUAL(feeRate.GetFee(8), -1); // Special case: returns -1 instead of 0
- BOOST_CHECK_EQUAL(feeRate.GetFee(9), -1);
+ BOOST_CHECK_EQUAL(feeRate.GetFee(0), CAmount(0));
+ BOOST_CHECK_EQUAL(feeRate.GetFee(8), CAmount(-1)); // Special case: returns -1 instead of 0
+ BOOST_CHECK_EQUAL(feeRate.GetFee(9), CAmount(-1));
// check alternate constructor
feeRate = CFeeRate(1000);
@@ -67,6 +69,9 @@ BOOST_AUTO_TEST_CASE(GetFeeTest)
BOOST_CHECK_EQUAL(feeRate.GetFee(100), altFeeRate.GetFee(100));
// Check full constructor
+ BOOST_CHECK(CFeeRate(CAmount(-1), 0) == CFeeRate(0));
+ BOOST_CHECK(CFeeRate(CAmount(0), 0) == CFeeRate(0));
+ BOOST_CHECK(CFeeRate(CAmount(1), 0) == CFeeRate(0));
// default value
BOOST_CHECK(CFeeRate(CAmount(-1), 1000) == CFeeRate(-1));
BOOST_CHECK(CFeeRate(CAmount(0), 1000) == CFeeRate(0));
diff --git a/src/test/descriptor_tests.cpp b/src/test/descriptor_tests.cpp
index e739b84a48..f3083bab4a 100644
--- a/src/test/descriptor_tests.cpp
+++ b/src/test/descriptor_tests.cpp
@@ -43,9 +43,12 @@ std::string MaybeUseHInsteadOfApostrophy(std::string ret)
return ret;
}
-void Check(const std::string& prv, const std::string& pub, int flags, const std::vector<std::vector<std::string>>& scripts)
+const std::set<std::vector<uint32_t>> ONLY_EMPTY{{}};
+
+void Check(const std::string& prv, const std::string& pub, int flags, const std::vector<std::vector<std::string>>& scripts, const std::set<std::vector<uint32_t>>& paths = ONLY_EMPTY)
{
FlatSigningProvider keys_priv, keys_pub;
+ std::set<std::vector<uint32_t>> left_paths = paths;
// Check that parsing succeeds.
auto parse_priv = Parse(MaybeUseHInsteadOfApostrophy(prv), keys_priv);
@@ -84,7 +87,7 @@ void Check(const std::string& prv, const std::string& pub, int flags, const std:
for (size_t i = 0; i < max; ++i) {
const auto& ref = scripts[(flags & RANGE) ? i : 0];
for (int t = 0; t < 2; ++t) {
- FlatSigningProvider key_provider = (flags & HARDENED) ? keys_priv : keys_pub;
+ const FlatSigningProvider& key_provider = (flags & HARDENED) ? keys_priv : keys_pub;
FlatSigningProvider script_provider;
std::vector<CScript> spks;
BOOST_CHECK((t ? parse_priv : parse_pub)->Expand(i, key_provider, spks, script_provider));
@@ -100,9 +103,16 @@ void Check(const std::string& prv, const std::string& pub, int flags, const std:
BOOST_CHECK_MESSAGE(SignSignature(Merge(keys_priv, script_provider), spks[n], spend, 0, 1, SIGHASH_ALL), prv);
}
}
-
+ // Test whether the observed key path is present in the 'paths' variable (which contains expected, unobserved paths),
+ // and then remove it from that set.
+ for (const auto& origin : script_provider.origins) {
+ BOOST_CHECK_MESSAGE(paths.count(origin.second.path), "Unexpected key path: " + prv);
+ left_paths.erase(origin.second.path);
+ }
}
}
+ // Verify no expected paths remain that were not observed.
+ BOOST_CHECK_MESSAGE(left_paths.empty(), "Not all expected key paths found: " + prv);
}
}
@@ -114,7 +124,7 @@ BOOST_AUTO_TEST_CASE(descriptor_test)
// Basic single-key compressed
Check("combo(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "combo(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"2103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bdac","76a9149a1c78a507689f6f54b847ad1cef1e614ee23f1e88ac","00149a1c78a507689f6f54b847ad1cef1e614ee23f1e","a91484ab21b1b2fd065d4504ff693d832434b6108d7b87"}});
Check("pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"2103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bdac"}});
- Check("pkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"76a9149a1c78a507689f6f54b847ad1cef1e614ee23f1e88ac"}});
+ Check("pkh([deadbeef/1/2'/3/4']L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pkh([deadbeef/1/2'/3/4']03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"76a9149a1c78a507689f6f54b847ad1cef1e614ee23f1e88ac"}}, {{1,0x80000002UL,3,0x80000004UL}});
Check("wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"00149a1c78a507689f6f54b847ad1cef1e614ee23f1e"}});
Check("sh(wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"a91484ab21b1b2fd065d4504ff693d832434b6108d7b87"}});
@@ -135,20 +145,26 @@ BOOST_AUTO_TEST_CASE(descriptor_test)
Check("sh(wsh(pkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "sh(wsh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", SIGNABLE, {{"a914b61b92e2ca21bac1e72a3ab859a742982bea960a87"}});
// Versions with BIP32 derivations
- Check("combo(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)", "combo(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)", SIGNABLE, {{"2102d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0ac","76a91431a507b815593dfc51ffc7245ae7e5aee304246e88ac","001431a507b815593dfc51ffc7245ae7e5aee304246e","a9142aafb926eb247cb18240a7f4c07983ad1f37922687"}});
- Check("pk(xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0)", "pk(xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)", DEFAULT, {{"210379e45b3cf75f9c5f9befd8e9506fb962f6a9d185ac87001ec44a8d3df8d4a9e3ac"}});
- Check("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0)", HARDENED, {{"76a914ebdc90806a9c4356c1c88e42216611e1cb4c1c1788ac"}});
- Check("wpkh(xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*)", "wpkh(xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*)", RANGE, {{"0014326b2249e3a25d5dc60935f044ee835d090ba859"},{"0014af0bd98abc2f2cae66e36896a39ffe2d32984fb7"},{"00141fa798efd1cbf95cebf912c031b8a4a6e9fb9f27"}});
- Check("sh(wpkh(xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "sh(wpkh(xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", RANGE | HARDENED, {{"a9149a4d9901d6af519b2a23d4a2f51650fcba87ce7b87"},{"a914bed59fc0024fae941d6e20a3b44a109ae740129287"},{"a9148483aa1116eb9c05c482a72bada4b1db24af654387"}});
- Check("combo(xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/*)", "combo(xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/*)", RANGE, {{"2102df12b7035bdac8e3bab862a3a83d06ea6b17b6753d52edecba9be46f5d09e076ac","76a914f90e3178ca25f2c808dc76624032d352fdbdfaf288ac","0014f90e3178ca25f2c808dc76624032d352fdbdfaf2","a91408f3ea8c68d4a7585bf9e8bda226723f70e445f087"},{"21032869a233c9adff9a994e4966e5b821fd5bac066da6c3112488dc52383b4a98ecac","76a914a8409d1b6dfb1ed2a3e8aa5e0ef2ff26b15b75b788ac","0014a8409d1b6dfb1ed2a3e8aa5e0ef2ff26b15b75b7","a91473e39884cb71ae4e5ac9739e9225026c99763e6687"}});
+ Check("combo([01234567]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)", "combo([01234567]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)", SIGNABLE, {{"2102d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0ac","76a91431a507b815593dfc51ffc7245ae7e5aee304246e88ac","001431a507b815593dfc51ffc7245ae7e5aee304246e","a9142aafb926eb247cb18240a7f4c07983ad1f37922687"}});
+ Check("pk(xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0)", "pk(xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)", DEFAULT, {{"210379e45b3cf75f9c5f9befd8e9506fb962f6a9d185ac87001ec44a8d3df8d4a9e3ac"}}, {{0}});
+ Check("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0)", HARDENED, {{"76a914ebdc90806a9c4356c1c88e42216611e1cb4c1c1788ac"}}, {{0xFFFFFFFFUL,0}});
+ Check("wpkh([ffffffff/13']xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*)", "wpkh([ffffffff/13']xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*)", RANGE, {{"0014326b2249e3a25d5dc60935f044ee835d090ba859"},{"0014af0bd98abc2f2cae66e36896a39ffe2d32984fb7"},{"00141fa798efd1cbf95cebf912c031b8a4a6e9fb9f27"}}, {{0x8000000DUL, 1, 2, 0}, {0x8000000DUL, 1, 2, 1}, {0x8000000DUL, 1, 2, 2}});
+ Check("sh(wpkh(xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "sh(wpkh(xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", RANGE | HARDENED, {{"a9149a4d9901d6af519b2a23d4a2f51650fcba87ce7b87"},{"a914bed59fc0024fae941d6e20a3b44a109ae740129287"},{"a9148483aa1116eb9c05c482a72bada4b1db24af654387"}}, {{10, 20, 30, 40, 0x80000000UL}, {10, 20, 30, 40, 0x80000001UL}, {10, 20, 30, 40, 0x80000002UL}});
+ Check("combo(xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/*)", "combo(xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/*)", RANGE, {{"2102df12b7035bdac8e3bab862a3a83d06ea6b17b6753d52edecba9be46f5d09e076ac","76a914f90e3178ca25f2c808dc76624032d352fdbdfaf288ac","0014f90e3178ca25f2c808dc76624032d352fdbdfaf2","a91408f3ea8c68d4a7585bf9e8bda226723f70e445f087"},{"21032869a233c9adff9a994e4966e5b821fd5bac066da6c3112488dc52383b4a98ecac","76a914a8409d1b6dfb1ed2a3e8aa5e0ef2ff26b15b75b788ac","0014a8409d1b6dfb1ed2a3e8aa5e0ef2ff26b15b75b7","a91473e39884cb71ae4e5ac9739e9225026c99763e6687"}}, {{0}, {1}});
+ CheckUnparsable("combo([012345678]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)", "combo([012345678]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)"); // Too long key fingerprint
CheckUnparsable("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483648)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483648)"); // BIP 32 path element overflow
// Multisig constructions
Check("multi(1,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}});
- Check("sh(multi(2,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))", "sh(multi(2,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}});
- Check("wsh(multi(2,xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", HARDENED | RANGE, {{"0020b92623201f3bb7c3771d45b2ad1d0351ea8fbf8cfe0a0e570264e1075fa1948f"},{"002036a08bbe4923af41cf4316817c93b8d37e2f635dd25cfff06bd50df6ae7ea203"},{"0020a96e7ab4607ca6b261bfe3245ffda9c746b28d3f59e83d34820ec0e2b36c139c"}});
+ Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, {{0x8000006FUL,222},{0}});
+ Check("wsh(multi(2,xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", HARDENED | RANGE, {{"0020b92623201f3bb7c3771d45b2ad1d0351ea8fbf8cfe0a0e570264e1075fa1948f"},{"002036a08bbe4923af41cf4316817c93b8d37e2f635dd25cfff06bd50df6ae7ea203"},{"0020a96e7ab4607ca6b261bfe3245ffda9c746b28d3f59e83d34820ec0e2b36c139c"}}, {{0xFFFFFFFFUL,0}, {1,2,0}, {1,2,1}, {1,2,2}, {10, 20, 30, 40, 0x80000000UL}, {10, 20, 30, 40, 0x80000001UL}, {10, 20, 30, 40, 0x80000002UL}});
Check("sh(wsh(multi(16,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9)))","sh(wsh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232)))", SIGNABLE, {{"a9147fc63e13dc25e8a95a3cee3d9a714ac3afd96f1e87"}});
CheckUnparsable("sh(multi(16,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9))","sh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232))"); // P2SH does not fit 16 compressed pubkeys in a redeemscript
+ CheckUnparsable("wsh(multi(2,[aaaaaaaa][aaaaaaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaaaaaaa][aaaaaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))"); // Double key origin descriptor
+ CheckUnparsable("wsh(multi(2,[aaaagaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaagaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))"); // Non hex fingerprint
+ CheckUnparsable("wsh(multi(2,[aaaaaaaa],xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaaaaaaa],xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))"); // No public key with origin
+ CheckUnparsable("wsh(multi(2,[aaaaaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaaaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))"); // Too short fingerprint
+ CheckUnparsable("wsh(multi(2,[aaaaaaaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaaaaaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))"); // Too long fingerprint
// Check for invalid nesting of structures
CheckUnparsable("sh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "sh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)"); // P2SH needs a script, not a key
diff --git a/src/test/fs_tests.cpp b/src/test/fs_tests.cpp
new file mode 100644
index 0000000000..93aee10bb7
--- /dev/null
+++ b/src/test/fs_tests.cpp
@@ -0,0 +1,56 @@
+// Copyright (c) 2011-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 <fs.h>
+#include <test/test_bitcoin.h>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_FIXTURE_TEST_SUITE(fs_tests, BasicTestingSetup)
+
+BOOST_AUTO_TEST_CASE(fsbridge_fstream)
+{
+ fs::path tmpfolder = SetDataDir("fsbridge_fstream");
+ // tmpfile1 should be the same as tmpfile2
+ fs::path tmpfile1 = tmpfolder / "fs_tests_₿_🏃";
+ fs::path tmpfile2 = tmpfolder / L"fs_tests_₿_🏃";
+ {
+ fsbridge::ofstream file(tmpfile1);
+ file << "bitcoin";
+ }
+ {
+ fsbridge::ifstream file(tmpfile2);
+ std::string input_buffer;
+ file >> input_buffer;
+ BOOST_CHECK_EQUAL(input_buffer, "bitcoin");
+ }
+ {
+ fsbridge::ifstream file(tmpfile1, std::ios_base::in | std::ios_base::ate);
+ std::string input_buffer;
+ file >> input_buffer;
+ BOOST_CHECK_EQUAL(input_buffer, "");
+ }
+ {
+ fsbridge::ofstream file(tmpfile2, std::ios_base::out | std::ios_base::app);
+ file << "tests";
+ }
+ {
+ fsbridge::ifstream file(tmpfile1);
+ std::string input_buffer;
+ file >> input_buffer;
+ BOOST_CHECK_EQUAL(input_buffer, "bitcointests");
+ }
+ {
+ fsbridge::ofstream file(tmpfile2, std::ios_base::out | std::ios_base::trunc);
+ file << "bitcoin";
+ }
+ {
+ fsbridge::ifstream file(tmpfile1);
+ std::string input_buffer;
+ file >> input_buffer;
+ BOOST_CHECK_EQUAL(input_buffer, "bitcoin");
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index 3eb8aa14fd..354ca7507e 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -92,8 +92,8 @@ static CBlockIndex CreateBlockIndex(int nHeight)
static bool TestSequenceLocks(const CTransaction &tx, int flags) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
- LOCK(mempool.cs);
- return CheckSequenceLocks(tx, flags);
+ LOCK(::mempool.cs);
+ return CheckSequenceLocks(::mempool, tx, flags);
}
// Test suite for ancestor feerate transaction selection.
diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp
index 67c377778f..7fbf37e7fb 100644
--- a/src/test/script_tests.cpp
+++ b/src/test/script_tests.cpp
@@ -1013,21 +1013,21 @@ BOOST_AUTO_TEST_CASE(script_PushData)
ScriptError err;
std::vector<std::vector<unsigned char> > directStack;
- BOOST_CHECK(EvalScript(directStack, CScript(&direct[0], &direct[sizeof(direct)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SigVersion::BASE, &err));
+ BOOST_CHECK(EvalScript(directStack, CScript(direct, direct + sizeof(direct)), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SigVersion::BASE, &err));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
std::vector<std::vector<unsigned char> > pushdata1Stack;
- BOOST_CHECK(EvalScript(pushdata1Stack, CScript(&pushdata1[0], &pushdata1[sizeof(pushdata1)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SigVersion::BASE, &err));
+ BOOST_CHECK(EvalScript(pushdata1Stack, CScript(pushdata1, pushdata1 + sizeof(pushdata1)), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SigVersion::BASE, &err));
BOOST_CHECK(pushdata1Stack == directStack);
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
std::vector<std::vector<unsigned char> > pushdata2Stack;
- BOOST_CHECK(EvalScript(pushdata2Stack, CScript(&pushdata2[0], &pushdata2[sizeof(pushdata2)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SigVersion::BASE, &err));
+ BOOST_CHECK(EvalScript(pushdata2Stack, CScript(pushdata2, pushdata2 + sizeof(pushdata2)), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SigVersion::BASE, &err));
BOOST_CHECK(pushdata2Stack == directStack);
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
std::vector<std::vector<unsigned char> > pushdata4Stack;
- BOOST_CHECK(EvalScript(pushdata4Stack, CScript(&pushdata4[0], &pushdata4[sizeof(pushdata4)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SigVersion::BASE, &err));
+ BOOST_CHECK(EvalScript(pushdata4Stack, CScript(pushdata4, pushdata4 + sizeof(pushdata4)), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SigVersion::BASE, &err));
BOOST_CHECK(pushdata4Stack == directStack);
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
}
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 3ad93342c4..34a1e539df 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -498,7 +498,7 @@ void CTxMemPool::removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMem
const CTransaction& tx = it->GetTx();
LockPoints lp = it->GetLockPoints();
bool validLP = TestLockPointValidity(&lp);
- if (!CheckFinalTx(tx, flags) || !CheckSequenceLocks(tx, flags, &lp, validLP)) {
+ if (!CheckFinalTx(tx, flags) || !CheckSequenceLocks(*this, tx, flags, &lp, validLP)) {
// Note if CheckSequenceLocks fails the LockPoints may still be invalid
// So it's critical that we remove the tx and not depend on the LockPoints.
txToRemove.insert(it);
diff --git a/src/uint256.cpp b/src/uint256.cpp
index e513dc1de7..e940f90cf0 100644
--- a/src/uint256.cpp
+++ b/src/uint256.cpp
@@ -29,7 +29,7 @@ void base_blob<BITS>::SetHex(const char* psz)
memset(data, 0, sizeof(data));
// skip leading spaces
- while (isspace(*psz))
+ while (IsSpace(*psz))
psz++;
// skip 0x
diff --git a/src/util.cpp b/src/util.cpp
index fa624aee90..6479b9b9ce 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -61,6 +61,7 @@
#include <codecvt>
#include <io.h> /* for _commit */
+#include <shellapi.h>
#include <shlobj.h>
#endif
@@ -891,7 +892,7 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
}
const std::string confPath = GetArg("-conf", BITCOIN_CONF_FILENAME);
- fs::ifstream stream(GetConfigFile(confPath));
+ fsbridge::ifstream stream(GetConfigFile(confPath));
// ok to not have a config file
if (stream.good()) {
@@ -924,7 +925,7 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
}
for (const std::string& to_include : includeconf) {
- fs::ifstream include_config(GetConfigFile(to_include));
+ fsbridge::ifstream include_config(GetConfigFile(to_include));
if (include_config.good()) {
if (!ReadConfigStream(include_config, error, ignore_invalid_keys)) {
return false;
@@ -1200,6 +1201,10 @@ void SetupEnvironment()
} catch (const std::runtime_error&) {
setenv("LC_ALL", "C", 1);
}
+#elif defined(WIN32)
+ // Set the default input/output charset is utf-8
+ SetConsoleCP(CP_UTF8);
+ SetConsoleOutputCP(CP_UTF8);
#endif
// The path locale is lazy initialized and to avoid deinitialization errors
// in multithreading environments, it is set explicitly by the main thread.
@@ -1265,3 +1270,30 @@ int ScheduleBatchPriority()
return 1;
#endif
}
+
+namespace util {
+#ifdef WIN32
+WinCmdLineArgs::WinCmdLineArgs()
+{
+ wchar_t** wargv = CommandLineToArgvW(GetCommandLineW(), &argc);
+ std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> utf8_cvt;
+ argv = new char*[argc];
+ args.resize(argc);
+ for (int i = 0; i < argc; i++) {
+ args[i] = utf8_cvt.to_bytes(wargv[i]);
+ argv[i] = &*args[i].begin();
+ }
+ LocalFree(wargv);
+}
+
+WinCmdLineArgs::~WinCmdLineArgs()
+{
+ delete[] argv;
+}
+
+std::pair<int, char**> WinCmdLineArgs::get()
+{
+ return std::make_pair(argc, argv);
+}
+#endif
+} // namespace util
diff --git a/src/util.h b/src/util.h
index f119385e48..fa6d2cd489 100644
--- a/src/util.h
+++ b/src/util.h
@@ -29,6 +29,7 @@
#include <stdint.h>
#include <string>
#include <unordered_set>
+#include <utility>
#include <vector>
#include <boost/thread/condition_variable.hpp> // for boost::thread_interrupted
@@ -361,6 +362,21 @@ inline void insert(std::set<TsetT>& dst, const Tsrc& src) {
dst.insert(src.begin(), src.end());
}
+#ifdef WIN32
+class WinCmdLineArgs
+{
+public:
+ WinCmdLineArgs();
+ ~WinCmdLineArgs();
+ std::pair<int, char**> get();
+
+private:
+ int argc;
+ char** argv;
+ std::vector<std::string> args;
+};
+#endif
+
} // namespace util
#endif // BITCOIN_UTIL_H
diff --git a/src/utilmoneystr.cpp b/src/utilmoneystr.cpp
index 326ef9b27a..7bae161a1d 100644
--- a/src/utilmoneystr.cpp
+++ b/src/utilmoneystr.cpp
@@ -41,7 +41,7 @@ bool ParseMoney(const char* pszIn, CAmount& nRet)
std::string strWhole;
int64_t nUnits = 0;
const char* p = pszIn;
- while (isspace(*p))
+ while (IsSpace(*p))
p++;
for (; *p; p++)
{
@@ -56,14 +56,14 @@ bool ParseMoney(const char* pszIn, CAmount& nRet)
}
break;
}
- if (isspace(*p))
+ if (IsSpace(*p))
break;
if (!isdigit(*p))
return false;
strWhole.insert(strWhole.end(), *p);
}
for (; *p; p++)
- if (!isspace(*p))
+ if (!IsSpace(*p))
return false;
if (strWhole.size() > 10) // guard against 63 bit overflow
return false;
diff --git a/src/utilstrencodings.cpp b/src/utilstrencodings.cpp
index 4940267bae..a1700d2514 100644
--- a/src/utilstrencodings.cpp
+++ b/src/utilstrencodings.cpp
@@ -85,7 +85,7 @@ std::vector<unsigned char> ParseHex(const char* psz)
std::vector<unsigned char> vch;
while (true)
{
- while (isspace(*psz))
+ while (IsSpace(*psz))
psz++;
signed char c = HexDigit(*psz++);
if (c == (signed char)-1)
@@ -266,7 +266,7 @@ static bool ParsePrechecks(const std::string& str)
{
if (str.empty()) // No empty string allowed
return false;
- if (str.size() >= 1 && (isspace(str[0]) || isspace(str[str.size()-1]))) // No padding allowed
+ if (str.size() >= 1 && (IsSpace(str[0]) || IsSpace(str[str.size()-1]))) // No padding allowed
return false;
if (str.size() != strlen(str.c_str())) // No embedded NUL characters allowed
return false;
diff --git a/src/utilstrencodings.h b/src/utilstrencodings.h
index 846a3917a2..1610c8c268 100644
--- a/src/utilstrencodings.h
+++ b/src/utilstrencodings.h
@@ -72,6 +72,21 @@ constexpr bool IsDigit(char c)
}
/**
+ * Tests if the given character is a whitespace character. The whitespace characters
+ * are: space, form-feed ('\f'), newline ('\n'), carriage return ('\r'), horizontal
+ * tab ('\t'), and vertical tab ('\v').
+ *
+ * This function is locale independent. Under the C locale this function gives the
+ * same result as std::isspace.
+ *
+ * @param[in] c character to test
+ * @return true if the argument is a whitespace character; otherwise false
+ */
+constexpr inline bool IsSpace(char c) noexcept {
+ return c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v';
+}
+
+/**
* 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 458458d85d..0c446c262d 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -361,10 +361,10 @@ bool TestLockPointValidity(const LockPoints* lp)
return true;
}
-bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp, bool useExistingLockPoints)
+bool CheckSequenceLocks(const CTxMemPool& pool, const CTransaction& tx, int flags, LockPoints* lp, bool useExistingLockPoints)
{
AssertLockHeld(cs_main);
- AssertLockHeld(mempool.cs);
+ AssertLockHeld(pool.cs);
CBlockIndex* tip = chainActive.Tip();
assert(tip != nullptr);
@@ -387,7 +387,7 @@ bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp, bool
}
else {
// pcoinsTip contains the UTXO set for chainActive.Tip()
- CCoinsViewMemPool viewMemPool(pcoinsTip.get(), mempool);
+ CCoinsViewMemPool viewMemPool(pcoinsTip.get(), pool);
std::vector<int> prevheights;
prevheights.resize(tx.vin.size());
for (size_t txinIndex = 0; txinIndex < tx.vin.size(); txinIndex++) {
@@ -679,7 +679,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
// be mined yet.
// Must keep pool.cs for this unless we change CheckSequenceLocks to take a
// CoinsViewCache instead of create its own
- if (!CheckSequenceLocks(tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp))
+ if (!CheckSequenceLocks(pool, tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp))
return state.DoS(0, false, REJECT_NONSTANDARD, "non-BIP68-final");
CAmount nFees = 0;
@@ -918,7 +918,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
// There is a similar check in CreateNewBlock() to prevent creating
// invalid blocks (using TestBlockValidity), however allowing such
// transactions into the mempool can be exploited as a DoS attack.
- unsigned int currentBlockScriptVerifyFlags = GetBlockScriptFlags(chainActive.Tip(), Params().GetConsensus());
+ unsigned int currentBlockScriptVerifyFlags = GetBlockScriptFlags(chainActive.Tip(), chainparams.GetConsensus());
if (!CheckInputsFromMempoolAndCache(tx, state, view, pool, currentBlockScriptVerifyFlags, true, txdata)) {
return error("%s: BUG! PLEASE REPORT THIS! CheckInputs failed against latest-block but not STANDARD flags %s, %s",
__func__, hash.ToString(), FormatStateMessage(state));
@@ -4766,6 +4766,9 @@ bool DumpMempool()
std::map<uint256, CAmount> mapDeltas;
std::vector<TxMempoolInfo> vinfo;
+ static Mutex dump_mutex;
+ LOCK(dump_mutex);
+
{
LOCK(mempool.cs);
for (const auto &i : mempool.mapDeltas) {
diff --git a/src/validation.h b/src/validation.h
index 1034ba4665..3e98ebc866 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -257,7 +257,7 @@ bool LoadGenesisBlock(const CChainParams& chainparams);
* initializing state if we're running with -reindex. */
bool LoadBlockIndex(const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Update the chain tip based on database information. */
-bool LoadChainTip(const CChainParams& chainparams);
+bool LoadChainTip(const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Unload database information */
void UnloadBlockIndex();
/** Run an instance of the script checking thread */
@@ -347,7 +347,7 @@ bool TestLockPointValidity(const LockPoints* lp) EXCLUSIVE_LOCKS_REQUIRED(cs_mai
*
* See consensus/consensus.h for flag definitions.
*/
-bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp = nullptr, bool useExistingLockPoints = false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+bool CheckSequenceLocks(const CTxMemPool& pool, const CTransaction& tx, int flags, LockPoints* lp = nullptr, bool useExistingLockPoints = false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/**
* Closure representing one script verification
@@ -436,7 +436,7 @@ inline CBlockIndex* LookupBlockIndex(const uint256& hash)
}
/** Find the last common block between the parameter chain and a locator. */
-CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator);
+CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Mark a block as precious and reorganize.
*
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index a7bf89c572..25880f3529 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -20,6 +20,7 @@
#include <boost/thread.hpp>
namespace {
+
//! Make sure database has a unique fileid within the environment. If it
//! doesn't, throw an error. BDB caches do not work properly when more than one
//! open database has the same fileid (values written to one database may show
@@ -29,25 +30,19 @@ namespace {
//! (https://docs.oracle.com/cd/E17275_01/html/programmer_reference/program_copy.html),
//! so bitcoin should never create different databases with the same fileid, but
//! this error can be triggered if users manually copy database files.
-void CheckUniqueFileid(const BerkeleyEnvironment& env, const std::string& filename, Db& db)
+void CheckUniqueFileid(const BerkeleyEnvironment& env, const std::string& filename, Db& db, WalletDatabaseFileId& fileid)
{
if (env.IsMock()) return;
- u_int8_t fileid[DB_FILE_ID_LEN];
- int ret = db.get_mpf()->get_fileid(fileid);
+ int ret = db.get_mpf()->get_fileid(fileid.value);
if (ret != 0) {
throw std::runtime_error(strprintf("BerkeleyBatch: Can't open database %s (get_fileid failed with %d)", filename, ret));
}
- for (const auto& item : env.mapDb) {
- u_int8_t item_fileid[DB_FILE_ID_LEN];
- if (item.second && item.second->get_mpf()->get_fileid(item_fileid) == 0 &&
- memcmp(fileid, item_fileid, sizeof(fileid)) == 0) {
- const char* item_filename = nullptr;
- item.second->get_dbname(&item_filename, nullptr);
+ for (const auto& item : env.m_fileids) {
+ if (fileid == item.second && &fileid != &item.second) {
throw std::runtime_error(strprintf("BerkeleyBatch: Can't open database %s (duplicates fileid %s from %s)", filename,
- HexStr(std::begin(item_fileid), std::end(item_fileid)),
- item_filename ? item_filename : "(unknown database)"));
+ HexStr(std::begin(item.second.value), std::end(item.second.value)), item.first));
}
}
}
@@ -56,6 +51,11 @@ CCriticalSection cs_db;
std::map<std::string, BerkeleyEnvironment> g_dbenvs GUARDED_BY(cs_db); //!< Map from directory name to open db environment.
} // namespace
+bool WalletDatabaseFileId::operator==(const WalletDatabaseFileId& rhs) const
+{
+ return memcmp(value, &rhs.value, sizeof(value)) == 0;
+}
+
BerkeleyEnvironment* GetWalletEnv(const fs::path& wallet_path, std::string& database_filename)
{
fs::path env_directory;
@@ -504,7 +504,7 @@ BerkeleyBatch::BerkeleyBatch(BerkeleyDatabase& database, const char* pszMode, bo
// versions of BDB have an set_lk_exclusive method for this
// purpose, but the older version we use does not.)
for (const auto& env : g_dbenvs) {
- CheckUniqueFileid(env.second, strFilename, *pdb_temp);
+ CheckUniqueFileid(env.second, strFilename, *pdb_temp, this->env->m_fileids[strFilename]);
}
pdb = pdb_temp.release();
@@ -826,6 +826,13 @@ void BerkeleyDatabase::Flush(bool shutdown)
LOCK(cs_db);
g_dbenvs.erase(env->Directory().string());
env = nullptr;
+ } else {
+ // TODO: To avoid g_dbenvs.erase erasing the environment prematurely after the
+ // first database shutdown when multiple databases are open in the same
+ // environment, should replace raw database `env` pointers with shared or weak
+ // pointers, or else separate the database and environment shutdowns so
+ // environments can be shut down after databases.
+ env->m_fileids.erase(strFile);
}
}
}
diff --git a/src/wallet/db.h b/src/wallet/db.h
index 467ed13b45..68a59607ae 100644
--- a/src/wallet/db.h
+++ b/src/wallet/db.h
@@ -18,6 +18,7 @@
#include <map>
#include <memory>
#include <string>
+#include <unordered_map>
#include <vector>
#include <db_cxx.h>
@@ -25,6 +26,11 @@
static const unsigned int DEFAULT_WALLET_DBLOGSIZE = 100;
static const bool DEFAULT_WALLET_PRIVDB = true;
+struct WalletDatabaseFileId {
+ u_int8_t value[DB_FILE_ID_LEN];
+ bool operator==(const WalletDatabaseFileId& rhs) const;
+};
+
class BerkeleyEnvironment
{
private:
@@ -38,6 +44,7 @@ public:
std::unique_ptr<DbEnv> dbenv;
std::map<std::string, int> mapFileUseCount;
std::map<std::string, Db*> mapDb;
+ std::unordered_map<std::string, WalletDatabaseFileId> m_fileids;
std::condition_variable_any m_db_in_use;
BerkeleyEnvironment(const fs::path& env_directory);
diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp
index a299a4ee44..46983642f0 100644
--- a/src/wallet/init.cpp
+++ b/src/wallet/init.cpp
@@ -182,13 +182,18 @@ bool WalletInit::Verify() const
if (gArgs.IsArgSet("-walletdir")) {
fs::path wallet_dir = gArgs.GetArg("-walletdir", "");
- if (!fs::exists(wallet_dir)) {
+ boost::system::error_code error;
+ // The canonical path cleans the path, preventing >1 Berkeley environment instances for the same directory
+ fs::path canonical_wallet_dir = fs::canonical(wallet_dir, error);
+ if (error || !fs::exists(wallet_dir)) {
return InitError(strprintf(_("Specified -walletdir \"%s\" does not exist"), wallet_dir.string()));
} else if (!fs::is_directory(wallet_dir)) {
return InitError(strprintf(_("Specified -walletdir \"%s\" is not a directory"), wallet_dir.string()));
+ // The canonical path transforms relative paths into absolute ones, so we check the non-canonical version
} else if (!wallet_dir.is_absolute()) {
return InitError(strprintf(_("Specified -walletdir \"%s\" is a relative path"), wallet_dir.string()));
}
+ gArgs.ForceSetArg("-walletdir", canonical_wallet_dir.string());
}
LogPrintf("Using wallet directory %s\n", GetWalletDir().string());
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index c97bc38e6f..c17d5b48d4 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -17,7 +17,6 @@
#include <wallet/rpcwallet.h>
-#include <fstream>
#include <stdint.h>
#include <boost/algorithm/string.hpp>
@@ -540,8 +539,8 @@ UniValue importwallet(const JSONRPCRequest& request)
EnsureWalletIsUnlocked(pwallet);
- std::ifstream file;
- file.open(request.params[0].get_str().c_str(), std::ios::in | std::ios::ate);
+ fsbridge::ifstream file;
+ file.open(request.params[0].get_str(), std::ios::in | std::ios::ate);
if (!file.is_open()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");
}
@@ -717,8 +716,8 @@ UniValue dumpwallet(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INVALID_PARAMETER, filepath.string() + " already exists. If you are sure this is what you want, move it out of the way first");
}
- std::ofstream file;
- file.open(filepath.string().c_str());
+ fsbridge::ofstream file;
+ file.open(filepath);
if (!file.is_open())
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");
@@ -809,29 +808,24 @@ UniValue dumpwallet(const JSONRPCRequest& request)
static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
{
try {
- bool success = false;
-
- // Required fields.
+ // First ensure scriptPubKey has either a script or JSON with "address" string
const UniValue& scriptPubKey = data["scriptPubKey"];
-
- // Should have script or JSON with "address".
- if (!(scriptPubKey.getType() == UniValue::VOBJ && scriptPubKey.exists("address")) && !(scriptPubKey.getType() == UniValue::VSTR)) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid scriptPubKey");
+ bool isScript = scriptPubKey.getType() == UniValue::VSTR;
+ if (!isScript && !(scriptPubKey.getType() == UniValue::VOBJ && scriptPubKey.exists("address"))) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "scriptPubKey must be string with script or JSON with address string");
}
+ const std::string& output = isScript ? scriptPubKey.get_str() : scriptPubKey["address"].get_str();
// Optional fields.
const std::string& strRedeemScript = data.exists("redeemscript") ? data["redeemscript"].get_str() : "";
+ const std::string& witness_script_hex = data.exists("witnessscript") ? data["witnessscript"].get_str() : "";
const UniValue& pubKeys = data.exists("pubkeys") ? data["pubkeys"].get_array() : UniValue();
const UniValue& keys = data.exists("keys") ? data["keys"].get_array() : UniValue();
const bool internal = data.exists("internal") ? data["internal"].get_bool() : false;
const bool watchOnly = data.exists("watchonly") ? data["watchonly"].get_bool() : false;
- const std::string& label = data.exists("label") && !internal ? data["label"].get_str() : "";
-
- bool isScript = scriptPubKey.getType() == UniValue::VSTR;
- bool isP2SH = strRedeemScript.length() > 0;
- const std::string& output = isScript ? scriptPubKey.get_str() : scriptPubKey["address"].get_str();
+ const std::string& label = data.exists("label") ? data["label"].get_str() : "";
- // Parse the output.
+ // Generate the script and destination for the scriptPubKey provided
CScript script;
CTxDestination dest;
@@ -855,35 +849,38 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con
// Watchonly and private keys
if (watchOnly && keys.size()) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Incompatibility found between watchonly and keys");
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Watch-only addresses should not include private keys");
}
- // Internal + Label
+ // Internal addresses should not have a label
if (internal && data.exists("label")) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Incompatibility found between internal and label");
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Internal addresses should not have a label");
}
- // Keys / PubKeys size check.
- if (!isP2SH && (keys.size() > 1 || pubKeys.size() > 1)) { // Address / scriptPubKey
- throw JSONRPCError(RPC_INVALID_PARAMETER, "More than private key given for one address");
+ // Force users to provide the witness script in its field rather than redeemscript
+ if (!strRedeemScript.empty() && script.IsPayToWitnessScriptHash()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "P2WSH addresses have an empty redeemscript. Please provide the witnessscript instead.");
}
- // Invalid P2SH redeemScript
- if (isP2SH && !IsHex(strRedeemScript)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid redeem script");
- }
-
- // Process. //
+ CScript scriptpubkey_script = script;
+ CTxDestination scriptpubkey_dest = dest;
+ bool allow_p2wpkh = true;
// P2SH
- if (isP2SH) {
+ if (!strRedeemScript.empty() && script.IsPayToScriptHash()) {
+ // Check the redeemScript is valid
+ if (!IsHex(strRedeemScript)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid redeem script: must be hex string");
+ }
+
// Import redeem script.
std::vector<unsigned char> vData(ParseHex(strRedeemScript));
CScript redeemScript = CScript(vData.begin(), vData.end());
+ CScriptID redeem_id(redeemScript);
- // Invalid P2SH address
- if (!script.IsPayToScriptHash()) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid P2SH address / script");
+ // Check that the redeemScript and scriptPubKey match
+ if (GetScriptForDestination(redeem_id) != script) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "The redeemScript does not match the scriptPubKey");
}
pwallet->MarkDirty();
@@ -892,103 +889,83 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
}
- CScriptID redeem_id(redeemScript);
if (!pwallet->HaveCScript(redeem_id) && !pwallet->AddCScript(redeemScript)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2sh redeemScript to wallet");
}
- CScript redeemDestination = GetScriptForDestination(redeem_id);
+ // Now set script to the redeemScript so we parse the inner script as P2WSH or P2WPKH below
+ script = redeemScript;
+ ExtractDestination(script, dest);
+ }
- if (::IsMine(*pwallet, redeemDestination) == ISMINE_SPENDABLE) {
- throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
+ // (P2SH-)P2WSH
+ if (!witness_script_hex.empty() && script.IsPayToWitnessScriptHash()) {
+ if (!IsHex(witness_script_hex)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid witness script: must be hex string");
}
- pwallet->MarkDirty();
+ // Generate the scripts
+ std::vector<unsigned char> witness_script_parsed(ParseHex(witness_script_hex));
+ CScript witness_script = CScript(witness_script_parsed.begin(), witness_script_parsed.end());
+ CScriptID witness_id(witness_script);
- if (!pwallet->AddWatchOnly(redeemDestination, timestamp)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
+ // Check that the witnessScript and scriptPubKey match
+ if (GetScriptForDestination(WitnessV0ScriptHash(witness_script)) != script) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "The witnessScript does not match the scriptPubKey or redeemScript");
}
- // add to address book or update label
- if (IsValidDestination(dest)) {
- pwallet->SetAddressBook(dest, label, "receive");
+ // Add the witness script as watch only only if it is not for P2SH-P2WSH
+ if (!scriptpubkey_script.IsPayToScriptHash() && !pwallet->AddWatchOnly(witness_script, timestamp)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
}
- // Import private keys.
- if (keys.size()) {
- for (size_t i = 0; i < keys.size(); i++) {
- const std::string& privkey = keys[i].get_str();
-
- CKey key = DecodeSecret(privkey);
-
- if (!key.IsValid()) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
- }
-
- CPubKey pubkey = key.GetPubKey();
- assert(key.VerifyPubKey(pubkey));
-
- CKeyID vchAddress = pubkey.GetID();
- pwallet->MarkDirty();
- pwallet->SetAddressBook(vchAddress, label, "receive");
-
- if (pwallet->HaveKey(vchAddress)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Already have this key");
- }
-
- pwallet->mapKeyMetadata[vchAddress].nCreateTime = timestamp;
+ if (!pwallet->HaveCScript(witness_id) && !pwallet->AddCScript(witness_script)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2wsh witnessScript to wallet");
+ }
- if (!pwallet->AddKeyPubKey(key, pubkey)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
- }
+ // Now set script to the witnessScript so we parse the inner script as P2PK or P2PKH below
+ script = witness_script;
+ ExtractDestination(script, dest);
+ allow_p2wpkh = false; // P2WPKH cannot be embedded in P2WSH
+ }
- pwallet->UpdateTimeFirstKey(timestamp);
- }
+ // (P2SH-)P2PK/P2PKH/P2WPKH
+ if (dest.type() == typeid(CKeyID) || dest.type() == typeid(WitnessV0KeyHash)) {
+ if (!allow_p2wpkh && dest.type() == typeid(WitnessV0KeyHash)) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "P2WPKH cannot be embedded in P2WSH");
}
-
- success = true;
- } else {
- // Import public keys.
- if (pubKeys.size() && keys.size() == 0) {
+ if (keys.size() > 1 || pubKeys.size() > 1) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "More than one key given for one single-key address");
+ }
+ CPubKey pubkey;
+ if (keys.size()) {
+ pubkey = DecodeSecret(keys[0].get_str()).GetPubKey();
+ }
+ if (pubKeys.size()) {
const std::string& strPubKey = pubKeys[0].get_str();
-
if (!IsHex(strPubKey)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey must be a hex string");
}
-
- std::vector<unsigned char> vData(ParseHex(strPubKey));
- CPubKey pubKey(vData.begin(), vData.end());
-
- if (!pubKey.IsFullyValid()) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey is not a valid public key");
- }
-
- CTxDestination pubkey_dest = pubKey.GetID();
-
- // Consistency check.
- if (!(pubkey_dest == dest)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed");
- }
-
- CScript pubKeyScript = GetScriptForDestination(pubkey_dest);
-
- if (::IsMine(*pwallet, pubKeyScript) == ISMINE_SPENDABLE) {
- throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
+ std::vector<unsigned char> vData(ParseHex(pubKeys[0].get_str()));
+ CPubKey pubkey_temp(vData.begin(), vData.end());
+ if (pubkey.size() && pubkey_temp != pubkey) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key does not match public key for address");
}
-
- pwallet->MarkDirty();
-
- if (!pwallet->AddWatchOnly(pubKeyScript, timestamp)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
+ pubkey = pubkey_temp;
+ }
+ if (pubkey.size() > 0) {
+ if (!pubkey.IsFullyValid()) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey is not a valid public key");
}
- // add to address book or update label
- if (IsValidDestination(pubkey_dest)) {
- pwallet->SetAddressBook(pubkey_dest, label, "receive");
+ // Check the key corresponds to the destination given
+ std::vector<CTxDestination> destinations = GetAllDestinationsForKey(pubkey);
+ if (std::find(destinations.begin(), destinations.end(), dest) == destinations.end()) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Key does not match address destination");
}
- // TODO Is this necessary?
- CScript scriptRawPubKey = GetScriptForRawPubKey(pubKey);
+ // This is necessary to force the wallet to import the pubKey
+ CScript scriptRawPubKey = GetScriptForRawPubKey(pubkey);
if (::IsMine(*pwallet, scriptRawPubKey) == ISMINE_SPENDABLE) {
throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
@@ -999,73 +976,61 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con
if (!pwallet->AddWatchOnly(scriptRawPubKey, timestamp)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
}
-
- success = true;
}
+ }
- // Import private keys.
- if (keys.size()) {
- const std::string& strPrivkey = keys[0].get_str();
-
- // Checks.
- CKey key = DecodeSecret(strPrivkey);
-
- if (!key.IsValid()) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
- }
-
- CPubKey pubKey = key.GetPubKey();
- assert(key.VerifyPubKey(pubKey));
-
- CTxDestination pubkey_dest = pubKey.GetID();
+ // Import the address
+ if (::IsMine(*pwallet, scriptpubkey_script) == ISMINE_SPENDABLE) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
+ }
- // Consistency check.
- if (!(pubkey_dest == dest)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed");
- }
+ pwallet->MarkDirty();
- CKeyID vchAddress = pubKey.GetID();
- pwallet->MarkDirty();
- pwallet->SetAddressBook(vchAddress, label, "receive");
+ if (!pwallet->AddWatchOnly(scriptpubkey_script, timestamp)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
+ }
- if (pwallet->HaveKey(vchAddress)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
- }
+ if (!watchOnly && !pwallet->HaveCScript(CScriptID(scriptpubkey_script)) && !pwallet->AddCScript(scriptpubkey_script)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding scriptPubKey script to wallet");
+ }
- pwallet->mapKeyMetadata[vchAddress].nCreateTime = timestamp;
+ // add to address book or update label
+ if (IsValidDestination(scriptpubkey_dest)) {
+ pwallet->SetAddressBook(scriptpubkey_dest, label, "receive");
+ }
- if (!pwallet->AddKeyPubKey(key, pubKey)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
- }
+ // Import private keys.
+ for (size_t i = 0; i < keys.size(); i++) {
+ const std::string& strPrivkey = keys[i].get_str();
- pwallet->UpdateTimeFirstKey(timestamp);
+ // Checks.
+ CKey key = DecodeSecret(strPrivkey);
- success = true;
+ if (!key.IsValid()) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
}
- // Import scriptPubKey only.
- if (pubKeys.size() == 0 && keys.size() == 0) {
- if (::IsMine(*pwallet, script) == ISMINE_SPENDABLE) {
- throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
- }
+ CPubKey pubKey = key.GetPubKey();
+ assert(key.VerifyPubKey(pubKey));
- pwallet->MarkDirty();
+ CKeyID vchAddress = pubKey.GetID();
+ pwallet->MarkDirty();
- if (!pwallet->AddWatchOnly(script, timestamp)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
- }
+ if (pwallet->HaveKey(vchAddress)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Already have this key");
+ }
- // add to address book or update label
- if (IsValidDestination(dest)) {
- pwallet->SetAddressBook(dest, label, "receive");
- }
+ pwallet->mapKeyMetadata[vchAddress].nCreateTime = timestamp;
- success = true;
+ if (!pwallet->AddKeyPubKey(key, pubKey)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
}
+
+ pwallet->UpdateTimeFirstKey(timestamp);
}
UniValue result = UniValue(UniValue::VOBJ);
- result.pushKV("success", UniValue(success));
+ result.pushKV("success", UniValue(true));
return result;
} catch (const UniValue& e) {
UniValue result = UniValue(UniValue::VOBJ);
@@ -1118,7 +1083,8 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
" \"now\" can be specified to bypass scanning, for keys which are known to never have been used, and\n"
" 0 can be specified to scan the entire blockchain. Blocks up to 2 hours before the earliest key\n"
" creation time of all keys being imported by the importmulti call will be scanned.\n"
- " \"redeemscript\": \"<script>\" , (string, optional) Allowed only if the scriptPubKey is a P2SH address or a P2SH scriptPubKey\n"
+ " \"redeemscript\": \"<script>\" , (string, optional) Allowed only if the scriptPubKey is a P2SH or P2SH-P2WSH address/scriptPubKey\n"
+ " \"witnessscript\": \"<script>\" , (string, optional) Allowed only if the scriptPubKey is a P2SH-P2WSH or P2WSH address/scriptPubKey\n"
" \"pubkeys\": [\"<pubKey>\", ... ] , (array, optional) Array of strings giving pubkeys that must occur in the output or redeemscript\n"
" \"keys\": [\"<key>\", ... ] , (array, optional) Array of strings giving private keys whose corresponding public keys must occur in the output or redeemscript\n"
" \"internal\": <true> , (boolean, optional, default: false) Stating whether matching outputs should be treated as not incoming payments\n"
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 7d0219201e..2aeb5c2984 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -504,7 +504,7 @@ static UniValue signmessage(const JSONRPCRequest& request)
+ HelpExampleCli("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"my message\"") +
"\nVerify the signature\n"
+ HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"signature\" \"my message\"") +
- "\nAs json rpc\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"my message\"")
);
@@ -566,7 +566,7 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
+ HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" 0") +
"\nThe amount with at least 6 confirmations\n"
+ HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" 6") +
- "\nAs a json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", 6")
);
@@ -633,7 +633,7 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request)
+ HelpExampleCli("getreceivedbylabel", "\"tabby\" 0") +
"\nThe amount with at least 6 confirmations\n"
+ HelpExampleCli("getreceivedbylabel", "\"tabby\" 6") +
- "\nAs a json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("getreceivedbylabel", "\"tabby\", 6")
);
@@ -699,7 +699,7 @@ static UniValue getbalance(const JSONRPCRequest& request)
+ HelpExampleCli("getbalance", "") +
"\nThe total amount in the wallet at least 6 blocks confirmed\n"
+ HelpExampleCli("getbalance", "\"*\" 6") +
- "\nAs a json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("getbalance", "\"*\", 6")
);
@@ -798,7 +798,7 @@ static UniValue sendmany(const JSONRPCRequest& request)
+ HelpExampleCli("sendmany", "\"\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" 6 \"testing\"") +
"\nSend two amounts to two different addresses, subtract fee from amount:\n"
+ HelpExampleCli("sendmany", "\"\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" 1 \"\" \"[\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\",\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\"]\"") +
- "\nAs a json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("sendmany", "\"\", {\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\":0.01,\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\":0.02}, 6, \"testing\"")
);
@@ -939,7 +939,7 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request)
"\nExamples:\n"
"\nAdd a multisig address from 2 addresses\n"
+ HelpExampleCli("addmultisigaddress", "2 \"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") +
- "\nAs json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("addmultisigaddress", "2, \"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"")
;
throw std::runtime_error(msg);
@@ -982,131 +982,6 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request)
return result;
}
-class Witnessifier : public boost::static_visitor<bool>
-{
-public:
- CWallet * const pwallet;
- CTxDestination result;
- bool already_witness;
-
- explicit Witnessifier(CWallet *_pwallet) : pwallet(_pwallet), already_witness(false) {}
-
- bool operator()(const CKeyID &keyID) {
- if (pwallet) {
- CScript basescript = GetScriptForDestination(keyID);
- CScript witscript = GetScriptForWitness(basescript);
- if (!IsSolvable(*pwallet, witscript)) {
- return false;
- }
- return ExtractDestination(witscript, result);
- }
- return false;
- }
-
- bool operator()(const CScriptID &scriptID) {
- CScript subscript;
- if (pwallet && pwallet->GetCScript(scriptID, subscript)) {
- int witnessversion;
- std::vector<unsigned char> witprog;
- if (subscript.IsWitnessProgram(witnessversion, witprog)) {
- ExtractDestination(subscript, result);
- already_witness = true;
- return true;
- }
- CScript witscript = GetScriptForWitness(subscript);
- if (!IsSolvable(*pwallet, witscript)) {
- return false;
- }
- return ExtractDestination(witscript, result);
- }
- return false;
- }
-
- bool operator()(const WitnessV0KeyHash& id)
- {
- already_witness = true;
- result = id;
- return true;
- }
-
- bool operator()(const WitnessV0ScriptHash& id)
- {
- already_witness = true;
- result = id;
- return true;
- }
-
- template<typename T>
- bool operator()(const T& dest) { return false; }
-};
-
-static UniValue addwitnessaddress(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() > 2)
- {
- std::string msg = "addwitnessaddress \"address\" ( p2sh )\n"
- "\nDEPRECATED: set the address_type argument of getnewaddress, or option -addresstype=[bech32|p2sh-segwit] instead.\n"
- "Add a witness address for a script (with pubkey or redeemscript known). Requires a new wallet backup.\n"
- "It returns the witness script.\n"
-
- "\nArguments:\n"
- "1. \"address\" (string, required) An address known to the wallet\n"
- "2. p2sh (bool, optional, default=true) Embed inside P2SH\n"
-
- "\nResult:\n"
- "\"witnessaddress\", (string) The value of the new address (P2SH or BIP173).\n"
- "}\n"
- ;
- throw std::runtime_error(msg);
- }
-
- if (!IsDeprecatedRPCEnabled("addwitnessaddress")) {
- throw JSONRPCError(RPC_METHOD_DEPRECATED, "addwitnessaddress is deprecated and will be fully removed in v0.17. "
- "To use addwitnessaddress in v0.16, restart bitcoind with -deprecatedrpc=addwitnessaddress.\n"
- "Projects should transition to using the address_type argument of getnewaddress, or option -addresstype=[bech32|p2sh-segwit] instead.\n");
- }
-
- CTxDestination dest = DecodeDestination(request.params[0].get_str());
- if (!IsValidDestination(dest)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
- }
-
- bool p2sh = true;
- if (!request.params[1].isNull()) {
- p2sh = request.params[1].get_bool();
- }
-
- Witnessifier w(pwallet);
- bool ret = boost::apply_visitor(w, dest);
- if (!ret) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Public key or redeemscript not known to wallet, or the key is uncompressed");
- }
-
- CScript witprogram = GetScriptForDestination(w.result);
-
- if (p2sh) {
- w.result = CScriptID(witprogram);
- }
-
- if (w.already_witness) {
- if (!(dest == w.result)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Cannot convert between witness address types");
- }
- } else {
- pwallet->AddCScript(witprogram); // Implicit for single-key now, but necessary for multisig and for compatibility with older software
- pwallet->SetAddressBook(w.result, "", "receive");
- }
-
- return EncodeDestination(w.result);
-}
-
struct tallyitem
{
CAmount nAmount;
@@ -1121,7 +996,7 @@ struct tallyitem
}
};
-static UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool by_label) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+static UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool by_label) EXCLUSIVE_LOCKS_REQUIRED(cs_main, pwallet->cs_wallet)
{
// Minimum confirmations
int nMinDepth = 1;
@@ -1500,7 +1375,7 @@ UniValue listtransactions(const JSONRPCRequest& request)
+ HelpExampleCli("listtransactions", "") +
"\nList transactions 100 to 120\n"
+ HelpExampleCli("listtransactions", "\"*\" 20 100") +
- "\nAs a json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("listtransactions", "\"*\", 20, 100")
);
@@ -1928,13 +1803,6 @@ static UniValue keypoolrefill(const JSONRPCRequest& request)
}
-static void LockWallet(CWallet* pWallet)
-{
- LOCK(pWallet->cs_wallet);
- pWallet->nRelockTime = 0;
- pWallet->Lock();
-}
-
static UniValue walletpassphrase(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
@@ -1960,7 +1828,7 @@ static UniValue walletpassphrase(const JSONRPCRequest& request)
+ HelpExampleCli("walletpassphrase", "\"my pass phrase\" 60") +
"\nLock the wallet again (before 60 seconds)\n"
+ HelpExampleCli("walletlock", "") +
- "\nAs json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("walletpassphrase", "\"my pass phrase\", 60")
);
}
@@ -2004,7 +1872,18 @@ static UniValue walletpassphrase(const JSONRPCRequest& request)
pwallet->TopUpKeyPool();
pwallet->nRelockTime = GetTime() + nSleepTime;
- RPCRunLater(strprintf("lockwallet(%s)", pwallet->GetName()), std::bind(LockWallet, pwallet), nSleepTime);
+
+ // Keep a weak pointer to the wallet so that it is possible to unload the
+ // wallet before the following callback is called. If a valid shared pointer
+ // is acquired in the callback then the wallet is still loaded.
+ std::weak_ptr<CWallet> weak_wallet = wallet;
+ RPCRunLater(strprintf("lockwallet(%s)", pwallet->GetName()), [weak_wallet] {
+ if (auto shared_wallet = weak_wallet.lock()) {
+ LOCK(shared_wallet->cs_wallet);
+ shared_wallet->Lock();
+ shared_wallet->nRelockTime = 0;
+ }
+ }, nSleepTime);
return NullUniValue;
}
@@ -2083,7 +1962,7 @@ static UniValue walletlock(const JSONRPCRequest& request)
+ HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 1.0") +
"\nClear the passphrase since we are done before 2 minutes is up\n"
+ HelpExampleCli("walletlock", "") +
- "\nAs json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("walletlock", "")
);
}
@@ -2129,7 +2008,7 @@ static UniValue encryptwallet(const JSONRPCRequest& request)
+ HelpExampleCli("signmessage", "\"address\" \"test message\"") +
"\nNow lock the wallet again by removing the passphrase\n"
+ HelpExampleCli("walletlock", "") +
- "\nAs a json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("encryptwallet", "\"my pass phrase\"")
);
}
@@ -2200,7 +2079,7 @@ static UniValue lockunspent(const JSONRPCRequest& request)
+ HelpExampleCli("listlockunspent", "") +
"\nUnlock the transaction again\n"
+ HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
- "\nAs a json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("lockunspent", "false, \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"")
);
@@ -2314,7 +2193,7 @@ static UniValue listlockunspent(const JSONRPCRequest& request)
+ HelpExampleCli("listlockunspent", "") +
"\nUnlock the transaction again\n"
+ HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
- "\nAs a json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("listlockunspent", "")
);
@@ -2443,6 +2322,38 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
return obj;
}
+static UniValue listwalletdir(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 0) {
+ throw std::runtime_error(
+ "listwalletdir\n"
+ "Returns a list of wallets in the wallet directory.\n"
+ "{\n"
+ " \"wallets\" : [ (json array of objects)\n"
+ " {\n"
+ " \"name\" : \"name\" (string) The wallet name\n"
+ " }\n"
+ " ,...\n"
+ " ]\n"
+ "}\n"
+ "\nExamples:\n"
+ + HelpExampleCli("listwalletdir", "")
+ + HelpExampleRpc("listwalletdir", "")
+ );
+ }
+
+ UniValue wallets(UniValue::VARR);
+ for (const auto& path : ListWalletDir()) {
+ UniValue wallet(UniValue::VOBJ);
+ wallet.pushKV("name", path.string());
+ wallets.push_back(wallet);
+ }
+
+ UniValue result(UniValue::VOBJ);
+ result.pushKV("wallets", wallets);
+ return result;
+}
+
static UniValue listwallets(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
@@ -3289,6 +3200,12 @@ UniValue generate(const JSONRPCRequest& request)
);
}
+ if (!IsDeprecatedRPCEnabled("generate")) {
+ throw JSONRPCError(RPC_METHOD_DEPRECATED, "The wallet generate rpc method is deprecated and will be fully removed in v0.19. "
+ "To use generate in v0.18, restart bitcoind with -deprecatedrpc=generate.\n"
+ "Clients should transition to using the node rpc method generatetoaddress\n");
+ }
+
int num_generate = request.params[0].get_int();
uint64_t max_tries = 1000000;
if (!request.params[1].isNull()) {
@@ -3536,8 +3453,9 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
"\nResult:\n"
"{\n"
" \"address\" : \"address\", (string) The bitcoin address validated\n"
- " \"scriptPubKey\" : \"hex\", (string) The hex encoded scriptPubKey generated by the address\n"
+ " \"scriptPubKey\" : \"hex\", (string) The hex-encoded scriptPubKey generated by the address\n"
" \"ismine\" : true|false, (boolean) If the address is yours or not\n"
+ " \"solvable\" : true|false, (boolean) If the address is solvable by the wallet\n"
" \"iswatchonly\" : true|false, (boolean) If the address is watchonly\n"
" \"isscript\" : true|false, (boolean) If the key is a script\n"
" \"iswitness\" : true|false, (boolean) If the address is a witness address\n"
@@ -3592,6 +3510,7 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
isminetype mine = IsMine(*pwallet, dest);
ret.pushKV("ismine", bool(mine & ISMINE_SPENDABLE));
ret.pushKV("iswatchonly", bool(mine & ISMINE_WATCH_ONLY));
+ ret.pushKV("solvable", IsSolvable(*pwallet, scriptPubKey));
UniValue detail = DescribeWalletAddress(pwallet, dest);
ret.pushKVs(detail);
if (pwallet->mapAddressBook.count(dest)) {
@@ -3705,7 +3624,7 @@ static UniValue listlabels(const JSONRPCRequest& request)
+ HelpExampleCli("listlabels", "receive") +
"\nList labels that have sending addresses\n"
+ HelpExampleCli("listlabels", "send") +
- "\nAs json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("listlabels", "receive")
);
@@ -3959,7 +3878,7 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
" \"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"
+ " \"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"
@@ -4059,7 +3978,6 @@ static const CRPCCommand commands[] =
{ // category name actor (function) argNames
// --------------------- ------------------------ ----------------------- ----------
{ "generating", "generate", &generate, {"nblocks","maxtries"} },
- { "hidden", "addwitnessaddress", &addwitnessaddress, {"address","p2sh"} },
{ "hidden", "resendwallettransactions", &resendwallettransactions, {} },
{ "rawtransactions", "fundrawtransaction", &fundrawtransaction, {"hexstring","options","iswitness"} },
{ "wallet", "abandontransaction", &abandontransaction, {"txid"} },
@@ -4096,6 +4014,7 @@ static const CRPCCommand commands[] =
{ "wallet", "listsinceblock", &listsinceblock, {"blockhash","target_confirmations","include_watchonly","include_removed"} },
{ "wallet", "listtransactions", &listtransactions, {"dummy","count","skip","include_watchonly"} },
{ "wallet", "listunspent", &listunspent, {"minconf","maxconf","addresses","include_unsafe","query_options"} },
+ { "wallet", "listwalletdir", &listwalletdir, {} },
{ "wallet", "listwallets", &listwallets, {} },
{ "wallet", "loadwallet", &loadwallet, {"filename"} },
{ "wallet", "lockunspent", &lockunspent, {"unlock","transactions"} },
diff --git a/src/wallet/test/init_test_fixture.cpp b/src/wallet/test/init_test_fixture.cpp
new file mode 100644
index 0000000000..1453029c9c
--- /dev/null
+++ b/src/wallet/test/init_test_fixture.cpp
@@ -0,0 +1,42 @@
+// 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 <fs.h>
+
+#include <wallet/test/init_test_fixture.h>
+
+InitWalletDirTestingSetup::InitWalletDirTestingSetup(const std::string& chainName): BasicTestingSetup(chainName)
+{
+ std::string sep;
+ sep += fs::path::preferred_separator;
+
+ m_datadir = SetDataDir("tempdir");
+ m_cwd = fs::current_path();
+
+ m_walletdir_path_cases["default"] = m_datadir / "wallets";
+ m_walletdir_path_cases["custom"] = m_datadir / "my_wallets";
+ m_walletdir_path_cases["nonexistent"] = m_datadir / "path_does_not_exist";
+ m_walletdir_path_cases["file"] = m_datadir / "not_a_directory.dat";
+ m_walletdir_path_cases["trailing"] = m_datadir / "wallets" / sep;
+ m_walletdir_path_cases["trailing2"] = m_datadir / "wallets" / sep / sep;
+
+ fs::current_path(m_datadir);
+ m_walletdir_path_cases["relative"] = "wallets";
+
+ fs::create_directories(m_walletdir_path_cases["default"]);
+ fs::create_directories(m_walletdir_path_cases["custom"]);
+ fs::create_directories(m_walletdir_path_cases["relative"]);
+ std::ofstream f(m_walletdir_path_cases["file"].BOOST_FILESYSTEM_C_STR);
+ f.close();
+}
+
+InitWalletDirTestingSetup::~InitWalletDirTestingSetup()
+{
+ fs::current_path(m_cwd);
+}
+
+void InitWalletDirTestingSetup::SetWalletDir(const fs::path& walletdir_path)
+{
+ gArgs.ForceSetArg("-walletdir", walletdir_path.string());
+} \ No newline at end of file
diff --git a/src/wallet/test/init_test_fixture.h b/src/wallet/test/init_test_fixture.h
new file mode 100644
index 0000000000..5684adbece
--- /dev/null
+++ b/src/wallet/test/init_test_fixture.h
@@ -0,0 +1,21 @@
+// Copyright (c) 2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_WALLET_TEST_INIT_TEST_FIXTURE_H
+#define BITCOIN_WALLET_TEST_INIT_TEST_FIXTURE_H
+
+#include <test/test_bitcoin.h>
+
+
+struct InitWalletDirTestingSetup: public BasicTestingSetup {
+ explicit InitWalletDirTestingSetup(const std::string& chainName = CBaseChainParams::MAIN);
+ ~InitWalletDirTestingSetup();
+ void SetWalletDir(const fs::path& walletdir_path);
+
+ fs::path m_datadir;
+ fs::path m_cwd;
+ std::map<std::string, fs::path> m_walletdir_path_cases;
+};
+
+#endif // BITCOIN_WALLET_TEST_INIT_TEST_FIXTURE_H
diff --git a/src/wallet/test/init_tests.cpp b/src/wallet/test/init_tests.cpp
new file mode 100644
index 0000000000..7048547b2b
--- /dev/null
+++ b/src/wallet/test/init_tests.cpp
@@ -0,0 +1,78 @@
+// 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 <boost/test/unit_test.hpp>
+
+#include <test/test_bitcoin.h>
+#include <wallet/test/init_test_fixture.h>
+
+#include <init.h>
+#include <walletinitinterface.h>
+#include <wallet/wallet.h>
+
+
+BOOST_FIXTURE_TEST_SUITE(init_tests, InitWalletDirTestingSetup)
+
+BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_default)
+{
+ SetWalletDir(m_walletdir_path_cases["default"]);
+ bool result = g_wallet_init_interface.Verify();
+ BOOST_CHECK(result == true);
+ fs::path walletdir = gArgs.GetArg("-walletdir", "");
+ fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]);
+ BOOST_CHECK(walletdir == expected_path);
+}
+
+BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_custom)
+{
+ SetWalletDir(m_walletdir_path_cases["custom"]);
+ bool result = g_wallet_init_interface.Verify();
+ BOOST_CHECK(result == true);
+ fs::path walletdir = gArgs.GetArg("-walletdir", "");
+ fs::path expected_path = fs::canonical(m_walletdir_path_cases["custom"]);
+ BOOST_CHECK(walletdir == expected_path);
+}
+
+BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_does_not_exist)
+{
+ SetWalletDir(m_walletdir_path_cases["nonexistent"]);
+ bool result = g_wallet_init_interface.Verify();
+ BOOST_CHECK(result == false);
+}
+
+BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_is_not_directory)
+{
+ SetWalletDir(m_walletdir_path_cases["file"]);
+ bool result = g_wallet_init_interface.Verify();
+ BOOST_CHECK(result == false);
+}
+
+BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_is_not_relative)
+{
+ SetWalletDir(m_walletdir_path_cases["relative"]);
+ bool result = g_wallet_init_interface.Verify();
+ BOOST_CHECK(result == false);
+}
+
+BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing)
+{
+ SetWalletDir(m_walletdir_path_cases["trailing"]);
+ bool result = g_wallet_init_interface.Verify();
+ BOOST_CHECK(result == true);
+ fs::path walletdir = gArgs.GetArg("-walletdir", "");
+ fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]);
+ BOOST_CHECK(walletdir == expected_path);
+}
+
+BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing2)
+{
+ SetWalletDir(m_walletdir_path_cases["trailing2"]);
+ bool result = g_wallet_init_interface.Verify();
+ BOOST_CHECK(result == true);
+ fs::path walletdir = gArgs.GetArg("-walletdir", "");
+ fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]);
+ BOOST_CHECK(walletdir == expected_path);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/wallet/test/psbt_wallet_tests.cpp b/src/wallet/test/psbt_wallet_tests.cpp
index 526f2d983f..34d6e1b87c 100644
--- a/src/wallet/test/psbt_wallet_tests.cpp
+++ b/src/wallet/test/psbt_wallet_tests.cpp
@@ -17,6 +17,8 @@ BOOST_FIXTURE_TEST_SUITE(psbt_wallet_tests, WalletTestingSetup)
BOOST_AUTO_TEST_CASE(psbt_updater_test)
{
+ LOCK(m_wallet.cs_wallet);
+
// Create prevtxs and add to wallet
CDataStream s_prev_tx1(ParseHex("0200000000010158e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd7501000000171600145f275f436b09a8cc9a2eb2a2f528485c68a56323feffffff02d8231f1b0100000017a914aed962d6654f9a2b36608eb9d64d2b260db4f1118700c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e88702483045022100a22edcc6e5bc511af4cc4ae0de0fcd75c7e04d8c1c3a8aa9d820ed4b967384ec02200642963597b9b1bc22c75e9f3e117284a962188bf5e8a74c895089046a20ad770121035509a48eb623e10aace8bfd0212fdb8a8e5af3c94b0b133b95e114cab89e4f7965000000"), SER_NETWORK, PROTOCOL_VERSION);
CTransactionRef prev_tx1;
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index afe47d986e..29014790e9 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -4093,7 +4093,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name,
// Try to top up keypool. No-op if the wallet is locked.
walletInstance->TopUpKeyPool();
- LOCK(cs_main);
+ LOCK2(cs_main, walletInstance->cs_wallet);
CBlockIndex *pindexRescan = chainActive.Genesis();
if (!gArgs.GetBoolArg("-rescan", false))
@@ -4178,7 +4178,6 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name,
walletInstance->SetBroadcastTransactions(gArgs.GetBoolArg("-walletbroadcast", DEFAULT_WALLETBROADCAST));
{
- LOCK(walletInstance->cs_wallet);
walletInstance->WalletLogPrintf("setKeyPool.size() = %u\n", walletInstance->GetKeyPoolSize());
walletInstance->WalletLogPrintf("mapWallet.size() = %u\n", walletInstance->mapWallet.size());
walletInstance->WalletLogPrintf("mapAddressBook.size() = %u\n", walletInstance->mapAddressBook.size());
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index da326517c0..e6e23ab247 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -460,7 +460,11 @@ public:
CAmount GetDebit(const isminefilter& filter) const;
CAmount GetCredit(const isminefilter& filter) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
CAmount GetImmatureCredit(bool fUseCache=true) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- CAmount GetAvailableCredit(bool fUseCache=true, const isminefilter& filter=ISMINE_SPENDABLE) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ // TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
+ // annotation "EXCLUSIVE_LOCKS_REQUIRED(cs_main, pwallet->cs_wallet)". The
+ // annotation "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid
+ // having to resolve the issue of member access into incomplete type CWallet.
+ CAmount GetAvailableCredit(bool fUseCache=true, const isminefilter& filter=ISMINE_SPENDABLE) const NO_THREAD_SAFETY_ANALYSIS;
CAmount GetImmatureWatchOnlyCredit(const bool fUseCache=true) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
CAmount GetChange() const;
@@ -492,7 +496,13 @@ public:
/** Pass this transaction to the mempool. Fails if absolute fee exceeds absurd fee. */
bool AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- std::set<uint256> GetConflicts() const;
+ // TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
+ // annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The annotation
+ // "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid having to
+ // resolve the issue of member access into incomplete type CWallet. Note
+ // that we still have the runtime check "AssertLockHeld(pwallet->cs_wallet)"
+ // in place.
+ std::set<uint256> GetConflicts() const NO_THREAD_SAFETY_ANALYSIS;
};
class COutput
@@ -591,13 +601,13 @@ private:
std::mutex mutexScanning;
friend class WalletRescanReserver;
- WalletBatch *encrypted_batch = nullptr;
+ WalletBatch *encrypted_batch GUARDED_BY(cs_wallet) = nullptr;
//! the current wallet version: clients below this version are not able to load the wallet
int nWalletVersion = FEATURE_BASE;
//! the maximum wallet format version: memory-only variable that specifies to what version this wallet may be upgraded
- int nWalletMaxVersion = FEATURE_BASE;
+ int nWalletMaxVersion GUARDED_BY(cs_wallet) = FEATURE_BASE;
int64_t nNextResend = 0;
int64_t nLastResend = 0;
@@ -609,9 +619,9 @@ private:
* mutated transactions where the mutant gets mined).
*/
typedef std::multimap<COutPoint, uint256> TxSpends;
- TxSpends mapTxSpends;
- void AddToSpends(const COutPoint& outpoint, const uint256& wtxid);
- void AddToSpends(const uint256& wtxid);
+ TxSpends mapTxSpends GUARDED_BY(cs_wallet);
+ void AddToSpends(const COutPoint& outpoint, const uint256& wtxid) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ void AddToSpends(const uint256& wtxid) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/**
* Add a transaction to the wallet, or update it. pIndex and posInBlock should
@@ -632,9 +642,9 @@ private:
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 MarkInputsDirty(const CTransactionRef& tx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- void SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator>);
+ void SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator>) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/* 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. */
@@ -647,13 +657,13 @@ private:
void DeriveNewChildKey(WalletBatch &batch, CKeyMetadata& metadata, CKey& secret, bool internal = false) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
std::set<int64_t> setInternalKeyPool;
- std::set<int64_t> setExternalKeyPool;
+ std::set<int64_t> setExternalKeyPool GUARDED_BY(cs_wallet);
std::set<int64_t> set_pre_split_keypool;
- int64_t m_max_keypool_index = 0;
+ int64_t m_max_keypool_index GUARDED_BY(cs_wallet) = 0;
std::map<CKeyID, int64_t> m_pool_key_to_index;
std::atomic<uint64_t> m_wallet_flags{0};
- int64_t nTimeFirstKey = 0;
+ int64_t nTimeFirstKey GUARDED_BY(cs_wallet) = 0;
/**
* Private version of AddWatchOnly method which does not accept a
@@ -709,20 +719,20 @@ public:
* if they are not ours
*/
bool SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet,
- const CCoinControl& coin_control, CoinSelectionParams& coin_selection_params, bool& bnb_used) const;
+ const CCoinControl& coin_control, CoinSelectionParams& coin_selection_params, bool& bnb_used) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/** Get a name for this wallet for logging/debugging purposes.
*/
const std::string& GetName() const { return m_name; }
void LoadKeyPool(int64_t nIndex, const CKeyPool &keypool) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- void MarkPreSplitKeys();
+ void MarkPreSplitKeys() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
// Map from Key ID to key metadata.
- std::map<CKeyID, CKeyMetadata> mapKeyMetadata;
+ std::map<CKeyID, CKeyMetadata> mapKeyMetadata GUARDED_BY(cs_wallet);
// Map from Script ID to key metadata (for watch-only keys).
- std::map<CScriptID, CKeyMetadata> m_script_metadata;
+ std::map<CScriptID, CKeyMetadata> m_script_metadata GUARDED_BY(cs_wallet);
typedef std::map<unsigned int, CMasterKey> MasterKeyMap;
MasterKeyMap mapMasterKeys;
@@ -739,17 +749,17 @@ public:
encrypted_batch = nullptr;
}
- std::map<uint256, CWalletTx> mapWallet;
+ std::map<uint256, CWalletTx> mapWallet GUARDED_BY(cs_wallet);
typedef std::multimap<int64_t, CWalletTx*> TxItems;
TxItems wtxOrdered;
- int64_t nOrderPosNext = 0;
+ int64_t nOrderPosNext GUARDED_BY(cs_wallet) = 0;
uint64_t nAccountingEntryNumber = 0;
std::map<CTxDestination, CAddressBookData> mapAddressBook;
- std::set<COutPoint> setLockedCoins;
+ std::set<COutPoint> setLockedCoins GUARDED_BY(cs_wallet);
const CWalletTx* GetWalletTx(const uint256& hash) const;
@@ -769,7 +779,7 @@ public:
/**
* Find non-change parent output.
*/
- const CTxOut& FindNonChangeParentOutput(const CTransaction& tx, int output) const;
+ const CTxOut& FindNonChangeParentOutput(const CTransaction& tx, int output) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/**
* Shuffle and select coins until nTargetValue is reached while avoiding
@@ -780,7 +790,7 @@ public:
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 EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ bool IsSpent(const uint256& hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_main, cs_wallet);
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);
@@ -856,7 +866,7 @@ public:
void MarkDirty();
bool AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose=true);
- void LoadToWallet(const CWalletTx& wtxIn);
+ void LoadToWallet(const CWalletTx& wtxIn) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
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;
@@ -1000,7 +1010,7 @@ public:
int GetVersion() { LOCK(cs_wallet); return nWalletVersion; }
//! Get wallet transactions that conflict with given transaction (spend same outputs)
- std::set<uint256> GetConflicts(const uint256& txid) const;
+ std::set<uint256> GetConflicts(const uint256& txid) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Check if a given transaction has any of its outputs spent by another transaction in the wallet
bool HasWalletSpend(const uint256& txid) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
@@ -1198,6 +1208,6 @@ public:
// Use DummySignatureCreator, which inserts 71 byte signatures everywhere.
// NOTE: this requires that all inputs must be in mapWallet (eg the tx should
// be IsAllFromMe).
-int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, bool use_max_sig = false);
+int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, bool use_max_sig = false) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet);
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts, bool use_max_sig = false);
#endif // BITCOIN_WALLET_WALLET_H
diff --git a/src/wallet/walletutil.cpp b/src/wallet/walletutil.cpp
index 34c76ec4c4..d66d8a3775 100644
--- a/src/wallet/walletutil.cpp
+++ b/src/wallet/walletutil.cpp
@@ -4,6 +4,8 @@
#include <wallet/walletutil.h>
+#include <util.h>
+
fs::path GetWalletDir()
{
fs::path path;
@@ -25,3 +27,56 @@ fs::path GetWalletDir()
return path;
}
+
+static bool IsBerkeleyBtree(const fs::path& path)
+{
+ // A Berkeley DB Btree file has at least 4K.
+ // This check also prevents opening lock files.
+ boost::system::error_code ec;
+ if (fs::file_size(path, ec) < 4096) return false;
+
+ fs::ifstream file(path.string(), std::ios::binary);
+ if (!file.is_open()) return false;
+
+ file.seekg(12, std::ios::beg); // Magic bytes start at offset 12
+ uint32_t data = 0;
+ file.read((char*) &data, sizeof(data)); // Read 4 bytes of file to compare against magic
+
+ // Berkeley DB Btree magic bytes, from:
+ // https://github.com/file/file/blob/5824af38469ec1ca9ac3ffd251e7afe9dc11e227/magic/Magdir/database#L74-L75
+ // - big endian systems - 00 05 31 62
+ // - little endian systems - 62 31 05 00
+ return data == 0x00053162 || data == 0x62310500;
+}
+
+std::vector<fs::path> ListWalletDir()
+{
+ const fs::path wallet_dir = GetWalletDir();
+ const size_t offset = wallet_dir.string().size() + 1;
+ std::vector<fs::path> paths;
+
+ for (auto it = fs::recursive_directory_iterator(wallet_dir); it != fs::recursive_directory_iterator(); ++it) {
+ // Get wallet path relative to walletdir by removing walletdir from the wallet path.
+ // This can be replaced by boost::filesystem::lexically_relative once boost is bumped to 1.60.
+ const fs::path path = it->path().string().substr(offset);
+
+ if (it->status().type() == fs::directory_file && IsBerkeleyBtree(it->path() / "wallet.dat")) {
+ // Found a directory which contains wallet.dat btree file, add it as a wallet.
+ paths.emplace_back(path);
+ } else if (it.level() == 0 && it->symlink_status().type() == fs::regular_file && IsBerkeleyBtree(it->path())) {
+ if (it->path().filename() == "wallet.dat") {
+ // Found top-level wallet.dat btree file, add top level directory ""
+ // as a wallet.
+ paths.emplace_back();
+ } else {
+ // Found top-level btree file not called wallet.dat. Current bitcoin
+ // software will never create these files but will allow them to be
+ // opened in a shared database environment for backwards compatibility.
+ // Add it to the list of available wallets.
+ paths.emplace_back(path);
+ }
+ }
+ }
+
+ return paths;
+}
diff --git a/src/wallet/walletutil.h b/src/wallet/walletutil.h
index 7c42954616..77f5ca428d 100644
--- a/src/wallet/walletutil.h
+++ b/src/wallet/walletutil.h
@@ -5,10 +5,14 @@
#ifndef BITCOIN_WALLET_WALLETUTIL_H
#define BITCOIN_WALLET_WALLETUTIL_H
-#include <chainparamsbase.h>
-#include <util.h>
+#include <fs.h>
+
+#include <vector>
//! Get the path of the wallet directory.
fs::path GetWalletDir();
+//! Get wallets in wallet directory.
+std::vector<fs::path> ListWalletDir();
+
#endif // BITCOIN_WALLET_WALLETUTIL_H
diff --git a/test/README.md b/test/README.md
index a5ba732bc0..680f9bf9a6 100644
--- a/test/README.md
+++ b/test/README.md
@@ -3,17 +3,18 @@ utilities in their entirety. It does not contain unit tests, which
can be found in [/src/test](/src/test), [/src/wallet/test](/src/wallet/test),
etc.
-There are currently two sets of tests in this directory:
+This directory contains the following sets of tests:
- [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
bitcoin-tx.
+- [lint](/test/lint/) which perform various static analysis checks.
The util tests are run as part of `make check` target. The functional
-tests are run by the travis continuous build process whenever a pull
-request is opened. Both sets of tests can also be run locally.
+tests and lint scripts are run by the travis continuous build process whenever a pull
+request is opened. All sets of tests can also be run locally.
# Running tests locally
@@ -30,7 +31,7 @@ The ZMQ functional test requires a python ZMQ library. To install it:
#### Running the tests
-Individual tests can be run by directly calling the test script, eg:
+Individual tests can be run by directly calling the test script, e.g.:
```
test/functional/feature_rbf.py
@@ -180,6 +181,26 @@ Note: gdb attach step may require `sudo`
Util tests can be run locally by running `test/util/bitcoin-util-test.py`.
Use the `-v` option for verbose output.
+### Lint tests
+
+#### Dependencies
+
+The lint tests require codespell and flake8. To install: `pip3 install codespell flake8`.
+
+#### Running the tests
+
+Individual tests can be run by directly calling the test script, e.g.:
+
+```
+test/lint/lint-filenames.sh
+```
+
+You can run all the shell-based lint tests by running:
+
+```
+test/lint/lint-all.sh
+```
+
# Writing functional tests
You are encouraged to write functional tests for new or existing features.
diff --git a/test/functional/feature_config_args.py b/test/functional/feature_config_args.py
index 1124119e2b..492772d5e3 100755
--- a/test/functional/feature_config_args.py
+++ b/test/functional/feature_config_args.py
@@ -14,9 +14,6 @@ class ConfArgsTest(BitcoinTestFramework):
self.setup_clean_chain = True
self.num_nodes = 1
- def skip_test_if_missing_module(self):
- self.skip_if_no_wallet()
-
def test_config_file_parser(self):
# Assume node is stopped
@@ -68,13 +65,18 @@ class ConfArgsTest(BitcoinTestFramework):
# 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', 'blocks'))
+ #if self.is_wallet_compiled():
#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)
self.nodes[0].datadir = new_data_dir_2
self.start_node(0, ['-datadir='+new_data_dir_2, '-conf='+conf_file, '-wallet=w2'])
- assert os.path.exists(os.path.join(new_data_dir_2, 'regtest', 'wallets', 'w2'))
+ assert os.path.exists(os.path.join(new_data_dir_2, 'regtest', 'blocks'))
+ if self.is_wallet_compiled():
+ assert os.path.exists(os.path.join(new_data_dir_2, 'regtest', 'wallets', 'w2'))
+
if __name__ == '__main__':
ConfArgsTest().main()
diff --git a/test/functional/feature_filelock.py b/test/functional/feature_filelock.py
new file mode 100755
index 0000000000..9fb0d35a68
--- /dev/null
+++ b/test/functional/feature_filelock.py
@@ -0,0 +1,36 @@
+#!/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.
+"""Check that it's not possible to start a second bitcoind instance using the same datadir or wallet."""
+import os
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.test_node import ErrorMatch
+
+class FilelockTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.num_nodes = 2
+
+ def setup_network(self):
+ self.add_nodes(self.num_nodes, extra_args=None)
+ self.nodes[0].start([])
+ self.nodes[0].wait_for_rpc_connection()
+
+ def run_test(self):
+ datadir = os.path.join(self.nodes[0].datadir, 'regtest')
+ self.log.info("Using datadir {}".format(datadir))
+
+ self.log.info("Check that we can't start a second bitcoind instance using the same datadir")
+ expected_msg = "Error: Cannot obtain a lock on data directory {}. Bitcoin Core is probably already running.".format(datadir)
+ self.nodes[1].assert_start_raises_init_error(extra_args=['-datadir={}'.format(self.nodes[0].datadir), '-noserver'], expected_msg=expected_msg)
+
+ if self.is_wallet_compiled():
+ wallet_dir = os.path.join(datadir, 'wallets')
+ self.log.info("Check that we can't start a second bitcoind instance using the same wallet")
+ expected_msg = "Error: Error initializing wallet database environment"
+ self.nodes[1].assert_start_raises_init_error(extra_args=['-walletdir={}'.format(wallet_dir), '-noserver'], expected_msg=expected_msg, match=ErrorMatch.PARTIAL_REGEX)
+
+if __name__ == '__main__':
+ FilelockTest().main()
diff --git a/test/functional/feature_notifications.py b/test/functional/feature_notifications.py
index a93443f2db..d8083b2840 100755
--- a/test/functional/feature_notifications.py
+++ b/test/functional/feature_notifications.py
@@ -5,17 +5,16 @@
"""Test the -alertnotify, -blocknotify and -walletnotify options."""
import os
+from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, wait_until, connect_nodes_bi
+
class NotificationsTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
self.setup_clean_chain = True
- def skip_test_if_missing_module(self):
- self.skip_if_no_wallet()
-
def setup_network(self):
self.alertnotify_dir = os.path.join(self.options.tmpdir, "alertnotify")
self.blocknotify_dir = os.path.join(self.options.tmpdir, "blocknotify")
@@ -25,7 +24,7 @@ class NotificationsTest(BitcoinTestFramework):
os.mkdir(self.walletnotify_dir)
# -alertnotify and -blocknotify on node0, walletnotify on node1
- self.extra_args = [["-blockversion=2",
+ self.extra_args = [[
"-alertnotify=echo > {}".format(os.path.join(self.alertnotify_dir, '%s')),
"-blocknotify=echo > {}".format(os.path.join(self.blocknotify_dir, '%s'))],
["-blockversion=211",
@@ -36,7 +35,7 @@ class NotificationsTest(BitcoinTestFramework):
def run_test(self):
self.log.info("test -blocknotify")
block_count = 10
- blocks = self.nodes[1].generate(block_count)
+ blocks = self.nodes[1].generatetoaddress(block_count, self.nodes[1].getnewaddress() if self.is_wallet_compiled() else ADDRESS_BCRT1_UNSPENDABLE)
# wait at most 10 seconds for expected number of files before reading the content
wait_until(lambda: len(os.listdir(self.blocknotify_dir)) == block_count, timeout=10)
@@ -44,30 +43,32 @@ class NotificationsTest(BitcoinTestFramework):
# directory content should equal the generated blocks hashes
assert_equal(sorted(blocks), sorted(os.listdir(self.blocknotify_dir)))
- self.log.info("test -walletnotify")
- # wait at most 10 seconds for expected number of files before reading the content
- wait_until(lambda: len(os.listdir(self.walletnotify_dir)) == block_count, timeout=10)
+ if self.is_wallet_compiled():
+ self.log.info("test -walletnotify")
+ # wait at most 10 seconds for expected number of files before reading the content
+ wait_until(lambda: len(os.listdir(self.walletnotify_dir)) == block_count, timeout=10)
- # directory content should equal the generated transaction hashes
- txids_rpc = list(map(lambda t: t['txid'], self.nodes[1].listtransactions("*", block_count)))
- assert_equal(sorted(txids_rpc), sorted(os.listdir(self.walletnotify_dir)))
- for tx_file in os.listdir(self.walletnotify_dir):
- os.remove(os.path.join(self.walletnotify_dir, tx_file))
+ # directory content should equal the generated transaction hashes
+ txids_rpc = list(map(lambda t: t['txid'], self.nodes[1].listtransactions("*", block_count)))
+ assert_equal(sorted(txids_rpc), sorted(os.listdir(self.walletnotify_dir)))
+ self.stop_node(1)
+ for tx_file in os.listdir(self.walletnotify_dir):
+ os.remove(os.path.join(self.walletnotify_dir, tx_file))
- self.log.info("test -walletnotify after rescan")
- # restart node to rescan to force wallet notifications
- self.restart_node(1)
- connect_nodes_bi(self.nodes, 0, 1)
+ self.log.info("test -walletnotify after rescan")
+ # restart node to rescan to force wallet notifications
+ self.start_node(1)
+ connect_nodes_bi(self.nodes, 0, 1)
- wait_until(lambda: len(os.listdir(self.walletnotify_dir)) == block_count, timeout=10)
+ wait_until(lambda: len(os.listdir(self.walletnotify_dir)) == block_count, timeout=10)
- # directory content should equal the generated transaction hashes
- txids_rpc = list(map(lambda t: t['txid'], self.nodes[1].listtransactions("*", block_count)))
- assert_equal(sorted(txids_rpc), sorted(os.listdir(self.walletnotify_dir)))
+ # directory content should equal the generated transaction hashes
+ txids_rpc = list(map(lambda t: t['txid'], self.nodes[1].listtransactions("*", block_count)))
+ assert_equal(sorted(txids_rpc), sorted(os.listdir(self.walletnotify_dir)))
# Mine another 41 up-version blocks. -alertnotify should trigger on the 51st.
self.log.info("test -alertnotify")
- self.nodes[1].generate(41)
+ self.nodes[1].generatetoaddress(41, ADDRESS_BCRT1_UNSPENDABLE)
self.sync_all()
# Give bitcoind 10 seconds to write the alert notification
@@ -77,7 +78,7 @@ class NotificationsTest(BitcoinTestFramework):
os.remove(os.path.join(self.alertnotify_dir, notify_file))
# Mine more up-version blocks, should not get more alerts:
- self.nodes[1].generate(2)
+ self.nodes[1].generatetoaddress(2, ADDRESS_BCRT1_UNSPENDABLE)
self.sync_all()
self.log.info("-alertnotify should not continue notifying for more unknown version blocks")
diff --git a/test/functional/feature_nulldummy.py b/test/functional/feature_nulldummy.py
index a79cc3d34b..eb76089d9c 100755
--- a/test/functional/feature_nulldummy.py
+++ b/test/functional/feature_nulldummy.py
@@ -12,6 +12,7 @@ Generate 427 more blocks.
[Consensus] Check that the new NULLDUMMY rules are not enforced on the 431st block.
[Policy/Consensus] Check that the new NULLDUMMY rules are enforced on the 432nd block.
"""
+import time
from test_framework.blocktools import create_coinbase, create_block, create_transaction, add_witness_commitment
from test_framework.messages import CTransaction
@@ -19,8 +20,6 @@ from test_framework.script import CScript
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, assert_raises_rpc_error, bytes_to_hex_str
-import time
-
NULLDUMMY_ERROR = "non-mandatory-script-verify-flag (Dummy CHECKMULTISIG argument must be zero) (code 64)"
def trueDummy(tx):
@@ -42,7 +41,7 @@ class NULLDUMMYTest(BitcoinTestFramework):
self.setup_clean_chain = True
# This script tests NULLDUMMY activation, which is part of the 'segwit' deployment, so we go through
# normal segwit activation here (and don't use the default always-on behaviour).
- self.extra_args = [['-whitelist=127.0.0.1', '-vbparams=segwit:0:999999999999', '-addresstype=legacy', "-deprecatedrpc=addwitnessaddress"]]
+ self.extra_args = [['-whitelist=127.0.0.1', '-vbparams=segwit:0:999999999999', '-addresstype=legacy']]
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
@@ -50,14 +49,14 @@ class NULLDUMMYTest(BitcoinTestFramework):
def run_test(self):
self.address = self.nodes[0].getnewaddress()
self.ms_address = self.nodes[0].addmultisigaddress(1, [self.address])['address']
- self.wit_address = self.nodes[0].addwitnessaddress(self.address)
+ self.wit_address = self.nodes[0].getnewaddress(address_type='p2sh-segwit')
self.wit_ms_address = self.nodes[0].addmultisigaddress(1, [self.address], '', 'p2sh-segwit')['address']
- self.coinbase_blocks = self.nodes[0].generate(2) # Block 2
+ self.coinbase_blocks = self.nodes[0].generate(2) # Block 2
coinbase_txid = []
for i in self.coinbase_blocks:
coinbase_txid.append(self.nodes[0].getblock(i)['tx'][0])
- self.nodes[0].generate(427) # Block 429
+ self.nodes[0].generate(427) # Block 429
self.lastblockhash = self.nodes[0].getbestblockhash()
self.tip = int("0x" + self.lastblockhash, 0)
self.lastblockheight = 429
@@ -82,7 +81,7 @@ class NULLDUMMYTest(BitcoinTestFramework):
self.log.info("Test 4: Non-NULLDUMMY base multisig transaction is invalid after activation")
test4tx = create_transaction(self.nodes[0], test2tx.hash, self.address, amount=46)
- test6txs=[CTransaction(test4tx)]
+ test6txs = [CTransaction(test4tx)]
trueDummy(test4tx)
assert_raises_rpc_error(-26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction, bytes_to_hex_str(test4tx.serialize_with_witness()), True)
self.block_submit(self.nodes[0], [test4tx])
@@ -99,8 +98,7 @@ class NULLDUMMYTest(BitcoinTestFramework):
self.nodes[0].sendrawtransaction(bytes_to_hex_str(i.serialize_with_witness()), True)
self.block_submit(self.nodes[0], test6txs, True, True)
-
- def block_submit(self, node, txs, witness = False, accept = False):
+ def block_submit(self, node, txs, witness=False, accept=False):
block = create_block(self.tip, create_coinbase(self.lastblockheight + 1), self.lastblocktime + 1)
block.nVersion = 4
for tx in txs:
diff --git a/test/functional/feature_segwit.py b/test/functional/feature_segwit.py
index 2cbfc26e89..7098a03f1e 100755
--- a/test/functional/feature_segwit.py
+++ b/test/functional/feature_segwit.py
@@ -5,11 +5,10 @@
"""Test the SegWit changeover logic."""
from decimal import Decimal
+from io import BytesIO
from test_framework.address import (
key_to_p2pkh,
- key_to_p2sh_p2wpkh,
- key_to_p2wpkh,
program_to_witness,
script_to_p2sh,
script_to_p2sh_p2wsh,
@@ -21,8 +20,6 @@ from test_framework.script import CScript, OP_HASH160, OP_CHECKSIG, OP_0, hash16
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, assert_raises_rpc_error, bytes_to_hex_str, connect_nodes, hex_str_to_bytes, sync_blocks, try_rpc
-from io import BytesIO
-
NODE_0 = 0
NODE_2 = 2
WIT_V0 = 0
@@ -51,20 +48,17 @@ class SegWitTest(BitcoinTestFramework):
"-rpcserialversion=0",
"-vbparams=segwit:0:999999999999",
"-addresstype=legacy",
- "-deprecatedrpc=addwitnessaddress",
],
[
"-blockversion=4",
"-rpcserialversion=1",
"-vbparams=segwit:0:999999999999",
"-addresstype=legacy",
- "-deprecatedrpc=addwitnessaddress",
],
[
"-blockversion=536870915",
"-vbparams=segwit:0:999999999999",
"-addresstype=legacy",
- "-deprecatedrpc=addwitnessaddress",
],
]
@@ -91,9 +85,8 @@ class SegWitTest(BitcoinTestFramework):
def fail_accept(self, node, error_msg, txid, sign, redeem_script=""):
assert_raises_rpc_error(-26, error_msg, send_to_witness, use_p2wsh=1, node=node, utxo=getutxo(txid), pubkey=self.pubkey[0], encode_p2sh=False, amount=Decimal("49.998"), sign=sign, insert_redeem_script=redeem_script)
-
def run_test(self):
- self.nodes[0].generate(161) #block 161
+ self.nodes[0].generate(161) # block 161
self.log.info("Verify sigops are counted in GBT with pre-BIP141 rules before the fork")
txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1)
@@ -103,28 +96,24 @@ class SegWitTest(BitcoinTestFramework):
assert(tmpl['sigoplimit'] == 20000)
assert(tmpl['transactions'][0]['hash'] == txid)
assert(tmpl['transactions'][0]['sigops'] == 2)
- tmpl = self.nodes[0].getblocktemplate({'rules':['segwit']})
+ tmpl = self.nodes[0].getblocktemplate({'rules': ['segwit']})
assert(tmpl['sizelimit'] == 1000000)
assert('weightlimit' not in tmpl)
assert(tmpl['sigoplimit'] == 20000)
assert(tmpl['transactions'][0]['hash'] == txid)
assert(tmpl['transactions'][0]['sigops'] == 2)
- self.nodes[0].generate(1) #block 162
+ self.nodes[0].generate(1) # block 162
balance_presetup = self.nodes[0].getbalance()
self.pubkey = []
- p2sh_ids = [] # p2sh_ids[NODE][VER] is an array of txids that spend to a witness version VER pkscript to an address for NODE embedded in p2sh
- wit_ids = [] # wit_ids[NODE][VER] is an array of txids that spend to a witness version VER pkscript to an address for NODE via bare witness
+ p2sh_ids = [] # p2sh_ids[NODE][VER] is an array of txids that spend to a witness version VER pkscript to an address for NODE embedded in p2sh
+ wit_ids = [] # wit_ids[NODE][VER] is an array of txids that spend to a witness version VER pkscript to an address for NODE via bare witness
for i in range(3):
newaddress = self.nodes[i].getnewaddress()
self.pubkey.append(self.nodes[i].getaddressinfo(newaddress)["pubkey"])
multiscript = CScript([OP_1, hex_str_to_bytes(self.pubkey[-1]), OP_1, OP_CHECKMULTISIG])
- p2sh_addr = self.nodes[i].addwitnessaddress(newaddress)
- bip173_addr = self.nodes[i].addwitnessaddress(newaddress, False)
p2sh_ms_addr = self.nodes[i].addmultisigaddress(1, [self.pubkey[-1]], '', 'p2sh-segwit')['address']
bip173_ms_addr = self.nodes[i].addmultisigaddress(1, [self.pubkey[-1]], '', 'bech32')['address']
- assert_equal(p2sh_addr, key_to_p2sh_p2wpkh(self.pubkey[-1]))
- assert_equal(bip173_addr, key_to_p2wpkh(self.pubkey[-1]))
assert_equal(p2sh_ms_addr, script_to_p2sh_p2wsh(multiscript))
assert_equal(bip173_ms_addr, script_to_p2wsh(multiscript))
p2sh_ids.append([])
@@ -139,32 +128,32 @@ class SegWitTest(BitcoinTestFramework):
wit_ids[n][v].append(send_to_witness(v, self.nodes[0], find_spendable_utxo(self.nodes[0], 50), self.pubkey[n], False, Decimal("49.999")))
p2sh_ids[n][v].append(send_to_witness(v, self.nodes[0], find_spendable_utxo(self.nodes[0], 50), self.pubkey[n], True, Decimal("49.999")))
- self.nodes[0].generate(1) #block 163
+ self.nodes[0].generate(1) # block 163
sync_blocks(self.nodes)
# Make sure all nodes recognize the transactions as theirs
- assert_equal(self.nodes[0].getbalance(), balance_presetup - 60*50 + 20*Decimal("49.999") + 50)
- assert_equal(self.nodes[1].getbalance(), 20*Decimal("49.999"))
- assert_equal(self.nodes[2].getbalance(), 20*Decimal("49.999"))
+ assert_equal(self.nodes[0].getbalance(), balance_presetup - 60 * 50 + 20 * Decimal("49.999") + 50)
+ assert_equal(self.nodes[1].getbalance(), 20 * Decimal("49.999"))
+ assert_equal(self.nodes[2].getbalance(), 20 * Decimal("49.999"))
- self.nodes[0].generate(260) #block 423
+ self.nodes[0].generate(260) # block 423
sync_blocks(self.nodes)
self.log.info("Verify witness txs are skipped for mining before the fork")
- self.skip_mine(self.nodes[2], wit_ids[NODE_2][WIT_V0][0], True) #block 424
- self.skip_mine(self.nodes[2], wit_ids[NODE_2][WIT_V1][0], True) #block 425
- self.skip_mine(self.nodes[2], p2sh_ids[NODE_2][WIT_V0][0], True) #block 426
- self.skip_mine(self.nodes[2], p2sh_ids[NODE_2][WIT_V1][0], True) #block 427
+ self.skip_mine(self.nodes[2], wit_ids[NODE_2][WIT_V0][0], True) # block 424
+ self.skip_mine(self.nodes[2], wit_ids[NODE_2][WIT_V1][0], True) # block 425
+ self.skip_mine(self.nodes[2], p2sh_ids[NODE_2][WIT_V0][0], True) # block 426
+ self.skip_mine(self.nodes[2], p2sh_ids[NODE_2][WIT_V1][0], True) # block 427
self.log.info("Verify unsigned p2sh witness txs without a redeem script are invalid")
self.fail_accept(self.nodes[2], "mandatory-script-verify-flag", p2sh_ids[NODE_2][WIT_V0][1], False)
self.fail_accept(self.nodes[2], "mandatory-script-verify-flag", p2sh_ids[NODE_2][WIT_V1][1], False)
- self.nodes[2].generate(4) # blocks 428-431
+ self.nodes[2].generate(4) # blocks 428-431
self.log.info("Verify previous witness txs skipped for mining can now be mined")
assert_equal(len(self.nodes[2].getrawmempool()), 4)
- block = self.nodes[2].generate(1) #block 432 (first block with new rules; 432 = 144 * 3)
+ block = self.nodes[2].generate(1) # block 432 (first block with new rules; 432 = 144 * 3)
sync_blocks(self.nodes)
assert_equal(len(self.nodes[2].getrawmempool()), 0)
segwit_tx_list = self.nodes[2].getblock(block[0])["tx"]
@@ -181,8 +170,8 @@ class SegWitTest(BitcoinTestFramework):
self.fail_accept(self.nodes[0], "mandatory-script-verify-flag", p2sh_ids[NODE_0][WIT_V1][0], False, witness_script(True, self.pubkey[0]))
self.log.info("Verify block and transaction serialization rpcs return differing serializations depending on rpc serialization flag")
- assert(self.nodes[2].getblock(block[0], False) != self.nodes[0].getblock(block[0], False))
- assert(self.nodes[1].getblock(block[0], False) == self.nodes[2].getblock(block[0], False))
+ assert(self.nodes[2].getblock(block[0], False) != self.nodes[0].getblock(block[0], False))
+ assert(self.nodes[1].getblock(block[0], False) == self.nodes[2].getblock(block[0], False))
for i in range(len(segwit_tx_list)):
tx = FromHex(CTransaction(), self.nodes[2].gettransaction(segwit_tx_list[i])["hex"])
assert(self.nodes[2].getrawtransaction(segwit_tx_list[i]) != self.nodes[0].getrawtransaction(segwit_tx_list[i]))
@@ -198,21 +187,21 @@ class SegWitTest(BitcoinTestFramework):
self.fail_accept(self.nodes[2], 'non-mandatory-script-verify-flag (Witness program was passed an empty witness) (code 64)', p2sh_ids[NODE_2][WIT_V1][2], sign=False, redeem_script=witness_script(True, self.pubkey[2]))
self.log.info("Verify default node can now use witness txs")
- self.success_mine(self.nodes[0], wit_ids[NODE_0][WIT_V0][0], True) #block 432
- self.success_mine(self.nodes[0], wit_ids[NODE_0][WIT_V1][0], True) #block 433
- self.success_mine(self.nodes[0], p2sh_ids[NODE_0][WIT_V0][0], True) #block 434
- self.success_mine(self.nodes[0], p2sh_ids[NODE_0][WIT_V1][0], True) #block 435
+ self.success_mine(self.nodes[0], wit_ids[NODE_0][WIT_V0][0], True) # block 432
+ self.success_mine(self.nodes[0], wit_ids[NODE_0][WIT_V1][0], True) # block 433
+ self.success_mine(self.nodes[0], p2sh_ids[NODE_0][WIT_V0][0], True) # block 434
+ self.success_mine(self.nodes[0], p2sh_ids[NODE_0][WIT_V1][0], True) # block 435
self.log.info("Verify sigops are counted in GBT with BIP141 rules after the fork")
txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1)
- tmpl = self.nodes[0].getblocktemplate({'rules':['segwit']})
+ tmpl = self.nodes[0].getblocktemplate({'rules': ['segwit']})
assert(tmpl['sizelimit'] >= 3999577) # actual maximum size is lower due to minimum mandatory non-witness data
assert(tmpl['weightlimit'] == 4000000)
assert(tmpl['sigoplimit'] == 80000)
assert(tmpl['transactions'][0]['txid'] == txid)
assert(tmpl['transactions'][0]['sigops'] == 8)
- self.nodes[0].generate(1) # Mine a block to clear the gbt cache
+ self.nodes[0].generate(1) # Mine a block to clear the gbt cache
self.log.info("Non-segwit miners are able to use GBT response after activation.")
# Create a 3-tx chain: tx1 (non-segwit input, paying to a segwit output) ->
@@ -222,7 +211,7 @@ class SegWitTest(BitcoinTestFramework):
txid1 = send_to_witness(1, self.nodes[0], find_spendable_utxo(self.nodes[0], 50), self.pubkey[0], False, Decimal("49.996"))
hex_tx = self.nodes[0].gettransaction(txid)['hex']
tx = FromHex(CTransaction(), hex_tx)
- assert(tx.wit.is_null()) # This should not be a segwit input
+ assert(tx.wit.is_null()) # This should not be a segwit input
assert(txid1 in self.nodes[0].getrawmempool())
# Now create tx2, which will spend from txid1.
@@ -247,13 +236,13 @@ class SegWitTest(BitcoinTestFramework):
template = self.nodes[0].getblocktemplate()
# Check that tx1 is the only transaction of the 3 in the template.
- template_txids = [ t['txid'] for t in template['transactions'] ]
+ template_txids = [t['txid'] for t in template['transactions']]
assert(txid2 not in template_txids and txid3 not in template_txids)
assert(txid1 in template_txids)
# Check that running with segwit support results in all 3 being included.
template = self.nodes[0].getblocktemplate({"rules": ["segwit"]})
- template_txids = [ t['txid'] for t in template['transactions'] ]
+ template_txids = [t['txid'] for t in template['transactions']]
assert(txid1 in template_txids)
assert(txid2 in template_txids)
assert(txid3 in template_txids)
@@ -264,17 +253,17 @@ class SegWitTest(BitcoinTestFramework):
# Mine a block to clear the gbt cache again.
self.nodes[0].generate(1)
- self.log.info("Verify behaviour of importaddress, addwitnessaddress and listunspent")
+ self.log.info("Verify behaviour of importaddress and listunspent")
# Some public keys to be used later
pubkeys = [
- "0363D44AABD0F1699138239DF2F042C3282C0671CC7A76826A55C8203D90E39242", # cPiM8Ub4heR9NBYmgVzJQiUH1if44GSBGiqaeJySuL2BKxubvgwb
- "02D3E626B3E616FC8662B489C123349FECBFC611E778E5BE739B257EAE4721E5BF", # cPpAdHaD6VoYbW78kveN2bsvb45Q7G5PhaPApVUGwvF8VQ9brD97
- "04A47F2CBCEFFA7B9BCDA184E7D5668D3DA6F9079AD41E422FA5FD7B2D458F2538A62F5BD8EC85C2477F39650BD391EA6250207065B2A81DA8B009FC891E898F0E", # 91zqCU5B9sdWxzMt1ca3VzbtVm2YM6Hi5Rxn4UDtxEaN9C9nzXV
- "02A47F2CBCEFFA7B9BCDA184E7D5668D3DA6F9079AD41E422FA5FD7B2D458F2538", # cPQFjcVRpAUBG8BA9hzr2yEzHwKoMgLkJZBBtK9vJnvGJgMjzTbd
- "036722F784214129FEB9E8129D626324F3F6716555B603FFE8300BBCB882151228", # cQGtcm34xiLjB1v7bkRa4V3aAc9tS2UTuBZ1UnZGeSeNy627fN66
- "0266A8396EE936BF6D99D17920DB21C6C7B1AB14C639D5CD72B300297E416FD2EC", # cTW5mR5M45vHxXkeChZdtSPozrFwFgmEvTNnanCW6wrqwaCZ1X7K
- "0450A38BD7F0AC212FEBA77354A9B036A32E0F7C81FC4E0C5ADCA7C549C4505D2522458C2D9AE3CEFD684E039194B72C8A10F9CB9D4764AB26FCC2718D421D3B84", # 92h2XPssjBpsJN5CqSP7v9a7cf2kgDunBC6PDFwJHMACM1rrVBJ
+ "0363D44AABD0F1699138239DF2F042C3282C0671CC7A76826A55C8203D90E39242", # cPiM8Ub4heR9NBYmgVzJQiUH1if44GSBGiqaeJySuL2BKxubvgwb
+ "02D3E626B3E616FC8662B489C123349FECBFC611E778E5BE739B257EAE4721E5BF", # cPpAdHaD6VoYbW78kveN2bsvb45Q7G5PhaPApVUGwvF8VQ9brD97
+ "04A47F2CBCEFFA7B9BCDA184E7D5668D3DA6F9079AD41E422FA5FD7B2D458F2538A62F5BD8EC85C2477F39650BD391EA6250207065B2A81DA8B009FC891E898F0E", # 91zqCU5B9sdWxzMt1ca3VzbtVm2YM6Hi5Rxn4UDtxEaN9C9nzXV
+ "02A47F2CBCEFFA7B9BCDA184E7D5668D3DA6F9079AD41E422FA5FD7B2D458F2538", # cPQFjcVRpAUBG8BA9hzr2yEzHwKoMgLkJZBBtK9vJnvGJgMjzTbd
+ "036722F784214129FEB9E8129D626324F3F6716555B603FFE8300BBCB882151228", # cQGtcm34xiLjB1v7bkRa4V3aAc9tS2UTuBZ1UnZGeSeNy627fN66
+ "0266A8396EE936BF6D99D17920DB21C6C7B1AB14C639D5CD72B300297E416FD2EC", # cTW5mR5M45vHxXkeChZdtSPozrFwFgmEvTNnanCW6wrqwaCZ1X7K
+ "0450A38BD7F0AC212FEBA77354A9B036A32E0F7C81FC4E0C5ADCA7C549C4505D2522458C2D9AE3CEFD684E039194B72C8A10F9CB9D4764AB26FCC2718D421D3B84", # 92h2XPssjBpsJN5CqSP7v9a7cf2kgDunBC6PDFwJHMACM1rrVBJ
]
# Import a compressed key and an uncompressed key, generate some multisig addresses
@@ -282,8 +271,8 @@ class SegWitTest(BitcoinTestFramework):
uncompressed_spendable_address = ["mvozP4UwyGD2mGZU4D2eMvMLPB9WkMmMQu"]
self.nodes[0].importprivkey("cNC8eQ5dg3mFAVePDX4ddmPYpPbw41r9bm2jd1nLJT77e6RrzTRR")
compressed_spendable_address = ["mmWQubrDomqpgSYekvsU7HWEVjLFHAakLe"]
- assert ((self.nodes[0].getaddressinfo(uncompressed_spendable_address[0])['iscompressed'] == False))
- assert ((self.nodes[0].getaddressinfo(compressed_spendable_address[0])['iscompressed'] == True))
+ assert not self.nodes[0].getaddressinfo(uncompressed_spendable_address[0])['iscompressed']
+ assert self.nodes[0].getaddressinfo(compressed_spendable_address[0])['iscompressed']
self.nodes[0].importpubkey(pubkeys[0])
compressed_solvable_address = [key_to_p2pkh(pubkeys[0])]
@@ -305,7 +294,6 @@ class SegWitTest(BitcoinTestFramework):
uncompressed_solvable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_spendable_address[0], uncompressed_solvable_address[0]])['address'])
compressed_solvable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_spendable_address[0], compressed_solvable_address[0]])['address'])
compressed_solvable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_solvable_address[0], compressed_solvable_address[1]])['address'])
- unknown_address = ["mtKKyoHabkk6e4ppT7NaM7THqPUt7AzPrT", "2NDP3jLWAFT8NDAiUa9qiE6oBt2awmMq7Dx"]
# Test multisig_without_privkey
# We have 2 public keys without private keys, use addmultisigaddress to add to wallet.
@@ -386,7 +374,6 @@ class SegWitTest(BitcoinTestFramework):
op1 = CScript([OP_1])
op0 = CScript([OP_0])
# 2N7MGY19ti4KDMSzRfPAssP6Pxyuxoi6jLe is the P2SH(P2PKH) version of mjoE3sSrb8ByYEvgnC3Aox86u1CHnfJA4V
- unsolvable_address = ["mjoE3sSrb8ByYEvgnC3Aox86u1CHnfJA4V", "2N7MGY19ti4KDMSzRfPAssP6Pxyuxoi6jLe", script_to_p2sh(op1), script_to_p2sh(op0)]
unsolvable_address_key = hex_str_to_bytes("02341AEC7587A51CDE5279E0630A531AEA2615A9F80B17E8D9376327BAEAA59E3D")
unsolvablep2pkh = CScript([OP_DUP, OP_HASH160, hash160(unsolvable_address_key), OP_EQUALVERIFY, OP_CHECKSIG])
unsolvablep2wshp2pkh = CScript([OP_0, sha256(unsolvablep2pkh)])
@@ -394,9 +381,9 @@ class SegWitTest(BitcoinTestFramework):
p2wshop1 = CScript([OP_0, sha256(op1)])
unsolvable_after_importaddress.append(unsolvablep2pkh)
unsolvable_after_importaddress.append(unsolvablep2wshp2pkh)
- unsolvable_after_importaddress.append(op1) # OP_1 will be imported as script
+ unsolvable_after_importaddress.append(op1) # OP_1 will be imported as script
unsolvable_after_importaddress.append(p2wshop1)
- unseen_anytime.append(op0) # OP_0 will be imported as P2SH address with no script provided
+ unseen_anytime.append(op0) # OP_0 will be imported as P2SH address with no script provided
unsolvable_after_importaddress.append(p2shop0)
spendable_txid = []
@@ -432,27 +419,14 @@ class SegWitTest(BitcoinTestFramework):
# exceptions and continue.
try_rpc(-4, "The wallet already contains the private key for this address or script", self.nodes[0].importaddress, i, "", False, True)
- self.nodes[0].importaddress(script_to_p2sh(op0)) # import OP_0 as address only
- self.nodes[0].importaddress(multisig_without_privkey_address) # Test multisig_without_privkey
+ self.nodes[0].importaddress(script_to_p2sh(op0)) # import OP_0 as address only
+ self.nodes[0].importaddress(multisig_without_privkey_address) # Test multisig_without_privkey
spendable_txid.append(self.mine_and_test_listunspent(spendable_anytime + spendable_after_importaddress, 2))
solvable_txid.append(self.mine_and_test_listunspent(solvable_anytime + solvable_after_importaddress, 1))
self.mine_and_test_listunspent(unsolvable_after_importaddress, 1)
self.mine_and_test_listunspent(unseen_anytime, 0)
- # addwitnessaddress should refuse to return a witness address if an uncompressed key is used
- # note that no witness address should be returned by unsolvable addresses
- for i in uncompressed_spendable_address + uncompressed_solvable_address + unknown_address + unsolvable_address:
- assert_raises_rpc_error(-4, "Public key or redeemscript not known to wallet, or the key is uncompressed", self.nodes[0].addwitnessaddress, i)
-
- # addwitnessaddress should return a witness addresses even if keys are not in the wallet
- self.nodes[0].addwitnessaddress(multisig_without_privkey_address)
-
- for i in compressed_spendable_address + compressed_solvable_address:
- witaddress = self.nodes[0].addwitnessaddress(i)
- # addwitnessaddress should return the same address if it is a known P2SH-witness address
- assert_equal(witaddress, self.nodes[0].addwitnessaddress(witaddress))
-
spendable_txid.append(self.mine_and_test_listunspent(spendable_anytime + spendable_after_importaddress, 2))
solvable_txid.append(self.mine_and_test_listunspent(solvable_anytime + solvable_after_importaddress, 1))
self.mine_and_test_listunspent(unsolvable_after_importaddress, 1)
@@ -470,8 +444,6 @@ class SegWitTest(BitcoinTestFramework):
self.nodes[0].importpubkey(pubkeys[6])
uncompressed_solvable_address = [key_to_p2pkh(pubkeys[6])]
- spendable_after_addwitnessaddress = [] # These outputs should be seen after importaddress
- solvable_after_addwitnessaddress=[] # These outputs should be seen after importaddress but not spendable
unseen_anytime = [] # These outputs should never be seen
solvable_anytime = [] # These outputs should be solvable after importpubkey
unseen_anytime = [] # These outputs should never be seen
@@ -488,8 +460,6 @@ class SegWitTest(BitcoinTestFramework):
v = self.nodes[0].getaddressinfo(i)
if (v['isscript']):
[bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
- # P2WSH and P2SH(P2WSH) multisig with compressed keys are spendable after addwitnessaddress
- spendable_after_addwitnessaddress.extend([p2wsh, p2sh_p2wsh])
premature_witaddress.append(script_to_p2sh(p2wsh))
else:
[p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
@@ -510,9 +480,7 @@ class SegWitTest(BitcoinTestFramework):
for i in compressed_solvable_address:
v = self.nodes[0].getaddressinfo(i)
if (v['isscript']):
- # P2WSH multisig without private key are seen after addwitnessaddress
[bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
- solvable_after_addwitnessaddress.extend([p2wsh, p2sh_p2wsh])
premature_witaddress.append(script_to_p2sh(p2wsh))
else:
[p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
@@ -521,29 +489,11 @@ class SegWitTest(BitcoinTestFramework):
self.mine_and_test_listunspent(spendable_anytime, 2)
self.mine_and_test_listunspent(solvable_anytime, 1)
- self.mine_and_test_listunspent(spendable_after_addwitnessaddress + solvable_after_addwitnessaddress + unseen_anytime, 0)
-
- # addwitnessaddress should refuse to return a witness address if an uncompressed key is used
- # note that a multisig address returned by addmultisigaddress is not solvable until it is added with importaddress
- # premature_witaddress are not accepted until the script is added with addwitnessaddress first
- for i in uncompressed_spendable_address + uncompressed_solvable_address + premature_witaddress:
- # This will raise an exception
- assert_raises_rpc_error(-4, "Public key or redeemscript not known to wallet, or the key is uncompressed", self.nodes[0].addwitnessaddress, i)
-
- # after importaddress it should pass addwitnessaddress
- v = self.nodes[0].getaddressinfo(compressed_solvable_address[1])
- self.nodes[0].importaddress(v['hex'],"",False,True)
- for i in compressed_spendable_address + compressed_solvable_address + premature_witaddress:
- witaddress = self.nodes[0].addwitnessaddress(i)
- assert_equal(witaddress, self.nodes[0].addwitnessaddress(witaddress))
-
- spendable_txid.append(self.mine_and_test_listunspent(spendable_after_addwitnessaddress + spendable_anytime, 2))
- solvable_txid.append(self.mine_and_test_listunspent(solvable_after_addwitnessaddress + solvable_anytime, 1))
self.mine_and_test_listunspent(unseen_anytime, 0)
# Check that createrawtransaction/decoderawtransaction with non-v0 Bech32 works
- v1_addr = program_to_witness(1, [3,5])
- v1_tx = self.nodes[0].createrawtransaction([getutxo(spendable_txid[0])],{v1_addr: 1})
+ v1_addr = program_to_witness(1, [3, 5])
+ v1_tx = self.nodes[0].createrawtransaction([getutxo(spendable_txid[0])], {v1_addr: 1})
v1_decoded = self.nodes[1].decoderawtransaction(v1_tx)
assert_equal(v1_decoded['vout'][0]['scriptPubKey']['addresses'][0], v1_addr)
assert_equal(v1_decoded['vout'][0]['scriptPubKey']['hex'], "51020305")
@@ -586,7 +536,7 @@ class SegWitTest(BitcoinTestFramework):
def mine_and_test_listunspent(self, script_list, ismine):
utxo = find_spendable_utxo(self.nodes[0], 50)
tx = CTransaction()
- tx.vin.append(CTxIn(COutPoint(int('0x'+utxo['txid'],0), utxo['vout'])))
+ tx.vin.append(CTxIn(COutPoint(int('0x' + utxo['txid'], 0), utxo['vout'])))
for i in script_list:
tx.vout.append(CTxOut(10000000, i))
tx.rehash()
@@ -599,7 +549,7 @@ class SegWitTest(BitcoinTestFramework):
for i in self.nodes[0].listunspent():
if (i['txid'] == txid):
watchcount += 1
- if (i['spendable'] == True):
+ if i['spendable']:
spendcount += 1
if (ismine == 2):
assert_equal(spendcount, len(script_list))
@@ -610,14 +560,14 @@ class SegWitTest(BitcoinTestFramework):
assert_equal(watchcount, 0)
return txid
- def p2sh_address_to_script(self,v):
+ def p2sh_address_to_script(self, v):
bare = CScript(hex_str_to_bytes(v['hex']))
p2sh = CScript(hex_str_to_bytes(v['scriptPubKey']))
p2wsh = CScript([OP_0, sha256(bare)])
p2sh_p2wsh = CScript([OP_HASH160, hash160(p2wsh), OP_EQUAL])
return([bare, p2sh, p2wsh, p2sh_p2wsh])
- def p2pkh_address_to_script(self,v):
+ def p2pkh_address_to_script(self, v):
pubkey = hex_str_to_bytes(v['pubkey'])
p2wpkh = CScript([OP_0, hash160(pubkey)])
p2sh_p2wpkh = CScript([OP_HASH160, hash160(p2wpkh), OP_EQUAL])
@@ -631,7 +581,7 @@ class SegWitTest(BitcoinTestFramework):
p2sh_p2wsh_p2pkh = CScript([OP_HASH160, hash160(p2wsh_p2pkh), OP_EQUAL])
return [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh]
- def create_and_mine_tx_from_txids(self, txids, success = True):
+ def create_and_mine_tx_from_txids(self, txids, success=True):
tx = CTransaction()
for i in txids:
txtmp = CTransaction()
@@ -639,7 +589,7 @@ class SegWitTest(BitcoinTestFramework):
f = BytesIO(hex_str_to_bytes(txraw))
txtmp.deserialize(f)
for j in range(len(txtmp.vout)):
- tx.vin.append(CTxIn(COutPoint(int('0x'+i,0), j)))
+ tx.vin.append(CTxIn(COutPoint(int('0x' + i, 0), j)))
tx.vout.append(CTxOut(0, CScript()))
tx.rehash()
signresults = self.nodes[0].signrawtransactionwithwallet(bytes_to_hex_str(tx.serialize_without_witness()))['hex']
diff --git a/test/functional/feature_uacomment.py b/test/functional/feature_uacomment.py
index 691a39b825..fb4ad21359 100755
--- a/test/functional/feature_uacomment.py
+++ b/test/functional/feature_uacomment.py
@@ -31,7 +31,7 @@ class UacommentTest(BitcoinTestFramework):
self.nodes[0].assert_start_raises_init_error(["-uacomment=" + 'a' * 256], expected, match=ErrorMatch.FULL_REGEX)
self.log.info("test -uacomment unsafe characters")
- for unsafe_char in ['/', ':', '(', ')']:
+ for unsafe_char in ['/', ':', '(', ')', '₿', '🏃']:
expected = "Error: User Agent comment \(" + re.escape(unsafe_char) + "\) contains unsafe characters."
self.nodes[0].assert_start_raises_init_error(["-uacomment=" + unsafe_char], expected, match=ErrorMatch.FULL_REGEX)
diff --git a/test/functional/interface_bitcoin_cli.py b/test/functional/interface_bitcoin_cli.py
index f311858bee..aed439339f 100755
--- a/test/functional/interface_bitcoin_cli.py
+++ b/test/functional/interface_bitcoin_cli.py
@@ -12,19 +12,17 @@ class TestBitcoinCli(BitcoinTestFramework):
self.setup_clean_chain = True
self.num_nodes = 1
- def skip_test_if_missing_module(self):
- self.skip_if_no_wallet()
-
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()
- assert_equal(cli_response, rpc_response)
+ self.log.info("Compare responses from getwalletinfo RPC and `bitcoin-cli getwalletinfo`")
+ if self.is_wallet_compiled():
+ cli_response = self.nodes[0].cli.getwalletinfo()
+ rpc_response = self.nodes[0].getwalletinfo()
+ assert_equal(cli_response, rpc_response)
self.log.info("Compare responses from getblockchaininfo RPC and `bitcoin-cli getblockchaininfo`")
cli_response = self.nodes[0].cli.getblockchaininfo()
@@ -52,26 +50,30 @@ class TestBitcoinCli(BitcoinTestFramework):
self.log.info("Compare responses from `bitcoin-cli -getinfo` and the RPCs data is retrieved from.")
cli_get_info = self.nodes[0].cli('-getinfo').send_cli()
- wallet_info = self.nodes[0].getwalletinfo()
+ if self.is_wallet_compiled():
+ wallet_info = self.nodes[0].getwalletinfo()
network_info = self.nodes[0].getnetworkinfo()
blockchain_info = self.nodes[0].getblockchaininfo()
assert_equal(cli_get_info['version'], network_info['version'])
assert_equal(cli_get_info['protocolversion'], network_info['protocolversion'])
- assert_equal(cli_get_info['walletversion'], wallet_info['walletversion'])
- assert_equal(cli_get_info['balance'], wallet_info['balance'])
+ if self.is_wallet_compiled():
+ assert_equal(cli_get_info['walletversion'], wallet_info['walletversion'])
+ assert_equal(cli_get_info['balance'], wallet_info['balance'])
assert_equal(cli_get_info['blocks'], blockchain_info['blocks'])
assert_equal(cli_get_info['timeoffset'], network_info['timeoffset'])
assert_equal(cli_get_info['connections'], network_info['connections'])
assert_equal(cli_get_info['proxy'], network_info['networks'][0]['proxy'])
assert_equal(cli_get_info['difficulty'], blockchain_info['difficulty'])
assert_equal(cli_get_info['testnet'], blockchain_info['chain'] == "test")
- assert_equal(cli_get_info['balance'], wallet_info['balance'])
- assert_equal(cli_get_info['keypoololdest'], wallet_info['keypoololdest'])
- assert_equal(cli_get_info['keypoolsize'], wallet_info['keypoolsize'])
- assert_equal(cli_get_info['paytxfee'], wallet_info['paytxfee'])
- assert_equal(cli_get_info['relayfee'], network_info['relayfee'])
- # unlocked_until is not tested because the wallet is not encrypted
+ if self.is_wallet_compiled():
+ assert_equal(cli_get_info['balance'], wallet_info['balance'])
+ assert_equal(cli_get_info['keypoololdest'], wallet_info['keypoololdest'])
+ assert_equal(cli_get_info['keypoolsize'], wallet_info['keypoolsize'])
+ assert_equal(cli_get_info['paytxfee'], wallet_info['paytxfee'])
+ assert_equal(cli_get_info['relayfee'], network_info['relayfee'])
+ # unlocked_until is not tested because the wallet is not encrypted
+
if __name__ == '__main__':
TestBitcoinCli().main()
diff --git a/test/functional/interface_zmq.py b/test/functional/interface_zmq.py
index 1c518eab75..48136a0108 100755
--- a/test/functional/interface_zmq.py
+++ b/test/functional/interface_zmq.py
@@ -5,6 +5,7 @@
"""Test the ZMQ notification interface."""
import struct
+from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE
from test_framework.test_framework import BitcoinTestFramework
from test_framework.messages import CTransaction
from test_framework.util import (
@@ -14,6 +15,7 @@ from test_framework.util import (
)
from io import BytesIO
+ADDRESS = "tcp://127.0.0.1:28332"
class ZMQSubscriber:
def __init__(self, socket, topic):
@@ -41,7 +43,6 @@ class ZMQTest (BitcoinTestFramework):
def skip_test_if_missing_module(self):
self.skip_if_no_py3_zmq()
self.skip_if_no_bitcoind_zmq()
- self.skip_if_no_wallet()
def setup_nodes(self):
import zmq
@@ -51,11 +52,10 @@ class ZMQTest (BitcoinTestFramework):
# that this test fails if the publishing order changes.
# Note that the publishing order is not defined in the documentation and
# is subject to change.
- address = "tcp://127.0.0.1:28332"
self.zmq_context = zmq.Context()
socket = self.zmq_context.socket(zmq.SUB)
socket.set(zmq.RCVTIMEO, 60000)
- socket.connect(address)
+ socket.connect(ADDRESS)
# Subscribe to all available topics.
self.hashblock = ZMQSubscriber(socket, b"hashblock")
@@ -64,7 +64,7 @@ class ZMQTest (BitcoinTestFramework):
self.rawtx = ZMQSubscriber(socket, b"rawtx")
self.extra_args = [
- ["-zmqpub%s=%s" % (sub.topic.decode(), address) for sub in [self.hashblock, self.hashtx, self.rawblock, self.rawtx]],
+ ["-zmqpub%s=%s" % (sub.topic.decode(), ADDRESS) for sub in [self.hashblock, self.hashtx, self.rawblock, self.rawtx]],
[],
]
self.add_nodes(self.num_nodes, self.extra_args)
@@ -81,7 +81,7 @@ class ZMQTest (BitcoinTestFramework):
def _zmq_test(self):
num_blocks = 5
self.log.info("Generate %(n)d blocks (and %(n)d coinbase txes)" % {"n": num_blocks})
- genhashes = self.nodes[0].generate(num_blocks)
+ genhashes = self.nodes[0].generatetoaddress(num_blocks, ADDRESS_BCRT1_UNSPENDABLE)
self.sync_all()
for x in range(num_blocks):
@@ -105,17 +105,29 @@ class ZMQTest (BitcoinTestFramework):
block = self.rawblock.receive()
assert_equal(genhashes[x], bytes_to_hex_str(hash256(block[:80])))
- self.log.info("Wait for tx from second node")
- payment_txid = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1.0)
- self.sync_all()
+ if self.is_wallet_compiled():
+ self.log.info("Wait for tx from second node")
+ payment_txid = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1.0)
+ self.sync_all()
+
+ # Should receive the broadcasted txid.
+ txid = self.hashtx.receive()
+ assert_equal(payment_txid, bytes_to_hex_str(txid))
+
+ # Should receive the broadcasted raw transaction.
+ hex = self.rawtx.receive()
+ assert_equal(payment_txid, bytes_to_hex_str(hash256(hex)))
+
- # Should receive the broadcasted txid.
- txid = self.hashtx.receive()
- assert_equal(payment_txid, bytes_to_hex_str(txid))
+ self.log.info("Test the getzmqnotifications RPC")
+ assert_equal(self.nodes[0].getzmqnotifications(), [
+ {"type": "pubhashblock", "address": ADDRESS},
+ {"type": "pubhashtx", "address": ADDRESS},
+ {"type": "pubrawblock", "address": ADDRESS},
+ {"type": "pubrawtx", "address": ADDRESS},
+ ])
- # Should receive the broadcasted raw transaction.
- hex = self.rawtx.receive()
- assert_equal(payment_txid, bytes_to_hex_str(hash256(hex)))
+ assert_equal(self.nodes[1].getzmqnotifications(), [])
if __name__ == '__main__':
ZMQTest().main()
diff --git a/test/functional/p2p_compactblocks.py b/test/functional/p2p_compactblocks.py
index fafc27d16a..f0dee59b5c 100755
--- a/test/functional/p2p_compactblocks.py
+++ b/test/functional/p2p_compactblocks.py
@@ -7,7 +7,6 @@
Version 1 compact blocks are pre-segwit (txids)
Version 2 compact blocks are post-segwit (wtxids)
"""
-
from decimal import Decimal
import random
@@ -99,7 +98,7 @@ class CompactBlocksTest(BitcoinTestFramework):
self.num_nodes = 2
# This test was written assuming SegWit is activated using BIP9 at height 432 (3x confirmation window).
# TODO: Rewrite this test to support SegWit being always active.
- self.extra_args = [["-vbparams=segwit:0:0"], ["-vbparams=segwit:0:999999999999", "-txindex", "-deprecatedrpc=addwitnessaddress"]]
+ self.extra_args = [["-vbparams=segwit:0:0"], ["-vbparams=segwit:0:999999999999", "-txindex"]]
self.utxos = []
def skip_test_if_missing_module(self):
@@ -189,7 +188,7 @@ class CompactBlocksTest(BitcoinTestFramework):
# Now try a SENDCMPCT message with too-high version
sendcmpct = msg_sendcmpct()
- sendcmpct.version = preferred_version+1
+ sendcmpct.version = preferred_version + 1
sendcmpct.announce = True
test_node.send_and_ping(sendcmpct)
check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" not in p.last_message)
@@ -220,7 +219,7 @@ class CompactBlocksTest(BitcoinTestFramework):
check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" in p.last_message)
# Try one more time, after sending a version-1, announce=false message.
- sendcmpct.version = preferred_version-1
+ sendcmpct.version = preferred_version - 1
sendcmpct.announce = False
test_node.send_and_ping(sendcmpct)
check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" in p.last_message)
@@ -234,7 +233,7 @@ class CompactBlocksTest(BitcoinTestFramework):
if old_node is not None:
# Verify that a peer using an older protocol version can receive
# announcements from this node.
- sendcmpct.version = preferred_version-1
+ sendcmpct.version = preferred_version - 1
sendcmpct.announce = True
old_node.send_and_ping(sendcmpct)
# Header sync
@@ -265,9 +264,9 @@ class CompactBlocksTest(BitcoinTestFramework):
if use_witness_address:
# Want at least one segwit spend, so move all funds to
# a witness address.
- address = node.addwitnessaddress(address)
+ address = node.getnewaddress(address_type='bech32')
value_to_send = node.getbalance()
- node.sendtoaddress(address, satoshi_round(value_to_send-Decimal(0.1)))
+ node.sendtoaddress(address, satoshi_round(value_to_send - Decimal(0.1)))
node.generate(1)
segwit_tx_generated = False
@@ -279,7 +278,7 @@ class CompactBlocksTest(BitcoinTestFramework):
segwit_tx_generated = True
if use_witness_address:
- assert(segwit_tx_generated) # check that our test is not broken
+ assert segwit_tx_generated # check that our test is not broken
# Wait until we've seen the block announcement for the resulting tip
tip = int(node.getbestblockhash(), 16)
@@ -403,8 +402,7 @@ class CompactBlocksTest(BitcoinTestFramework):
coinbase_hash = block.vtx[0].sha256
if version == 2:
coinbase_hash = block.vtx[0].calc_sha256(True)
- comp_block.shortids = [
- calculate_shortid(k0, k1, coinbase_hash) ]
+ comp_block.shortids = [calculate_shortid(k0, k1, coinbase_hash)]
test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p()))
assert_equal(int(node.getbestblockhash(), 16), block.hashPrevBlock)
# Expect a getblocktxn message.
@@ -443,7 +441,7 @@ class CompactBlocksTest(BitcoinTestFramework):
# node needs, and that responding to them causes the block to be
# reconstructed.
def test_getblocktxn_requests(self, node, test_node, version):
- with_witness = (version==2)
+ with_witness = (version == 2)
def test_getblocktxn_response(compact_block, peer, expected_result):
msg = msg_cmpctblock(compact_block.to_p2p())
@@ -470,7 +468,7 @@ class CompactBlocksTest(BitcoinTestFramework):
msg_bt = msg_blocktxn()
if with_witness:
- msg_bt = msg_witness_blocktxn() # serialize with witnesses
+ msg_bt = msg_witness_blocktxn() # serialize with witnesses
msg_bt.block_transactions = BlockTransactions(block.sha256, block.vtx[1:])
test_tip_after_message(node, test_node, msg_bt, block.sha256)
@@ -560,7 +558,7 @@ class CompactBlocksTest(BitcoinTestFramework):
# verifying that the block isn't marked bad permanently. This is good
# enough for now.
msg = msg_blocktxn()
- if version==2:
+ if version == 2:
msg = msg_witness_blocktxn()
msg.block_transactions = BlockTransactions(block.sha256, [block.vtx[5]] + block.vtx[7:])
test_node.send_and_ping(msg)
@@ -571,11 +569,11 @@ class CompactBlocksTest(BitcoinTestFramework):
# We should receive a getdata request
wait_until(lambda: "getdata" in test_node.last_message, timeout=10, lock=mininode_lock)
assert_equal(len(test_node.last_message["getdata"].inv), 1)
- assert(test_node.last_message["getdata"].inv[0].type == 2 or test_node.last_message["getdata"].inv[0].type == 2|MSG_WITNESS_FLAG)
+ assert(test_node.last_message["getdata"].inv[0].type == 2 or test_node.last_message["getdata"].inv[0].type == 2 | MSG_WITNESS_FLAG)
assert_equal(test_node.last_message["getdata"].inv[0].hash, block.sha256)
# Deliver the block
- if version==2:
+ if version == 2:
test_node.send_and_ping(msg_witness_block(block))
else:
test_node.send_and_ping(msg_block(block))
@@ -655,7 +653,7 @@ class CompactBlocksTest(BitcoinTestFramework):
# Generate an old compactblock, and verify that it's not accepted.
cur_height = node.getblockcount()
- hashPrevBlock = int(node.getblockhash(cur_height-5), 16)
+ hashPrevBlock = int(node.getblockhash(cur_height - 5), 16)
block = self.build_block_on_tip(node)
block.hashPrevBlock = hashPrevBlock
block.solve()
@@ -684,7 +682,7 @@ class CompactBlocksTest(BitcoinTestFramework):
assert "blocktxn" not in test_node.last_message
def activate_segwit(self, node):
- node.generate(144*3)
+ node.generate(144 * 3)
assert_equal(get_bip9_status(node, "segwit")["status"], 'active')
def test_end_to_end_block_relay(self, node, listeners):
@@ -780,7 +778,7 @@ class CompactBlocksTest(BitcoinTestFramework):
delivery_peer.send_message(msg_tx(tx))
delivery_peer.sync_with_ping()
- cmpct_block.prefilled_txn[0].tx.wit.vtxinwit = [ CTxInWitness() ]
+ cmpct_block.prefilled_txn[0].tx.wit.vtxinwit = [CTxInWitness()]
cmpct_block.prefilled_txn[0].tx.wit.vtxinwit[0].scriptWitness.stack = [ser_uint256(0)]
cmpct_block.use_witness = True
@@ -886,7 +884,7 @@ class CompactBlocksTest(BitcoinTestFramework):
self.log.info("Syncing nodes...")
assert(self.nodes[0].getbestblockhash() != self.nodes[1].getbestblockhash())
while (self.nodes[0].getblockcount() > self.nodes[1].getblockcount()):
- block_hash = self.nodes[0].getblockhash(self.nodes[1].getblockcount()+1)
+ block_hash = self.nodes[0].getblockhash(self.nodes[1].getblockcount() + 1)
self.nodes[1].submitblock(self.nodes[0].getblock(block_hash, False))
assert_equal(self.nodes[0].getbestblockhash(), self.nodes[1].getbestblockhash())
diff --git a/test/functional/p2p_leak_tx.py b/test/functional/p2p_leak_tx.py
new file mode 100755
index 0000000000..dc4d475b2d
--- /dev/null
+++ b/test/functional/p2p_leak_tx.py
@@ -0,0 +1,57 @@
+#!/usr/bin/env python3
+# Copyright (c) 2017-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 that we don't leak txs to inbound peers that we haven't yet announced to"""
+
+from test_framework.messages import msg_getdata, CInv
+from test_framework.mininode import P2PDataStore
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+)
+
+
+class P2PNode(P2PDataStore):
+ def on_inv(self, msg):
+ pass
+
+
+class P2PLeakTxTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 1
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+
+ def run_test(self):
+ gen_node = self.nodes[0] # The block and tx generating node
+ gen_node.generate(1)
+
+ inbound_peer = self.nodes[0].add_p2p_connection(P2PNode()) # An "attacking" inbound peer
+
+ MAX_REPEATS = 100
+ self.log.info("Running test up to {} times.".format(MAX_REPEATS))
+ for i in range(MAX_REPEATS):
+ self.log.info('Run repeat {}'.format(i + 1))
+ txid = gen_node.sendtoaddress(gen_node.getnewaddress(), 0.01)
+
+ want_tx = msg_getdata()
+ want_tx.inv.append(CInv(t=1, h=int(txid, 16)))
+ inbound_peer.last_message.pop('notfound', None)
+ inbound_peer.send_message(want_tx)
+ inbound_peer.sync_with_ping()
+
+ if inbound_peer.last_message.get('notfound'):
+ self.log.debug('tx {} was not yet announced to us.'.format(txid))
+ self.log.debug("node has responded with a notfound message. End test.")
+ assert_equal(inbound_peer.last_message['notfound'].vec[0].hash, int(txid, 16))
+ inbound_peer.last_message.pop('notfound')
+ break
+ else:
+ self.log.debug('tx {} was already announced to us. Try test again.'.format(txid))
+ assert int(txid, 16) in [inv.hash for inv in inbound_peer.last_message['inv'].inv]
+
+
+if __name__ == '__main__':
+ P2PLeakTxTest().main()
diff --git a/test/functional/rpc_deprecated.py b/test/functional/rpc_deprecated.py
index 58074803cc..588bfbe083 100755
--- a/test/functional/rpc_deprecated.py
+++ b/test/functional/rpc_deprecated.py
@@ -4,12 +4,17 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test deprecation of RPC calls."""
from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_raises_rpc_error
class DeprecatedRpcTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
self.setup_clean_chain = True
- self.extra_args = [[], ["-deprecatedrpc=validateaddress"]]
+ self.extra_args = [[], ["-deprecatedrpc=generate"]]
+
+ def skip_test_if_missing_module(self):
+ # The generate RPC method requires the wallet to be compiled
+ self.skip_if_no_wallet()
def run_test(self):
# This test should be used to verify correct behaviour of deprecated
@@ -18,7 +23,10 @@ class DeprecatedRpcTest(BitcoinTestFramework):
# self.log.info("Make sure that -deprecatedrpc=createmultisig allows it to take addresses")
# assert_raises_rpc_error(-5, "Invalid public key", self.nodes[0].createmultisig, 1, [self.nodes[0].getnewaddress()])
# self.nodes[1].createmultisig(1, [self.nodes[1].getnewaddress()])
- pass
+
+ self.log.info("Test generate RPC")
+ assert_raises_rpc_error(-32, 'The wallet generate rpc method is deprecated', self.nodes[0].rpc.generate, 1)
+ self.nodes[1].generate(1)
if __name__ == '__main__':
DeprecatedRpcTest().main()
diff --git a/test/functional/rpc_getblockstats.py b/test/functional/rpc_getblockstats.py
index b24bed6adf..ca9e24367a 100755
--- a/test/functional/rpc_getblockstats.py
+++ b/test/functional/rpc_getblockstats.py
@@ -170,6 +170,8 @@ class GetblockstatsTest(BitcoinTestFramework):
self.nodes[0].getblockstats, hash_or_height=1, stats=['minfee' , 'aaa%s' % inv_sel_stat])
assert_raises_rpc_error(-8, 'One or more of the selected stats requires -txindex enabled',
+ self.nodes[1].getblockstats, hash_or_height=1)
+ assert_raises_rpc_error(-8, 'One or more of the selected stats requires -txindex enabled',
self.nodes[1].getblockstats, hash_or_height=self.start_height + self.max_stat_pos)
# Mainchain's genesis block shouldn't be found on regtest
diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py
index 54dc871448..fca910bf64 100755
--- a/test/functional/rpc_psbt.py
+++ b/test/functional/rpc_psbt.py
@@ -6,7 +6,7 @@
"""
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, assert_raises_rpc_error, find_output
+from test_framework.util import assert_equal, assert_raises_rpc_error, find_output, disconnect_nodes, connect_nodes_bi, sync_blocks
import json
import os
@@ -23,6 +23,45 @@ class PSBTTest(BitcoinTestFramework):
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
+ def test_utxo_conversion(self):
+ mining_node = self.nodes[2]
+ offline_node = self.nodes[0]
+ online_node = self.nodes[1]
+
+ # Disconnect offline node from others
+ disconnect_nodes(offline_node, 1)
+ disconnect_nodes(online_node, 0)
+ disconnect_nodes(offline_node, 2)
+ disconnect_nodes(mining_node, 0)
+
+ # Mine a transaction that credits the offline address
+ offline_addr = offline_node.getnewaddress(address_type="p2sh-segwit")
+ online_addr = online_node.getnewaddress(address_type="p2sh-segwit")
+ online_node.importaddress(offline_addr, "", False)
+ mining_node.sendtoaddress(address=offline_addr, amount=1.0)
+ mining_node.generate(nblocks=1)
+ sync_blocks([mining_node, online_node])
+
+ # Construct an unsigned PSBT on the online node (who doesn't know the output is Segwit, so will include a non-witness UTXO)
+ utxos = online_node.listunspent(addresses=[offline_addr])
+ raw = online_node.createrawtransaction([{"txid":utxos[0]["txid"], "vout":utxos[0]["vout"]}],[{online_addr:0.9999}])
+ psbt = online_node.walletprocesspsbt(online_node.converttopsbt(raw))["psbt"]
+ assert("non_witness_utxo" in mining_node.decodepsbt(psbt)["inputs"][0])
+
+ # Have the offline node sign the PSBT (which will update the UTXO to segwit)
+ signed_psbt = offline_node.walletprocesspsbt(psbt)["psbt"]
+ assert("witness_utxo" in mining_node.decodepsbt(signed_psbt)["inputs"][0])
+
+ # Make sure we can mine the resulting transaction
+ txid = mining_node.sendrawtransaction(mining_node.finalizepsbt(signed_psbt)["hex"])
+ mining_node.generate(1)
+ sync_blocks([mining_node, online_node])
+ assert_equal(online_node.gettxout(txid,0)["confirmations"], 1)
+
+ # Reconnect
+ connect_nodes_bi(self.nodes, 0, 1)
+ connect_nodes_bi(self.nodes, 0, 2)
+
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']
@@ -224,6 +263,12 @@ class PSBTTest(BitcoinTestFramework):
extracted = self.nodes[2].finalizepsbt(extractor['extract'], True)['hex']
assert_equal(extracted, extractor['result'])
+ # Unload extra wallets
+ for i, signer in enumerate(signers):
+ self.nodes[2].unloadwallet("wallet{}".format(i))
+
+ self.test_utxo_conversion()
+
if __name__ == '__main__':
PSBTTest().main()
diff --git a/test/functional/rpc_scantxoutset.py b/test/functional/rpc_scantxoutset.py
index 96f9ccdbdb..881b839a4e 100755
--- a/test/functional/rpc_scantxoutset.py
+++ b/test/functional/rpc_scantxoutset.py
@@ -65,6 +65,8 @@ class ScantxoutsetTest(BitcoinTestFramework):
assert_equal(self.nodes[0].scantxoutset("start", [ "addr(" + addr_P2SH_SEGWIT + ")", "addr(" + addr_LEGACY + ")", "combo(" + pubk3 + ")"])['total_amount'], Decimal("0.007"))
self.log.info("Test extended key derivation.")
+ # Run various scans, and verify that the sum of the amounts of the matches corresponds to the expected subset.
+ # Note that all amounts in the UTXO set are powers of 2 multiplied by 0.001 BTC, so each amounts uniquely identifies a subset.
assert_equal(self.nodes[0].scantxoutset("start", [ "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0'/0h/0h)"])['total_amount'], Decimal("0.008"))
assert_equal(self.nodes[0].scantxoutset("start", [ "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0'/0'/1h)"])['total_amount'], Decimal("0.016"))
assert_equal(self.nodes[0].scantxoutset("start", [ "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0h/0'/1500')"])['total_amount'], Decimal("0.032"))
@@ -82,7 +84,7 @@ class ScantxoutsetTest(BitcoinTestFramework):
assert_equal(self.nodes[0].scantxoutset("start", [ "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/1)"])['total_amount'], Decimal("8.192"))
assert_equal(self.nodes[0].scantxoutset("start", [ "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/1500)"])['total_amount'], Decimal("16.384"))
assert_equal(self.nodes[0].scantxoutset("start", [ "combo(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/1/0)"])['total_amount'], Decimal("4.096"))
- assert_equal(self.nodes[0].scantxoutset("start", [ "combo(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/1/1)"])['total_amount'], Decimal("8.192"))
+ assert_equal(self.nodes[0].scantxoutset("start", [ "combo([abcdef88/1/2'/3/4h]tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/1/1)"])['total_amount'], Decimal("8.192"))
assert_equal(self.nodes[0].scantxoutset("start", [ "combo(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/1/1500)"])['total_amount'], Decimal("16.384"))
assert_equal(self.nodes[0].scantxoutset("start", [ {"desc": "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/*')", "range": 1499}])['total_amount'], Decimal("1.536"))
assert_equal(self.nodes[0].scantxoutset("start", [ {"desc": "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/*')", "range": 1500}])['total_amount'], Decimal("3.584"))
diff --git a/test/functional/rpc_zmq.py b/test/functional/rpc_zmq.py
deleted file mode 100755
index bfa6b06f67..0000000000
--- a/test/functional/rpc_zmq.py
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (c) 2018 The Bitcoin Core developers
-# Distributed under the MIT software license, see the accompanying
-# file COPYING or http://www.opensource.org/licenses/mit-license.php.
-"""Test for the ZMQ RPC methods."""
-
-from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal
-
-
-class RPCZMQTest(BitcoinTestFramework):
-
- address = "tcp://127.0.0.1:28332"
-
- def set_test_params(self):
- self.num_nodes = 1
- self.setup_clean_chain = True
-
- def skip_test_if_missing_module(self):
- self.skip_if_no_py3_zmq()
- self.skip_if_no_bitcoind_zmq()
-
- def run_test(self):
- self._test_getzmqnotifications()
-
- def _test_getzmqnotifications(self):
- self.restart_node(0, extra_args=[])
- assert_equal(self.nodes[0].getzmqnotifications(), [])
-
- self.restart_node(0, extra_args=["-zmqpubhashtx=%s" % self.address])
- assert_equal(self.nodes[0].getzmqnotifications(), [
- {"type": "pubhashtx", "address": self.address},
- ])
-
-
-if __name__ == '__main__':
- RPCZMQTest().main()
diff --git a/test/functional/test_framework/address.py b/test/functional/test_framework/address.py
index d1fb97b024..456d43aa2e 100644
--- a/test/functional/test_framework/address.py
+++ b/test/functional/test_framework/address.py
@@ -9,8 +9,11 @@ from .util import bytes_to_hex_str, hex_str_to_bytes
from . import segwit_addr
+ADDRESS_BCRT1_UNSPENDABLE = 'bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3xueyj'
+
chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
+
def byte_to_base58(b, version):
result = ''
str = bytes_to_hex_str(b)
diff --git a/test/functional/test_framework/blocktools.py b/test/functional/test_framework/blocktools.py
index 35004fb588..81cce1167b 100644
--- a/test/functional/test_framework/blocktools.py
+++ b/test/functional/test_framework/blocktools.py
@@ -169,7 +169,7 @@ def get_legacy_sigopcount_tx(tx, accurate=True):
return count
def witness_script(use_p2wsh, pubkey):
- """Create a scriptPubKey for a pay-to-wtiness TxOut.
+ """Create a scriptPubKey for a pay-to-witness TxOut.
This is either a P2WPKH output for the given pubkey, or a P2WSH output of a
1-of-1 multisig for the given pubkey. Returns the hex encoding of the
diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py
index 8e9372767d..92acbb9a09 100755
--- a/test/functional/test_framework/messages.py
+++ b/test/functional/test_framework/messages.py
@@ -1232,6 +1232,23 @@ class msg_mempool:
return "msg_mempool()"
+class msg_notfound:
+ __slots__ = ("vec", )
+ command = b"notfound"
+
+ def __init__(self, vec=None):
+ self.vec = vec or []
+
+ def deserialize(self, f):
+ self.vec = deser_vector(f, CInv)
+
+ def serialize(self):
+ return ser_vector(self.vec)
+
+ def __repr__(self):
+ return "msg_notfound(vec=%s)" % (repr(self.vec))
+
+
class msg_sendheaders:
__slots__ = ()
command = b"sendheaders"
diff --git a/test/functional/test_framework/mininode.py b/test/functional/test_framework/mininode.py
index f8caa57250..91fde136de 100755
--- a/test/functional/test_framework/mininode.py
+++ b/test/functional/test_framework/mininode.py
@@ -21,7 +21,38 @@ import struct
import sys
import threading
-from test_framework.messages import CBlockHeader, MIN_VERSION_SUPPORTED, msg_addr, msg_block, MSG_BLOCK, msg_blocktxn, msg_cmpctblock, msg_feefilter, msg_getaddr, msg_getblocks, msg_getblocktxn, msg_getdata, msg_getheaders, msg_headers, msg_inv, msg_mempool, msg_ping, msg_pong, msg_reject, msg_sendcmpct, msg_sendheaders, msg_tx, MSG_TX, MSG_TYPE_MASK, msg_verack, msg_version, NODE_NETWORK, NODE_WITNESS, sha256
+from test_framework.messages import (
+ CBlockHeader,
+ MIN_VERSION_SUPPORTED,
+ msg_addr,
+ msg_block,
+ MSG_BLOCK,
+ msg_blocktxn,
+ msg_cmpctblock,
+ msg_feefilter,
+ msg_getaddr,
+ msg_getblocks,
+ msg_getblocktxn,
+ msg_getdata,
+ msg_getheaders,
+ msg_headers,
+ msg_inv,
+ msg_mempool,
+ msg_notfound,
+ msg_ping,
+ msg_pong,
+ msg_reject,
+ msg_sendcmpct,
+ msg_sendheaders,
+ msg_tx,
+ MSG_TX,
+ MSG_TYPE_MASK,
+ msg_verack,
+ msg_version,
+ NODE_NETWORK,
+ NODE_WITNESS,
+ sha256,
+)
from test_framework.util import wait_until
logger = logging.getLogger("TestFramework.mininode")
@@ -40,6 +71,7 @@ MESSAGEMAP = {
b"headers": msg_headers,
b"inv": msg_inv,
b"mempool": msg_mempool,
+ b"notfound": msg_notfound,
b"ping": msg_ping,
b"pong": msg_pong,
b"reject": msg_reject,
@@ -295,6 +327,7 @@ class P2PInterface(P2PConnection):
def on_getheaders(self, message): pass
def on_headers(self, message): pass
def on_mempool(self, message): pass
+ def on_notfound(self, message): pass
def on_pong(self, message): pass
def on_reject(self, message): pass
def on_sendcmpct(self, message): pass
@@ -500,9 +533,9 @@ class P2PDataStore(P2PInterface):
wait_until(lambda: blocks[-1].sha256 in self.getdata_requests, timeout=timeout, lock=mininode_lock)
if expect_disconnect:
- self.wait_for_disconnect()
+ self.wait_for_disconnect(timeout=timeout)
else:
- self.sync_with_ping()
+ self.sync_with_ping(timeout=timeout)
if success:
wait_until(lambda: node.getbestblockhash() == blocks[-1].hash, timeout=timeout)
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index 97710bd6cd..7e2ec673df 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -161,8 +161,10 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
success = TestStatus.FAILED
try:
- if self.options.usecli and not self.supports_cli:
- raise SkipTest("--usecli specified but test does not support using CLI")
+ if self.options.usecli:
+ if not self.supports_cli:
+ raise SkipTest("--usecli specified but test does not support using CLI")
+ self.skip_if_no_cli()
self.skip_test_if_missing_module()
self.setup_chain()
self.setup_network()
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index 7ab7fcfcb4..cc1bfabbfa 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -187,7 +187,9 @@ class TestNode():
if e.errno != errno.ECONNREFUSED: # Port not yet open?
raise # unknown IO error
except JSONRPCException as e: # Initialization phase
- if e.error['code'] != -28: # RPC in warmup?
+ # -28 RPC in warmup
+ # -342 Service unavailable, RPC server started but is shutting down due to error
+ if e.error['code'] != -28 and e.error['code'] != -342:
raise # unknown JSON RPC exception
except ValueError as e: # cookie file not found and no rpcuser or rpcassword. bitcoind still starting
if "No RPC credentials" not in str(e):
@@ -195,6 +197,25 @@ class TestNode():
time.sleep(1.0 / poll_per_s)
self._raise_assertion_error("Unable to connect to bitcoind")
+ def generate(self, nblocks, maxtries=1000000):
+ self.log.debug("TestNode.generate() dispatches `generate` call to `generatetoaddress`")
+ # Try to import the node's deterministic private key. This is a no-op if the private key
+ # has already been imported.
+ try:
+ self.rpc.importprivkey(privkey=self.get_deterministic_priv_key().key, label='coinbase', rescan=False)
+ except JSONRPCException as e:
+ # This may fail if:
+ # - wallet is disabled ('Method not found')
+ # - there are multiple wallets to import to ('Wallet file not specified')
+ # - wallet is locked ('Error: Please enter the wallet passphrase with walletpassphrase first')
+ # Just ignore those errors. We can make this tidier by importing the privkey during TestFramework.setup_nodes
+ # TODO: tidy up deterministic privkey import.
+ assert str(e).startswith('Method not found') or \
+ str(e).startswith('Wallet file not specified') or \
+ str(e).startswith('Error: Please enter the wallet passphrase with walletpassphrase first')
+
+ return self.generatetoaddress(nblocks=nblocks, address=self.get_deterministic_priv_key().address, maxtries=maxtries)
+
def get_wallet_rpc(self, wallet_name):
if self.use_cli:
return self.cli("-rpcwallet={}".format(wallet_name))
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index d9960460d9..85a5de6976 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -152,7 +152,7 @@ BASE_SCRIPTS = [
'feature_versionbits_warning.py',
'rpc_preciousblock.py',
'wallet_importprunedfunds.py',
- 'rpc_zmq.py',
+ 'p2p_leak_tx.py',
'rpc_signmessage.py',
'feature_nulldummy.py',
'mempool_accept.py',
@@ -175,6 +175,7 @@ BASE_SCRIPTS = [
'rpc_getblockstats.py',
'p2p_fingerprint.py',
'feature_uacomment.py',
+ 'feature_filelock.py',
'p2p_unrequested_blocks.py',
'feature_includeconf.py',
'rpc_scantxoutset.py',
@@ -221,7 +222,7 @@ def main():
parser.add_argument('--help', '-h', '-?', action='store_true', help='print help text and exit')
parser.add_argument('--jobs', '-j', type=int, default=4, help='how many test scripts to run in parallel. Default=4.')
parser.add_argument('--keepcache', '-k', action='store_true', help='the default behavior is to flush the cache directory on startup. --keepcache retains the cache from the previous testrun.')
- parser.add_argument('--quiet', '-q', action='store_true', help='only print results summary and failure logs')
+ parser.add_argument('--quiet', '-q', action='store_true', help='only print dots, results summary and failure logs')
parser.add_argument('--tmpdirprefix', '-t', default=tempfile.gettempdir(), help="Root directory for datadirs")
parser.add_argument('--failfast', action='store_true', help='stop execution after the first test failure')
args, unknown_args = parser.parse_known_args()
@@ -320,7 +321,7 @@ def main():
enable_coverage=args.coverage,
args=passon_args,
combined_logs_len=args.combinedlogslen,
- failfast=args.failfast,
+ failfast=args.failfast
)
def run_tests(test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage=False, args=None, combined_logs_len=0, failfast=False):
@@ -363,17 +364,17 @@ def run_tests(test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage=Fal
test_results = []
max_len_name = len(max(test_list, key=len))
-
- for _ in range(len(test_list)):
+ test_count = len(test_list)
+ for i in range(test_count):
test_result, testdir, stdout, stderr = job_queue.get_next()
test_results.append(test_result)
-
+ done_str = "{}/{} - {}{}{}".format(i + 1, test_count, BOLD[1], test_result.name, BOLD[0])
if test_result.status == "Passed":
- logging.debug("\n%s%s%s passed, Duration: %s s" % (BOLD[1], test_result.name, BOLD[0], test_result.time))
+ logging.debug("%s passed, Duration: %s s" % (done_str, test_result.time))
elif test_result.status == "Skipped":
- logging.debug("\n%s%s%s skipped" % (BOLD[1], test_result.name, BOLD[0]))
+ logging.debug("%s skipped" % (done_str))
else:
- print("\n%s%s%s failed, Duration: %s s\n" % (BOLD[1], test_result.name, BOLD[0], test_result.time))
+ print("%s failed, Duration: %s s\n" % (done_str, test_result.time))
print(BOLD[1] + 'stdout:\n' + BOLD[0] + stdout + '\n')
print(BOLD[1] + 'stderr:\n' + BOLD[0] + stderr + '\n')
if combined_logs_len and os.path.isdir(testdir):
@@ -472,6 +473,7 @@ class TestHandler:
log_stderr))
if not self.jobs:
raise IndexError('pop from empty list')
+ dot_count = 0
while True:
# Return first proc that finishes
time.sleep(.5)
@@ -492,9 +494,12 @@ class TestHandler:
status = "Failed"
self.num_running -= 1
self.jobs.remove(job)
-
+ clearline = '\r' + (' ' * dot_count) + '\r'
+ print(clearline, end='', flush=True)
+ dot_count = 0
return TestResult(name, status, int(time.time() - start_time)), testdir, stdout, stderr
print('.', end='', flush=True)
+ dot_count += 1
def kill_and_join(self):
"""Send SIGKILL to all jobs and block until all have ended."""
diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py
index 8bbff7f7ef..4de3356d79 100755
--- a/test/functional/wallet_basic.py
+++ b/test/functional/wallet_basic.py
@@ -11,6 +11,7 @@ from test_framework.util import (
assert_array_result,
assert_equal,
assert_fee_amount,
+ assert_greater_than,
assert_raises_rpc_error,
connect_nodes_bi,
sync_blocks,
@@ -92,13 +93,13 @@ class WalletTest(BitcoinTestFramework):
assert_equal(txout['value'], 50)
# Send 21 BTC from 0 to 2 using sendtoaddress call.
- # Locked memory should use at least 32 bytes to sign each transaction
+ # Locked memory should increase to sign transactions
self.log.info("test getmemoryinfo")
memory_before = self.nodes[0].getmemoryinfo()
self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 11)
mempool_txid = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 10)
memory_after = self.nodes[0].getmemoryinfo()
- assert(memory_before['locked']['used'] + 64 <= memory_after['locked']['used'])
+ assert_greater_than(memory_after['locked']['used'], memory_before['locked']['used'])
self.log.info("test gettxout (second part)")
# utxo spent in mempool should be visible if you exclude mempool
diff --git a/test/functional/wallet_bumpfee.py b/test/functional/wallet_bumpfee.py
index 67ee00871d..7d3d9b61e2 100755
--- a/test/functional/wallet_bumpfee.py
+++ b/test/functional/wallet_bumpfee.py
@@ -13,26 +13,22 @@ can be disabled or reordered if needed for debugging. If new test cases are
added in the future, they should try to follow the same convention and not
make assumptions about execution order.
"""
-
from decimal import Decimal
+import io
from test_framework.blocktools import add_witness_commitment, create_block, create_coinbase, send_to_witness
from test_framework.messages import BIP125_SEQUENCE_NUMBER, CTransaction
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, assert_greater_than, assert_raises_rpc_error, bytes_to_hex_str, connect_nodes_bi, hex_str_to_bytes, sync_mempools
-import io
-
WALLET_PASSPHRASE = "test"
WALLET_PASSPHRASE_TIMEOUT = 3600
-
class BumpFeeTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
self.setup_clean_chain = True
self.extra_args = [[
- "-deprecatedrpc=addwitnessaddress",
"-walletrbf={}".format(i),
"-mintxfee=0.00002",
] for i in range(self.num_nodes)]
@@ -107,8 +103,7 @@ def test_segwit_bumpfee_succeeds(rbf_node, dest_address):
# which spends it, and make sure bumpfee can be called on it.
segwit_in = next(u for u in rbf_node.listunspent() if u["amount"] == Decimal("0.001"))
- segwit_out = rbf_node.getaddressinfo(rbf_node.getnewaddress())
- rbf_node.addwitnessaddress(segwit_out["address"])
+ segwit_out = rbf_node.getaddressinfo(rbf_node.getnewaddress(address_type='p2sh-segwit'))
segwitid = send_to_witness(
use_p2wsh=False,
node=rbf_node,
@@ -157,7 +152,7 @@ def test_notmine_bumpfee_fails(rbf_node, peer_node, dest_address):
signedtx = peer_node.signrawtransactionwithwallet(signedtx["hex"])
rbfid = rbf_node.sendrawtransaction(signedtx["hex"])
assert_raises_rpc_error(-4, "Transaction contains inputs that don't belong to this wallet",
- rbf_node.bumpfee, rbfid)
+ rbf_node.bumpfee, rbfid)
def test_bumpfee_with_descendant_fails(rbf_node, rbf_node_address, dest_address):
@@ -187,11 +182,11 @@ def test_dust_to_fee(rbf_node, dest_address):
# (32-byte p2sh-pwpkh output size + 148 p2pkh spend estimate) * 10k(discard_rate) / 1000 = 1800
# P2SH outputs are slightly "over-discarding" due to the IsDust calculation assuming it will
# be spent as a P2PKH.
- bumped_tx = rbf_node.bumpfee(rbfid, {"totalFee": 50000-1800})
+ bumped_tx = rbf_node.bumpfee(rbfid, {"totalFee": 50000 - 1800})
full_bumped_tx = rbf_node.getrawtransaction(bumped_tx["txid"], 1)
assert_equal(bumped_tx["fee"], Decimal("0.00050000"))
assert_equal(len(fulltx["vout"]), 2)
- assert_equal(len(full_bumped_tx["vout"]), 1) #change output is eliminated
+ assert_equal(len(full_bumped_tx["vout"]), 1) # change output is eliminated
def test_settxfee(rbf_node, dest_address):
@@ -222,7 +217,7 @@ def test_rebumping_not_replaceable(rbf_node, dest_address):
rbfid = spend_one_input(rbf_node, dest_address)
bumped = rbf_node.bumpfee(rbfid, {"totalFee": 10000, "replaceable": False})
assert_raises_rpc_error(-4, "Transaction is not BIP 125 replaceable", rbf_node.bumpfee, bumped["txid"],
- {"totalFee": 20000})
+ {"totalFee": 20000})
def test_unconfirmed_not_spendable(rbf_node, rbf_node_address):
@@ -276,7 +271,7 @@ def test_locked_wallet_fails(rbf_node, dest_address):
rbfid = spend_one_input(rbf_node, dest_address)
rbf_node.walletlock()
assert_raises_rpc_error(-13, "Please enter the wallet passphrase with walletpassphrase first.",
- rbf_node.bumpfee, rbfid)
+ rbf_node.bumpfee, rbfid)
def spend_one_input(node, dest_address):
diff --git a/test/functional/wallet_dump.py b/test/functional/wallet_dump.py
index b1db1e4ab9..20cb816ee8 100755
--- a/test/functional/wallet_dump.py
+++ b/test/functional/wallet_dump.py
@@ -3,7 +3,6 @@
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test the dumpwallet RPC."""
-
import os
from test_framework.test_framework import BitcoinTestFramework
@@ -19,11 +18,12 @@ def read_dump(file_name, addrs, script_addrs, hd_master_addr_old):
Also check that the old hd_master is inactive
"""
with open(file_name, encoding='utf8') as inputfile:
- found_addr = 0
+ found_legacy_addr = 0
+ found_p2sh_segwit_addr = 0
+ found_bech32_addr = 0
found_script_addr = 0
found_addr_chg = 0
found_addr_rsv = 0
- witness_addr_ret = None
hd_master_addr_ret = None
for line in inputfile:
# only read non comment lines
@@ -60,14 +60,14 @@ def read_dump(file_name, addrs, script_addrs, hd_master_addr_old):
# count key types
for addrObj in addrs:
if addrObj['address'] == addr.split(",")[0] and addrObj['hdkeypath'] == keypath and keytype == "label=":
- # a labeled entry in the wallet should contain both a native address
- # and the p2sh-p2wpkh address that was added at wallet setup
- if len(addr.split(",")) == 2:
- addr_list = addr.split(",")
- # the entry should be of the first key in the wallet
- assert_equal(addrs[0]['address'], addr_list[0])
- witness_addr_ret = addr_list[1]
- found_addr += 1
+ if addr.startswith('m') or addr.startswith('n'):
+ # P2PKH address
+ found_legacy_addr += 1
+ elif addr.startswith('2'):
+ # P2SH-segwit address
+ found_p2sh_segwit_addr += 1
+ elif addr.startswith('bcrt1'):
+ found_bech32_addr += 1
break
elif keytype == "change=1":
found_addr_chg += 1
@@ -82,13 +82,13 @@ def read_dump(file_name, addrs, script_addrs, hd_master_addr_old):
found_script_addr += 1
break
- return found_addr, found_script_addr, found_addr_chg, found_addr_rsv, hd_master_addr_ret, witness_addr_ret
+ return found_legacy_addr, found_p2sh_segwit_addr, found_bech32_addr, found_script_addr, found_addr_chg, found_addr_rsv, hd_master_addr_ret
class WalletDumpTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
- self.extra_args = [["-keypool=90", "-addresstype=legacy", "-deprecatedrpc=addwitnessaddress"]]
+ self.extra_args = [["-keypool=90", "-addresstype=legacy"]]
self.rpc_timeout = 120
def skip_test_if_missing_module(self):
@@ -102,49 +102,54 @@ class WalletDumpTest(BitcoinTestFramework):
wallet_unenc_dump = os.path.join(self.nodes[0].datadir, "wallet.unencrypted.dump")
wallet_enc_dump = os.path.join(self.nodes[0].datadir, "wallet.encrypted.dump")
- # generate 20 addresses to compare against the dump
- # but since we add a p2sh-p2wpkh address for the first pubkey in the
- # wallet, we will expect 21 addresses in the dump
- test_addr_count = 20
+ # generate 30 addresses to compare against the dump
+ # - 10 legacy P2PKH
+ # - 10 P2SH-segwit
+ # - 10 bech32
+ test_addr_count = 10
addrs = []
- for i in range(0,test_addr_count):
- addr = self.nodes[0].getnewaddress()
- vaddr= self.nodes[0].getaddressinfo(addr) #required to get hd keypath
- addrs.append(vaddr)
- # Should be a no-op:
- self.nodes[0].keypoolrefill()
+ for address_type in ['legacy', 'p2sh-segwit', 'bech32']:
+ for i in range(0, test_addr_count):
+ addr = self.nodes[0].getnewaddress(address_type=address_type)
+ vaddr = self.nodes[0].getaddressinfo(addr) # required to get hd keypath
+ addrs.append(vaddr)
- # Test scripts dump by adding a P2SH witness and a 1-of-1 multisig address
- witness_addr = self.nodes[0].addwitnessaddress(addrs[0]["address"], True)
+ # Test scripts dump by adding a 1-of-1 multisig address
multisig_addr = self.nodes[0].addmultisigaddress(1, [addrs[1]["address"]])["address"]
- script_addrs = [witness_addr, multisig_addr]
+
+ # Refill the keypool. getnewaddress() refills the keypool *before* taking a key from
+ # the keypool, so the final call to getnewaddress leaves the keypool with one key below
+ # its capacity
+ self.nodes[0].keypoolrefill()
# dump unencrypted wallet
result = self.nodes[0].dumpwallet(wallet_unenc_dump)
assert_equal(result['filename'], wallet_unenc_dump)
- found_addr, found_script_addr, found_addr_chg, found_addr_rsv, hd_master_addr_unenc, witness_addr_ret = \
- read_dump(wallet_unenc_dump, addrs, script_addrs, None)
- assert_equal(found_addr, test_addr_count) # all keys must be in the dump
- assert_equal(found_script_addr, 2) # all scripts must be in the dump
+ found_legacy_addr, found_p2sh_segwit_addr, found_bech32_addr, found_script_addr, found_addr_chg, found_addr_rsv, hd_master_addr_unenc = \
+ read_dump(wallet_unenc_dump, addrs, [multisig_addr], None)
+ assert_equal(found_legacy_addr, test_addr_count) # all keys must be in the dump
+ assert_equal(found_p2sh_segwit_addr, test_addr_count) # all keys must be in the dump
+ assert_equal(found_bech32_addr, test_addr_count) # all keys must be in the dump
+ assert_equal(found_script_addr, 1) # all scripts must be in the dump
assert_equal(found_addr_chg, 0) # 0 blocks where mined
assert_equal(found_addr_rsv, 90 * 2) # 90 keys plus 100% internal keys
- assert_equal(witness_addr_ret, witness_addr) # p2sh-p2wsh address added to the first key
- #encrypt wallet, restart, unlock and dump
+ # encrypt wallet, restart, unlock and dump
self.nodes[0].encryptwallet('test')
self.nodes[0].walletpassphrase('test', 10)
# Should be a no-op:
self.nodes[0].keypoolrefill()
self.nodes[0].dumpwallet(wallet_enc_dump)
- found_addr, found_script_addr, found_addr_chg, found_addr_rsv, _, witness_addr_ret = \
- read_dump(wallet_enc_dump, addrs, script_addrs, hd_master_addr_unenc)
- assert_equal(found_addr, test_addr_count)
- assert_equal(found_script_addr, 2)
+ found_legacy_addr, found_p2sh_segwit_addr, found_bech32_addr, found_script_addr, found_addr_chg, found_addr_rsv, _ = \
+ read_dump(wallet_enc_dump, addrs, [multisig_addr], hd_master_addr_unenc)
+ assert_equal(found_legacy_addr, test_addr_count) # all keys must be in the dump
+ assert_equal(found_p2sh_segwit_addr, test_addr_count) # all keys must be in the dump
+ assert_equal(found_bech32_addr, test_addr_count) # all keys must be in the dump
+ assert_equal(found_script_addr, 1)
assert_equal(found_addr_chg, 90 * 2) # old reserve keys are marked as change now
assert_equal(found_addr_rsv, 90 * 2)
- assert_equal(witness_addr_ret, witness_addr)
# Overwriting should fail
assert_raises_rpc_error(-8, "already exists", lambda: self.nodes[0].dumpwallet(wallet_enc_dump))
@@ -155,13 +160,13 @@ class WalletDumpTest(BitcoinTestFramework):
# Make sure the address is not IsMine before import
result = self.nodes[0].getaddressinfo(multisig_addr)
- assert(result['ismine'] == False)
+ assert not result['ismine']
self.nodes[0].importwallet(wallet_unenc_dump)
# Now check IsMine is true
result = self.nodes[0].getaddressinfo(multisig_addr)
- assert(result['ismine'] == True)
+ assert result['ismine']
if __name__ == '__main__':
WalletDumpTest().main()
diff --git a/test/functional/wallet_importmulti.py b/test/functional/wallet_importmulti.py
index f78ff19791..9ba6860306 100755
--- a/test/functional/wallet_importmulti.py
+++ b/test/functional/wallet_importmulti.py
@@ -11,8 +11,14 @@ from test_framework.util import (
assert_greater_than,
assert_raises_rpc_error,
bytes_to_hex_str,
+ hex_str_to_bytes
)
-
+from test_framework.script import (
+ CScript,
+ OP_0,
+ hash160
+)
+from test_framework.messages import sha256
class ImportMultiTest(BitcoinTestFramework):
def set_test_params(self):
@@ -90,6 +96,19 @@ class ImportMultiTest(BitcoinTestFramework):
assert_equal(address_assert['ismine'], False)
assert_equal(address_assert['timestamp'], timestamp)
+ # ScriptPubKey + internal + label
+ self.log.info("Should not allow a label to be specified when internal is true")
+ address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
+ result = self.nodes[1].importmulti([{
+ "scriptPubKey": address['scriptPubKey'],
+ "timestamp": "now",
+ "internal": True,
+ "label": "Example label"
+ }])
+ assert_equal(result[0]['success'], False)
+ assert_equal(result[0]['error']['code'], -8)
+ assert_equal(result[0]['error']['message'], 'Internal addresses should not have a label')
+
# Nonstandard scriptPubKey + !internal
self.log.info("Should not import a nonstandard scriptPubKey without internal flag")
nonstandardScriptPubKey = address['scriptPubKey'] + bytes_to_hex_str(script.CScript([script.OP_NOP]))
@@ -198,7 +217,7 @@ class ImportMultiTest(BitcoinTestFramework):
}])
assert_equal(result[0]['success'], False)
assert_equal(result[0]['error']['code'], -8)
- assert_equal(result[0]['error']['message'], 'Incompatibility found between watchonly and keys')
+ assert_equal(result[0]['error']['message'], 'Watch-only addresses should not include private keys')
address_assert = self.nodes[1].getaddressinfo(address['address'])
assert_equal(address_assert['iswatchonly'], False)
assert_equal(address_assert['ismine'], False)
@@ -339,7 +358,7 @@ class ImportMultiTest(BitcoinTestFramework):
}])
assert_equal(result[0]['success'], False)
assert_equal(result[0]['error']['code'], -8)
- assert_equal(result[0]['error']['message'], 'Incompatibility found between watchonly and keys')
+ assert_equal(result[0]['error']['message'], 'Watch-only addresses should not include private keys')
# Address + Public key + !Internal + Wrong pubkey
@@ -355,7 +374,7 @@ class ImportMultiTest(BitcoinTestFramework):
}])
assert_equal(result[0]['success'], False)
assert_equal(result[0]['error']['code'], -5)
- assert_equal(result[0]['error']['message'], 'Consistency check failed')
+ assert_equal(result[0]['error']['message'], 'Key does not match address destination')
address_assert = self.nodes[1].getaddressinfo(address['address'])
assert_equal(address_assert['iswatchonly'], False)
assert_equal(address_assert['ismine'], False)
@@ -375,7 +394,7 @@ class ImportMultiTest(BitcoinTestFramework):
result = self.nodes[1].importmulti(request)
assert_equal(result[0]['success'], False)
assert_equal(result[0]['error']['code'], -5)
- assert_equal(result[0]['error']['message'], 'Consistency check failed')
+ assert_equal(result[0]['error']['message'], 'Key does not match address destination')
address_assert = self.nodes[1].getaddressinfo(address['address'])
assert_equal(address_assert['iswatchonly'], False)
assert_equal(address_assert['ismine'], False)
@@ -395,7 +414,7 @@ class ImportMultiTest(BitcoinTestFramework):
}])
assert_equal(result[0]['success'], False)
assert_equal(result[0]['error']['code'], -5)
- assert_equal(result[0]['error']['message'], 'Consistency check failed')
+ assert_equal(result[0]['error']['message'], 'Key does not match address destination')
address_assert = self.nodes[1].getaddressinfo(address['address'])
assert_equal(address_assert['iswatchonly'], False)
assert_equal(address_assert['ismine'], False)
@@ -414,7 +433,7 @@ class ImportMultiTest(BitcoinTestFramework):
}])
assert_equal(result[0]['success'], False)
assert_equal(result[0]['error']['code'], -5)
- assert_equal(result[0]['error']['message'], 'Consistency check failed')
+ assert_equal(result[0]['error']['message'], 'Key does not match address destination')
address_assert = self.nodes[1].getaddressinfo(address['address'])
assert_equal(address_assert['iswatchonly'], False)
assert_equal(address_assert['ismine'], False)
@@ -458,6 +477,147 @@ class ImportMultiTest(BitcoinTestFramework):
"timestamp": "",
}])
+ # Import P2WPKH address as watch only
+ self.log.info("Should import a P2WPKH address as watch only")
+ address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress(address_type="bech32"))
+ result = self.nodes[1].importmulti([{
+ "scriptPubKey": {
+ "address": address['address']
+ },
+ "timestamp": "now",
+ }])
+ assert_equal(result[0]['success'], True)
+ address_assert = self.nodes[1].getaddressinfo(address['address'])
+ assert_equal(address_assert['iswatchonly'], True)
+ assert_equal(address_assert['solvable'], False)
+
+ # Import P2WPKH address with public key but no private key
+ self.log.info("Should import a P2WPKH address and public key as solvable but not spendable")
+ address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress(address_type="bech32"))
+ result = self.nodes[1].importmulti([{
+ "scriptPubKey": {
+ "address": address['address']
+ },
+ "timestamp": "now",
+ "pubkeys": [ address['pubkey'] ]
+ }])
+ assert_equal(result[0]['success'], True)
+ address_assert = self.nodes[1].getaddressinfo(address['address'])
+ assert_equal(address_assert['ismine'], False)
+ assert_equal(address_assert['solvable'], True)
+
+ # Import P2WPKH address with key and check it is spendable
+ self.log.info("Should import a P2WPKH address with key")
+ address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress(address_type="bech32"))
+ result = self.nodes[1].importmulti([{
+ "scriptPubKey": {
+ "address": address['address']
+ },
+ "timestamp": "now",
+ "keys": [self.nodes[0].dumpprivkey(address['address'])]
+ }])
+ assert_equal(result[0]['success'], True)
+ address_assert = self.nodes[1].getaddressinfo(address['address'])
+ assert_equal(address_assert['iswatchonly'], False)
+ assert_equal(address_assert['ismine'], True)
+
+ # P2WSH multisig address without scripts or keys
+ sig_address_1 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
+ sig_address_2 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
+ multi_sig_script = self.nodes[0].addmultisigaddress(2, [sig_address_1['pubkey'], sig_address_2['pubkey']], "", "bech32")
+ self.log.info("Should import a p2wsh multisig as watch only without respective redeem script and private keys")
+ result = self.nodes[1].importmulti([{
+ "scriptPubKey": {
+ "address": multi_sig_script['address']
+ },
+ "timestamp": "now"
+ }])
+ assert_equal(result[0]['success'], True)
+ address_assert = self.nodes[1].getaddressinfo(multi_sig_script['address'])
+ assert_equal(address_assert['solvable'], False)
+
+ # Same P2WSH multisig address as above, but now with witnessscript + private keys
+ self.log.info("Should import a p2wsh with respective redeem script and private keys")
+ result = self.nodes[1].importmulti([{
+ "scriptPubKey": {
+ "address": multi_sig_script['address']
+ },
+ "timestamp": "now",
+ "witnessscript": multi_sig_script['redeemScript'],
+ "keys": [ self.nodes[0].dumpprivkey(sig_address_1['address']), self.nodes[0].dumpprivkey(sig_address_2['address']) ]
+ }])
+ assert_equal(result[0]['success'], True)
+ address_assert = self.nodes[1].getaddressinfo(multi_sig_script['address'])
+ assert_equal(address_assert['solvable'], True)
+ assert_equal(address_assert['ismine'], True)
+ assert_equal(address_assert['sigsrequired'], 2)
+
+ # P2SH-P2WPKH address with no redeemscript or public or private key
+ sig_address_1 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress(address_type="p2sh-segwit"))
+ pubkeyhash = hash160(hex_str_to_bytes(sig_address_1['pubkey']))
+ pkscript = CScript([OP_0, pubkeyhash])
+ self.log.info("Should import a p2sh-p2wpkh without redeem script or keys")
+ result = self.nodes[1].importmulti([{
+ "scriptPubKey": {
+ "address": sig_address_1['address']
+ },
+ "timestamp": "now"
+ }])
+ assert_equal(result[0]['success'], True)
+ address_assert = self.nodes[1].getaddressinfo(sig_address_1['address'])
+ assert_equal(address_assert['solvable'], False)
+ assert_equal(address_assert['ismine'], False)
+
+ # P2SH-P2WPKH address + redeemscript + public key with no private key
+ self.log.info("Should import a p2sh-p2wpkh with respective redeem script and pubkey as solvable")
+ result = self.nodes[1].importmulti([{
+ "scriptPubKey": {
+ "address": sig_address_1['address']
+ },
+ "timestamp": "now",
+ "redeemscript": bytes_to_hex_str(pkscript),
+ "pubkeys": [ sig_address_1['pubkey'] ]
+ }])
+ assert_equal(result[0]['success'], True)
+ address_assert = self.nodes[1].getaddressinfo(sig_address_1['address'])
+ assert_equal(address_assert['solvable'], True)
+ assert_equal(address_assert['ismine'], False)
+
+ # P2SH-P2WPKH address + redeemscript + private key
+ sig_address_1 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress(address_type="p2sh-segwit"))
+ pubkeyhash = hash160(hex_str_to_bytes(sig_address_1['pubkey']))
+ pkscript = CScript([OP_0, pubkeyhash])
+ self.log.info("Should import a p2sh-p2wpkh with respective redeem script and private keys")
+ result = self.nodes[1].importmulti([{
+ "scriptPubKey": {
+ "address": sig_address_1['address']
+ },
+ "timestamp": "now",
+ "redeemscript": bytes_to_hex_str(pkscript),
+ "keys": [ self.nodes[0].dumpprivkey(sig_address_1['address'])]
+ }])
+ assert_equal(result[0]['success'], True)
+ address_assert = self.nodes[1].getaddressinfo(sig_address_1['address'])
+ assert_equal(address_assert['solvable'], True)
+ assert_equal(address_assert['ismine'], True)
+
+ # P2SH-P2WSH 1-of-1 multisig + redeemscript with no private key
+ sig_address_1 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
+ multi_sig_script = self.nodes[0].addmultisigaddress(1, [sig_address_1['pubkey']], "", "p2sh-segwit")
+ scripthash = sha256(hex_str_to_bytes(multi_sig_script['redeemScript']))
+ redeem_script = CScript([OP_0, scripthash])
+ self.log.info("Should import a p2sh-p2wsh with respective redeem script but no private key")
+ result = self.nodes[1].importmulti([{
+ "scriptPubKey": {
+ "address": multi_sig_script['address']
+ },
+ "timestamp": "now",
+ "redeemscript": bytes_to_hex_str(redeem_script),
+ "witnessscript": multi_sig_script['redeemScript']
+ }])
+ assert_equal(result[0]['success'], True)
+ address_assert = self.nodes[1].getaddressinfo(multi_sig_script['address'])
+ assert_equal(address_assert['solvable'], True)
if __name__ == '__main__':
ImportMultiTest ().main ()
diff --git a/test/functional/wallet_keypool.py b/test/functional/wallet_keypool.py
index 51afa0cb1a..ceb9709712 100755
--- a/test/functional/wallet_keypool.py
+++ b/test/functional/wallet_keypool.py
@@ -73,11 +73,10 @@ class KeyPoolTest(BitcoinTestFramework):
time.sleep(1.1)
assert_equal(nodes[0].getwalletinfo()["unlocked_until"], 0)
- # drain them by mining
- nodes[0].generate(1)
- nodes[0].generate(1)
- nodes[0].generate(1)
- assert_raises_rpc_error(-12, "Keypool ran out", nodes[0].generate, 1)
+ # drain the keypool
+ for _ in range(3):
+ nodes[0].getnewaddress()
+ assert_raises_rpc_error(-12, "Keypool ran out", nodes[0].getnewaddress)
nodes[0].walletpassphrase('test', 100)
nodes[0].keypoolrefill(100)
diff --git a/test/functional/wallet_labels.py b/test/functional/wallet_labels.py
index 8d7c77bb96..b71dae9f40 100755
--- a/test/functional/wallet_labels.py
+++ b/test/functional/wallet_labels.py
@@ -29,8 +29,8 @@ class WalletLabelsTest(BitcoinTestFramework):
# Note each time we call generate, all generated coins go into
# the same address, so we call twice to get two addresses w/50 each
- node.generate(1)
- node.generate(101)
+ node.generatetoaddress(nblocks=1, address=node.getnewaddress(label='coinbase'))
+ node.generatetoaddress(nblocks=101, address=node.getnewaddress(label='coinbase'))
assert_equal(node.getbalance(), 100)
# there should be 2 address groups
@@ -42,8 +42,9 @@ class WalletLabelsTest(BitcoinTestFramework):
linked_addresses = set()
for address_group in address_groups:
assert_equal(len(address_group), 1)
- assert_equal(len(address_group[0]), 2)
+ assert_equal(len(address_group[0]), 3)
assert_equal(address_group[0][1], 50)
+ assert_equal(address_group[0][2], 'coinbase')
linked_addresses.add(address_group[0][0])
# send 50 from each address to a third address not in this wallet
@@ -77,7 +78,7 @@ class WalletLabelsTest(BitcoinTestFramework):
label.verify(node)
# Check all labels are returned by listlabels.
- assert_equal(node.listlabels(), [label.name for label in labels])
+ assert_equal(node.listlabels(), sorted(['coinbase'] + [label.name for label in labels]))
# Send a transaction to each label.
for label in labels:
diff --git a/test/functional/wallet_listreceivedby.py b/test/functional/wallet_listreceivedby.py
index 3485c4470f..9e8667c600 100755
--- a/test/functional/wallet_listreceivedby.py
+++ b/test/functional/wallet_listreceivedby.py
@@ -68,6 +68,10 @@ class ReceivedByTest(BitcoinTestFramework):
res = self.nodes[1].listreceivedbyaddress(minconf=0, include_empty=True, include_watchonly=True, address_filter=addr)
assert_array_result(res, {"address": addr}, expected)
assert_equal(len(res), 1)
+ # Test for regression on CLI calls with address string (#14173)
+ cli_res = self.nodes[1].cli.listreceivedbyaddress(0, True, True, addr)
+ assert_array_result(cli_res, {"address": addr}, expected)
+ assert_equal(len(cli_res), 1)
# Error on invalid address
assert_raises_rpc_error(-4, "address_filter parameter was invalid", self.nodes[1].listreceivedbyaddress, minconf=0, include_empty=True, include_watchonly=True, address_filter="bamboozling")
# Another address receive money
diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py
index 189bc2d50e..4f663c82c7 100755
--- a/test/functional/wallet_multiwallet.py
+++ b/test/functional/wallet_multiwallet.py
@@ -8,6 +8,7 @@ Verify that a bitcoind node can load multiple wallet files
"""
import os
import shutil
+import time
from test_framework.test_framework import BitcoinTestFramework
from test_framework.test_node import ErrorMatch
@@ -38,15 +39,16 @@ class MultiWalletTest(BitcoinTestFramework):
return wallet_dir(name, "wallet.dat")
return wallet_dir(name)
+ assert_equal(self.nodes[0].listwalletdir(), { 'wallets': [{ 'name': '' }] })
+
# check wallet.dat is created
self.stop_nodes()
assert_equal(os.path.isfile(wallet_dir('wallet.dat')), True)
# create symlink to verify wallet directory path can be referenced
# through symlink
- if os.name != 'nt':
- os.mkdir(wallet_dir('w7'))
- os.symlink('w7', wallet_dir('w7_symlink'))
+ os.mkdir(wallet_dir('w7'))
+ os.symlink('w7', wallet_dir('w7_symlink'))
# rename wallet.dat to make sure plain wallet file paths (as opposed to
# directory paths) can be loaded
@@ -67,10 +69,10 @@ class MultiWalletTest(BitcoinTestFramework):
# w8 - to verify existing wallet file is loaded correctly
# '' - to verify default wallet file is created correctly
wallet_names = ['w1', 'w2', 'w3', 'w', 'sub/w5', os.path.join(self.options.tmpdir, 'extern/w6'), 'w7_symlink', 'w8', '']
- if os.name == 'nt':
- wallet_names.remove('w7_symlink')
extra_args = ['-wallet={}'.format(n) for n in wallet_names]
self.start_node(0, extra_args)
+ assert_equal(sorted(map(lambda w: w['name'], self.nodes[0].listwalletdir()['wallets'])), ['', os.path.join('sub', 'w5'), 'w', 'w1', 'w2', 'w3', 'w7', 'w7_symlink', 'w8'])
+
assert_equal(set(node.listwallets()), set(wallet_names))
# check that all requested wallets were created
@@ -95,9 +97,8 @@ class MultiWalletTest(BitcoinTestFramework):
self.nodes[0].assert_start_raises_init_error(['-wallet=w8', '-wallet=w8_copy'], exp_stderr, match=ErrorMatch.PARTIAL_REGEX)
# should not initialize if wallet file is a symlink
- if os.name != 'nt':
- os.symlink('w8', wallet_dir('w8_symlink'))
- self.nodes[0].assert_start_raises_init_error(['-wallet=w8_symlink'], 'Error: Invalid -wallet path \'w8_symlink\'\. .*', match=ErrorMatch.FULL_REGEX)
+ os.symlink('w8', wallet_dir('w8_symlink'))
+ self.nodes[0].assert_start_raises_init_error(['-wallet=w8_symlink'], 'Error: Invalid -wallet path \'w8_symlink\'\. .*', match=ErrorMatch.FULL_REGEX)
# should not initialize if the specified walletdir does not exist
self.nodes[0].assert_start_raises_init_error(['-walletdir=bad'], 'Error: Specified -walletdir "bad" does not exist')
@@ -125,7 +126,7 @@ class MultiWalletTest(BitcoinTestFramework):
self.start_node(0, ['-wallet=w4', '-wallet=w5'])
assert_equal(set(node.listwallets()), {"w4", "w5"})
w5 = wallet("w5")
- w5.generate(1)
+ node.generatetoaddress(nblocks=1, address=w5.getnewaddress())
# now if wallets/ exists again, but the rootdir is specified as the walletdir, w4 and w5 should still be loaded
os.rename(wallet_dir2, wallet_dir())
@@ -143,11 +144,13 @@ class MultiWalletTest(BitcoinTestFramework):
self.restart_node(0, extra_args)
+ assert_equal(sorted(map(lambda w: w['name'], self.nodes[0].listwalletdir()['wallets'])), ['', os.path.join('sub', 'w5'), 'w', 'w1', 'w2', 'w3', 'w7', 'w7_symlink', 'w8', 'w8_copy'])
+
wallets = [wallet(w) for w in wallet_names]
wallet_bad = wallet("bad")
# check wallet names and balances
- wallets[0].generate(1)
+ node.generatetoaddress(nblocks=1, address=wallets[0].getnewaddress())
for wallet_name, wallet in zip(wallet_names, wallets):
info = wallet.getwalletinfo()
assert_equal(info['immature_balance'], 50 if wallet is wallets[0] else 0)
@@ -160,7 +163,7 @@ class MultiWalletTest(BitcoinTestFramework):
assert_raises_rpc_error(-19, "Wallet file not specified", node.getwalletinfo)
w1, w2, w3, w4, *_ = wallets
- w1.generate(101)
+ node.generatetoaddress(nblocks=101, address=w1.getnewaddress())
assert_equal(w1.getbalance(), 100)
assert_equal(w2.getbalance(), 0)
assert_equal(w3.getbalance(), 0)
@@ -169,7 +172,7 @@ class MultiWalletTest(BitcoinTestFramework):
w1.sendtoaddress(w2.getnewaddress(), 1)
w1.sendtoaddress(w3.getnewaddress(), 2)
w1.sendtoaddress(w4.getnewaddress(), 3)
- w1.generate(1)
+ node.generatetoaddress(nblocks=1, address=w1.getnewaddress())
assert_equal(w2.getbalance(), 1)
assert_equal(w3.getbalance(), 2)
assert_equal(w4.getbalance(), 3)
@@ -223,9 +226,12 @@ class MultiWalletTest(BitcoinTestFramework):
# Fail to load if one wallet is a copy of another
assert_raises_rpc_error(-1, "BerkeleyBatch: Can't open database w8_copy (duplicates fileid", self.nodes[0].loadwallet, 'w8_copy')
+ # Fail to load if one wallet is a copy of another, test this twice to make sure that we don't re-introduce #14304
+ assert_raises_rpc_error(-1, "BerkeleyBatch: Can't open database w8_copy (duplicates fileid", self.nodes[0].loadwallet, 'w8_copy')
+
+
# Fail to load if wallet file is a symlink
- if os.name != 'nt':
- assert_raises_rpc_error(-4, "Wallet file verification failed: Invalid -wallet path 'w8_symlink'", self.nodes[0].loadwallet, 'w8_symlink')
+ assert_raises_rpc_error(-4, "Wallet file verification failed: Invalid -wallet path 'w8_symlink'", self.nodes[0].loadwallet, 'w8_symlink')
# Fail to load if a directory is specified that doesn't contain a wallet
os.mkdir(wallet_dir('empty_wallet_dir'))
@@ -267,7 +273,11 @@ class MultiWalletTest(BitcoinTestFramework):
assert 'w1' not in self.nodes[0].listwallets()
# Successfully unload the wallet referenced by the request endpoint
+ # Also ensure unload works during walletpassphrase timeout
+ w2.encryptwallet('test')
+ w2.walletpassphrase('test', 1)
w2.unloadwallet()
+ time.sleep(1.1)
assert 'w2' not in self.nodes[0].listwallets()
# Successfully unload all wallets
@@ -281,6 +291,8 @@ class MultiWalletTest(BitcoinTestFramework):
assert_equal(self.nodes[0].listwallets(), ['w1'])
assert_equal(w1.getwalletinfo()['walletname'], 'w1')
+ assert_equal(sorted(map(lambda w: w['name'], self.nodes[0].listwalletdir()['wallets'])), ['', os.path.join('sub', 'w5'), 'w', 'w1', 'w2', 'w3', 'w7', 'w7_symlink', 'w8', 'w8_copy', 'w9'])
+
# Test backing up and restoring wallets
self.log.info("Test wallet backup")
self.restart_node(0, ['-nowallet'])
diff --git a/test/lint/lint-locale-dependence.sh b/test/lint/lint-locale-dependence.sh
index cbee437c91..216cabbce9 100755
--- a/test/lint/lint-locale-dependence.sh
+++ b/test/lint/lint-locale-dependence.sh
@@ -2,7 +2,6 @@
export LC_ALL=C
KNOWN_VIOLATIONS=(
- "src/base58.cpp:.*isspace"
"src/bitcoin-tx.cpp.*stoul"
"src/bitcoin-tx.cpp.*trim_right"
"src/bitcoin-tx.cpp:.*atoi"
@@ -18,15 +17,12 @@ KNOWN_VIOLATIONS=(
"src/test/getarg_tests.cpp.*split"
"src/torcontrol.cpp:.*atoi"
"src/torcontrol.cpp:.*strtol"
- "src/uint256.cpp:.*isspace"
"src/uint256.cpp:.*tolower"
"src/util.cpp:.*atoi"
"src/util.cpp:.*fprintf"
"src/util.cpp:.*tolower"
"src/utilmoneystr.cpp:.*isdigit"
- "src/utilmoneystr.cpp:.*isspace"
"src/utilstrencodings.cpp:.*atoi"
- "src/utilstrencodings.cpp:.*isspace"
"src/utilstrencodings.cpp:.*strtol"
"src/utilstrencodings.cpp:.*strtoll"
"src/utilstrencodings.cpp:.*strtoul"
diff --git a/test/lint/lint-python.sh b/test/lint/lint-python.sh
index 4f4542ff0c..d44a585294 100755
--- a/test/lint/lint-python.sh
+++ b/test/lint/lint-python.sh
@@ -87,4 +87,4 @@ elif PYTHONWARNINGS="ignore" flake8 --version | grep -q "Python 2"; then
exit 0
fi
-PYTHONWARNINGS="ignore" flake8 --ignore=B,C,E,F,I,N,W --select=E101,E112,E113,E115,E116,E125,E129,E131,E133,E223,E224,E242,E266,E271,E272,E273,E274,E275,E304,E306,E401,E402,E502,E701,E702,E703,E714,E721,E741,E742,E743,E901,E902,F401,F402,F403,F404,F405,F406,F407,F601,F602,F621,F622,F631,F701,F702,F703,F704,F705,F706,F707,F811,F812,F821,F822,F823,F831,F841,W191,W291,W292,W293,W504,W601,W602,W603,W604,W605,W606 .
+PYTHONWARNINGS="ignore" flake8 --ignore=B,C,E,F,I,N,W --select=E101,E112,E113,E115,E116,E125,E129,E131,E133,E223,E224,E242,E266,E271,E272,E273,E274,E275,E304,E306,E401,E402,E502,E701,E702,E703,E714,E721,E741,E742,E743,E901,E902,F401,F402,F403,F404,F405,F406,F407,F601,F602,F621,F622,F631,F701,F702,F703,F704,F705,F706,F707,F811,F812,F821,F822,F823,F831,F841,W191,W291,W292,W293,W504,W601,W602,W603,W604,W605,W606 "${@:-.}"
diff --git a/test/lint/lint-spelling.ignore-words.txt b/test/lint/lint-spelling.ignore-words.txt
index 9a49f32271..f0415443db 100644
--- a/test/lint/lint-spelling.ignore-words.txt
+++ b/test/lint/lint-spelling.ignore-words.txt
@@ -1,6 +1,7 @@
cas
hights
mor
+mut
objext
unselect
useable
diff --git a/test/lint/lint-spelling.sh b/test/lint/lint-spelling.sh
index ebeafd7d58..5d672698a7 100755
--- a/test/lint/lint-spelling.sh
+++ b/test/lint/lint-spelling.sh
@@ -9,10 +9,7 @@
export LC_ALL=C
-EXIT_CODE=0
IGNORE_WORDS_FILE=test/lint/lint-spelling.ignore-words.txt
if ! codespell --check-filenames --disable-colors --quiet-level=7 --ignore-words=${IGNORE_WORDS_FILE} $(git ls-files -- ":(exclude)build-aux/m4/" ":(exclude)contrib/seeds/*.txt" ":(exclude)depends/" ":(exclude)doc/release-notes/" ":(exclude)src/leveldb/" ":(exclude)src/qt/locale/" ":(exclude)src/secp256k1/" ":(exclude)src/univalue/"); then
echo "^ Warning: codespell identified likely spelling errors. Any false positives? Add them to the list of ignored words in ${IGNORE_WORDS_FILE}"
- EXIT_CODE=1
fi
-exit ${EXIT_CODE}