diff options
87 files changed, 1141 insertions, 700 deletions
diff --git a/.travis.yml b/.travis.yml index d051f37d98..258fd1b1c5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -dist: trusty +dist: xenial os: linux language: minimal cache: @@ -99,9 +99,8 @@ jobs: DOCKER_NAME_TAG=ubuntu:16.04 PACKAGES="clang llvm python3-zmq qtbase5-dev qttools5-dev-tools libssl-dev libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libzmq3-dev libprotobuf-dev protobuf-compiler libqrencode-dev" NO_DEPENDS=1 - RUN_FUNCTIONAL_TESTS=false # Disabled for now. TODO identify suppressions or exclude specific tests GOAL="install" - BITCOIN_CONFIG="--enable-zmq --with-incompatible-bdb --with-gui=qt5 CPPFLAGS=-DDEBUG_LOCKORDER --with-sanitizers=thread --disable-hardening --disable-asm CC=clang CXX=clang++" + BITCOIN_CONFIG="--enable-zmq --disable-wallet --with-gui=qt5 CPPFLAGS=-DDEBUG_LOCKORDER --with-sanitizers=thread --disable-hardening --disable-asm CC=clang CXX=clang++" # x86_64 Linux (no depends, only system libs, sanitizers: address/leak (ASan + LSan) + undefined (UBSan) + integer) - stage: test env: >- diff --git a/.travis/test_04_install.sh b/.travis/test_04_install.sh index 03a61ea9f8..a111387f10 100755 --- a/.travis/test_04_install.sh +++ b/.travis/test_04_install.sh @@ -7,13 +7,16 @@ export LC_ALL=C.UTF-8 travis_retry docker pull "$DOCKER_NAME_TAG" +mkdir -p "${TRAVIS_BUILD_DIR}/sanitizer-output/" export ASAN_OPTIONS="" export LSAN_OPTIONS="suppressions=${TRAVIS_BUILD_DIR}/test/sanitizer_suppressions/lsan" -export TSAN_OPTIONS="suppressions=${TRAVIS_BUILD_DIR}/test/sanitizer_suppressions/tsan" +export TSAN_OPTIONS="suppressions=${TRAVIS_BUILD_DIR}/test/sanitizer_suppressions/tsan:log_path=${TRAVIS_BUILD_DIR}/sanitizer-output/tsan" export UBSAN_OPTIONS="suppressions=${TRAVIS_BUILD_DIR}/test/sanitizer_suppressions/ubsan:print_stacktrace=1:halt_on_error=1" env | grep -E '^(BITCOIN_CONFIG|CCACHE_|WINEDEBUG|LC_ALL|BOOST_TEST_RANDOM|CONFIG_SHELL|(ASAN|LSAN|TSAN|UBSAN)_OPTIONS)' | tee /tmp/env if [[ $HOST = *-mingw32 ]]; then DOCKER_ADMIN="--cap-add SYS_ADMIN" +elif [[ $BITCOIN_CONFIG = *--with-sanitizers=*address* ]]; then # If ran with (ASan + LSan), Docker needs access to ptrace (https://github.com/google/sanitizers/issues/764) + DOCKER_ADMIN="--cap-add SYS_PTRACE" fi DOCKER_ID=$(docker run $DOCKER_ADMIN -idt --mount type=bind,src=$TRAVIS_BUILD_DIR,dst=$TRAVIS_BUILD_DIR --mount type=bind,src=$CCACHE_DIR,dst=$CCACHE_DIR -w $TRAVIS_BUILD_DIR --env-file /tmp/env $DOCKER_NAME_TAG) diff --git a/.travis/test_06_script.sh b/.travis/test_06_script.sh index 506d2b518c..618aa2c3b6 100755 --- a/.travis/test_06_script.sh +++ b/.travis/test_06_script.sh @@ -40,6 +40,9 @@ BEGIN_FOLD configure DOCKER_EXEC ./configure --cache-file=../config.cache $BITCOIN_CONFIG_ALL $BITCOIN_CONFIG || ( cat config.log && false) END_FOLD +set -o errtrace +trap 'DOCKER_EXEC "cat ${TRAVIS_BUILD_DIR}/sanitizer-output/* 2> /dev/null"' ERR + BEGIN_FOLD build DOCKER_EXEC make $MAKEJOBS $GOAL || ( echo "Build failure. Verbose build follows." && DOCKER_EXEC make $GOAL V=1 ; false ) END_FOLD @@ -1,7 +1,7 @@ The MIT License (MIT) -Copyright (c) 2009-2018 The Bitcoin Core developers -Copyright (c) 2009-2018 Bitcoin Developers +Copyright (c) 2009-2019 The Bitcoin Core developers +Copyright (c) 2009-2019 Bitcoin Developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/build_msvc/README.md b/build_msvc/README.md index 5fb08df8d7..63c5babf35 100644 --- a/build_msvc/README.md +++ b/build_msvc/README.md @@ -31,20 +31,13 @@ Additional dependencies required from the [bitcoin-core](https://github.com/bitc Building --------------------- -The instructions below use vcpkg to install the dependencies. +The instructions below use `vcpkg` to install the dependencies. -- Clone and vcpkg from the [github repository](https://github.com/Microsoft/vcpkg) and install as per the instructions in the main README.md. +- Clone `vcpkg` from the [github repository](https://github.com/Microsoft/vcpkg) and install as per the instructions in the main README.md. - Install the required packages (replace x64 with x86 as required): -- Install the required dependencies with vcpkg: ``` - PS >.\vcpkg install boost:x64-windows-static ` - libevent:x64-windows-static ` - openssl:x64-windows-static ` - zeromq:x64-windows-static ` - berkeleydb:x64-windows-static ` - secp256k1:x64-windows-static ` - leveldb:x64-windows-static + PS >.\vcpkg install --triplet x64-windows-static boost-filesystem boost-signals2 boost-test libevent openssl zeromq berkeleydb secp256k1 leveldb ``` - Use Python to generate *.vcxproj from Makefile @@ -53,4 +46,4 @@ The instructions below use vcpkg to install the dependencies. PS >python msvc-autogen.py ``` -- Build in Visual Studio.
\ No newline at end of file +- Build in Visual Studio. diff --git a/configure.ac b/configure.ac index 2d34b83047..4b34082270 100644 --- a/configure.ac +++ b/configure.ac @@ -6,7 +6,7 @@ define(_CLIENT_VERSION_REVISION, 99) define(_CLIENT_VERSION_BUILD, 0) define(_CLIENT_VERSION_RC, 0) define(_CLIENT_VERSION_IS_RELEASE, false) -define(_COPYRIGHT_YEAR, 2018) +define(_COPYRIGHT_YEAR, 2019) define(_COPYRIGHT_HOLDERS,[The %s developers]) define(_COPYRIGHT_HOLDERS_SUBSTITUTION,[[Bitcoin Core]]) AC_INIT([Bitcoin Core],m4_join([.], _CLIENT_VERSION_MAJOR, _CLIENT_VERSION_MINOR, _CLIENT_VERSION_REVISION, m4_if(_CLIENT_VERSION_BUILD, [0], [], _CLIENT_VERSION_BUILD))m4_if(_CLIENT_VERSION_RC, [0], [], [rc]_CLIENT_VERSION_RC),[https://github.com/bitcoin/bitcoin/issues],[bitcoin],[https://bitcoincore.org/]) diff --git a/contrib/debian/copyright b/contrib/debian/copyright index e5b9cbaa40..2d5b0188d2 100644 --- a/contrib/debian/copyright +++ b/contrib/debian/copyright @@ -5,7 +5,7 @@ Upstream-Contact: Satoshi Nakamoto <satoshin@gmx.com> Source: https://github.com/bitcoin/bitcoin Files: * -Copyright: 2009-2018, Bitcoin Core Developers +Copyright: 2009-2019, Bitcoin Core Developers License: Expat Comment: The Bitcoin Core Developers encompasses the current developers listed on bitcoin.org, as well as the numerous contributors to the project. diff --git a/contrib/macdeploy/custom_dsstore.py b/contrib/macdeploy/custom_dsstore.py index c29f83a91e..dc1c1882dd 100755 --- a/contrib/macdeploy/custom_dsstore.py +++ b/contrib/macdeploy/custom_dsstore.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2013-2017 The Bitcoin Core developers +# Copyright (c) 2013-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. import biplist diff --git a/doc/build-netbsd.md b/doc/build-netbsd.md index 5bf2d6b59b..ab422f6aa7 100644 --- a/doc/build-netbsd.md +++ b/doc/build-netbsd.md @@ -1,6 +1,6 @@ NetBSD build guide ====================== -(updated for NetBSD 7.0) +(updated for NetBSD 8.0) This guide describes how to build bitcoind and command-line utilities on NetBSD. @@ -15,21 +15,38 @@ You will need the following modules, which can be installed via pkgsrc or pkgin: autoconf automake boost -db4 git gmake libevent libtool -python27 -``` +pkg-config +python37 -Download the source code: -``` -git clone https://github.com/bitcoin/bitcoin +git clone https://github.com/bitcoin/bitcoin.git ``` See [dependencies.md](dependencies.md) for a complete overview. +### Building BerkeleyDB + +BerkeleyDB is only necessary for the wallet functionality. To skip this, pass +`--disable-wallet` to `./configure` and skip to the next section. + +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: + +```shell +./contrib/install_db4.sh `pwd` +``` + +from the root of the repository. Then set `BDB_PREFIX` for the next section: + +```shell +export BDB_PREFIX="$PWD/db4" +``` + ### Building Bitcoin Core **Important**: Use `gmake` (the non-GNU `make` will exit with an error). @@ -37,13 +54,26 @@ See [dependencies.md](dependencies.md) for a complete overview. With wallet: ``` ./autogen.sh -./configure CPPFLAGS="-I/usr/pkg/include" LDFLAGS="-L/usr/pkg/lib" BOOST_CPPFLAGS="-I/usr/pkg/include" BOOST_LDFLAGS="-L/usr/pkg/lib" -gmake +./configure --with-gui=no CPPFLAGS="-I/usr/pkg/include" \ + LDFLAGS="-L/usr/pkg/lib" \ + BOOST_CPPFLAGS="-I/usr/pkg/include" \ + BOOST_LDFLAGS="-L/usr/pkg/lib" \ + BDB_LIBS="-L${BDB_PREFIX}/lib -ldb_cxx-4.8" \ + BDB_CFLAGS="-I${BDB_PREFIX}/include" ``` Without wallet: ``` ./autogen.sh -./configure --disable-wallet CPPFLAGS="-I/usr/pkg/include" LDFLAGS="-L/usr/pkg/lib" BOOST_CPPFLAGS="-I/usr/pkg/include" BOOST_LDFLAGS="-L/usr/pkg/lib" -gmake +./configure --with-gui=no --disable-wallet \ + CPPFLAGS="-I/usr/pkg/include" \ + LDFLAGS="-L/usr/pkg/lib" \ + BOOST_CPPFLAGS="-I/usr/pkg/include" \ + BOOST_LDFLAGS="-L/usr/pkg/lib" +``` + +Build and run the tests: +```bash +gmake # use -jX here for parallelism +gmake check ``` diff --git a/doc/build-windows.md b/doc/build-windows.md index fc93a0c6e4..9641e0d3fd 100644 --- a/doc/build-windows.md +++ b/doc/build-windows.md @@ -65,7 +65,11 @@ A host toolchain (`build-essential`) is necessary because some dependency packages (such as `protobuf`) need to build host utilities that are used in the build process. -See also: [dependencies.md](dependencies.md). +See [dependencies.md](dependencies.md) for a complete overview. + +If you want to build the windows installer with `make deploy` you need [NSIS](https://nsis.sourceforge.io/Main_Page): + + sudo apt install nsis ## Building for 64-bit Windows @@ -139,6 +143,10 @@ way. This will install to `c:\workspace\bitcoin`, for example: make install DESTDIR=/mnt/c/workspace/bitcoin +You can also create an installer using: + + make deploy + Footnotes --------- diff --git a/doc/man/bitcoin-cli.1 b/doc/man/bitcoin-cli.1 index bf24d929bc..553addfa84 100644 --- a/doc/man/bitcoin-cli.1 +++ b/doc/man/bitcoin-cli.1 @@ -1,17 +1,21 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.6. -.TH BITCOIN-CLI "1" "July 2018" "bitcoin-cli v0.16.99.0" "User Commands" +.TH BITCOIN-CLI "1" "December 2018" "bitcoin-cli v0.17.99.0" "User Commands" .SH NAME -bitcoin-cli \- manual page for bitcoin-cli v0.16.99.0 +bitcoin-cli \- manual page for bitcoin-cli v0.17.99.0 +.SH SYNOPSIS +.B bitcoin-cli +[\fI\,options\/\fR] \fI\,<command> \/\fR[\fI\,params\/\fR] \fI\,Send command to Bitcoin Core\/\fR +.br +.B bitcoin-cli +[\fI\,options\/\fR] \fI\,-named <command> \/\fR[\fI\,name=value\/\fR]... \fI\,Send command to Bitcoin Core (with named arguments)\/\fR +.br +.B bitcoin-cli +[\fI\,options\/\fR] \fI\,help List commands\/\fR +.br +.B bitcoin-cli +[\fI\,options\/\fR] \fI\,help <command> Get help for a command\/\fR .SH DESCRIPTION -Bitcoin Core RPC client version v0.16.99.0 -.SS "Usage:" -.TP -bitcoin\-cli [options] <command> [params] -Send command to Bitcoin Core -.IP -bitcoin\-cli [options] \fB\-named\fR <command> [name=value] ... Send command to Bitcoin Core (with named arguments) -bitcoin\-cli [options] help List commands -bitcoin\-cli [options] help <command> Get help for a command +Bitcoin Core RPC client version v0.17.99.0 .SH OPTIONS .HP \-? @@ -59,7 +63,8 @@ Password for JSON\-RPC connections .HP \fB\-rpcport=\fR<port> .IP -Connect to JSON\-RPC on <port> (default: 8332 or testnet: 18332) +Connect to JSON\-RPC on <port> (default: 8332, testnet: 18332, regtest: +18443) .HP \fB\-rpcuser=\fR<user> .IP @@ -72,20 +77,20 @@ Wait for RPC server to start \fB\-rpcwallet=\fR<walletname> .IP Send RPC for non\-default wallet on RPC server (needs to exactly match -corresponding \fB\-wallet\fR option passed to bitcoind) +corresponding \fB\-wallet\fR option passed to bitcoind). This changes +the RPC endpoint used, e.g. +http://127.0.0.1:8332/wallet/<walletname> .HP \fB\-stdin\fR .IP Read extra arguments from standard input, one per line until EOF/Ctrl\-D -(recommended for sensitive information such as passphrases). -When combined with \fB\-stdinrpcpass\fR, the first line from standard -input is used for the RPC password. +(recommended for sensitive information such as passphrases). When +combined with \fB\-stdinrpcpass\fR, the first line from standard input +is used for the RPC password. .HP \fB\-stdinrpcpass\fR -.TP -Read RPC password from standard input as a single line. -When combined .IP +Read RPC password from standard input as a single line. When combined with \fB\-stdin\fR, the first line from standard input is used for the RPC password. .HP diff --git a/doc/man/bitcoin-qt.1 b/doc/man/bitcoin-qt.1 index 3a18c9f49f..1d87acd3de 100644 --- a/doc/man/bitcoin-qt.1 +++ b/doc/man/bitcoin-qt.1 @@ -1,12 +1,12 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.6. -.TH BITCOIN-QT "1" "July 2018" "bitcoin-qt v0.16.99.0" "User Commands" +.TH BITCOIN-QT "1" "December 2018" "bitcoin-qt v0.17.99.0" "User Commands" .SH NAME -bitcoin-qt \- manual page for bitcoin-qt v0.16.99.0 +bitcoin-qt \- manual page for bitcoin-qt v0.17.99.0 +.SH SYNOPSIS +.B bitcoin-qt +[\fI\,command-line options\/\fR] .SH DESCRIPTION -Bitcoin Core version v0.16.99.0 (64\-bit) -Usage: -.IP -bitcoin\-qt [command\-line options] +Bitcoin Core version v0.17.99.0 (64\-bit) .SH OPTIONS .HP \-? @@ -23,9 +23,9 @@ long fork (%s in cmd is replaced by message) If this block is in the chain assume that it and its ancestors are valid and potentially skip their script verification (0 to verify all, default: -0000000000000000005214481d2d96f898e3d5416e43359c145944a909d242e0, +0000000000000000002e63058c023a9a1de233554f28c7b21380b6c9003f36a8, testnet: -0000000002e9e7b00e1f6dc5123a04aad68dd0f0968d8c7aa45f6640795c37b1) +0000000000000037a8cd3e06cd5edbfe9dd1dbcc5dacab279376ef7cfc2b4c75) .HP \fB\-blocknotify=\fR<cmd> .IP @@ -61,7 +61,8 @@ Set database cache size in megabytes (4 to 16384, default: 450) \fB\-debuglogfile=\fR<file> .IP Specify location of debug log file. Relative paths will be prefixed by a -net\-specific datadir location. (0 to disable; default: debug.log) +net\-specific datadir location. (\fB\-nodebuglogfile\fR to disable; +default: debug.log) .HP \fB\-includeconf=\fR<file> .IP @@ -108,7 +109,7 @@ blocks if a target size in MiB is provided. This mode is incompatible with \fB\-txindex\fR and \fB\-rescan\fR. Warning: Reverting this setting requires re\-downloading the entire blockchain. (default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, ->550 = automatically prune block files to stay under the +>=550 = automatically prune block files to stay under the specified target size in MiB) .HP \fB\-reindex\fR @@ -117,7 +118,9 @@ Rebuild chain state and block index from the blk*.dat files on disk .HP \fB\-reindex\-chainstate\fR .IP -Rebuild chain state from the currently indexed blocks +Rebuild chain state from the currently indexed blocks. When in pruning +mode or if blocks on disk might be corrupted, use full \fB\-reindex\fR +instead. .HP \fB\-sysperms\fR .IP @@ -157,7 +160,7 @@ for IPv6 .HP \fB\-connect=\fR<ip> .IP -Connect only to the specified node; \fB\-connect\fR=\fI\,0\/\fR disables automatic +Connect only to the specified node; \fB\-noconnect\fR disables automatic connections (the rules for this peer are the same as for \fB\-addnode\fR). This option can be specified multiple times to connect to multiple nodes. @@ -178,7 +181,7 @@ unless \fB\-connect\fR used) .HP \fB\-enablebip61\fR .IP -Send reject messages per BIP61 (default: 1) +Send reject messages per BIP61 (default: 0) .HP \fB\-externalip=\fR<ip> .IP @@ -221,8 +224,8 @@ Tries to keep outbound traffic under the given target (in MiB per 24h), .HP \fB\-onion=\fR<ip:port> .IP -Use separate SOCKS5 proxy to reach peers via Tor hidden services -(default: \fB\-proxy\fR) +Use separate SOCKS5 proxy to reach peers via Tor hidden services, set +\fB\-noonion\fR to disable (default: \fB\-proxy\fR) .HP \fB\-onlynet=\fR<net> .IP @@ -242,11 +245,13 @@ Relay non\-P2SH multisig (default: 1) .HP \fB\-port=\fR<port> .IP -Listen for connections on <port> (default: 8333 or testnet: 18333) +Listen for connections on <port> (default: 8333, testnet: 18333, +regtest: 18444) .HP \fB\-proxy=\fR<ip:port> .IP -Connect through SOCKS5 proxy +Connect through SOCKS5 proxy, set \fB\-noproxy\fR to disable (default: +disabled) .HP \fB\-proxyrandomize\fR .IP @@ -393,8 +398,7 @@ Send transactions with full\-RBF opt\-in enabled (RPC only, default: 0) .IP Delete all wallet transactions and only recover those parts of the blockchain through \fB\-rescan\fR on startup (1 = keep tx meta data e.g. -account owner and payment request information, 2 = drop tx meta -data) +payment request information, 2 = drop tx meta data) .PP ZeroMQ notification options: .HP @@ -418,7 +422,7 @@ Debugging/Testing options: .HP \fB\-debug=\fR<category> .IP -Output debugging information (default: 0, supplying <category> is +Output debugging information (default: \fB\-nodebug\fR, supplying <category> is optional). If <category> is not supplied or if <category> = 1, output all debugging information. <category> can be: net, tor, mempool, http, bench, zmq, db, rpc, estimatefee, addrman, @@ -433,7 +437,7 @@ or more specified categories. .HP \fB\-help\-debug\fR .IP -Show all debugging options (usage: \fB\-\-help\fR \fB\-help\-debug\fR) +Print help message with debugging options and exit .HP \fB\-logips\fR .IP @@ -452,7 +456,7 @@ transaction; setting this too low may abort large transactions \fB\-printtoconsole\fR .IP Send trace/debug info to console (default: 1 when no \fB\-daemon\fR. To disable -logging to file, set debuglogfile=0) +logging to file, set \fB\-nodebuglogfile\fR) .HP \fB\-shrinkdebugfile\fR .IP @@ -529,8 +533,8 @@ option can be specified multiple times .HP \fB\-rpcauth=\fR<userpw> .IP -Username and hashed password for JSON\-RPC connections. The field -<userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A +Username and HMAC\-SHA\-256 hashed password for JSON\-RPC connections. The +field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcauth. The client then connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> pair of arguments. This @@ -538,12 +542,12 @@ option can be specified multiple times .HP \fB\-rpcbind=\fR<addr>[:port] .IP -Bind to given address to listen for JSON\-RPC connections. This option is -ignored unless \fB\-rpcallowip\fR is also passed. Port is optional and -overrides \fB\-rpcport\fR. Use [host]:port notation for IPv6. This -option can be specified multiple times (default: 127.0.0.1 and -::1 i.e., localhost, or if \fB\-rpcallowip\fR has been specified, -0.0.0.0 and :: i.e., all addresses) +Bind to given address to listen for JSON\-RPC connections. Do not expose +the RPC server to untrusted networks such as the public internet! +This option is ignored unless \fB\-rpcallowip\fR is also passed. Port is +optional and overrides \fB\-rpcport\fR. Use [host]:port notation for +IPv6. This option can be specified multiple times (default: +127.0.0.1 and ::1 i.e., localhost) .HP \fB\-rpccookiefile=\fR<loc> .IP @@ -556,8 +560,8 @@ Password for JSON\-RPC connections .HP \fB\-rpcport=\fR<port> .IP -Listen for JSON\-RPC connections on <port> (default: 8332 or testnet: -18332) +Listen for JSON\-RPC connections on <port> (default: 8332, testnet: +18332, regtest: 18443) .HP \fB\-rpcserialversion\fR .IP diff --git a/doc/man/bitcoin-tx.1 b/doc/man/bitcoin-tx.1 index e1b81bad6c..f16c68ca14 100644 --- a/doc/man/bitcoin-tx.1 +++ b/doc/man/bitcoin-tx.1 @@ -1,16 +1,15 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.6. -.TH BITCOIN-TX "1" "July 2018" "bitcoin-tx v0.16.99.0" "User Commands" +.TH BITCOIN-TX "1" "December 2018" "bitcoin-tx v0.17.99.0" "User Commands" .SH NAME -bitcoin-tx \- manual page for bitcoin-tx v0.16.99.0 +bitcoin-tx \- manual page for bitcoin-tx v0.17.99.0 +.SH SYNOPSIS +.B bitcoin-tx +[\fI\,options\/\fR] \fI\,<hex-tx> \/\fR[\fI\,commands\/\fR] \fI\,Update hex-encoded bitcoin transaction\/\fR +.br +.B bitcoin-tx +[\fI\,options\/\fR] \fI\,-create \/\fR[\fI\,commands\/\fR] \fI\,Create hex-encoded bitcoin transaction\/\fR .SH DESCRIPTION -Bitcoin Core bitcoin\-tx utility version v0.16.99.0 -.SS "Usage:" -.TP -bitcoin\-tx [options] <hex\-tx> [commands] -Update hex\-encoded bitcoin transaction -.TP -bitcoin\-tx [options] \fB\-create\fR [commands] -Create hex\-encoded bitcoin transaction +Bitcoin Core bitcoin\-tx utility version v0.17.99.0 .SH OPTIONS .HP \-? diff --git a/doc/man/bitcoind.1 b/doc/man/bitcoind.1 index d0ba131cde..5c4b1cd03b 100644 --- a/doc/man/bitcoind.1 +++ b/doc/man/bitcoind.1 @@ -1,13 +1,12 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.6. -.TH BITCOIND "1" "July 2018" "bitcoind v0.16.99.0" "User Commands" +.TH BITCOIND "1" "December 2018" "bitcoind v0.17.99.0" "User Commands" .SH NAME -bitcoind \- manual page for bitcoind v0.16.99.0 +bitcoind \- manual page for bitcoind v0.17.99.0 +.SH SYNOPSIS +.B bitcoind +[\fI\,options\/\fR] \fI\,Start Bitcoin Core Daemon\/\fR .SH DESCRIPTION -Bitcoin Core Daemon version v0.16.99.0 -.SS "Usage:" -.TP -bitcoind [options] -Start Bitcoin Core Daemon +Bitcoin Core Daemon version v0.17.99.0 .SH OPTIONS .HP \-? @@ -24,9 +23,9 @@ long fork (%s in cmd is replaced by message) If this block is in the chain assume that it and its ancestors are valid and potentially skip their script verification (0 to verify all, default: -0000000000000000005214481d2d96f898e3d5416e43359c145944a909d242e0, +0000000000000000002e63058c023a9a1de233554f28c7b21380b6c9003f36a8, testnet: -0000000002e9e7b00e1f6dc5123a04aad68dd0f0968d8c7aa45f6640795c37b1) +0000000000000037a8cd3e06cd5edbfe9dd1dbcc5dacab279376ef7cfc2b4c75) .HP \fB\-blocknotify=\fR<cmd> .IP @@ -62,7 +61,8 @@ Set database cache size in megabytes (4 to 16384, default: 450) \fB\-debuglogfile=\fR<file> .IP Specify location of debug log file. Relative paths will be prefixed by a -net\-specific datadir location. (0 to disable; default: debug.log) +net\-specific datadir location. (\fB\-nodebuglogfile\fR to disable; +default: debug.log) .HP \fB\-includeconf=\fR<file> .IP @@ -109,7 +109,7 @@ blocks if a target size in MiB is provided. This mode is incompatible with \fB\-txindex\fR and \fB\-rescan\fR. Warning: Reverting this setting requires re\-downloading the entire blockchain. (default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, ->550 = automatically prune block files to stay under the +>=550 = automatically prune block files to stay under the specified target size in MiB) .HP \fB\-reindex\fR @@ -118,7 +118,9 @@ Rebuild chain state and block index from the blk*.dat files on disk .HP \fB\-reindex\-chainstate\fR .IP -Rebuild chain state from the currently indexed blocks +Rebuild chain state from the currently indexed blocks. When in pruning +mode or if blocks on disk might be corrupted, use full \fB\-reindex\fR +instead. .HP \fB\-sysperms\fR .IP @@ -158,7 +160,7 @@ for IPv6 .HP \fB\-connect=\fR<ip> .IP -Connect only to the specified node; \fB\-connect\fR=\fI\,0\/\fR disables automatic +Connect only to the specified node; \fB\-noconnect\fR disables automatic connections (the rules for this peer are the same as for \fB\-addnode\fR). This option can be specified multiple times to connect to multiple nodes. @@ -179,7 +181,7 @@ unless \fB\-connect\fR used) .HP \fB\-enablebip61\fR .IP -Send reject messages per BIP61 (default: 1) +Send reject messages per BIP61 (default: 0) .HP \fB\-externalip=\fR<ip> .IP @@ -222,8 +224,8 @@ Tries to keep outbound traffic under the given target (in MiB per 24h), .HP \fB\-onion=\fR<ip:port> .IP -Use separate SOCKS5 proxy to reach peers via Tor hidden services -(default: \fB\-proxy\fR) +Use separate SOCKS5 proxy to reach peers via Tor hidden services, set +\fB\-noonion\fR to disable (default: \fB\-proxy\fR) .HP \fB\-onlynet=\fR<net> .IP @@ -243,11 +245,13 @@ Relay non\-P2SH multisig (default: 1) .HP \fB\-port=\fR<port> .IP -Listen for connections on <port> (default: 8333 or testnet: 18333) +Listen for connections on <port> (default: 8333, testnet: 18333, +regtest: 18444) .HP \fB\-proxy=\fR<ip:port> .IP -Connect through SOCKS5 proxy +Connect through SOCKS5 proxy, set \fB\-noproxy\fR to disable (default: +disabled) .HP \fB\-proxyrandomize\fR .IP @@ -394,8 +398,7 @@ Send transactions with full\-RBF opt\-in enabled (RPC only, default: 0) .IP Delete all wallet transactions and only recover those parts of the blockchain through \fB\-rescan\fR on startup (1 = keep tx meta data e.g. -account owner and payment request information, 2 = drop tx meta -data) +payment request information, 2 = drop tx meta data) .PP ZeroMQ notification options: .HP @@ -419,7 +422,7 @@ Debugging/Testing options: .HP \fB\-debug=\fR<category> .IP -Output debugging information (default: 0, supplying <category> is +Output debugging information (default: \fB\-nodebug\fR, supplying <category> is optional). If <category> is not supplied or if <category> = 1, output all debugging information. <category> can be: net, tor, mempool, http, bench, zmq, db, rpc, estimatefee, addrman, @@ -434,7 +437,7 @@ or more specified categories. .HP \fB\-help\-debug\fR .IP -Show all debugging options (usage: \fB\-\-help\fR \fB\-help\-debug\fR) +Print help message with debugging options and exit .HP \fB\-logips\fR .IP @@ -453,7 +456,7 @@ transaction; setting this too low may abort large transactions \fB\-printtoconsole\fR .IP Send trace/debug info to console (default: 1 when no \fB\-daemon\fR. To disable -logging to file, set debuglogfile=0) +logging to file, set \fB\-nodebuglogfile\fR) .HP \fB\-shrinkdebugfile\fR .IP @@ -530,8 +533,8 @@ option can be specified multiple times .HP \fB\-rpcauth=\fR<userpw> .IP -Username and hashed password for JSON\-RPC connections. The field -<userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A +Username and HMAC\-SHA\-256 hashed password for JSON\-RPC connections. The +field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcauth. The client then connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> pair of arguments. This @@ -539,12 +542,12 @@ option can be specified multiple times .HP \fB\-rpcbind=\fR<addr>[:port] .IP -Bind to given address to listen for JSON\-RPC connections. This option is -ignored unless \fB\-rpcallowip\fR is also passed. Port is optional and -overrides \fB\-rpcport\fR. Use [host]:port notation for IPv6. This -option can be specified multiple times (default: 127.0.0.1 and -::1 i.e., localhost, or if \fB\-rpcallowip\fR has been specified, -0.0.0.0 and :: i.e., all addresses) +Bind to given address to listen for JSON\-RPC connections. Do not expose +the RPC server to untrusted networks such as the public internet! +This option is ignored unless \fB\-rpcallowip\fR is also passed. Port is +optional and overrides \fB\-rpcport\fR. Use [host]:port notation for +IPv6. This option can be specified multiple times (default: +127.0.0.1 and ::1 i.e., localhost) .HP \fB\-rpccookiefile=\fR<loc> .IP @@ -557,8 +560,8 @@ Password for JSON\-RPC connections .HP \fB\-rpcport=\fR<port> .IP -Listen for JSON\-RPC connections on <port> (default: 8332 or testnet: -18332) +Listen for JSON\-RPC connections on <port> (default: 8332, testnet: +18332, regtest: 18443) .HP \fB\-rpcserialversion\fR .IP diff --git a/doc/psbt.md b/doc/psbt.md index 7e6a93714d..560b45ef31 100644 --- a/doc/psbt.md +++ b/doc/psbt.md @@ -124,7 +124,7 @@ does not need to be involved. - Finally anyone can broadcast the transaction using `sendrawtransaction "T"`. In case there are more signers, it may be advantageous to let them all sign in -parallel, rather passing the PSBT from one signer to the next one. In the +parallel, rather than passing the PSBT from one signer to the next one. In the above example this would translate to Carol handing a copy of *P* to each signer separately. They can then all invoke `walletprocesspsbt "P"`, and end up with their individually-signed PSBT structures. They then all send those back to diff --git a/doc/release-notes-14565.md b/doc/release-notes-14565.md new file mode 100644 index 0000000000..38d76fee46 --- /dev/null +++ b/doc/release-notes-14565.md @@ -0,0 +1,5 @@ +Low-level RPC changes +--------------------- + +The `importmulti` RPC will now contain a new per-request `warnings` field with strings +that explain when fields are being ignored or inconsistant, if any. diff --git a/doc/release-notes.md b/doc/release-notes.md index 2c9c672ab8..6b4bb478e9 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -66,6 +66,13 @@ platform. Notable changes =============== +Mining +------ + +- Calls to `getblocktemplate` will fail if the segwit rule is not specified. + Calling `getblocktemplate` without segwit specified is almost certainly + a misconfiguration since doing so results in lower rewards for the miner. + Command line option changes --------------------------- @@ -183,6 +190,8 @@ in the Low-level Changes section below. P2SH-P2WPKH, and P2SH-P2WSH. Requests for P2WSH and P2SH-P2WSH accept an additional `witnessscript` parameter. +- See the [Mining](#mining) section for changes to `getblocktemplate`. + Low-level changes ================= diff --git a/doc/release-notes/release-notes-0.17.1.md b/doc/release-notes/release-notes-0.17.1.md new file mode 100644 index 0000000000..b1e50e0391 --- /dev/null +++ b/doc/release-notes/release-notes-0.17.1.md @@ -0,0 +1,168 @@ +Bitcoin Core version 0.17.1 is now available from: + + <https://bitcoincore.org/bin/bitcoin-core-0.17.1/> + +or through BitTorrent: + + magnet:?xt=urn:btih:c56c87ccfaa8e6fbccc90d549121e61efd97cb6f&dn=bitcoin-core-0.17.1&tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969&tr=udp%3A%2F%2Ftracker.leechers-paradise.org%3A6969&tr=udp%3A%2F%2Fzer0day.ch%3A1337&tr=udp%3A%2F%2Fexplodie.org%3A6969 + +This is a new minor version release, with 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. + +Notable changes +=============== + +`listtransactions` label support +-------------------------------- + +The `listtransactions` RPC `account` parameter which was deprecated in 0.17.0 +and renamed to `dummy` has been un-deprecated and renamed again to `label`. + +When bitcoin is configured with the `-deprecatedrpc=accounts` setting, specifying +a label/account/dummy argument will return both outgoing and incoming +transactions. Without the `-deprecatedrpc=accounts` setting, it will only return +incoming transactions (because it used to be possible to create transactions +spending from specific accounts, but this is no longer possible with labels). + +When `-deprecatedrpc=accounts` is set, it's possible to pass the empty string "" +to list transactions that don't have any label. Without +`-deprecatedrpc=accounts`, passing the empty string is an error because returning +only non-labeled transactions is not generally useful behavior and can cause +confusion. + +0.17.1 change log +================= + +### P2P protocol and network code +- #14685 `9406502` Fix a deserialization overflow edge case (kazcw) +- #14728 `b901578` Fix uninitialized read when stringifying an addrLocal (kazcw) + +### Wallet +- #14441 `5150acc` Restore ability to list incoming transactions by label (jnewbery) +- #13546 `91fa15a` Fix use of uninitialized value `bnb_used` in CWallet::CreateTransaction(…) (practicalswift) +- #14310 `bb90695` Ensure wallet is unlocked before signing (gustavonalle) +- #14690 `5782fdc` Throw error if CPubKey is invalid during PSBT keypath serialization (instagibbs) +- #14852 `2528443` backport: [tests] Add `wallet_balance.py` (MarcoFalke) +- #14196 `3362a95` psbt: always drop the unnecessary utxo and convert non-witness utxo to witness when necessary (achow101) +- #14588 `70ee1f8` Refactor PSBT signing logic to enforce invariant and fix signing bug (gwillen) +- #14424 `89a9a9d` Stop requiring imported pubkey to sign non-PKH schemes (sipa, MeshCollider) + +### RPC and other APIs +- #14417 `fb9ad04` Fix listreceivedbyaddress not taking address as a string (etscrivner) +- #14596 `de5e48a` Bugfix: RPC: Add `address_type` named param for createmultisig (luke-jr) +- #14618 `9666dba` Make HTTP RPC debug logging more informative (practicalswift) +- #14197 `7bee414` [psbt] Convert non-witness UTXOs to witness if witness sig created (achow101) +- #14377 `a3fe125` Check that a separator is found for psbt inputs, outputs, and global map (achow101) +- #14356 `7a590d8` Fix converttopsbt permitsigdata arg, add basic test (instagibbs) +- #14453 `75b5d8c` Fix wallet unload during walletpassphrase timeout (promag) + +### GUI +- #14403 `0242b5a` Revert "Force TLS1.0+ for SSL connections" (real-or-random) +- #14593 `df5131b` Explicitly disable "Dark Mode" appearance on macOS (fanquake) + +### Build system +- #14647 `7edebed` Remove illegal spacing in darwin.mk (ch4ot1c) +- #14698 `ec71f06` Add bitcoin-tx.exe into Windows installer (ken2812221) + +### Tests and QA +- #13965 `29899ec` Fix extended functional tests fail (ken2812221) +- #14011 `9461f98` Disable wallet and address book Qt tests on macOS minimal platform (ryanofsky) +- #14180 `86fadee` Run all tests even if wallet is not compiled (MarcoFalke) +- #14122 `8bc1bad` Test `rpc_help.py` failed: Check whether ZMQ is enabled or not (Kvaciral) +- #14101 `96dc936` Use named args in validation acceptance tests (MarcoFalke) +- #14020 `24d796a` Add tests for RPC help (promag) +- #14052 `7ff32a6` Add some actual witness in `rpc_rawtransaction` (MarcoFalke) +- #14215 `b72fbab` Use correct python index slices in example test (sdaftuar) +- #14024 `06544fa` Add `TestNode::assert_debug_log` (MarcoFalke) +- #14658 `60f7a97` Add test to ensure node can generate all rpc help texts at runtime (MarcoFalke) +- #14632 `96f15e8` Fix a comment (fridokus) +- #14700 `f9db08e` Avoid race in `p2p_invalid_block` by waiting for the block request (MarcoFalke) +- #14845 `67225e2` Add `wallet_balance.py` (jnewbery) + +### Documentation +- #14161 `5f51fd6` doc/descriptors.md tweaks (ryanofsky) +- #14276 `85aacc4` Add autogen.sh in ARM Cross-compilation (walterwhite81) + +Credits +======= + +Thanks to everyone who directly contributed to this release: + +- Andrew Chow +- Chun Kuan Lee +- David A. Harding +- Eric Scrivner +- fanquake +- fridokus +- Glenn Willen +- Gregory Sanders +- gustavonalle +- John Newbery +- Jon Layton +- Jonas Schnelli +- João Barbosa +- Kaz Wesley +- Kvaciral +- Luke Dashjr +- MarcoFalke +- MeshCollider +- Pieter Wuille +- practicalswift +- Russell Yanofsky +- Sjors Provoost +- Suhas Daftuar +- Tim Ruffing +- Walter +- Wladimir J. van der Laan + +As well as everyone that helped translating on [Transifex](https://www.transifex.com/projects/p/bitcoin/). diff --git a/src/amount.h b/src/amount.h index 449fd1b15f..47968e80b1 100644 --- a/src/amount.h +++ b/src/amount.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2016 The Bitcoin Core developers +// Copyright (c) 2009-2018 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/bench/block_assemble.cpp b/src/bench/block_assemble.cpp index 2def0b23e2..8133fd9d65 100644 --- a/src/bench/block_assemble.cpp +++ b/src/bench/block_assemble.cpp @@ -27,7 +27,7 @@ static std::shared_ptr<CBlock> PrepareBlock(const CScript& coinbase_scriptPubKey { auto block = std::make_shared<CBlock>( BlockAssembler{Params()} - .CreateNewBlock(coinbase_scriptPubKey, /* fMineWitnessTx */ true) + .CreateNewBlock(coinbase_scriptPubKey) ->block); block->nTime = ::chainActive.Tip()->GetMedianTimePast() + 1; @@ -78,7 +78,7 @@ static void AssembleBlock(benchmark::State& state) ::pcoinsTip.reset(new CCoinsViewCache(pcoinsdbview.get())); const CChainParams& chainparams = Params(); - thread_group.create_thread(boost::bind(&CScheduler::serviceQueue, &scheduler)); + thread_group.create_thread(std::bind(&CScheduler::serviceQueue, &scheduler)); GetMainSignals().RegisterBackgroundSignalScheduler(scheduler); LoadGenesisBlock(chainparams); CValidationState state; diff --git a/src/bench/gcs_filter.cpp b/src/bench/gcs_filter.cpp index 6f4e384e3b..535ad35571 100644 --- a/src/bench/gcs_filter.cpp +++ b/src/bench/gcs_filter.cpp @@ -17,7 +17,7 @@ static void ConstructGCSFilter(benchmark::State& state) uint64_t siphash_k0 = 0; while (state.KeepRunning()) { - GCSFilter filter(siphash_k0, 0, 20, 1 << 20, elements); + GCSFilter filter({siphash_k0, 0, 20, 1 << 20}, elements); siphash_k0++; } @@ -32,7 +32,7 @@ static void MatchGCSFilter(benchmark::State& state) element[1] = static_cast<unsigned char>(i >> 8); elements.insert(std::move(element)); } - GCSFilter filter(0, 0, 20, 1 << 20, elements); + GCSFilter filter({0, 0, 20, 1 << 20}, elements); while (state.KeepRunning()) { filter.Match(GCSFilter::Element()); diff --git a/src/blockfilter.cpp b/src/blockfilter.cpp index 163e2a52ef..bcf24047ff 100644 --- a/src/blockfilter.cpp +++ b/src/blockfilter.cpp @@ -79,7 +79,7 @@ static uint64_t MapIntoRange(uint64_t x, uint64_t n) uint64_t GCSFilter::HashToRange(const Element& element) const { - uint64_t hash = CSipHasher(m_siphash_k0, m_siphash_k1) + uint64_t hash = CSipHasher(m_params.m_siphash_k0, m_params.m_siphash_k1) .Write(element.data(), element.size()) .Finalize(); return MapIntoRange(hash, m_F); @@ -96,16 +96,13 @@ std::vector<uint64_t> GCSFilter::BuildHashedSet(const ElementSet& elements) cons return hashed_elements; } -GCSFilter::GCSFilter(uint64_t siphash_k0, uint64_t siphash_k1, uint8_t P, uint32_t M) - : m_siphash_k0(siphash_k0), m_siphash_k1(siphash_k1), m_P(P), m_M(M), m_N(0), m_F(0) +GCSFilter::GCSFilter(const Params& params) + : m_params(params), m_N(0), m_F(0), m_encoded{0} {} -GCSFilter::GCSFilter(uint64_t siphash_k0, uint64_t siphash_k1, uint8_t P, uint32_t M, - std::vector<unsigned char> encoded_filter) - : GCSFilter(siphash_k0, siphash_k1, P, M) +GCSFilter::GCSFilter(const Params& params, std::vector<unsigned char> encoded_filter) + : m_params(params), m_encoded(std::move(encoded_filter)) { - m_encoded = std::move(encoded_filter); - VectorReader stream(GCS_SER_TYPE, GCS_SER_VERSION, m_encoded, 0); uint64_t N = ReadCompactSize(stream); @@ -113,29 +110,28 @@ GCSFilter::GCSFilter(uint64_t siphash_k0, uint64_t siphash_k1, uint8_t P, uint32 if (m_N != N) { throw std::ios_base::failure("N must be <2^32"); } - m_F = static_cast<uint64_t>(m_N) * static_cast<uint64_t>(m_M); + m_F = static_cast<uint64_t>(m_N) * static_cast<uint64_t>(m_params.m_M); // Verify that the encoded filter contains exactly N elements. If it has too much or too little // data, a std::ios_base::failure exception will be raised. BitStreamReader<VectorReader> bitreader(stream); for (uint64_t i = 0; i < m_N; ++i) { - GolombRiceDecode(bitreader, m_P); + GolombRiceDecode(bitreader, m_params.m_P); } if (!stream.empty()) { throw std::ios_base::failure("encoded_filter contains excess data"); } } -GCSFilter::GCSFilter(uint64_t siphash_k0, uint64_t siphash_k1, uint8_t P, uint32_t M, - const ElementSet& elements) - : GCSFilter(siphash_k0, siphash_k1, P, M) +GCSFilter::GCSFilter(const Params& params, const ElementSet& elements) + : m_params(params) { size_t N = elements.size(); m_N = static_cast<uint32_t>(N); if (m_N != N) { throw std::invalid_argument("N must be <2^32"); } - m_F = static_cast<uint64_t>(m_N) * static_cast<uint64_t>(m_M); + m_F = static_cast<uint64_t>(m_N) * static_cast<uint64_t>(m_params.m_M); CVectorWriter stream(GCS_SER_TYPE, GCS_SER_VERSION, m_encoded, 0); @@ -150,7 +146,7 @@ GCSFilter::GCSFilter(uint64_t siphash_k0, uint64_t siphash_k1, uint8_t P, uint32 uint64_t last_value = 0; for (uint64_t value : BuildHashedSet(elements)) { uint64_t delta = value - last_value; - GolombRiceEncode(bitwriter, m_P, delta); + GolombRiceEncode(bitwriter, m_params.m_P, delta); last_value = value; } @@ -170,7 +166,7 @@ bool GCSFilter::MatchInternal(const uint64_t* element_hashes, size_t size) const uint64_t value = 0; size_t hashes_index = 0; for (uint32_t i = 0; i < m_N; ++i) { - uint64_t delta = GolombRiceDecode(bitreader, m_P); + uint64_t delta = GolombRiceDecode(bitreader, m_params.m_P); value += delta; while (true) { @@ -225,19 +221,39 @@ static GCSFilter::ElementSet BasicFilterElements(const CBlock& block, return elements; } +BlockFilter::BlockFilter(BlockFilterType filter_type, const uint256& block_hash, + std::vector<unsigned char> filter) + : m_filter_type(filter_type), m_block_hash(block_hash) +{ + GCSFilter::Params params; + if (!BuildParams(params)) { + throw std::invalid_argument("unknown filter_type"); + } + m_filter = GCSFilter(params, std::move(filter)); +} + BlockFilter::BlockFilter(BlockFilterType filter_type, const CBlock& block, const CBlockUndo& block_undo) : m_filter_type(filter_type), m_block_hash(block.GetHash()) { + GCSFilter::Params params; + if (!BuildParams(params)) { + throw std::invalid_argument("unknown filter_type"); + } + m_filter = GCSFilter(params, BasicFilterElements(block, block_undo)); +} + +bool BlockFilter::BuildParams(GCSFilter::Params& params) const +{ switch (m_filter_type) { case BlockFilterType::BASIC: - m_filter = GCSFilter(m_block_hash.GetUint64(0), m_block_hash.GetUint64(1), - BASIC_FILTER_P, BASIC_FILTER_M, - BasicFilterElements(block, block_undo)); - break; - - default: - throw std::invalid_argument("unknown filter_type"); + params.m_siphash_k0 = m_block_hash.GetUint64(0); + params.m_siphash_k1 = m_block_hash.GetUint64(1); + params.m_P = BASIC_FILTER_P; + params.m_M = BASIC_FILTER_M; + return true; } + + return false; } uint256 BlockFilter::GetHash() const diff --git a/src/blockfilter.h b/src/blockfilter.h index 871be11769..4d1f51dd60 100644 --- a/src/blockfilter.h +++ b/src/blockfilter.h @@ -25,11 +25,20 @@ public: typedef std::vector<unsigned char> Element; typedef std::unordered_set<Element, ByteVectorHash> ElementSet; + struct Params + { + uint64_t m_siphash_k0; + uint64_t m_siphash_k1; + uint8_t m_P; //!< Golomb-Rice coding parameter + uint32_t m_M; //!< Inverse false positive rate + + Params(uint64_t siphash_k0 = 0, uint64_t siphash_k1 = 0, uint8_t P = 0, uint32_t M = 1) + : m_siphash_k0(siphash_k0), m_siphash_k1(siphash_k1), m_P(P), m_M(M) + {} + }; + private: - uint64_t m_siphash_k0; - uint64_t m_siphash_k1; - uint8_t m_P; //!< Golomb-Rice coding parameter - uint32_t m_M; //!< Inverse false positive rate + Params m_params; uint32_t m_N; //!< Number of elements in the filter uint64_t m_F; //!< Range of element hashes, F = N * M std::vector<unsigned char> m_encoded; @@ -45,19 +54,16 @@ private: public: /** Constructs an empty filter. */ - GCSFilter(uint64_t siphash_k0 = 0, uint64_t siphash_k1 = 0, uint8_t P = 0, uint32_t M = 0); + explicit GCSFilter(const Params& params = Params()); /** Reconstructs an already-created filter from an encoding. */ - GCSFilter(uint64_t siphash_k0, uint64_t siphash_k1, uint8_t P, uint32_t M, - std::vector<unsigned char> encoded_filter); + GCSFilter(const Params& params, std::vector<unsigned char> encoded_filter); /** Builds a new filter from the params and set of elements. */ - GCSFilter(uint64_t siphash_k0, uint64_t siphash_k1, uint8_t P, uint32_t M, - const ElementSet& elements); + GCSFilter(const Params& params, const ElementSet& elements); - uint8_t GetP() const { return m_P; } uint32_t GetN() const { return m_N; } - uint32_t GetM() const { return m_M; } + const Params& GetParams() const { return m_params; } const std::vector<unsigned char>& GetEncoded() const { return m_encoded; } /** @@ -93,13 +99,21 @@ private: uint256 m_block_hash; GCSFilter m_filter; + bool BuildParams(GCSFilter::Params& params) const; + public: - // Construct a new BlockFilter of the specified type from a block. + BlockFilter() = default; + + //! Reconstruct a BlockFilter from parts. + BlockFilter(BlockFilterType filter_type, const uint256& block_hash, + std::vector<unsigned char> filter); + + //! Construct a new BlockFilter of the specified type from a block. BlockFilter(BlockFilterType filter_type, const CBlock& block, const CBlockUndo& block_undo); BlockFilterType GetFilterType() const { return m_filter_type; } - + const uint256& GetBlockHash() const { return m_block_hash; } const GCSFilter& GetFilter() const { return m_filter; } const std::vector<unsigned char>& GetEncodedFilter() const @@ -107,10 +121,10 @@ public: return m_filter.GetEncoded(); } - // Compute the filter hash. + //! Compute the filter hash. uint256 GetHash() const; - // Compute the filter header given the previous one. + //! Compute the filter header given the previous one. uint256 ComputeHeader(const uint256& prev_header) const; template <typename Stream> @@ -131,15 +145,11 @@ public: m_filter_type = static_cast<BlockFilterType>(filter_type); - switch (m_filter_type) { - case BlockFilterType::BASIC: - m_filter = GCSFilter(m_block_hash.GetUint64(0), m_block_hash.GetUint64(1), - BASIC_FILTER_P, BASIC_FILTER_M, std::move(encoded_filter)); - break; - - default: + GCSFilter::Params params; + if (!BuildParams(params)) { throw std::ios_base::failure("unknown filter_type"); } + m_filter = GCSFilter(params, std::move(encoded_filter)); } }; diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp index b17a8bb31d..0a7eacfb91 100644 --- a/src/consensus/tx_verify.cpp +++ b/src/consensus/tx_verify.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2017 The Bitcoin Core developers +// 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. @@ -1,4 +1,4 @@ -// Copyright (c) 2017 The Bitcoin Core developers +// 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. diff --git a/src/init.cpp b/src/init.cpp index 8ecd79197f..a3a7c5a3bb 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -59,7 +59,6 @@ #include <boost/algorithm/string/classification.hpp> #include <boost/algorithm/string/replace.hpp> #include <boost/algorithm/string/split.hpp> -#include <boost/bind.hpp> #include <boost/thread.hpp> #include <openssl/crypto.h> @@ -1237,8 +1236,8 @@ bool AppInitMain(InitInterfaces& interfaces) } // Start the lightweight task scheduler thread - CScheduler::Function serviceLoop = boost::bind(&CScheduler::serviceQueue, &scheduler); - threadGroup.create_thread(boost::bind(&TraceThread<CScheduler::Function>, "scheduler", serviceLoop)); + CScheduler::Function serviceLoop = std::bind(&CScheduler::serviceQueue, &scheduler); + threadGroup.create_thread(std::bind(&TraceThread<CScheduler::Function>, "scheduler", serviceLoop)); GetMainSignals().RegisterBackgroundSignalScheduler(scheduler); GetMainSignals().RegisterWithMempoolSignals(mempool); @@ -1646,7 +1645,7 @@ bool AppInitMain(InitInterfaces& interfaces) vImportFiles.push_back(strFile); } - threadGroup.create_thread(boost::bind(&ThreadImport, vImportFiles)); + threadGroup.create_thread(std::bind(&ThreadImport, vImportFiles)); // Wait for genesis block to be processed { diff --git a/src/key_io.cpp b/src/key_io.cpp index 282385f50d..d998089535 100644 --- a/src/key_io.cpp +++ b/src/key_io.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 The Bitcoin Core developers +// Copyright (c) 2014-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. diff --git a/src/miner.cpp b/src/miner.cpp index 96c9cd6d2a..ef48a86e32 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -95,7 +95,7 @@ void BlockAssembler::resetBlock() nFees = 0; } -std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn, bool fMineWitnessTx) +std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn) { int64_t nTimeStart = GetTimeMicros(); @@ -139,7 +139,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc // not activated. // TODO: replace this with a call to main to assess validity of a mempool // transaction (which in most cases can be a no-op). - fIncludeWitness = IsWitnessEnabled(pindexPrev, chainparams.GetConsensus()) && fMineWitnessTx; + fIncludeWitness = IsWitnessEnabled(pindexPrev, chainparams.GetConsensus()); int nPackagesSelected = 0; int nDescendantsUpdated = 0; diff --git a/src/miner.h b/src/miner.h index 8cdcf7133b..44c50b01ad 100644 --- a/src/miner.h +++ b/src/miner.h @@ -157,7 +157,7 @@ public: BlockAssembler(const CChainParams& params, const Options& options); /** Construct a new block template with coinbase to scriptPubKeyIn */ - std::unique_ptr<CBlockTemplate> CreateNewBlock(const CScript& scriptPubKeyIn, bool fMineWitnessTx=true); + std::unique_ptr<CBlockTemplate> CreateNewBlock(const CScript& scriptPubKeyIn); private: // utility functions diff --git a/src/noui.h b/src/noui.h index 169c2bbd7f..79a79a9af2 100644 --- a/src/noui.h +++ b/src/noui.h @@ -1,4 +1,4 @@ -// Copyright (c) 2013-2014 The Bitcoin Core developers +// Copyright (c) 2013-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. diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp index 3afe6fe1b7..c49b9fa36b 100644 --- a/src/policy/fees.cpp +++ b/src/policy/fees.cpp @@ -511,7 +511,7 @@ void TxConfirmStats::removeTx(unsigned int entryHeight, unsigned int nBestSeenHe // of no harm to try to remove them again. bool CBlockPolicyEstimator::removeTx(uint256 hash, bool inBlock) { - LOCK(cs_feeEstimator); + LOCK(m_cs_fee_estimator); std::map<uint256, TxStatsInfo>::iterator pos = mapMemPoolTxs.find(hash); if (pos != mapMemPoolTxs.end()) { feeStats->removeTx(pos->second.blockHeight, nBestSeenHeight, pos->second.bucketIndex, inBlock); @@ -548,7 +548,7 @@ CBlockPolicyEstimator::~CBlockPolicyEstimator() void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, bool validFeeEstimate) { - LOCK(cs_feeEstimator); + LOCK(m_cs_fee_estimator); unsigned int txHeight = entry.GetHeight(); uint256 hash = entry.GetTx().GetHash(); if (mapMemPoolTxs.count(hash)) { @@ -615,7 +615,7 @@ bool CBlockPolicyEstimator::processBlockTx(unsigned int nBlockHeight, const CTxM void CBlockPolicyEstimator::processBlock(unsigned int nBlockHeight, std::vector<const CTxMemPoolEntry*>& entries) { - LOCK(cs_feeEstimator); + LOCK(m_cs_fee_estimator); if (nBlockHeight <= nBestSeenHeight) { // Ignore side chains and re-orgs; assuming they are random // they don't affect the estimate. @@ -693,7 +693,7 @@ CFeeRate CBlockPolicyEstimator::estimateRawFee(int confTarget, double successThr } } - LOCK(cs_feeEstimator); + LOCK(m_cs_fee_estimator); // Return failure if trying to analyze a target we're not tracking if (confTarget <= 0 || (unsigned int)confTarget > stats->GetMaxConfirms()) return CFeeRate(0); @@ -710,6 +710,7 @@ CFeeRate CBlockPolicyEstimator::estimateRawFee(int confTarget, double successThr unsigned int CBlockPolicyEstimator::HighestTargetTracked(FeeEstimateHorizon horizon) const { + LOCK(m_cs_fee_estimator); switch (horizon) { case FeeEstimateHorizon::SHORT_HALFLIFE: { return shortStats->GetMaxConfirms(); @@ -819,7 +820,7 @@ double CBlockPolicyEstimator::estimateConservativeFee(unsigned int doubleTarget, */ CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, FeeCalculation *feeCalc, bool conservative) const { - LOCK(cs_feeEstimator); + LOCK(m_cs_fee_estimator); if (feeCalc) { feeCalc->desiredTarget = confTarget; @@ -899,7 +900,7 @@ CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, FeeCalculation bool CBlockPolicyEstimator::Write(CAutoFile& fileout) const { try { - LOCK(cs_feeEstimator); + LOCK(m_cs_fee_estimator); fileout << 149900; // version required to read: 0.14.99 or later fileout << CLIENT_VERSION; // version that wrote the file fileout << nBestSeenHeight; @@ -924,7 +925,7 @@ bool CBlockPolicyEstimator::Write(CAutoFile& fileout) const bool CBlockPolicyEstimator::Read(CAutoFile& filein) { try { - LOCK(cs_feeEstimator); + LOCK(m_cs_fee_estimator); int nVersionRequired, nVersionThatWrote; filein >> nVersionRequired >> nVersionThatWrote; if (nVersionRequired > CLIENT_VERSION) @@ -983,7 +984,7 @@ bool CBlockPolicyEstimator::Read(CAutoFile& filein) void CBlockPolicyEstimator::FlushUnconfirmed() { int64_t startclear = GetTimeMicros(); - LOCK(cs_feeEstimator); + LOCK(m_cs_fee_estimator); size_t num_entries = mapMemPoolTxs.size(); // Remove every entry in mapMemPoolTxs while (!mapMemPoolTxs.empty()) { diff --git a/src/policy/fees.h b/src/policy/fees.h index 90f159b48c..c8472a12f5 100644 --- a/src/policy/fees.h +++ b/src/policy/fees.h @@ -228,10 +228,12 @@ public: unsigned int HighestTargetTracked(FeeEstimateHorizon horizon) const; private: - unsigned int nBestSeenHeight; - unsigned int firstRecordedHeight; - unsigned int historicalFirst; - unsigned int historicalBest; + mutable CCriticalSection m_cs_fee_estimator; + + unsigned int nBestSeenHeight GUARDED_BY(m_cs_fee_estimator); + unsigned int firstRecordedHeight GUARDED_BY(m_cs_fee_estimator); + unsigned int historicalFirst GUARDED_BY(m_cs_fee_estimator); + unsigned int historicalBest GUARDED_BY(m_cs_fee_estimator); struct TxStatsInfo { @@ -241,34 +243,32 @@ private: }; // map of txids to information about that transaction - std::map<uint256, TxStatsInfo> mapMemPoolTxs; + std::map<uint256, TxStatsInfo> mapMemPoolTxs GUARDED_BY(m_cs_fee_estimator); /** Classes to track historical data on transaction confirmations */ - std::unique_ptr<TxConfirmStats> feeStats; - std::unique_ptr<TxConfirmStats> shortStats; - std::unique_ptr<TxConfirmStats> longStats; - - unsigned int trackedTxs; - unsigned int untrackedTxs; + std::unique_ptr<TxConfirmStats> feeStats PT_GUARDED_BY(m_cs_fee_estimator); + std::unique_ptr<TxConfirmStats> shortStats PT_GUARDED_BY(m_cs_fee_estimator); + std::unique_ptr<TxConfirmStats> longStats PT_GUARDED_BY(m_cs_fee_estimator); - std::vector<double> buckets; // The upper-bound of the range for the bucket (inclusive) - std::map<double, unsigned int> bucketMap; // Map of bucket upper-bound to index into all vectors by bucket + unsigned int trackedTxs GUARDED_BY(m_cs_fee_estimator); + unsigned int untrackedTxs GUARDED_BY(m_cs_fee_estimator); - mutable CCriticalSection cs_feeEstimator; + std::vector<double> buckets GUARDED_BY(m_cs_fee_estimator); // The upper-bound of the range for the bucket (inclusive) + std::map<double, unsigned int> bucketMap GUARDED_BY(m_cs_fee_estimator); // Map of bucket upper-bound to index into all vectors by bucket /** Process a transaction confirmed in a block*/ - bool processBlockTx(unsigned int nBlockHeight, const CTxMemPoolEntry* entry); + bool processBlockTx(unsigned int nBlockHeight, const CTxMemPoolEntry* entry) EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator); /** Helper for estimateSmartFee */ - double estimateCombinedFee(unsigned int confTarget, double successThreshold, bool checkShorterHorizon, EstimationResult *result) const; + double estimateCombinedFee(unsigned int confTarget, double successThreshold, bool checkShorterHorizon, EstimationResult *result) const EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator); /** Helper for estimateSmartFee */ - double estimateConservativeFee(unsigned int doubleTarget, EstimationResult *result) const; + double estimateConservativeFee(unsigned int doubleTarget, EstimationResult *result) const EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator); /** Number of blocks of data recorded while fee estimates have been running */ - unsigned int BlockSpan() const; + unsigned int BlockSpan() const EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator); /** Number of blocks of recorded fee estimate data represented in saved data file */ - unsigned int HistoricalBlockSpan() const; + unsigned int HistoricalBlockSpan() const EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator); /** Calculation of highest target that reasonable estimate can be provided for */ - unsigned int MaxUsableEstimate() const; + unsigned int MaxUsableEstimate() const EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator); }; class FeeFilterRounder diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index c281e2bb02..15e1b98fb4 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -58,7 +58,6 @@ #include <QVBoxLayout> #include <QWindow> -#include <boost/bind.hpp> const std::string BitcoinGUI::DEFAULT_UIPLATFORM = #if defined(Q_OS_MAC) @@ -400,16 +399,18 @@ void BitcoinGUI::createMenuBar() QMenu* window_menu = appMenuBar->addMenu(tr("&Window")); - QAction* minimize_action = window_menu->addAction(tr("Minimize"), [] { + QAction* minimize_action = window_menu->addAction(tr("Minimize")); + minimize_action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_M)); + connect(minimize_action, &QAction::triggered, [] { qApp->focusWindow()->showMinimized(); - }, QKeySequence(Qt::CTRL + Qt::Key_M)); - + }); connect(qApp, &QApplication::focusWindowChanged, [minimize_action] (QWindow* window) { minimize_action->setEnabled(window != nullptr && (window->flags() & Qt::Dialog) != Qt::Dialog && window->windowState() != Qt::WindowMinimized); }); #ifdef Q_OS_MAC - QAction* zoom_action = window_menu->addAction(tr("Zoom"), [] { + QAction* zoom_action = window_menu->addAction(tr("Zoom")); + connect(zoom_action, &QAction::triggered, [] { QWindow* window = qApp->focusWindow(); if (window->windowState() != Qt::WindowMaximized) { window->showMaximized(); @@ -422,7 +423,8 @@ void BitcoinGUI::createMenuBar() zoom_action->setEnabled(window != nullptr); }); #else - QAction* restore_action = window_menu->addAction(tr("Restore"), [] { + QAction* restore_action = window_menu->addAction(tr("Restore")); + connect(restore_action, &QAction::triggered, [] { qApp->focusWindow()->showNormal(); }); @@ -433,7 +435,8 @@ void BitcoinGUI::createMenuBar() if (walletFrame) { window_menu->addSeparator(); - window_menu->addAction(tr("Main Window"), [this] { + QAction* main_window_action = window_menu->addAction(tr("Main Window")); + connect(main_window_action, &QAction::triggered, [this] { GUIUtil::bringToFront(this); }); @@ -444,7 +447,8 @@ void BitcoinGUI::createMenuBar() window_menu->addSeparator(); for (RPCConsole::TabTypes tab_type : rpcConsole->tabs()) { - window_menu->addAction(rpcConsole->tabTitle(tab_type), [this, tab_type] { + QAction* tab_action = window_menu->addAction(rpcConsole->tabTitle(tab_type)); + connect(tab_action, &QAction::triggered, [this, tab_type] { rpcConsole->setTabFocus(tab_type); showDebugWindow(); }); @@ -1289,8 +1293,8 @@ static bool ThreadSafeMessageBox(BitcoinGUI* gui, const std::string& message, co void BitcoinGUI::subscribeToCoreSignals() { // Connect signals to client - m_handler_message_box = m_node.handleMessageBox(boost::bind(ThreadSafeMessageBox, this, _1, _2, _3)); - m_handler_question = m_node.handleQuestion(boost::bind(ThreadSafeMessageBox, this, _1, _3, _4)); + m_handler_message_box = m_node.handleMessageBox(std::bind(ThreadSafeMessageBox, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + m_handler_question = m_node.handleQuestion(std::bind(ThreadSafeMessageBox, this, std::placeholders::_1, std::placeholders::_3, std::placeholders::_4)); } void BitcoinGUI::unsubscribeFromCoreSignals() diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 75012b279c..217326f818 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -252,13 +252,13 @@ static void BlockTipChanged(ClientModel *clientmodel, bool initialSync, int heig void ClientModel::subscribeToCoreSignals() { // Connect signals to client - m_handler_show_progress = m_node.handleShowProgress(boost::bind(ShowProgress, this, _1, _2)); - m_handler_notify_num_connections_changed = m_node.handleNotifyNumConnectionsChanged(boost::bind(NotifyNumConnectionsChanged, this, _1)); - m_handler_notify_network_active_changed = m_node.handleNotifyNetworkActiveChanged(boost::bind(NotifyNetworkActiveChanged, this, _1)); - m_handler_notify_alert_changed = m_node.handleNotifyAlertChanged(boost::bind(NotifyAlertChanged, this)); - m_handler_banned_list_changed = m_node.handleBannedListChanged(boost::bind(BannedListChanged, this)); - m_handler_notify_block_tip = m_node.handleNotifyBlockTip(boost::bind(BlockTipChanged, this, _1, _2, _3, _4, false)); - m_handler_notify_header_tip = m_node.handleNotifyHeaderTip(boost::bind(BlockTipChanged, this, _1, _2, _3, _4, true)); + m_handler_show_progress = m_node.handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2)); + m_handler_notify_num_connections_changed = m_node.handleNotifyNumConnectionsChanged(std::bind(NotifyNumConnectionsChanged, this, std::placeholders::_1)); + m_handler_notify_network_active_changed = m_node.handleNotifyNetworkActiveChanged(std::bind(NotifyNetworkActiveChanged, this, std::placeholders::_1)); + m_handler_notify_alert_changed = m_node.handleNotifyAlertChanged(std::bind(NotifyAlertChanged, this)); + m_handler_banned_list_changed = m_node.handleBannedListChanged(std::bind(BannedListChanged, this)); + m_handler_notify_block_tip = m_node.handleNotifyBlockTip(std::bind(BlockTipChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, false)); + m_handler_notify_header_tip = m_node.handleNotifyHeaderTip(std::bind(BlockTipChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, true)); } void ClientModel::unsubscribeFromCoreSignals() diff --git a/src/qt/macnotificationhandler.mm b/src/qt/macnotificationhandler.mm index 0e04d50baa..a07079eece 100644 --- a/src/qt/macnotificationhandler.mm +++ b/src/qt/macnotificationhandler.mm @@ -24,25 +24,10 @@ void MacNotificationHandler::showNotification(const QString &title, const QStrin { // check if users OS has support for NSUserNotification if(this->hasUserNotificationCenterSupport()) { - // okay, seems like 10.8+ - QByteArray utf8 = title.toUtf8(); - char* cString = (char *)utf8.constData(); - NSString *titleMac = [[NSString alloc] initWithUTF8String:cString]; - - utf8 = text.toUtf8(); - cString = (char *)utf8.constData(); - NSString *textMac = [[NSString alloc] initWithUTF8String:cString]; - - // do everything weak linked (because we will keep <10.8 compatibility) - id userNotification = [[NSClassFromString(@"NSUserNotification") alloc] init]; - [userNotification performSelector:@selector(setTitle:) withObject:titleMac]; - [userNotification performSelector:@selector(setInformativeText:) withObject:textMac]; - - id notificationCenterInstance = [NSClassFromString(@"NSUserNotificationCenter") performSelector:@selector(defaultUserNotificationCenter)]; - [notificationCenterInstance performSelector:@selector(deliverNotification:) withObject:userNotification]; - - [titleMac release]; - [textMac release]; + NSUserNotification* userNotification = [[NSUserNotification alloc] init]; + userNotification.title = title.toNSString(); + userNotification.informativeText = text.toNSString(); + [[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification: userNotification]; [userNotification release]; } } diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp index 7b952f9fd7..b109a08b1c 100644 --- a/src/qt/splashscreen.cpp +++ b/src/qt/splashscreen.cpp @@ -24,7 +24,6 @@ #include <QPainter> #include <QRadialGradient> -#include <boost/bind.hpp> SplashScreen::SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const NetworkStyle *networkStyle) : QWidget(0, f), curAlignment(0), m_node(node) @@ -174,7 +173,7 @@ static void ShowProgress(SplashScreen *splash, const std::string &title, int nPr #ifdef ENABLE_WALLET void SplashScreen::ConnectWallet(std::unique_ptr<interfaces::Wallet> wallet) { - m_connected_wallet_handlers.emplace_back(wallet->handleShowProgress(boost::bind(ShowProgress, this, _1, _2, false))); + m_connected_wallet_handlers.emplace_back(wallet->handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2, false))); m_connected_wallets.emplace_back(std::move(wallet)); } #endif @@ -182,8 +181,8 @@ void SplashScreen::ConnectWallet(std::unique_ptr<interfaces::Wallet> wallet) void SplashScreen::subscribeToCoreSignals() { // Connect signals to client - m_handler_init_message = m_node.handleInitMessage(boost::bind(InitMessage, this, _1)); - m_handler_show_progress = m_node.handleShowProgress(boost::bind(ShowProgress, this, _1, _2, _3)); + m_handler_init_message = m_node.handleInitMessage(std::bind(InitMessage, this, std::placeholders::_1)); + m_handler_show_progress = m_node.handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); #ifdef ENABLE_WALLET m_handler_load_wallet = m_node.handleLoadWallet([this](std::unique_ptr<interfaces::Wallet> wallet) { ConnectWallet(std::move(wallet)); }); #endif diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 1983c3bc92..01722146c5 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -27,7 +27,6 @@ #include <QIcon> #include <QList> -#include <boost/bind.hpp> // Amount column is right-aligned it contains numbers static int column_alignments[] = { @@ -745,8 +744,8 @@ static void ShowProgress(TransactionTableModel *ttm, const std::string &title, i void TransactionTableModel::subscribeToCoreSignals() { // Connect signals to wallet - m_handler_transaction_changed = walletModel->wallet().handleTransactionChanged(boost::bind(NotifyTransactionChanged, this, _1, _2)); - m_handler_show_progress = walletModel->wallet().handleShowProgress(boost::bind(ShowProgress, this, _1, _2)); + m_handler_transaction_changed = walletModel->wallet().handleTransactionChanged(std::bind(NotifyTransactionChanged, this, std::placeholders::_1, std::placeholders::_2)); + m_handler_show_progress = walletModel->wallet().handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2)); } void TransactionTableModel::unsubscribeFromCoreSignals() diff --git a/src/qt/walletframe.h b/src/qt/walletframe.h index 05ca569d7a..1963e89b24 100644 --- a/src/qt/walletframe.h +++ b/src/qt/walletframe.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2017 The Bitcoin Core developers +// 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. diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 353da0c9b4..ba16fdd5e1 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -428,12 +428,12 @@ static void NotifyWatchonlyChanged(WalletModel *walletmodel, bool fHaveWatchonly void WalletModel::subscribeToCoreSignals() { // Connect signals to wallet - m_handler_unload = m_wallet->handleUnload(boost::bind(&NotifyUnload, this)); - m_handler_status_changed = m_wallet->handleStatusChanged(boost::bind(&NotifyKeyStoreStatusChanged, this)); - m_handler_address_book_changed = m_wallet->handleAddressBookChanged(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5)); - m_handler_transaction_changed = m_wallet->handleTransactionChanged(boost::bind(NotifyTransactionChanged, this, _1, _2)); - m_handler_show_progress = m_wallet->handleShowProgress(boost::bind(ShowProgress, this, _1, _2)); - m_handler_watch_only_changed = m_wallet->handleWatchOnlyChanged(boost::bind(NotifyWatchonlyChanged, this, _1)); + m_handler_unload = m_wallet->handleUnload(std::bind(&NotifyUnload, this)); + m_handler_status_changed = m_wallet->handleStatusChanged(std::bind(&NotifyKeyStoreStatusChanged, this)); + m_handler_address_book_changed = m_wallet->handleAddressBookChanged(std::bind(NotifyAddressBookChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5)); + m_handler_transaction_changed = m_wallet->handleTransactionChanged(std::bind(NotifyTransactionChanged, this, std::placeholders::_1, std::placeholders::_2)); + m_handler_show_progress = m_wallet->handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2)); + m_handler_watch_only_changed = m_wallet->handleWatchOnlyChanged(std::bind(NotifyWatchonlyChanged, this, std::placeholders::_1)); } void WalletModel::unsubscribeFromCoreSignals() diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 93fa3a2728..2fd6f99be5 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -241,7 +241,7 @@ static UniValue prioritisetransaction(const JSONRPCRequest& request) "Accepts the transaction into mined blocks at a higher (or lower) priority\n", { {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction id."}, - {"dummy", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "API-Compatibility for previous API. Must be zero or null.\n" + {"dummy", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "null", "API-Compatibility for previous API. Must be zero or null.\n" " DEPRECATED. For forward compatibility use named arguments and omit this parameter."}, {"fee_delta", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The fee value (in satoshis) to add (or subtract, if negative).\n" " Note, that this value is not a fee rate. It is a value to modify absolute fee of the TX.\n" @@ -311,7 +311,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) " https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki#getblocktemplate_changes\n" " https://github.com/bitcoin/bips/blob/master/bip-0145.mediawiki\n", { - {"template_request", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "", "A json object in the following spec", + {"template_request", RPCArg::Type::OBJ, /* opt */ false, /* default_val */ "", "A json object in the following spec", { {"mode", RPCArg::Type::STR, /* opt */ true, /* default_val */ "", "This must be set to \"template\", \"proposal\" (see BIP 23), or omitted"}, {"capabilities", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "", "A list of strings", @@ -319,7 +319,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) {"support", RPCArg::Type::STR, /* opt */ true, /* default_val */ "", "client side supported feature, 'longpoll', 'coinbasetxn', 'coinbasevalue', 'proposal', 'serverlist', 'workid'"}, }, }, - {"rules", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "", "A list of strings", + {"rules", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "A list of strings", { {"support", RPCArg::Type::STR, /* opt */ true, /* default_val */ "", "client side supported softfork deployment"}, }, @@ -503,21 +503,17 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) } const struct VBDeploymentInfo& segwit_info = VersionBitsDeploymentInfo[Consensus::DEPLOYMENT_SEGWIT]; - // If the caller is indicating segwit support, then allow CreateNewBlock() - // to select witness transactions, after segwit activates (otherwise - // don't). - bool fSupportsSegwit = setClientRules.find(segwit_info.name) != setClientRules.end(); + // GBT must be called with 'segwit' set in the rules + if (setClientRules.count(segwit_info.name) != 1) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "getblocktemplate must be called with the segwit rule set (call with {\"rules\": [\"segwit\"]})"); + } // Update block static CBlockIndex* pindexPrev; static int64_t nStart; static std::unique_ptr<CBlockTemplate> pblocktemplate; - // Cache whether the last invocation was with segwit support, to avoid returning - // a segwit-block to a non-segwit caller. - static bool fLastTemplateSupportsSegwit = true; if (pindexPrev != chainActive.Tip() || - (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 5) || - fLastTemplateSupportsSegwit != fSupportsSegwit) + (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 5)) { // Clear pindexPrev so future calls make a new block, despite any failures from here on pindexPrev = nullptr; @@ -526,11 +522,10 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) nTransactionsUpdatedLast = mempool.GetTransactionsUpdated(); CBlockIndex* pindexPrevNew = chainActive.Tip(); nStart = GetTime(); - fLastTemplateSupportsSegwit = fSupportsSegwit; // Create new block CScript scriptDummy = CScript() << OP_TRUE; - pblocktemplate = BlockAssembler(Params()).CreateNewBlock(scriptDummy, fSupportsSegwit); + pblocktemplate = BlockAssembler(Params()).CreateNewBlock(scriptDummy); if (!pblocktemplate) throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); @@ -682,7 +677,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) result.pushKV("bits", strprintf("%08x", pblock->nBits)); result.pushKV("height", (int64_t)(pindexPrev->nHeight+1)); - if (!pblocktemplate->vchCoinbaseCommitment.empty() && fSupportsSegwit) { + if (!pblocktemplate->vchCoinbaseCommitment.empty()) { result.pushKV("default_witness_commitment", HexStr(pblocktemplate->vchCoinbaseCommitment.begin(), pblocktemplate->vchCoinbaseCommitment.end())); } diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 370ae8e4d7..bf00870107 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -130,8 +130,8 @@ static UniValue getrawtransaction(const JSONRPCRequest& request) " ],\n" " \"blockhash\" : \"hash\", (string) the block hash\n" " \"confirmations\" : n, (numeric) The confirmations\n" - " \"time\" : ttt, (numeric) The transaction time in seconds since epoch (Jan 1 1970 GMT)\n" " \"blocktime\" : ttt (numeric) The block time in seconds since epoch (Jan 1 1970 GMT)\n" + " \"time\" : ttt, (numeric) Same as \"blocktime\"\n" "}\n" "\nExamples:\n" @@ -1135,7 +1135,7 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request) "Sign the transaction, and get back the hex\n" + HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"") + "\nTest acceptance of the transaction (signed hex)\n" - + HelpExampleCli("testmempoolaccept", "\"signedhex\"") + + + HelpExampleCli("testmempoolaccept", "[\"signedhex\"]") + "\nAs a JSON-RPC call\n" + HelpExampleRpc("testmempoolaccept", "[\"signedhex\"]") ); diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 733f8601ee..e7e047334e 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -15,7 +15,6 @@ #include <util/strencodings.h> #include <util/system.h> -#include <boost/bind.hpp> #include <boost/signals2/signal.hpp> #include <boost/algorithm/string/classification.hpp> #include <boost/algorithm/string/split.hpp> @@ -24,7 +23,7 @@ #include <unordered_map> static CCriticalSection cs_rpcWarmup; -static bool fRPCRunning = false; +static std::atomic<bool> g_rpc_running{false}; static bool fRPCInWarmup GUARDED_BY(cs_rpcWarmup) = true; static std::string rpcWarmupStatus GUARDED_BY(cs_rpcWarmup) = "RPC server started"; /* Timer-creating functions */ @@ -303,7 +302,7 @@ bool CRPCTable::appendCommand(const std::string& name, const CRPCCommand* pcmd) void StartRPC() { LogPrint(BCLog::RPC, "Starting RPC\n"); - fRPCRunning = true; + g_rpc_running = true; g_rpcSignals.Started(); } @@ -311,7 +310,7 @@ void InterruptRPC() { LogPrint(BCLog::RPC, "Interrupting RPC\n"); // Interrupt e.g. running longpolls - fRPCRunning = false; + g_rpc_running = false; } void StopRPC() @@ -324,7 +323,7 @@ void StopRPC() bool IsRPCRunning() { - return fRPCRunning; + return g_rpc_running; } void SetRPCWarmupStatus(const std::string& newStatus) @@ -504,11 +503,7 @@ UniValue CRPCTable::execute(const JSONRPCRequest &request) const std::vector<std::string> CRPCTable::listCommands() const { std::vector<std::string> commandList; - typedef std::map<std::string, const CRPCCommand*> commandMap; - - std::transform( mapCommands.begin(), mapCommands.end(), - std::back_inserter(commandList), - boost::bind(&commandMap::value_type::first,_1) ); + for (const auto& i : mapCommands) commandList.emplace_back(i.first); return commandList; } diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp index 740f8351fe..b91baee4ac 100644 --- a/src/rpc/util.cpp +++ b/src/rpc/util.cpp @@ -258,20 +258,19 @@ std::string RPCHelpMan::ToString() const // Oneline summary ret += m_name; - bool is_optional{false}; + bool was_optional{false}; for (const auto& arg : m_args) { ret += " "; if (arg.m_optional) { - if (!is_optional) ret += "( "; - is_optional = true; + if (!was_optional) ret += "( "; + was_optional = true; } else { - // Currently we still support unnamed arguments, so any argument following an optional argument must also be optional - // If support for positional arguments is deprecated in the future, remove this line - assert(!is_optional); + if (was_optional) ret += ") "; + was_optional = false; } ret += arg.ToString(/* oneline */ true); } - if (is_optional) ret += " )"; + if (was_optional) ret += " )"; ret += "\n"; // Description @@ -285,8 +284,7 @@ std::string RPCHelpMan::ToString() const if (i == 0) ret += "\nArguments:\n"; // Push named argument name and description - const auto str_wrapper = (arg.m_type == RPCArg::Type::STR || arg.m_type == RPCArg::Type::STR_HEX) ? "\"" : ""; - sections.m_sections.emplace_back(std::to_string(i + 1) + ". " + str_wrapper + arg.m_name + str_wrapper, arg.ToDescriptionString()); + sections.m_sections.emplace_back(std::to_string(i + 1) + ". " + arg.m_name, arg.ToDescriptionString()); sections.m_max_pad = std::max(sections.m_max_pad, sections.m_sections.back().m_left.size()); // Recursively push nested args diff --git a/src/scheduler.cpp b/src/scheduler.cpp index 552391d7d0..b2da62fc75 100644 --- a/src/scheduler.cpp +++ b/src/scheduler.cpp @@ -8,7 +8,6 @@ #include <reverselock.h> #include <assert.h> -#include <boost/bind.hpp> #include <utility> CScheduler::CScheduler() : nThreadsServicingQueue(0), stopRequested(false), stopWhenEmpty(false) @@ -120,12 +119,12 @@ void CScheduler::scheduleFromNow(CScheduler::Function f, int64_t deltaMilliSecon static void Repeat(CScheduler* s, CScheduler::Function f, int64_t deltaMilliSeconds) { f(); - s->scheduleFromNow(boost::bind(&Repeat, s, f, deltaMilliSeconds), deltaMilliSeconds); + s->scheduleFromNow(std::bind(&Repeat, s, f, deltaMilliSeconds), deltaMilliSeconds); } void CScheduler::scheduleEvery(CScheduler::Function f, int64_t deltaMilliSeconds) { - scheduleFromNow(boost::bind(&Repeat, this, f, deltaMilliSeconds), deltaMilliSeconds); + scheduleFromNow(std::bind(&Repeat, this, f, deltaMilliSeconds), deltaMilliSeconds); } size_t CScheduler::getQueueInfo(boost::chrono::system_clock::time_point &first, diff --git a/src/scheduler.h b/src/scheduler.h index 6c45f508ec..6d7f42cf9f 100644 --- a/src/scheduler.h +++ b/src/scheduler.h @@ -25,7 +25,7 @@ // CScheduler* s = new CScheduler(); // s->scheduleFromNow(doSomething, 11); // Assuming a: void doSomething() { } // s->scheduleFromNow(std::bind(Class::func, this, argument), 3); -// boost::thread* t = new boost::thread(boost::bind(CScheduler::serviceQueue, s)); +// boost::thread* t = new boost::thread(std::bind(CScheduler::serviceQueue, s)); // // ... then at program shutdown, clean up the thread running serviceQueue: // t->interrupt(); diff --git a/src/test/blockfilter_tests.cpp b/src/test/blockfilter_tests.cpp index 2144202b8d..625362f446 100644 --- a/src/test/blockfilter_tests.cpp +++ b/src/test/blockfilter_tests.cpp @@ -29,7 +29,7 @@ BOOST_AUTO_TEST_CASE(gcsfilter_test) excluded_elements.insert(std::move(element2)); } - GCSFilter filter(0, 0, 10, 1 << 10, included_elements); + GCSFilter filter({0, 0, 10, 1 << 10}, included_elements); for (const auto& element : included_elements) { BOOST_CHECK(filter.Match(element)); @@ -39,6 +39,19 @@ BOOST_AUTO_TEST_CASE(gcsfilter_test) } } +BOOST_AUTO_TEST_CASE(gcsfilter_default_constructor) +{ + GCSFilter filter; + BOOST_CHECK_EQUAL(filter.GetN(), 0); + BOOST_CHECK_EQUAL(filter.GetEncoded().size(), 1); + + const GCSFilter::Params& params = filter.GetParams(); + BOOST_CHECK_EQUAL(params.m_siphash_k0, 0); + BOOST_CHECK_EQUAL(params.m_siphash_k1, 0); + BOOST_CHECK_EQUAL(params.m_P, 0); + BOOST_CHECK_EQUAL(params.m_M, 1); +} + BOOST_AUTO_TEST_CASE(blockfilter_basic_test) { CScript included_scripts[5], excluded_scripts[3]; @@ -88,6 +101,17 @@ BOOST_AUTO_TEST_CASE(blockfilter_basic_test) for (const CScript& script : excluded_scripts) { BOOST_CHECK(!filter.Match(GCSFilter::Element(script.begin(), script.end()))); } + + // Test serialization/unserialization. + BlockFilter block_filter2; + + CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); + stream << block_filter; + stream >> block_filter2; + + BOOST_CHECK_EQUAL(block_filter.GetFilterType(), block_filter2.GetFilterType()); + BOOST_CHECK_EQUAL(block_filter.GetBlockHash(), block_filter2.GetBlockHash()); + BOOST_CHECK(block_filter.GetEncodedFilter() == block_filter2.GetEncodedFilter()); } BOOST_AUTO_TEST_CASE(blockfilters_json_test) diff --git a/src/test/key_io_tests.cpp b/src/test/key_io_tests.cpp index 1abaabd658..bf295042de 100644 --- a/src/test/key_io_tests.cpp +++ b/src/test/key_io_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2017 The Bitcoin Core developers +// 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. diff --git a/src/test/scheduler_tests.cpp b/src/test/scheduler_tests.cpp index 12ec00ce02..100d65b779 100644 --- a/src/test/scheduler_tests.cpp +++ b/src/test/scheduler_tests.cpp @@ -7,7 +7,6 @@ #include <test/test_bitcoin.h> -#include <boost/bind.hpp> #include <boost/thread.hpp> #include <boost/test/unit_test.hpp> @@ -21,7 +20,7 @@ static void microTask(CScheduler& s, boost::mutex& mutex, int& counter, int delt } boost::chrono::system_clock::time_point noTime = boost::chrono::system_clock::time_point::min(); if (rescheduleTime != noTime) { - CScheduler::Function f = boost::bind(µTask, boost::ref(s), boost::ref(mutex), boost::ref(counter), -delta + 1, noTime); + CScheduler::Function f = std::bind(µTask, std::ref(s), std::ref(mutex), std::ref(counter), -delta + 1, noTime); s.schedule(f, rescheduleTime); } } @@ -69,8 +68,8 @@ BOOST_AUTO_TEST_CASE(manythreads) boost::chrono::system_clock::time_point t = now + boost::chrono::microseconds(randomMsec(rng)); boost::chrono::system_clock::time_point tReschedule = now + boost::chrono::microseconds(500 + randomMsec(rng)); int whichCounter = zeroToNine(rng); - CScheduler::Function f = boost::bind(µTask, boost::ref(microTasks), - boost::ref(counterMutex[whichCounter]), boost::ref(counter[whichCounter]), + CScheduler::Function f = std::bind(µTask, std::ref(microTasks), + std::ref(counterMutex[whichCounter]), std::ref(counter[whichCounter]), randomDelta(rng), tReschedule); microTasks.schedule(f, t); } @@ -82,20 +81,20 @@ BOOST_AUTO_TEST_CASE(manythreads) // As soon as these are created they will start running and servicing the queue boost::thread_group microThreads; for (int i = 0; i < 5; i++) - microThreads.create_thread(boost::bind(&CScheduler::serviceQueue, µTasks)); + microThreads.create_thread(std::bind(&CScheduler::serviceQueue, µTasks)); MicroSleep(600); now = boost::chrono::system_clock::now(); // More threads and more tasks: for (int i = 0; i < 5; i++) - microThreads.create_thread(boost::bind(&CScheduler::serviceQueue, µTasks)); + microThreads.create_thread(std::bind(&CScheduler::serviceQueue, µTasks)); for (int i = 0; i < 100; i++) { boost::chrono::system_clock::time_point t = now + boost::chrono::microseconds(randomMsec(rng)); boost::chrono::system_clock::time_point tReschedule = now + boost::chrono::microseconds(500 + randomMsec(rng)); int whichCounter = zeroToNine(rng); - CScheduler::Function f = boost::bind(µTask, boost::ref(microTasks), - boost::ref(counterMutex[whichCounter]), boost::ref(counter[whichCounter]), + CScheduler::Function f = std::bind(µTask, std::ref(microTasks), + std::ref(counterMutex[whichCounter]), std::ref(counter[whichCounter]), randomDelta(rng), tReschedule); microTasks.schedule(f, t); } @@ -126,7 +125,7 @@ BOOST_AUTO_TEST_CASE(singlethreadedscheduler_ordered) // if they don't we'll get out of order behaviour boost::thread_group threads; for (int i = 0; i < 5; ++i) { - threads.create_thread(boost::bind(&CScheduler::serviceQueue, &scheduler)); + threads.create_thread(std::bind(&CScheduler::serviceQueue, &scheduler)); } // these are not atomic, if SinglethreadedSchedulerClient prevents diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index bb8db9fa8d..858bb512fc 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -22,6 +22,8 @@ const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr; +FastRandomContext g_insecure_rand_ctx; + void CConnmanTest::AddNode(CNode& node) { LOCK(g_connman->cs_vNodes); @@ -37,8 +39,6 @@ void CConnmanTest::ClearNodes() g_connman->vNodes.clear(); } -thread_local FastRandomContext g_insecure_rand_ctx; - std::ostream& operator<<(std::ostream& os, const uint256& num) { os << num.ToString(); @@ -89,7 +89,7 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha // We have to run a scheduler thread to prevent ActivateBestChain // from blocking due to queue overrun. - threadGroup.create_thread(boost::bind(&CScheduler::serviceQueue, &scheduler)); + threadGroup.create_thread(std::bind(&CScheduler::serviceQueue, &scheduler)); GetMainSignals().RegisterBackgroundSignalScheduler(scheduler); mempool.setSanityCheck(1.0); diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h index 71c0379eac..31d90c0151 100644 --- a/src/test/test_bitcoin.h +++ b/src/test/test_bitcoin.h @@ -26,7 +26,14 @@ std::ostream& operator<<(typename std::enable_if<std::is_enum<T>::value, std::os return stream << static_cast<typename std::underlying_type<T>::type>(e); } -thread_local extern FastRandomContext g_insecure_rand_ctx; +/** + * This global and the helpers that use it are not thread-safe. + * + * If thread-safety is needed, the global could be made thread_local (given + * that thread_local is supported on all architectures we support) or a + * per-thread instance could be used in the multi-threaded test. + */ +extern FastRandomContext g_insecure_rand_ctx; static inline void SeedInsecureRand(bool deterministic = false) { diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index c68b6bbb4d..d952054483 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -467,7 +467,7 @@ BOOST_AUTO_TEST_CASE(test_big_witness_transaction) { CCheckQueueControl<CScriptCheck> control(&scriptcheckqueue); for (int i=0; i<20; i++) - threadGroup.create_thread(boost::bind(&CCheckQueue<CScriptCheck>::Thread, boost::ref(scriptcheckqueue))); + threadGroup.create_thread(std::bind(&CCheckQueue<CScriptCheck>::Thread, std::ref(scriptcheckqueue))); std::vector<Coin> coins; for(uint32_t i = 0; i < mtx.vin.size(); i++) { diff --git a/src/test/txvalidation_tests.cpp b/src/test/txvalidation_tests.cpp index 473ec5addf..c2777cd6d1 100644 --- a/src/test/txvalidation_tests.cpp +++ b/src/test/txvalidation_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017 The Bitcoin Core developers +// 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. diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp index 7dd176b25d..44432cd0a1 100644 --- a/src/test/validation_block_tests.cpp +++ b/src/test/validation_block_tests.cpp @@ -54,7 +54,7 @@ std::shared_ptr<CBlock> Block(const uint256& prev_hash) CScript pubKey; pubKey << i++ << OP_TRUE; - auto ptemplate = BlockAssembler(Params()).CreateNewBlock(pubKey, false); + auto ptemplate = BlockAssembler(Params()).CreateNewBlock(pubKey); auto pblock = std::make_shared<CBlock>(ptemplate->block); pblock->hashPrevBlock = prev_hash; pblock->nTime = ++time; @@ -152,12 +152,13 @@ BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering) // create a bunch of threads that repeatedly process a block generated above at random // this will create parallelism and randomness inside validation - the ValidationInterface // will subscribe to events generated during block validation and assert on ordering invariance - boost::thread_group threads; + std::vector<std::thread> threads; for (int i = 0; i < 10; i++) { - threads.create_thread([&blocks]() { + threads.emplace_back([&blocks]() { bool ignored; + FastRandomContext insecure; for (int i = 0; i < 1000; i++) { - auto block = blocks[InsecureRandRange(blocks.size() - 1)]; + auto block = blocks[insecure.randrange(blocks.size() - 1)]; ProcessNewBlock(Params(), block, true, &ignored); } @@ -171,7 +172,9 @@ BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering) }); } - threads.join_all(); + for (auto& t : threads) { + t.join(); + } while (GetMainSignals().CallbacksPending() > 0) { MilliSleep(100); } diff --git a/src/threadinterrupt.h b/src/threadinterrupt.h index 9c6fccfcde..2743571379 100644 --- a/src/threadinterrupt.h +++ b/src/threadinterrupt.h @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2017 The Bitcoin Core developers +// Copyright (c) 2016-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. diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp index 229cc7d553..c9ee6f9f81 100644 --- a/src/torcontrol.cpp +++ b/src/torcontrol.cpp @@ -15,7 +15,6 @@ #include <set> #include <stdlib.h> -#include <boost/bind.hpp> #include <boost/signals2/signal.hpp> #include <boost/algorithm/string/split.hpp> #include <boost/algorithm/string/classification.hpp> @@ -459,8 +458,8 @@ TorController::TorController(struct event_base* _base, const std::string& _targe if (!reconnect_ev) LogPrintf("tor: Failed to create event for reconnection: out of memory?\n"); // Start connection attempts immediately - if (!conn.Connect(_target, boost::bind(&TorController::connected_cb, this, _1), - boost::bind(&TorController::disconnected_cb, this, _1) )) { + if (!conn.Connect(_target, std::bind(&TorController::connected_cb, this, std::placeholders::_1), + std::bind(&TorController::disconnected_cb, this, std::placeholders::_1) )) { LogPrintf("tor: Initiating connection to Tor control port %s failed\n", _target); } // Read service private key if cached @@ -538,7 +537,7 @@ void TorController::auth_cb(TorControlConnection& _conn, const TorControlReply& // Note that the 'virtual' port doesn't have to be the same as our internal port, but this is just a convenient // choice. TODO; refactor the shutdown sequence some day. _conn.Command(strprintf("ADD_ONION %s Port=%i,127.0.0.1:%i", private_key, GetListenPort(), GetListenPort()), - boost::bind(&TorController::add_onion_cb, this, _1, _2)); + std::bind(&TorController::add_onion_cb, this, std::placeholders::_1, std::placeholders::_2)); } else { LogPrintf("tor: Authentication failed\n"); } @@ -597,7 +596,7 @@ void TorController::authchallenge_cb(TorControlConnection& _conn, const TorContr } std::vector<uint8_t> computedClientHash = ComputeResponse(TOR_SAFE_CLIENTKEY, cookie, clientNonce, serverNonce); - _conn.Command("AUTHENTICATE " + HexStr(computedClientHash), boost::bind(&TorController::auth_cb, this, _1, _2)); + _conn.Command("AUTHENTICATE " + HexStr(computedClientHash), std::bind(&TorController::auth_cb, this, std::placeholders::_1, std::placeholders::_2)); } else { LogPrintf("tor: Invalid reply to AUTHCHALLENGE\n"); } @@ -646,23 +645,23 @@ void TorController::protocolinfo_cb(TorControlConnection& _conn, const TorContro if (methods.count("HASHEDPASSWORD")) { LogPrint(BCLog::TOR, "tor: Using HASHEDPASSWORD authentication\n"); boost::replace_all(torpassword, "\"", "\\\""); - _conn.Command("AUTHENTICATE \"" + torpassword + "\"", boost::bind(&TorController::auth_cb, this, _1, _2)); + _conn.Command("AUTHENTICATE \"" + torpassword + "\"", std::bind(&TorController::auth_cb, this, std::placeholders::_1, std::placeholders::_2)); } else { LogPrintf("tor: Password provided with -torpassword, but HASHEDPASSWORD authentication is not available\n"); } } else if (methods.count("NULL")) { LogPrint(BCLog::TOR, "tor: Using NULL authentication\n"); - _conn.Command("AUTHENTICATE", boost::bind(&TorController::auth_cb, this, _1, _2)); + _conn.Command("AUTHENTICATE", std::bind(&TorController::auth_cb, this, std::placeholders::_1, std::placeholders::_2)); } else if (methods.count("SAFECOOKIE")) { // Cookie: hexdump -e '32/1 "%02x""\n"' ~/.tor/control_auth_cookie LogPrint(BCLog::TOR, "tor: Using SAFECOOKIE authentication, reading cookie authentication from %s\n", cookiefile); std::pair<bool,std::string> status_cookie = ReadBinaryFile(cookiefile, TOR_COOKIE_SIZE); if (status_cookie.first && status_cookie.second.size() == TOR_COOKIE_SIZE) { - // _conn.Command("AUTHENTICATE " + HexStr(status_cookie.second), boost::bind(&TorController::auth_cb, this, _1, _2)); + // _conn.Command("AUTHENTICATE " + HexStr(status_cookie.second), std::bind(&TorController::auth_cb, this, std::placeholders::_1, std::placeholders::_2)); cookie = std::vector<uint8_t>(status_cookie.second.begin(), status_cookie.second.end()); clientNonce = std::vector<uint8_t>(TOR_NONCE_SIZE, 0); GetRandBytes(clientNonce.data(), TOR_NONCE_SIZE); - _conn.Command("AUTHCHALLENGE SAFECOOKIE " + HexStr(clientNonce), boost::bind(&TorController::authchallenge_cb, this, _1, _2)); + _conn.Command("AUTHCHALLENGE SAFECOOKIE " + HexStr(clientNonce), std::bind(&TorController::authchallenge_cb, this, std::placeholders::_1, std::placeholders::_2)); } else { if (status_cookie.first) { LogPrintf("tor: Authentication cookie %s is not exactly %i bytes, as is required by the spec\n", cookiefile, TOR_COOKIE_SIZE); @@ -684,7 +683,7 @@ void TorController::connected_cb(TorControlConnection& _conn) { reconnect_timeout = RECONNECT_TIMEOUT_START; // First send a PROTOCOLINFO command to figure out what authentication is expected - if (!_conn.Command("PROTOCOLINFO 1", boost::bind(&TorController::protocolinfo_cb, this, _1, _2))) + if (!_conn.Command("PROTOCOLINFO 1", std::bind(&TorController::protocolinfo_cb, this, std::placeholders::_1, std::placeholders::_2))) LogPrintf("tor: Error sending initial protocolinfo command\n"); } @@ -711,8 +710,8 @@ void TorController::Reconnect() /* Try to reconnect and reestablish if we get booted - for example, Tor * may be restarting. */ - if (!conn.Connect(target, boost::bind(&TorController::connected_cb, this, _1), - boost::bind(&TorController::disconnected_cb, this, _1) )) { + if (!conn.Connect(target, std::bind(&TorController::connected_cb, this, std::placeholders::_1), + std::bind(&TorController::disconnected_cb, this, std::placeholders::_1) )) { LogPrintf("tor: Re-initiating connection to Tor control port %s failed\n", target); } } diff --git a/src/validation.cpp b/src/validation.cpp index 5696684ed6..00d7a60cb7 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2366,14 +2366,11 @@ class ConnectTrace { private: std::vector<PerBlockConnectTrace> blocksConnected; CTxMemPool &pool; + boost::signals2::scoped_connection m_connNotifyEntryRemoved; public: explicit ConnectTrace(CTxMemPool &_pool) : blocksConnected(1), pool(_pool) { - pool.NotifyEntryRemoved.connect(boost::bind(&ConnectTrace::NotifyEntryRemoved, this, _1, _2)); - } - - ~ConnectTrace() { - pool.NotifyEntryRemoved.disconnect(boost::bind(&ConnectTrace::NotifyEntryRemoved, this, _1, _2)); + m_connNotifyEntryRemoved = pool.NotifyEntryRemoved.connect(std::bind(&ConnectTrace::NotifyEntryRemoved, this, std::placeholders::_1, std::placeholders::_2)); } void BlockConnected(CBlockIndex* pindex, std::shared_ptr<const CBlock> pblock) { @@ -3374,10 +3371,30 @@ bool CChainState::AcceptBlockHeader(const CBlockHeader& block, CValidationState& if (!ContextualCheckBlockHeader(block, state, chainparams, pindexPrev, GetAdjustedTime())) return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state)); - // If the previous block index isn't valid, determine if it descends from any block which - // has been found invalid (m_failed_blocks), then mark pindexPrev and any blocks - // between them as failed. + /* Determine if this block descends from any block which has been found + * invalid (m_failed_blocks), then mark pindexPrev and any blocks between + * them as failed. For example: + * + * D3 + * / + * B2 - C2 + * / \ + * A D2 - E2 - F2 + * \ + * B1 - C1 - D1 - E1 + * + * In the case that we attempted to reorg from E1 to F2, only to find + * C2 to be invalid, we would mark D2, E2, and F2 as BLOCK_FAILED_CHILD + * but NOT D3 (it was not in any of our candidate sets at the time). + * + * In any case D3 will also be marked as BLOCK_FAILED_CHILD at restart + * in LoadBlockIndex. + */ if (!pindexPrev->IsValid(BLOCK_VALID_SCRIPTS)) { + // The above does not mean "invalid": it checks if the previous block + // hasn't been validated up to BLOCK_VALID_SCRIPTS. This is a performance + // optimization, in the common case of adding a new block to the tip, + // we don't need to iterate over the failed blocks list. for (const CBlockIndex* failedit : m_failed_blocks) { if (pindexPrev->GetAncestor(failedit->nHeight) == failedit) { assert(failedit->nStatus & BLOCK_FAILED_VALID); diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp index 214a9ffba9..533d412888 100644 --- a/src/validationinterface.cpp +++ b/src/validationinterface.cpp @@ -17,6 +17,18 @@ #include <boost/signals2/signal.hpp> +struct ValidationInterfaceConnections { + boost::signals2::scoped_connection UpdatedBlockTip; + boost::signals2::scoped_connection TransactionAddedToMempool; + boost::signals2::scoped_connection BlockConnected; + boost::signals2::scoped_connection BlockDisconnected; + boost::signals2::scoped_connection TransactionRemovedFromMempool; + boost::signals2::scoped_connection ChainStateFlushed; + boost::signals2::scoped_connection Broadcast; + boost::signals2::scoped_connection BlockChecked; + boost::signals2::scoped_connection NewPoWValidBlock; +}; + struct MainSignalsInstance { boost::signals2::signal<void (const CBlockIndex *, const CBlockIndex *, bool fInitialDownload)> UpdatedBlockTip; boost::signals2::signal<void (const CTransactionRef &)> TransactionAddedToMempool; @@ -32,12 +44,18 @@ struct MainSignalsInstance { // but must ensure all callbacks happen in-order, so we end up creating // our own queue here :( SingleThreadedSchedulerClient m_schedulerClient; + std::unordered_map<CValidationInterface*, ValidationInterfaceConnections> m_connMainSignals; explicit MainSignalsInstance(CScheduler *pscheduler) : m_schedulerClient(pscheduler) {} }; static CMainSignals g_signals; +// This map has to a separate global instead of a member of MainSignalsInstance, +// because RegisterWithMempoolSignals is currently called before RegisterBackgroundSignalScheduler, +// so MainSignalsInstance hasn't been created yet. +static std::unordered_map<CTxMemPool*, boost::signals2::scoped_connection> g_connNotifyEntryRemoved; + void CMainSignals::RegisterBackgroundSignalScheduler(CScheduler& scheduler) { assert(!m_internals); m_internals.reset(new MainSignalsInstance(&scheduler)); @@ -59,11 +77,11 @@ size_t CMainSignals::CallbacksPending() { } void CMainSignals::RegisterWithMempoolSignals(CTxMemPool& pool) { - pool.NotifyEntryRemoved.connect(boost::bind(&CMainSignals::MempoolEntryRemoved, this, _1, _2)); + g_connNotifyEntryRemoved.emplace(&pool, pool.NotifyEntryRemoved.connect(std::bind(&CMainSignals::MempoolEntryRemoved, this, std::placeholders::_1, std::placeholders::_2))); } void CMainSignals::UnregisterWithMempoolSignals(CTxMemPool& pool) { - pool.NotifyEntryRemoved.disconnect(boost::bind(&CMainSignals::MempoolEntryRemoved, this, _1, _2)); + g_connNotifyEntryRemoved.erase(&pool); } CMainSignals& GetMainSignals() @@ -72,42 +90,27 @@ CMainSignals& GetMainSignals() } void RegisterValidationInterface(CValidationInterface* pwalletIn) { - g_signals.m_internals->UpdatedBlockTip.connect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1, _2, _3)); - g_signals.m_internals->TransactionAddedToMempool.connect(boost::bind(&CValidationInterface::TransactionAddedToMempool, pwalletIn, _1)); - g_signals.m_internals->BlockConnected.connect(boost::bind(&CValidationInterface::BlockConnected, pwalletIn, _1, _2, _3)); - g_signals.m_internals->BlockDisconnected.connect(boost::bind(&CValidationInterface::BlockDisconnected, pwalletIn, _1)); - g_signals.m_internals->TransactionRemovedFromMempool.connect(boost::bind(&CValidationInterface::TransactionRemovedFromMempool, pwalletIn, _1)); - g_signals.m_internals->ChainStateFlushed.connect(boost::bind(&CValidationInterface::ChainStateFlushed, pwalletIn, _1)); - g_signals.m_internals->Broadcast.connect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1, _2)); - g_signals.m_internals->BlockChecked.connect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2)); - g_signals.m_internals->NewPoWValidBlock.connect(boost::bind(&CValidationInterface::NewPoWValidBlock, pwalletIn, _1, _2)); + ValidationInterfaceConnections& conns = g_signals.m_internals->m_connMainSignals[pwalletIn]; + conns.UpdatedBlockTip = g_signals.m_internals->UpdatedBlockTip.connect(std::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + conns.TransactionAddedToMempool = g_signals.m_internals->TransactionAddedToMempool.connect(std::bind(&CValidationInterface::TransactionAddedToMempool, pwalletIn, std::placeholders::_1)); + conns.BlockConnected = g_signals.m_internals->BlockConnected.connect(std::bind(&CValidationInterface::BlockConnected, pwalletIn, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + conns.BlockDisconnected = g_signals.m_internals->BlockDisconnected.connect(std::bind(&CValidationInterface::BlockDisconnected, pwalletIn, std::placeholders::_1)); + conns.TransactionRemovedFromMempool = g_signals.m_internals->TransactionRemovedFromMempool.connect(std::bind(&CValidationInterface::TransactionRemovedFromMempool, pwalletIn, std::placeholders::_1)); + conns.ChainStateFlushed = g_signals.m_internals->ChainStateFlushed.connect(std::bind(&CValidationInterface::ChainStateFlushed, pwalletIn, std::placeholders::_1)); + conns.Broadcast = g_signals.m_internals->Broadcast.connect(std::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, std::placeholders::_1, std::placeholders::_2)); + conns.BlockChecked = g_signals.m_internals->BlockChecked.connect(std::bind(&CValidationInterface::BlockChecked, pwalletIn, std::placeholders::_1, std::placeholders::_2)); + conns.NewPoWValidBlock = g_signals.m_internals->NewPoWValidBlock.connect(std::bind(&CValidationInterface::NewPoWValidBlock, pwalletIn, std::placeholders::_1, std::placeholders::_2)); } void UnregisterValidationInterface(CValidationInterface* pwalletIn) { - g_signals.m_internals->BlockChecked.disconnect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2)); - g_signals.m_internals->Broadcast.disconnect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1, _2)); - g_signals.m_internals->ChainStateFlushed.disconnect(boost::bind(&CValidationInterface::ChainStateFlushed, pwalletIn, _1)); - g_signals.m_internals->TransactionAddedToMempool.disconnect(boost::bind(&CValidationInterface::TransactionAddedToMempool, pwalletIn, _1)); - g_signals.m_internals->BlockConnected.disconnect(boost::bind(&CValidationInterface::BlockConnected, pwalletIn, _1, _2, _3)); - g_signals.m_internals->BlockDisconnected.disconnect(boost::bind(&CValidationInterface::BlockDisconnected, pwalletIn, _1)); - g_signals.m_internals->TransactionRemovedFromMempool.disconnect(boost::bind(&CValidationInterface::TransactionRemovedFromMempool, pwalletIn, _1)); - g_signals.m_internals->UpdatedBlockTip.disconnect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1, _2, _3)); - g_signals.m_internals->NewPoWValidBlock.disconnect(boost::bind(&CValidationInterface::NewPoWValidBlock, pwalletIn, _1, _2)); + g_signals.m_internals->m_connMainSignals.erase(pwalletIn); } void UnregisterAllValidationInterfaces() { if (!g_signals.m_internals) { return; } - g_signals.m_internals->BlockChecked.disconnect_all_slots(); - g_signals.m_internals->Broadcast.disconnect_all_slots(); - g_signals.m_internals->ChainStateFlushed.disconnect_all_slots(); - g_signals.m_internals->TransactionAddedToMempool.disconnect_all_slots(); - g_signals.m_internals->BlockConnected.disconnect_all_slots(); - g_signals.m_internals->BlockDisconnected.disconnect_all_slots(); - g_signals.m_internals->TransactionRemovedFromMempool.disconnect_all_slots(); - g_signals.m_internals->UpdatedBlockTip.disconnect_all_slots(); - g_signals.m_internals->NewPoWValidBlock.disconnect_all_slots(); + g_signals.m_internals->m_connMainSignals.clear(); } void CallFunctionInValidationInterfaceQueue(std::function<void ()> func) { diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 647af3eb86..ed1e2d3940 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -836,9 +836,98 @@ UniValue dumpwallet(const JSONRPCRequest& request) return reply; } +struct ImportData +{ + // Input data + std::unique_ptr<CScript> redeemscript; //!< Provided redeemScript; will be moved to `import_scripts` if relevant. + std::unique_ptr<CScript> witnessscript; //!< Provided witnessScript; will be moved to `import_scripts` if relevant. + + // Output data + std::set<CScript> import_scripts; + std::map<CKeyID, bool> used_keys; //!< Import these private keys if available (the value indicates whether if the key is required for solvability) +}; + +enum class ScriptContext +{ + TOP, //! Top-level scriptPubKey + P2SH, //! P2SH redeemScript + WITNESS_V0, //! P2WSH witnessScript +}; + +// Analyse the provided scriptPubKey, determining which keys and which redeem scripts from the ImportData struct are needed to spend it, and mark them as used. +// Returns an error string, or the empty string for success. +static std::string RecurseImportData(const CScript& script, ImportData& import_data, const ScriptContext script_ctx) +{ + // Use Solver to obtain script type and parsed pubkeys or hashes: + std::vector<std::vector<unsigned char>> solverdata; + txnouttype script_type = Solver(script, solverdata); + + switch (script_type) { + case TX_PUBKEY: { + CPubKey pubkey(solverdata[0].begin(), solverdata[0].end()); + import_data.used_keys.emplace(pubkey.GetID(), false); + return ""; + } + case TX_PUBKEYHASH: { + CKeyID id = CKeyID(uint160(solverdata[0])); + import_data.used_keys[id] = true; + return ""; + } + case TX_SCRIPTHASH: { + if (script_ctx == ScriptContext::P2SH) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Trying to nest P2SH inside another P2SH"); + if (script_ctx == ScriptContext::WITNESS_V0) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Trying to nest P2SH inside a P2WSH"); + assert(script_ctx == ScriptContext::TOP); + CScriptID id = CScriptID(uint160(solverdata[0])); + auto subscript = std::move(import_data.redeemscript); // Remove redeemscript from import_data to check for superfluous script later. + if (!subscript) return "missing redeemscript"; + if (CScriptID(*subscript) != id) return "redeemScript does not match the scriptPubKey"; + import_data.import_scripts.emplace(*subscript); + return RecurseImportData(*subscript, import_data, ScriptContext::P2SH); + } + case TX_MULTISIG: { + for (size_t i = 1; i + 1< solverdata.size(); ++i) { + CPubKey pubkey(solverdata[i].begin(), solverdata[i].end()); + import_data.used_keys.emplace(pubkey.GetID(), false); + } + return ""; + } + case TX_WITNESS_V0_SCRIPTHASH: { + if (script_ctx == ScriptContext::WITNESS_V0) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Trying to nest P2WSH inside another P2WSH"); + uint256 fullid(solverdata[0]); + CScriptID id; + CRIPEMD160().Write(fullid.begin(), fullid.size()).Finalize(id.begin()); + auto subscript = std::move(import_data.witnessscript); // Remove redeemscript from import_data to check for superfluous script later. + if (!subscript) return "missing witnessscript"; + if (CScriptID(*subscript) != id) return "witnessScript does not match the scriptPubKey or redeemScript"; + if (script_ctx == ScriptContext::TOP) { + import_data.import_scripts.emplace(script); // Special rule for IsMine: native P2WSH requires the TOP script imported (see script/ismine.cpp) + } + import_data.import_scripts.emplace(*subscript); + return RecurseImportData(*subscript, import_data, ScriptContext::WITNESS_V0); + } + case TX_WITNESS_V0_KEYHASH: { + if (script_ctx == ScriptContext::WITNESS_V0) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Trying to nest P2WPKH inside P2WSH"); + CKeyID id = CKeyID(uint160(solverdata[0])); + import_data.used_keys[id] = true; + if (script_ctx == ScriptContext::TOP) { + import_data.import_scripts.emplace(script); // Special rule for IsMine: native P2WPKH requires the TOP script imported (see script/ismine.cpp) + } + return ""; + } + case TX_NULL_DATA: + return "unspendable script"; + case TX_NONSTANDARD: + case TX_WITNESS_UNKNOWN: + default: + return "unrecognized script"; + } +} static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) { + UniValue warnings(UniValue::VARR); + UniValue result(UniValue::VOBJ); + try { // First ensure scriptPubKey has either a script or JSON with "address" string const UniValue& scriptPubKey = data["scriptPubKey"]; @@ -860,18 +949,16 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con // Generate the script and destination for the scriptPubKey provided CScript script; CTxDestination dest; - if (!isScript) { dest = DecodeDestination(output); if (!IsValidDestination(dest)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address \"" + output + "\""); } script = GetScriptForDestination(dest); } else { if (!IsHex(output)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid scriptPubKey"); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid scriptPubKey \"" + output + "\""); } - std::vector<unsigned char> vData(ParseHex(output)); script = CScript(vData.begin(), vData.end()); if (!ExtractDestination(script, dest) && !internal) { @@ -879,203 +966,161 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con } } - // Watchonly and private keys - if (watchOnly && keys.size()) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Watch-only addresses should not include private keys"); - } - - // Internal addresses should not have a label - if (internal && data.exists("label")) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Internal addresses should not have a label"); - } - - // 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."); - } - - CScript scriptpubkey_script = script; - CTxDestination scriptpubkey_dest = dest; - bool allow_p2wpkh = true; - - // P2SH - if (!strRedeemScript.empty() && script.IsPayToScriptHash()) { - // Check the redeemScript is valid + // Parse all arguments + ImportData import_data; + if (strRedeemScript.size()) { 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); - - // 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"); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid redeem script \"" + strRedeemScript + "\": must be hex string"); } - - pwallet->MarkDirty(); - - if (!pwallet->AddWatchOnly(redeemScript, timestamp)) { - throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet"); - } - - if (!pwallet->HaveCScript(redeem_id) && !pwallet->AddCScript(redeemScript)) { - throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2sh redeemScript to wallet"); - } - - // Now set script to the redeemScript so we parse the inner script as P2WSH or P2WPKH below - script = redeemScript; - ExtractDestination(script, dest); + auto parsed_redeemscript = ParseHex(strRedeemScript); + import_data.redeemscript = MakeUnique<CScript>(parsed_redeemscript.begin(), parsed_redeemscript.end()); } - - // (P2SH-)P2WSH - if (!witness_script_hex.empty() && script.IsPayToWitnessScriptHash()) { + if (witness_script_hex.size()) { if (!IsHex(witness_script_hex)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid witness script: must be hex string"); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid witness script \"" + witness_script_hex + "\": must be hex string"); } - - // 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); - - // 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"); + auto parsed_witnessscript = ParseHex(witness_script_hex); + import_data.witnessscript = MakeUnique<CScript>(parsed_witnessscript.begin(), parsed_witnessscript.end()); + } + std::map<CKeyID, CPubKey> pubkey_map; + for (size_t i = 0; i < pubKeys.size(); ++i) { + const auto& str = pubKeys[i].get_str(); + if (!IsHex(str)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey \"" + str + "\" must be a hex string"); } - - // 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"); + auto parsed_pubkey = ParseHex(str); + CPubKey pubkey(parsed_pubkey.begin(), parsed_pubkey.end()); + if (!pubkey.IsFullyValid()) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey \"" + str + "\" is not a valid public key"); } - - if (!pwallet->HaveCScript(witness_id) && !pwallet->AddCScript(witness_script)) { - throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2wsh witnessScript to wallet"); + pubkey_map.emplace(pubkey.GetID(), pubkey); + } + std::map<CKeyID, CKey> privkey_map; + for (size_t i = 0; i < keys.size(); ++i) { + const auto& str = keys[i].get_str(); + CKey key = DecodeSecret(str); + if (!key.IsValid()) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding"); } + CPubKey pubkey = key.GetPubKey(); + CKeyID id = pubkey.GetID(); + if (pubkey_map.count(id)) { + pubkey_map.erase(id); + } + privkey_map.emplace(id, key); + } - // 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 + // Internal addresses should not have a label + if (internal && data.exists("label")) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Internal addresses should not have a label"); } - // (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"); - } - 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(); + // Verify and process input data + bool have_solving_data = import_data.redeemscript || import_data.witnessscript || pubkey_map.size() || privkey_map.size(); + if (have_solving_data) { + // Match up data in import_data with the scriptPubKey in script. + auto error = RecurseImportData(script, import_data, ScriptContext::TOP); + + // Verify whether the watchonly option corresponds to the availability of private keys. + bool spendable = std::all_of(import_data.used_keys.begin(), import_data.used_keys.end(), [&](const std::pair<CKeyID, bool>& used_key){ return privkey_map.count(used_key.first) > 0; }); + if (!watchOnly && !spendable) { + warnings.push_back("Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."); } - 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(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"); - } - pubkey = pubkey_temp; + if (watchOnly && spendable) { + warnings.push_back("All private keys are provided, outputs will be considered spendable. If this is intentional, do not specify the watchonly flag."); } - if (pubkey.size() > 0) { - if (!pubkey.IsFullyValid()) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey is not a valid public key"); - } - // 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"); + // Check that all required keys for solvability are provided. + if (error.empty()) { + for (const auto& require_key : import_data.used_keys) { + if (!require_key.second) continue; // Not a required key + if (pubkey_map.count(require_key.first) == 0 && privkey_map.count(require_key.first) == 0) { + error = "some required keys are missing"; + } } + } - // 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"); + if (!error.empty()) { + warnings.push_back("Importing as non-solvable: " + error + ". If this is intentional, don't provide any keys, pubkeys, witnessscript, or redeemscript."); + import_data = ImportData(); + pubkey_map.clear(); + privkey_map.clear(); + have_solving_data = false; + } else { + // RecurseImportData() removes any relevant redeemscript/witnessscript from import_data, so we can use that to discover if a superfluous one was provided. + if (import_data.redeemscript) warnings.push_back("Ignoring redeemscript as this is not a P2SH script."); + if (import_data.witnessscript) warnings.push_back("Ignoring witnessscript as this is not a (P2SH-)P2WSH script."); + for (auto it = privkey_map.begin(); it != privkey_map.end(); ) { + auto oldit = it++; + if (import_data.used_keys.count(oldit->first) == 0) { + warnings.push_back("Ignoring irrelevant private key."); + privkey_map.erase(oldit); + } } - - pwallet->MarkDirty(); - - if (!pwallet->AddWatchOnly(scriptRawPubKey, timestamp)) { - throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet"); + for (auto it = pubkey_map.begin(); it != pubkey_map.end(); ) { + auto oldit = it++; + auto key_data_it = import_data.used_keys.find(oldit->first); + if (key_data_it == import_data.used_keys.end() || !key_data_it->second) { + warnings.push_back("Ignoring public key \"" + HexStr(oldit->first) + "\" as it doesn't appear inside P2PKH or P2WPKH."); + pubkey_map.erase(oldit); + } } } } - // Import the address - if (::IsMine(*pwallet, scriptpubkey_script) == ISMINE_SPENDABLE) { + // Check whether we have any work to do + if (::IsMine(*pwallet, script) & ISMINE_SPENDABLE) { throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script"); } + // All good, time to import pwallet->MarkDirty(); - - if (!pwallet->AddWatchOnly(scriptpubkey_script, timestamp)) { - throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet"); - } - - if (!watchOnly && !pwallet->HaveCScript(CScriptID(scriptpubkey_script)) && !pwallet->AddCScript(scriptpubkey_script)) { - throw JSONRPCError(RPC_WALLET_ERROR, "Error adding scriptPubKey script to wallet"); - } - - // if not internal add to address book or update label - if (!internal) { - assert(IsValidDestination(scriptpubkey_dest)); - pwallet->SetAddressBook(scriptpubkey_dest, label, "receive"); - } - - // Import private keys. - for (size_t i = 0; i < keys.size(); i++) { - const std::string& strPrivkey = keys[i].get_str(); - - // Checks. - CKey key = DecodeSecret(strPrivkey); - - if (!key.IsValid()) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding"); + for (const auto& entry : import_data.import_scripts) { + if (!pwallet->HaveCScript(CScriptID(entry)) && !pwallet->AddCScript(entry)) { + throw JSONRPCError(RPC_WALLET_ERROR, "Error adding script to wallet"); } - - CPubKey pubKey = key.GetPubKey(); - assert(key.VerifyPubKey(pubKey)); - - CKeyID vchAddress = pubKey.GetID(); - pwallet->MarkDirty(); - - if (pwallet->HaveKey(vchAddress)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Already have this key"); - } - - pwallet->mapKeyMetadata[vchAddress].nCreateTime = timestamp; - - if (!pwallet->AddKeyPubKey(key, pubKey)) { + } + for (const auto& entry : privkey_map) { + const CKey& key = entry.second; + CPubKey pubkey = key.GetPubKey(); + const CKeyID& id = entry.first; + assert(key.VerifyPubKey(pubkey)); + pwallet->mapKeyMetadata[id].nCreateTime = timestamp; + // If the private key is not present in the wallet, insert it. + if (!pwallet->HaveKey(id) && !pwallet->AddKeyPubKey(key, pubkey)) { throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet"); } - pwallet->UpdateTimeFirstKey(timestamp); } + for (const auto& entry : pubkey_map) { + const CPubKey& pubkey = entry.second; + const CKeyID& id = entry.first; + CPubKey temp; + if (!pwallet->GetPubKey(id, temp) && !pwallet->AddWatchOnly(GetScriptForRawPubKey(pubkey), timestamp)) { + throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet"); + } + } + if (!have_solving_data || !::IsMine(*pwallet, script)) { // Always call AddWatchOnly for non-solvable watch-only, so that watch timestamp gets updated + if (!pwallet->AddWatchOnly(script, timestamp)) { + throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet"); + } + } + if (!internal) { + assert(IsValidDestination(dest)); + pwallet->SetAddressBook(dest, label, "receive"); + } - UniValue result = UniValue(UniValue::VOBJ); result.pushKV("success", UniValue(true)); - return result; } catch (const UniValue& e) { - UniValue result = UniValue(UniValue::VOBJ); result.pushKV("success", UniValue(false)); result.pushKV("error", e); - return result; } catch (...) { - UniValue result = UniValue(UniValue::VOBJ); result.pushKV("success", UniValue(false)); + result.pushKV("error", JSONRPCError(RPC_MISC_ERROR, "Missing required fields")); - return result; } + if (warnings.size()) result.pushKV("warnings", warnings); + return result; } static int64_t GetImportTimestamp(const UniValue& data, int64_t now) @@ -1122,18 +1167,18 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) }, {"redeemscript", RPCArg::Type::STR, /* opt */ true, /* default_val */ "omitted", "Allowed only if the scriptPubKey is a P2SH or P2SH-P2WSH address/scriptPubKey"}, {"witnessscript", RPCArg::Type::STR, /* opt */ true, /* default_val */ "omitted", "Allowed only if the scriptPubKey is a P2SH-P2WSH or P2WSH address/scriptPubKey"}, - {"pubkeys", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "empty array", "Array of strings giving pubkeys that must occur in the output or redeemscript", + {"pubkeys", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "empty array", "Array of strings giving pubkeys to import. They must occur in P2PKH or P2WPKH scripts. They are not required when the private key is also provided (see the \"keys\" argument).", { {"pubKey", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", ""}, } }, - {"keys", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "empty array", "Array of strings giving private keys whose corresponding public keys must occur in the output or redeemscript", + {"keys", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "empty array", "Array of strings giving private keys to import. The corresponding public keys must occur in the output or redeemscript.", { {"key", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", ""}, } }, - {"internal", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Stating whether matching outputs should be treated as not incoming payments aka change"}, - {"watchonly", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Stating whether matching outputs should be considered watched even when they're not spendable, only allowed if keys are empty"}, + {"internal", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Stating whether matching outputs should be treated as not incoming payments (also known as change)"}, + {"watchonly", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Stating whether matching outputs should be considered watched even when not all private keys are provided."}, {"label", RPCArg::Type::STR, /* opt */ true, /* default_val */ "''", "Label to assign to the address, only allowed with internal=false"}, }, }, @@ -1154,7 +1199,7 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) HelpExampleCli("importmulti", "'[{ \"scriptPubKey\": { \"address\": \"<my address>\" }, \"timestamp\":1455191478 }]' '{ \"rescan\": false}'") + "\nResponse is an array with the same size as the input that has the execution result :\n" - " [{ \"success\": true } , { \"success\": false, \"error\": { \"code\": -1, \"message\": \"Internal Server Error\"} }, ... ]\n"); + " [{\"success\": true}, {\"success\": true, \"warnings\": [\"Ignoring irrelevant private key\"]}, {\"success\": false, \"error\": {\"code\": -1, \"message\": \"Internal Server Error\"}}, ...]\n"); RPCTypeCheck(mainRequest.params, {UniValue::VARR, UniValue::VOBJ}); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 1d9ab54857..c1cdd0b2ee 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1397,9 +1397,14 @@ UniValue listtransactions(const JSONRPCRequest& request) "[\n" " {\n" " \"address\":\"address\", (string) The bitcoin address of the transaction.\n" - " \"category\":\"send|receive\", (string) The transaction category.\n" + " \"category\": (string) The transaction category.\n" + " \"send\" Transactions sent.\n" + " \"receive\" Non-coinbase transactions received.\n" + " \"generate\" Coinbase transactions received with more than 100 confirmations.\n" + " \"immature\" Coinbase transactions received with 100 or fewer confirmations.\n" + " \"orphan\" Orphaned coinbase transactions received.\n" " \"amount\": x.xxx, (numeric) The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and is positive\n" - " for the 'receive' category,\n" + " for all other categories\n" " \"label\": \"label\", (string) A comment for the address/transaction, if any\n" " \"vout\": n, (numeric) the vout value\n" " \"fee\": x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n" @@ -1526,20 +1531,25 @@ static UniValue listsinceblock(const JSONRPCRequest& request) "\nResult:\n" "{\n" " \"transactions\": [\n" - " \"address\":\"address\", (string) The bitcoin address of the transaction. Not present for move transactions (category = move).\n" - " \"category\":\"send|receive\", (string) The transaction category. 'send' has negative amounts, 'receive' has positive amounts.\n" - " \"amount\": x.xxx, (numeric) The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and for the 'move' category for moves \n" - " outbound. It is positive for the 'receive' category, and for the 'move' category for inbound funds.\n" + " \"address\":\"address\", (string) The bitcoin address of the transaction.\n" + " \"category\": (string) The transaction category.\n" + " \"send\" Transactions sent.\n" + " \"receive\" Non-coinbase transactions received.\n" + " \"generate\" Coinbase transactions received with more than 100 confirmations.\n" + " \"immature\" Coinbase transactions received with 100 or fewer confirmations.\n" + " \"orphan\" Orphaned coinbase transactions received.\n" + " \"amount\": x.xxx, (numeric) The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and is positive\n" + " for all other categories\n" " \"vout\" : n, (numeric) the vout value\n" " \"fee\": x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the 'send' category of transactions.\n" - " \"confirmations\": n, (numeric) The number of confirmations for the transaction. Available for 'send' and 'receive' category of transactions.\n" + " \"confirmations\": n, (numeric) The number of confirmations for the transaction.\n" " When it's < 0, it means the transaction conflicted that many blocks ago.\n" - " \"blockhash\": \"hashvalue\", (string) The block hash containing the transaction. Available for 'send' and 'receive' category of transactions.\n" - " \"blockindex\": n, (numeric) The index of the transaction in the block that includes it. Available for 'send' and 'receive' category of transactions.\n" + " \"blockhash\": \"hashvalue\", (string) The block hash containing the transaction.\n" + " \"blockindex\": n, (numeric) The index of the transaction in the block that includes it.\n" " \"blocktime\": xxx, (numeric) The block time in seconds since epoch (1 Jan 1970 GMT).\n" - " \"txid\": \"transactionid\", (string) The transaction id. Available for 'send' and 'receive' category of transactions.\n" + " \"txid\": \"transactionid\", (string) The transaction id.\n" " \"time\": xxx, (numeric) The transaction time in seconds since epoch (Jan 1 1970 GMT).\n" - " \"timereceived\": xxx, (numeric) The time received in seconds since epoch (Jan 1 1970 GMT). Available for 'send' and 'receive' category of transactions.\n" + " \"timereceived\": xxx, (numeric) The time received in seconds since epoch (Jan 1 1970 GMT).\n" " \"bip125-replaceable\": \"yes|no|unknown\", (string) Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n" " may be unknown for unconfirmed transactions not in the mempool\n" " \"abandoned\": xxx, (bool) 'true' if the transaction has been abandoned (inputs are respendable). Only available for the 'send' category of transactions.\n" @@ -1677,7 +1687,12 @@ static UniValue gettransaction(const JSONRPCRequest& request) " \"details\" : [\n" " {\n" " \"address\" : \"address\", (string) The bitcoin address involved in the transaction\n" - " \"category\" : \"send|receive\", (string) The category, either 'send' or 'receive'\n" + " \"category\" : (string) The transaction category.\n" + " \"send\" Transactions sent.\n" + " \"receive\" Non-coinbase transactions received.\n" + " \"generate\" Coinbase transactions received with more than 100 confirmations.\n" + " \"immature\" Coinbase transactions received with 100 or fewer confirmations.\n" + " \"orphan\" Orphaned coinbase transactions received.\n" " \"amount\" : x.xxx, (numeric) The amount in " + CURRENCY_UNIT + "\n" " \"label\" : \"label\", (string) A comment for the address/transaction, if any\n" " \"vout\" : n, (numeric) the vout value\n" diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index 5731a27e00..1ed1926af2 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -34,7 +34,7 @@ static void AddKey(CWallet& wallet, const CKey& key) wallet.AddKeyPubKey(key, key.GetPubKey()); } -BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) +BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup) { auto chain = interfaces::MakeChain(); @@ -47,6 +47,19 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) auto locked_chain = chain->lock(); + // Verify ScanForWalletTransactions accomodates a null start block. + { + CWallet wallet(*chain, WalletLocation(), WalletDatabase::CreateDummy()); + AddKey(wallet, coinbaseKey); + WalletRescanReserver reserver(&wallet); + reserver.reserve(); + const CBlockIndex *stop_block = null_block + 1, *failed_block = null_block + 1; + BOOST_CHECK_EQUAL(wallet.ScanForWalletTransactions(nullptr, nullptr, reserver, failed_block, stop_block), CWallet::ScanResult::SUCCESS); + BOOST_CHECK_EQUAL(failed_block, null_block); + BOOST_CHECK_EQUAL(stop_block, null_block); + BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 0); + } + // Verify ScanForWalletTransactions picks up transactions in both the old // and new block files. { @@ -54,7 +67,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) AddKey(wallet, coinbaseKey); WalletRescanReserver reserver(&wallet); reserver.reserve(); - const CBlockIndex *stop_block, *failed_block; + const CBlockIndex *stop_block = null_block + 1, *failed_block = null_block + 1; BOOST_CHECK_EQUAL(wallet.ScanForWalletTransactions(oldTip, nullptr, reserver, failed_block, stop_block), CWallet::ScanResult::SUCCESS); BOOST_CHECK_EQUAL(failed_block, null_block); BOOST_CHECK_EQUAL(stop_block, newTip); @@ -72,13 +85,47 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) AddKey(wallet, coinbaseKey); WalletRescanReserver reserver(&wallet); reserver.reserve(); - const CBlockIndex *stop_block, *failed_block; + const CBlockIndex *stop_block = null_block + 1, *failed_block = null_block + 1; BOOST_CHECK_EQUAL(wallet.ScanForWalletTransactions(oldTip, nullptr, reserver, failed_block, stop_block), CWallet::ScanResult::FAILURE); BOOST_CHECK_EQUAL(failed_block, oldTip); BOOST_CHECK_EQUAL(stop_block, newTip); BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 50 * COIN); } + // Prune the remaining block file. + PruneOneBlockFile(newTip->GetBlockPos().nFile); + UnlinkPrunedFiles({newTip->GetBlockPos().nFile}); + + // Verify ScanForWalletTransactions scans no blocks. + { + CWallet wallet(*chain, WalletLocation(), WalletDatabase::CreateDummy()); + AddKey(wallet, coinbaseKey); + WalletRescanReserver reserver(&wallet); + reserver.reserve(); + const CBlockIndex *stop_block = null_block + 1, *failed_block = null_block + 1; + BOOST_CHECK_EQUAL(wallet.ScanForWalletTransactions(oldTip, nullptr, reserver, failed_block, stop_block), CWallet::ScanResult::FAILURE); + BOOST_CHECK_EQUAL(failed_block, newTip); + BOOST_CHECK_EQUAL(stop_block, null_block); + BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 0); + } +} + +BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup) +{ + auto chain = interfaces::MakeChain(); + + // Cap last block file size, and mine new block in a new block file. + CBlockIndex* oldTip = chainActive.Tip(); + GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE; + CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())); + CBlockIndex* newTip = chainActive.Tip(); + + auto locked_chain = chain->lock(); + + // Prune the older block file. + PruneOneBlockFile(oldTip->GetBlockPos().nFile); + UnlinkPrunedFiles({oldTip->GetBlockPos().nFile}); + // Verify importmulti RPC returns failure for a key whose creation time is // before the missing block, and success for a key whose creation time is // after. @@ -294,7 +341,7 @@ public: WalletRescanReserver reserver(wallet.get()); reserver.reserve(); const CBlockIndex* const null_block = nullptr; - const CBlockIndex *stop_block, *failed_block; + const CBlockIndex *stop_block = null_block + 1, *failed_block = null_block + 1; BOOST_CHECK_EQUAL(wallet->ScanForWalletTransactions(chainActive.Genesis(), nullptr, reserver, failed_block, stop_block), CWallet::ScanResult::SUCCESS); BOOST_CHECK_EQUAL(stop_block, chainActive.Tip()); BOOST_CHECK_EQUAL(failed_block, null_block); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index fea0378839..109f8e6da0 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1601,6 +1601,7 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const CBlockIndex* const const CBlockIndex* pindex = pindexStart; failed_block = nullptr; + stop_block = nullptr; if (pindex) WalletLogPrintf("Rescan started from block %d...\n", pindex->nHeight); diff --git a/test/functional/feature_assumevalid.py b/test/functional/feature_assumevalid.py index 3d0467038d..12a4ce9aff 100755 --- a/test/functional/feature_assumevalid.py +++ b/test/functional/feature_assumevalid.py @@ -180,7 +180,7 @@ class AssumeValidTest(BitcoinTestFramework): for i in range(2202): p2p1.send_message(msg_block(self.blocks[i])) # Syncing 2200 blocks can take a while on slow systems. Give it plenty of time to sync. - p2p1.sync_with_ping(120) + p2p1.sync_with_ping(150) assert_equal(self.nodes[1].getblock(self.nodes[1].getbestblockhash())['height'], 2202) # Send blocks to node2. Block 102 will be rejected. diff --git a/test/functional/feature_block.py b/test/functional/feature_block.py index e50f67a345..b741339351 100755 --- a/test/functional/feature_block.py +++ b/test/functional/feature_block.py @@ -1181,7 +1181,7 @@ class FullBlockTest(BitcoinTestFramework): self.save_spendable_output() spend = self.get_spendable_output() - self.sync_blocks(blocks, True, timeout=180) + self.sync_blocks(blocks, True, timeout=480) chain1_tip = i # now create alt chain of same length @@ -1193,14 +1193,14 @@ class FullBlockTest(BitcoinTestFramework): # extend alt chain to trigger re-org block = self.next_block("alt" + str(chain1_tip + 1)) - self.sync_blocks([block], True, timeout=180) + self.sync_blocks([block], True, timeout=480) # ... and re-org back to the first chain self.move_tip(chain1_tip) block = self.next_block(chain1_tip + 1) self.sync_blocks([block], False, force_send=True) block = self.next_block(chain1_tip + 2) - self.sync_blocks([block], True, timeout=180) + self.sync_blocks([block], True, timeout=480) # Helper methods ################ diff --git a/test/functional/feature_dbcrash.py b/test/functional/feature_dbcrash.py index 70d67aa53a..8b06cc7372 100755 --- a/test/functional/feature_dbcrash.py +++ b/test/functional/feature_dbcrash.py @@ -46,7 +46,7 @@ class ChainstateWriteCrashTest(BitcoinTestFramework): self.num_nodes = 4 self.setup_clean_chain = False # Need a bit of extra time for the nodes to start up for this test - self.rpc_timewait = 90 + self.rpc_timeout = 90 # Set -maxmempool=0 to turn off mempool memory sharing with dbcache # Set -rpcservertimeout=900 to reduce socket disconnects in this diff --git a/test/functional/feature_pruning.py b/test/functional/feature_pruning.py index c162f46d63..9a3f4fae45 100755 --- a/test/functional/feature_pruning.py +++ b/test/functional/feature_pruning.py @@ -29,7 +29,7 @@ class PruneTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 6 - self.rpc_timewait = 900 + self.rpc_timeout = 900 # Create nodes 0 and 1 to mine. # Create node 2 to test pruning. @@ -191,6 +191,8 @@ class PruneTest(BitcoinTestFramework): def reorg_back(self): # Verify that a block on the old main chain fork has been pruned away assert_raises_rpc_error(-1, "Block not available (pruned data)", self.nodes[2].getblock, self.forkhash) + with self.nodes[2].assert_debug_log(expected_msgs=['block verification stopping at height', '(pruning, no data)']): + self.nodes[2].verifychain(checklevel=4, nblocks=0) self.log.info("Will need to redownload block %d" % self.forkheight) # Verify that we have enough history to reorg back to the fork point diff --git a/test/functional/feature_segwit.py b/test/functional/feature_segwit.py index 7098a03f1e..4bcdf9af55 100755 --- a/test/functional/feature_segwit.py +++ b/test/functional/feature_segwit.py @@ -90,7 +90,7 @@ class SegWitTest(BitcoinTestFramework): 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) - tmpl = self.nodes[0].getblocktemplate({}) + tmpl = self.nodes[0].getblocktemplate({'rules': ['segwit']}) assert(tmpl['sizelimit'] == 1000000) assert('weightlimit' not in tmpl) assert(tmpl['sigoplimit'] == 20000) @@ -232,15 +232,7 @@ class SegWitTest(BitcoinTestFramework): assert(tx.wit.is_null()) assert(txid3 in self.nodes[0].getrawmempool()) - # Now try calling getblocktemplate() without segwit support. - 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']] - 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. + # Check that getblocktemplate includes all transactions. template = self.nodes[0].getblocktemplate({"rules": ["segwit"]}) template_txids = [t['txid'] for t in template['transactions']] assert(txid1 in template_txids) diff --git a/test/functional/mempool_accept.py b/test/functional/mempool_accept.py index 442aa8e99a..e2a219b85a 100755 --- a/test/functional/mempool_accept.py +++ b/test/functional/mempool_accept.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2017 The Bitcoin Core developers +# 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 mempool acceptance of raw transactions.""" @@ -58,6 +58,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework): self.mempool_size = 0 wait_until(lambda: node.getblockcount() == 200) assert_equal(node.getmempoolinfo()['size'], self.mempool_size) + coins = node.listunspent() self.log.info('Should not accept garbage to testmempoolaccept') assert_raises_rpc_error(-3, 'Expected type array, got string', lambda: node.testmempoolaccept(rawtxs='ff00baar')) @@ -65,7 +66,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework): assert_raises_rpc_error(-22, 'TX decode failed', lambda: node.testmempoolaccept(rawtxs=['ff00baar'])) self.log.info('A transaction already in the blockchain') - coin = node.listunspent()[0] # Pick a random coin(base) to spend + coin = coins.pop() # Pick a random coin(base) to spend raw_tx_in_block = node.signrawtransactionwithwallet(node.createrawtransaction( inputs=[{'txid': coin['txid'], 'vout': coin['vout']}], outputs=[{node.getnewaddress(): 0.3}, {node.getnewaddress(): 49}], @@ -93,7 +94,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework): ) self.log.info('A final transaction not in the mempool') - coin = node.listunspent()[0] # Pick a random coin(base) to spend + coin = coins.pop() # Pick a random coin(base) to spend raw_tx_final = node.signrawtransactionwithwallet(node.createrawtransaction( inputs=[{'txid': coin['txid'], 'vout': coin['vout'], "sequence": 0xffffffff}], # SEQUENCE_FINAL outputs=[{node.getnewaddress(): 0.025}], diff --git a/test/functional/mining_basic.py b/test/functional/mining_basic.py index 6e74731349..661d9f4c97 100755 --- a/test/functional/mining_basic.py +++ b/test/functional/mining_basic.py @@ -30,7 +30,7 @@ from test_framework.script import CScriptNum def assert_template(node, block, expect, rehash=True): if rehash: block.hashMerkleRoot = block.calc_merkle_root() - rsp = node.getblocktemplate(template_request={'data': b2x(block.serialize()), 'mode': 'proposal'}) + rsp = node.getblocktemplate(template_request={'data': b2x(block.serialize()), 'mode': 'proposal', 'rules': ['segwit']}) assert_equal(rsp, expect) @@ -60,7 +60,7 @@ class MiningTest(BitcoinTestFramework): # Mine a block to leave initial block download node.generatetoaddress(1, node.get_deterministic_priv_key().address) - tmpl = node.getblocktemplate() + tmpl = node.getblocktemplate({'rules': ['segwit']}) self.log.info("getblocktemplate: Test capability advertised") assert 'proposal' in tmpl['capabilities'] assert 'coinbasetxn' not in tmpl @@ -86,6 +86,9 @@ class MiningTest(BitcoinTestFramework): block.nNonce = 0 block.vtx = [coinbase_tx] + self.log.info("getblocktemplate: segwit rule must be set") + assert_raises_rpc_error(-8, "getblocktemplate must be called with the segwit rule set", node.getblocktemplate) + self.log.info("getblocktemplate: Test valid block") assert_template(node, block, None) @@ -102,7 +105,7 @@ class MiningTest(BitcoinTestFramework): assert_raises_rpc_error(-22, "Block does not start with a coinbase", node.submitblock, b2x(bad_block.serialize())) self.log.info("getblocktemplate: Test truncated final transaction") - assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, {'data': b2x(block.serialize()[:-1]), 'mode': 'proposal'}) + assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, {'data': b2x(block.serialize()[:-1]), 'mode': 'proposal', 'rules': ['segwit']}) self.log.info("getblocktemplate: Test duplicate transaction") bad_block = copy.deepcopy(block) @@ -132,7 +135,7 @@ class MiningTest(BitcoinTestFramework): bad_block_sn = bytearray(block.serialize()) assert_equal(bad_block_sn[TX_COUNT_OFFSET], 1) bad_block_sn[TX_COUNT_OFFSET] += 1 - assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, {'data': b2x(bad_block_sn), 'mode': 'proposal'}) + assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, {'data': b2x(bad_block_sn), 'mode': 'proposal', 'rules': ['segwit']}) self.log.info("getblocktemplate: Test bad bits") bad_block = copy.deepcopy(block) diff --git a/test/functional/mining_getblocktemplate_longpoll.py b/test/functional/mining_getblocktemplate_longpoll.py index 9a3c15a4a7..72cde8e811 100755 --- a/test/functional/mining_getblocktemplate_longpoll.py +++ b/test/functional/mining_getblocktemplate_longpoll.py @@ -15,14 +15,14 @@ class LongpollThread(threading.Thread): def __init__(self, node): threading.Thread.__init__(self) # query current longpollid - template = node.getblocktemplate() + template = node.getblocktemplate({'rules': ['segwit']}) self.longpollid = template['longpollid'] # create a new connection to the node, we can't use the same # connection from two threads self.node = get_rpc_proxy(node.url, 1, timeout=600, coveragedir=node.coverage_dir) def run(self): - self.node.getblocktemplate({'longpollid':self.longpollid}) + self.node.getblocktemplate({'longpollid': self.longpollid, 'rules': ['segwit']}) class GetBlockTemplateLPTest(BitcoinTestFramework): def set_test_params(self): @@ -34,10 +34,10 @@ class GetBlockTemplateLPTest(BitcoinTestFramework): def run_test(self): self.log.info("Warning: this test will take about 70 seconds in the best case. Be patient.") self.nodes[0].generate(10) - template = self.nodes[0].getblocktemplate() + template = self.nodes[0].getblocktemplate({'rules': ['segwit']}) longpollid = template['longpollid'] # longpollid should not change between successive invocations if nothing else happens - template2 = self.nodes[0].getblocktemplate() + template2 = self.nodes[0].getblocktemplate({'rules': ['segwit']}) assert(template2['longpollid'] == longpollid) # Test 1: test that the longpolling wait if we do nothing diff --git a/test/functional/mining_prioritisetransaction.py b/test/functional/mining_prioritisetransaction.py index da16bfbbfb..ca4b621a78 100755 --- a/test/functional/mining_prioritisetransaction.py +++ b/test/functional/mining_prioritisetransaction.py @@ -142,10 +142,10 @@ class PrioritiseTransactionTest(BitcoinTestFramework): # getblocktemplate to (eventually) return a new block. mock_time = int(time.time()) self.nodes[0].setmocktime(mock_time) - template = self.nodes[0].getblocktemplate() + template = self.nodes[0].getblocktemplate({'rules': ['segwit']}) self.nodes[0].prioritisetransaction(txid=tx_id, fee_delta=-int(self.relayfee*COIN)) self.nodes[0].setmocktime(mock_time+10) - new_template = self.nodes[0].getblocktemplate() + new_template = self.nodes[0].getblocktemplate({'rules': ['segwit']}) assert(template != new_template) diff --git a/test/functional/p2p_invalid_locator.py b/test/functional/p2p_invalid_locator.py index c8c752d1f7..33b7060060 100755 --- a/test/functional/p2p_invalid_locator.py +++ b/test/functional/p2p_invalid_locator.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2017 The Bitcoin Core developers +# Copyright (c) 2015-2018 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test node responses to invalid locators. diff --git a/test/functional/p2p_invalid_messages.py b/test/functional/p2p_invalid_messages.py index 65997a5f9d..dbc5c5fff6 100755 --- a/test/functional/p2p_invalid_messages.py +++ b/test/functional/p2p_invalid_messages.py @@ -86,7 +86,7 @@ class InvalidMessagesTest(BitcoinTestFramework): # Peer 1, despite serving up a bunch of nonsense, should still be connected. self.log.info("Waiting for node to drop junk messages.") - node.p2p.sync_with_ping(timeout=30) + node.p2p.sync_with_ping(timeout=120) assert node.p2p.is_connected # diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py index afbbfa8992..d95da227e5 100755 --- a/test/functional/p2p_segwit.py +++ b/test/functional/p2p_segwit.py @@ -545,31 +545,13 @@ class SegWitTest(BitcoinTestFramework): @subtest def test_getblocktemplate_before_lockin(self): - # Node0 is segwit aware, node2 is not. - for node in [self.nodes[0], self.nodes[2]]: - gbt_results = node.getblocktemplate() - block_version = gbt_results['version'] - # If we're not indicating segwit support, we will still be - # signalling for segwit activation. - assert_equal((block_version & (1 << VB_WITNESS_BIT) != 0), node == self.nodes[0]) - # If we don't specify the segwit rule, then we won't get a default - # commitment. - assert('default_witness_commitment' not in gbt_results) - - # Workaround: - # Can either change the tip, or change the mempool and wait 5 seconds - # to trigger a recomputation of getblocktemplate. txid = int(self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1), 16) - # Using mocktime lets us avoid sleep() - sync_mempools(self.nodes) - self.nodes[0].setmocktime(int(time.time()) + 10) - self.nodes[2].setmocktime(int(time.time()) + 10) for node in [self.nodes[0], self.nodes[2]]: gbt_results = node.getblocktemplate({"rules": ["segwit"]}) block_version = gbt_results['version'] if node == self.nodes[2]: - # If this is a non-segwit node, we should still not get a witness + # If this is a non-segwit node, we should not get a witness # commitment, nor a version bit signalling segwit. assert_equal(block_version & (1 << VB_WITNESS_BIT), 0) assert('default_witness_commitment' not in gbt_results) @@ -586,10 +568,6 @@ class SegWitTest(BitcoinTestFramework): script = get_witness_script(witness_root, 0) assert_equal(witness_commitment, bytes_to_hex_str(script)) - # undo mocktime - self.nodes[0].setmocktime(0) - self.nodes[2].setmocktime(0) - @subtest def advance_to_segwit_lockin(self): """Mine enough blocks to lock in segwit, but don't activate.""" diff --git a/test/functional/p2p_timeouts.py b/test/functional/p2p_timeouts.py index ffed853033..02ceec3dc1 100755 --- a/test/functional/p2p_timeouts.py +++ b/test/functional/p2p_timeouts.py @@ -72,7 +72,11 @@ class TimeoutsTest(BitcoinTestFramework): ] with self.nodes[0].assert_debug_log(expected_msgs=expected_timeout_logs): - sleep(2) + sleep(3) + # By now, we waited a total of 5 seconds. Off-by-two for two + # reasons: + # * The internal precision is one second + # * Account for network delay assert not no_verack_node.is_connected assert not no_version_node.is_connected assert not no_send_node.is_connected diff --git a/test/functional/rpc_fundrawtransaction.py b/test/functional/rpc_fundrawtransaction.py index 0c61e9ab62..4f350953b2 100755 --- a/test/functional/rpc_fundrawtransaction.py +++ b/test/functional/rpc_fundrawtransaction.py @@ -32,7 +32,7 @@ class RawTransactionsTest(BitcoinTestFramework): def skip_test_if_missing_module(self): self.skip_if_no_wallet() - def setup_network(self, split=False): + def setup_network(self): self.setup_nodes() connect_nodes_bi(self.nodes, 0, 1) diff --git a/test/functional/rpc_rawtransaction.py b/test/functional/rpc_rawtransaction.py index fc012e6e3a..5b9dbef68d 100755 --- a/test/functional/rpc_rawtransaction.py +++ b/test/functional/rpc_rawtransaction.py @@ -47,7 +47,7 @@ class RawTransactionsTest(BitcoinTestFramework): def skip_test_if_missing_module(self): self.skip_if_no_wallet() - def setup_network(self, split=False): + def setup_network(self): super().setup_network() connect_nodes_bi(self.nodes, 0, 2) diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index 21bf35597e..352fa32b5b 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -95,7 +95,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): self.nodes = [] self.network_thread = None self.mocktime = 0 - self.rpc_timewait = 60 # Wait for up to 60 seconds for the RPC server to respond + self.rpc_timeout = 60 # Wait for up to 60 seconds for the RPC server to respond self.supports_cli = False self.bind_to_localhost_only = True self.set_test_params() @@ -301,7 +301,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): i, get_datadir_path(self.options.tmpdir, i), rpchost=rpchost, - timewait=self.rpc_timewait, + timewait=self.rpc_timeout, bitcoind=binary[i], bitcoin_cli=self.options.bitcoincli, mocktime=self.mocktime, @@ -388,21 +388,6 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): sync_blocks(group) sync_mempools(group) - def enable_mocktime(self): - """Enable mocktime for the script. - - mocktime may be needed for scripts that use the cached version of the - blockchain. If the cached version of the blockchain is used without - mocktime then the mempools will not sync due to IBD. - - For backward compatibility of the python scripts with previous - versions of the cache, this helper function sets mocktime to Jan 1, - 2014 + (201 * 10 * 60)""" - self.mocktime = 1388534400 + (201 * 10 * 60) - - def disable_mocktime(self): - self.mocktime = 0 - # Private helper methods. These should not be accessed by the subclass test scripts. def _start_logging(self): @@ -460,7 +445,18 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): args = [self.options.bitcoind, "-datadir=" + datadir, '-disablewallet'] if i > 0: args.append("-connect=127.0.0.1:" + str(p2p_port(0))) - self.nodes.append(TestNode(i, get_datadir_path(self.options.cachedir, i), extra_conf=["bind=127.0.0.1"], extra_args=[], rpchost=None, timewait=self.rpc_timewait, bitcoind=self.options.bitcoind, bitcoin_cli=self.options.bitcoincli, mocktime=self.mocktime, coverage_dir=None)) + self.nodes.append(TestNode( + i, + get_datadir_path(self.options.cachedir, i), + extra_conf=["bind=127.0.0.1"], + extra_args=[], + rpchost=None, + timewait=self.rpc_timeout, + bitcoind=self.options.bitcoind, + bitcoin_cli=self.options.bitcoincli, + mocktime=self.mocktime, + coverage_dir=None, + )) self.nodes[i].args = args self.start_node(i) @@ -468,6 +464,11 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): for node in self.nodes: node.wait_for_rpc_connection() + # For backward compatibility of the python scripts with previous + # versions of the cache, set mocktime to Jan 1, + # 2014 + (201 * 10 * 60)""" + self.mocktime = 1388534400 + (201 * 10 * 60) + # Create a 200-block-long chain; each of the 4 first nodes # gets 25 mature blocks and 25 immature. # Note: To preserve compatibility with older versions of @@ -475,7 +476,6 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): # # blocks are created with timestamps 10 minutes apart # starting from 2010 minutes in the past - self.enable_mocktime() block_time = self.mocktime - (201 * 10 * 60) for i in range(2): for peer in range(4): @@ -489,7 +489,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): # Shut them down, and clean up cache directories: self.stop_nodes() self.nodes = [] - self.disable_mocktime() + self.mocktime = 0 def cache_path(n, *paths): return os.path.join(get_datadir_path(self.options.cachedir, n), "regtest", *paths) diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index a68b544738..a094433942 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -176,6 +176,7 @@ BASE_SCRIPTS = [ 'rpc_getblockstats.py', 'p2p_fingerprint.py', 'feature_uacomment.py', + 'wallet_coinbase_category.py', 'feature_filelock.py', 'p2p_unrequested_blocks.py', 'feature_includeconf.py', diff --git a/test/functional/wallet_backup.py b/test/functional/wallet_backup.py index 32ec385fa1..dd3750203a 100755 --- a/test/functional/wallet_backup.py +++ b/test/functional/wallet_backup.py @@ -48,7 +48,7 @@ class WalletBackupTest(BitcoinTestFramework): def skip_test_if_missing_module(self): self.skip_if_no_wallet() - def setup_network(self, split=False): + def setup_network(self): self.setup_nodes() connect_nodes(self.nodes[0], 3) connect_nodes(self.nodes[1], 3) diff --git a/test/functional/wallet_coinbase_category.py b/test/functional/wallet_coinbase_category.py new file mode 100755 index 0000000000..7aa8b44ebd --- /dev/null +++ b/test/functional/wallet_coinbase_category.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +# Copyright (c) 2014-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 coinbase transactions return the correct categories. + +Tests listtransactions, listsinceblock, and gettransaction. +""" + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import ( + assert_array_result +) + +class CoinbaseCategoryTest(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 1 + + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + + def assert_category(self, category, address, txid, skip): + assert_array_result(self.nodes[0].listtransactions(skip=skip), + {"address": address}, + {"category": category}) + assert_array_result(self.nodes[0].listsinceblock()["transactions"], + {"address": address}, + {"category": category}) + assert_array_result(self.nodes[0].gettransaction(txid)["details"], + {"address": address}, + {"category": category}) + + def run_test(self): + # Generate one block to an address + address = self.nodes[0].getnewaddress() + self.nodes[0].generatetoaddress(1, address) + hash = self.nodes[0].getbestblockhash() + txid = self.nodes[0].getblock(hash)["tx"][0] + + # Coinbase transaction is immature after 1 confirmation + self.assert_category("immature", address, txid, 0) + + # Mine another 99 blocks on top + self.nodes[0].generate(99) + # Coinbase transaction is still immature after 100 confirmations + self.assert_category("immature", address, txid, 99) + + # Mine one more block + self.nodes[0].generate(1) + # Coinbase transaction is now matured, so category is "generate" + self.assert_category("generate", address, txid, 100) + + # Orphan block that paid to address + self.nodes[0].invalidateblock(hash) + # Coinbase transaction is now orphaned + self.assert_category("orphan", address, txid, 100) + +if __name__ == '__main__': + CoinbaseCategoryTest().main() diff --git a/test/functional/wallet_dump.py b/test/functional/wallet_dump.py index 20cb816ee8..3f39654bb8 100755 --- a/test/functional/wallet_dump.py +++ b/test/functional/wallet_dump.py @@ -94,7 +94,7 @@ class WalletDumpTest(BitcoinTestFramework): def skip_test_if_missing_module(self): self.skip_if_no_wallet() - def setup_network(self, split=False): + def setup_network(self): self.add_nodes(self.num_nodes, extra_args=self.extra_args) self.start_nodes() diff --git a/test/functional/wallet_groups.py b/test/functional/wallet_groups.py index 9d61483868..5452433acf 100755 --- a/test/functional/wallet_groups.py +++ b/test/functional/wallet_groups.py @@ -21,7 +21,7 @@ class WalletGroupTest(BitcoinTestFramework): self.setup_clean_chain = True self.num_nodes = 3 self.extra_args = [[], [], ['-avoidpartialspends']] - self.rpc_timewait = 120 + self.rpc_timeout = 120 def skip_test_if_missing_module(self): self.skip_if_no_wallet() diff --git a/test/functional/wallet_importmulti.py b/test/functional/wallet_importmulti.py index a163f7018c..3492075694 100755 --- a/test/functional/wallet_importmulti.py +++ b/test/functional/wallet_importmulti.py @@ -119,9 +119,13 @@ class ImportMultiTest(BitcoinTestFramework): CScript([OP_HASH160, witness_script, OP_EQUAL]).hex(), # p2sh-p2wsh script_to_p2sh_p2wsh(script_code)) # p2sh-p2wsh addr - def test_importmulti(self, req, success, error_code=None, error_message=None): + def test_importmulti(self, req, success, error_code=None, error_message=None, warnings=[]): """Run importmulti and assert success""" result = self.nodes[1].importmulti([req]) + observed_warnings = [] + if 'warnings' in result[0]: + observed_warnings = result[0]['warnings'] + assert_equal("\n".join(sorted(warnings)), "\n".join(sorted(observed_warnings))) assert_equal(result[0]['success'], success) if error_code is not None: assert_equal(result[0]['error']['code'], error_code) @@ -178,7 +182,7 @@ class ImportMultiTest(BitcoinTestFramework): "timestamp": "now"}, False, error_code=-5, - error_message='Invalid address') + error_message='Invalid address \"not valid address\"') # ScriptPubKey + internal self.log.info("Should import a scriptPubKey with internal flag") @@ -227,7 +231,8 @@ class ImportMultiTest(BitcoinTestFramework): "timestamp": "now", "pubkeys": [key.pubkey], "internal": False}, - True) + True, + warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."]) self.test_address(address, iswatchonly=True, ismine=False, @@ -241,7 +246,8 @@ class ImportMultiTest(BitcoinTestFramework): "timestamp": "now", "pubkeys": [key.pubkey], "internal": True}, - True) + True, + warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."]) self.test_address(address, iswatchonly=True, ismine=False, @@ -284,20 +290,19 @@ class ImportMultiTest(BitcoinTestFramework): error_message='The wallet already contains the private key for this address or script') # Address + Private key + watchonly - self.log.info("Should not import an address with private key and with watchonly") + self.log.info("Should import an address with private key and with watchonly") key = self.get_key() address = key.p2pkh_addr self.test_importmulti({"scriptPubKey": {"address": address}, "timestamp": "now", "keys": [key.privkey], "watchonly": True}, - False, - error_code=-8, - error_message='Watch-only addresses should not include private keys') + True, + warnings=["All private keys are provided, outputs will be considered spendable. If this is intentional, do not specify the watchonly flag."]) self.test_address(address, iswatchonly=False, - ismine=False, - timestamp=None) + ismine=True, + timestamp=timestamp) # ScriptPubKey + Private key + internal self.log.info("Should import a scriptPubKey with internal and with private key") @@ -358,8 +363,9 @@ class ImportMultiTest(BitcoinTestFramework): self.test_importmulti({"scriptPubKey": {"address": multisig.p2sh_addr}, "timestamp": "now", "redeemscript": multisig.redeem_script}, - True) - self.test_address(multisig.p2sh_addr, timestamp=timestamp) + True, + warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."]) + self.test_address(multisig.p2sh_addr, timestamp=timestamp, iswatchonly=True, ismine=False, solvable=True) p2shunspent = self.nodes[1].listunspent(0, 999999, [multisig.p2sh_addr])[0] assert_equal(p2shunspent['spendable'], False) @@ -377,9 +383,13 @@ class ImportMultiTest(BitcoinTestFramework): "timestamp": "now", "redeemscript": multisig.redeem_script, "keys": multisig.privkeys[0:2]}, - True) + True, + warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."]) self.test_address(multisig.p2sh_addr, - timestamp=timestamp) + timestamp=timestamp, + ismine=False, + iswatchonly=True, + solvable=True) p2shunspent = self.nodes[1].listunspent(0, 999999, [multisig.p2sh_addr])[0] assert_equal(p2shunspent['spendable'], False) @@ -398,28 +408,31 @@ class ImportMultiTest(BitcoinTestFramework): "redeemscript": multisig.redeem_script, "keys": multisig.privkeys[0:2], "watchonly": True}, - False, - error_code=-8, - error_message='Watch-only addresses should not include private keys') + True) + self.test_address(multisig.p2sh_addr, + iswatchonly=True, + ismine=False, + solvable=True, + timestamp=timestamp) # Address + Public key + !Internal + Wrong pubkey - self.log.info("Should not import an address with a wrong public key") + self.log.info("Should not import an address with the wrong public key as non-solvable") key = self.get_key() address = key.p2pkh_addr wrong_key = self.get_key().pubkey self.test_importmulti({"scriptPubKey": {"address": address}, "timestamp": "now", "pubkeys": [wrong_key]}, - False, - error_code=-5, - error_message='Key does not match address destination') + True, + warnings=["Importing as non-solvable: some required keys are missing. If this is intentional, don't provide any keys, pubkeys, witnessscript, or redeemscript.", "Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."]) self.test_address(address, - iswatchonly=False, + iswatchonly=True, ismine=False, - timestamp=None) + solvable=False, + timestamp=timestamp) # ScriptPubKey + Public key + internal + Wrong pubkey - self.log.info("Should not import a scriptPubKey with internal and with a wrong public key") + self.log.info("Should import a scriptPubKey with internal and with a wrong public key as non-solvable") key = self.get_key() address = key.p2pkh_addr wrong_key = self.get_key().pubkey @@ -427,32 +440,32 @@ class ImportMultiTest(BitcoinTestFramework): "timestamp": "now", "pubkeys": [wrong_key], "internal": True}, - False, - error_code=-5, - error_message='Key does not match address destination') + True, + warnings=["Importing as non-solvable: some required keys are missing. If this is intentional, don't provide any keys, pubkeys, witnessscript, or redeemscript.", "Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."]) self.test_address(address, - iswatchonly=False, + iswatchonly=True, ismine=False, - timestamp=None) + solvable=False, + timestamp=timestamp) # Address + Private key + !watchonly + Wrong private key - self.log.info("Should not import an address with a wrong private key") + self.log.info("Should import an address with a wrong private key as non-solvable") key = self.get_key() address = key.p2pkh_addr wrong_privkey = self.get_key().privkey self.test_importmulti({"scriptPubKey": {"address": address}, "timestamp": "now", "keys": [wrong_privkey]}, - False, - error_code=-5, - error_message='Key does not match address destination') + True, + warnings=["Importing as non-solvable: some required keys are missing. If this is intentional, don't provide any keys, pubkeys, witnessscript, or redeemscript.", "Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."]) self.test_address(address, - iswatchonly=False, + iswatchonly=True, ismine=False, - timestamp=None) + solvable=False, + timestamp=timestamp) # ScriptPubKey + Private key + internal + Wrong private key - self.log.info("Should not import a scriptPubKey with internal and with a wrong private key") + self.log.info("Should import a scriptPubKey with internal and with a wrong private key as non-solvable") key = self.get_key() address = key.p2pkh_addr wrong_privkey = self.get_key().privkey @@ -460,13 +473,13 @@ class ImportMultiTest(BitcoinTestFramework): "timestamp": "now", "keys": [wrong_privkey], "internal": True}, - False, - error_code=-5, - error_message='Key does not match address destination') + True, + warnings=["Importing as non-solvable: some required keys are missing. If this is intentional, don't provide any keys, pubkeys, witnessscript, or redeemscript.", "Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."]) self.test_address(address, - iswatchonly=False, + iswatchonly=True, ismine=False, - timestamp=None) + solvable=False, + timestamp=timestamp) # Importing existing watch only address with new timestamp should replace saved timestamp. assert_greater_than(timestamp, watchonly_timestamp) @@ -516,7 +529,8 @@ class ImportMultiTest(BitcoinTestFramework): self.test_importmulti({"scriptPubKey": {"address": address}, "timestamp": "now", "pubkeys": [key.pubkey]}, - True) + True, + warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."]) self.test_address(address, ismine=False, solvable=True) @@ -571,7 +585,8 @@ class ImportMultiTest(BitcoinTestFramework): "timestamp": "now", "redeemscript": key.p2sh_p2wpkh_redeem_script, "pubkeys": [key.pubkey]}, - True) + True, + warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."]) self.test_address(address, solvable=True, ismine=False) @@ -591,14 +606,17 @@ class ImportMultiTest(BitcoinTestFramework): # P2SH-P2WSH multisig + redeemscript with no private key multisig = self.get_multisig() + address = multisig.p2sh_p2wsh_addr self.log.info("Should import a p2sh-p2wsh with respective redeem script but no private key") - self.test_importmulti({"scriptPubKey": {"address": multisig.p2sh_p2wsh_addr}, + self.test_importmulti({"scriptPubKey": {"address": address}, "timestamp": "now", "redeemscript": multisig.p2wsh_script, "witnessscript": multisig.redeem_script}, - True) + True, + warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."]) self.test_address(address, - solvable=True) + solvable=True, + ismine=False) if __name__ == '__main__': ImportMultiTest().main() diff --git a/test/functional/wallet_listtransactions.py b/test/functional/wallet_listtransactions.py index 8ca0387268..17f044bf65 100755 --- a/test/functional/wallet_listtransactions.py +++ b/test/functional/wallet_listtransactions.py @@ -25,12 +25,13 @@ def tx_from_hex(hexstring): class ListTransactionsTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 - self.enable_mocktime() def skip_test_if_missing_module(self): self.skip_if_no_wallet() def run_test(self): + self.nodes[0].generate(1) # Get out of IBD + self.sync_all() # Simple send, 0 to 1: txid = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 0.1) self.sync_all() diff --git a/test/lint/lint-format-strings.py b/test/lint/lint-format-strings.py index 5caebf3739..f5d4780b6d 100755 --- a/test/lint/lint-format-strings.py +++ b/test/lint/lint-format-strings.py @@ -241,12 +241,11 @@ def count_format_specifiers(format_string): 4 """ assert(type(format_string) is str) + format_string = format_string.replace('%%', 'X') n = 0 in_specifier = False for i, char in enumerate(format_string): - if format_string[i - 1:i + 1] == "%%" or format_string[i:i + 2] == "%%": - pass - elif char == "%": + if char == "%": in_specifier = True n += 1 elif char in "aAcdeEfFgGinopsuxX": diff --git a/test/lint/lint-includes.sh b/test/lint/lint-includes.sh index f6d0fd382b..4b9e2615b6 100755 --- a/test/lint/lint-includes.sh +++ b/test/lint/lint-includes.sh @@ -50,7 +50,6 @@ EXPECTED_BOOST_INCLUDES=( boost/algorithm/string/classification.hpp boost/algorithm/string/replace.hpp boost/algorithm/string/split.hpp - boost/bind.hpp boost/chrono/chrono.hpp boost/date_time/posix_time/posix_time.hpp boost/filesystem.hpp diff --git a/test/sanitizer_suppressions/tsan b/test/sanitizer_suppressions/tsan index 996f342eb9..70eea34363 100644 --- a/test/sanitizer_suppressions/tsan +++ b/test/sanitizer_suppressions/tsan @@ -11,11 +11,6 @@ deadlock:TestPotentialDeadLockDetected race:src/qt/test/* deadlock:src/qt/test/* -# WIP: Unidentified suppressions to run the functional tests -#race:zmqpublishnotifier.cpp -# -#deadlock:CreateWalletFromFile -#deadlock:importprivkey -#deadlock:walletdb.h -#deadlock:walletdb.cpp -#deadlock:wallet/db.cpp +# External libraries +deadlock:libdb +race:libzmq |