aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--CONTRIBUTING.md12
-rw-r--r--configure.ac4
-rw-r--r--contrib/gitian-keys/sjors-key.pgp76
-rw-r--r--contrib/init/bitcoind.service27
-rw-r--r--doc/build-openbsd.md103
-rw-r--r--doc/release-notes.md3
-rw-r--r--src/addrman.h1
-rw-r--r--src/bench/bench.cpp34
-rw-r--r--src/bench/bench.h26
-rw-r--r--src/bench/rollingbloom.cpp7
-rw-r--r--src/bitcoin-tx.cpp13
-rw-r--r--src/bitcoind.cpp10
-rw-r--r--src/chainparams.cpp11
-rw-r--r--src/consensus/params.h12
-rw-r--r--src/httpserver.cpp28
-rw-r--r--src/init.cpp8
-rw-r--r--src/net.cpp87
-rw-r--r--src/net.h20
-rw-r--r--src/net_processing.cpp153
-rw-r--r--src/net_processing.h16
-rw-r--r--src/qt/askpassphrasedialog.cpp10
-rw-r--r--src/qt/askpassphrasedialog.h1
-rw-r--r--src/qt/forms/askpassphrasedialog.ui7
-rw-r--r--src/qt/paymentrequestplus.cpp3
-rw-r--r--src/qt/paymentserver.cpp3
-rw-r--r--src/qt/transactiondesc.cpp2
-rw-r--r--src/qt/transactionrecord.cpp2
-rw-r--r--src/qt/transactiontablemodel.cpp2
-rw-r--r--src/qt/walletmodel.cpp2
-rw-r--r--src/qt/walletmodeltransaction.cpp2
-rw-r--r--src/rest.cpp13
-rw-r--r--src/rpc/blockchain.cpp2
-rw-r--r--src/rpc/mining.cpp10
-rw-r--r--src/rpc/misc.cpp2
-rw-r--r--src/streams.h12
-rw-r--r--src/sync.h46
-rw-r--r--src/test/DoS_tests.cpp85
-rw-r--r--src/test/miner_tests.cpp35
-rw-r--r--src/test/test_bitcoin.cpp17
-rw-r--r--src/test/test_bitcoin.h6
-rw-r--r--src/test/versionbits_tests.cpp15
-rw-r--r--src/tinyformat.h14
-rw-r--r--src/validation.cpp91
-rw-r--r--src/versionbits.cpp10
-rw-r--r--src/wallet/db.cpp5
-rw-r--r--src/wallet/feebumper.cpp18
-rw-r--r--src/wallet/init.cpp18
-rw-r--r--src/wallet/rpcdump.cpp15
-rw-r--r--src/wallet/rpcwallet.cpp35
-rw-r--r--src/wallet/test/accounting_tests.cpp4
-rw-r--r--src/wallet/wallet.cpp23
-rw-r--r--src/wallet/wallet.h4
-rw-r--r--src/wallet/walletdb.cpp2
-rwxr-xr-xtest/functional/assumevalid.py46
-rwxr-xr-xtest/functional/bip65-cltv-p2p.py39
-rwxr-xr-xtest/functional/bipdersig-p2p.py40
-rwxr-xr-xtest/functional/example_test.py23
-rwxr-xr-xtest/functional/listsinceblock.py35
-rwxr-xr-xtest/functional/maxuploadtarget.py71
-rwxr-xr-xtest/functional/nulldummy.py4
-rwxr-xr-xtest/functional/p2p-acceptblock.py313
-rwxr-xr-xtest/functional/p2p-compactblocks.py21
-rwxr-xr-xtest/functional/p2p-feefilter.py26
-rwxr-xr-xtest/functional/p2p-fingerprint.py7
-rwxr-xr-xtest/functional/p2p-leaktests.py36
-rwxr-xr-xtest/functional/p2p-mempool.py12
-rwxr-xr-xtest/functional/p2p-segwit.py24
-rwxr-xr-xtest/functional/p2p-timeouts.py43
-rwxr-xr-xtest/functional/p2p-versionbits-warning.py12
-rwxr-xr-xtest/functional/segwit.py7
-rwxr-xr-xtest/functional/sendheaders.py16
-rw-r--r--test/functional/test_framework/blockstore.py4
-rwxr-xr-xtest/functional/test_framework/comptool.py4
-rwxr-xr-xtest/functional/test_framework/mininode.py228
-rwxr-xr-xtest/functional/test_framework/test_node.py49
-rwxr-xr-xtest/functional/test_runner.py2
-rwxr-xr-xtest/functional/wallet-accounts.py137
-rwxr-xr-xtest/functional/walletbackup.py10
-rwxr-xr-xtest/functional/zmq_test.py7
80 files changed, 1450 insertions, 934 deletions
diff --git a/.gitignore b/.gitignore
index 60c26dae8b..ff297fbeca 100644
--- a/.gitignore
+++ b/.gitignore
@@ -76,6 +76,7 @@ src/qt/test/moc*.cpp
Makefile
bitcoin-qt
Bitcoin-Qt.app
+background.tiff*
# Unit-tests
Makefile.test
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index aed6d79542..9f95d3f818 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -24,9 +24,9 @@ facilitates social contribution, easy testing and peer review.
To contribute a patch, the workflow is as follows:
- - Fork repository
- - Create topic branch
- - Commit patches
+ 1. Fork repository
+ 1. Create topic branch
+ 1. Commit patches
The project coding conventions in the [developer notes](doc/developer-notes.md)
must be adhered to.
@@ -42,8 +42,8 @@ in init.cpp") in which case a single title line is sufficient. Commit messages s
helpful to people reading your code in the future, so explain the reasoning for
your decisions. Further explanation [here](http://chris.beams.io/posts/git-commit/).
-If a particular commit references another issue, please add the reference, for
-example `refs #1234`, or `fixes #4321`. Using the `fixes` or `closes` keywords
+If a particular commit references another issue, please add the reference. For
+example: `refs #1234` or `fixes #4321`. Using the `fixes` or `closes` keywords
will cause the corresponding issue to be closed when the pull request is merged.
Please refer to the [Git manual](https://git-scm.com/doc) for more information
@@ -85,7 +85,7 @@ Note that translations should not be submitted as pull requests, please see
[Translation Process](https://github.com/bitcoin/bitcoin/blob/master/doc/translation_process.md)
for more information on helping with translations.
-If a pull request is specifically not to be considered for merging (yet) please
+If a pull request is not to be considered for merging (yet), please
prefix the title with [WIP] or use [Tasks Lists](https://help.github.com/articles/basic-writing-and-formatting-syntax/#task-lists)
in the body of the pull request to indicate tasks are pending.
diff --git a/configure.ac b/configure.ac
index 81c84a8af4..8e5561243a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -162,7 +162,7 @@ AC_ARG_ENABLE([ccache],
AC_ARG_ENABLE([lcov],
[AS_HELP_STRING([--enable-lcov],
[enable lcov testing (default is no)])],
- [use_lcov=yes],
+ [use_lcov=$enableval],
[use_lcov=no])
AC_ARG_ENABLE([lcov-branch-coverage],
@@ -241,6 +241,7 @@ if test "x$enable_werror" = "xyes"; then
AC_MSG_ERROR("enable-werror set but -Werror is not usable")
fi
AX_CHECK_COMPILE_FLAG([-Werror=vla],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=vla"],,[[$CXXFLAG_WERROR]])
+ AX_CHECK_COMPILE_FLAG([-Werror=thread-safety-analysis],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=thread-safety-analysis"],,[[$CXXFLAG_WERROR]])
fi
if test "x$CXXFLAGS_overridden" = "xno"; then
@@ -249,6 +250,7 @@ if test "x$CXXFLAGS_overridden" = "xno"; then
AX_CHECK_COMPILE_FLAG([-Wformat],[CXXFLAGS="$CXXFLAGS -Wformat"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Wvla],[CXXFLAGS="$CXXFLAGS -Wvla"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Wformat-security],[CXXFLAGS="$CXXFLAGS -Wformat-security"],,[[$CXXFLAG_WERROR]])
+ AX_CHECK_COMPILE_FLAG([-Wthread-safety-analysis],[CXXFLAGS="$CXXFLAGS -Wthread-safety-analysis"],,[[$CXXFLAG_WERROR]])
## Some compilers (gcc) ignore unknown -Wno-* options, but warn about all
## unknown options if any other warning is produced. Test the -Wfoo case, and
diff --git a/contrib/gitian-keys/sjors-key.pgp b/contrib/gitian-keys/sjors-key.pgp
new file mode 100644
index 0000000000..2b5acc82aa
--- /dev/null
+++ b/contrib/gitian-keys/sjors-key.pgp
@@ -0,0 +1,76 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQINBFWSwMoBEADG31O8+ex+xpgzVKQgF4iVRE5uBPT0+GM6FnwqIIhXVKiBLQh8
+YDhhgk6joh+vsLrFzKZ9kXwoiHN8y/AiNCQ0xjAUdpznD5xvHAaGIAlT/sodRNT+
+869WgT9G1uiVp0P4ucEeilmhCn9o51LqkS3roXkj0ec52b1pslUl2WKdu1ZD+Bj4
+3/oVZm7mmjkDwl0RHJQmqlK0bunq0jlVlgH5sdQfmLbCZaq3LhVPf73zt5qHH+J6
+ZbU7A4cqm2eN5SyH+Nno+cq3+vXmvVI+x/jPe/dPDCXaGWf5fWI/Lbk/mMP7JAl1
+6X44CN+hZHUnNuzeZt2/ROWZ0s0JJcjQkSe9noUQedjBAHX82s886vsFzOHvDtul
+EuV/XAjUlkhMbhZkZaIq9ucqHmUBI4+OcFEIbbKc9TrKtJe+CYuWTNlomVk/iFr8
+zSm/S64NiqKi/BeQGgcsDZIaJDYfDP83esOOaaxFswHnJNtHnU1PwntrJtXft0dK
+ydtlQZ6r96SYxLDTeGfC2SNk0zbnKAGvjj04vzQeN+JSRZ75tNKmgdbJdNL8wvPh
+879TpCwMhNDvSRG+YqCe6whaJV76a+Doxg48HCJYaj6bnRn41/QGJEyL31I8l/7S
+YsLLmAEbqwG7erYi7WZS3cRrGJI8RwohGMZf7yraqoaOgMKmtE/Sq0tLtwARAQAB
+tCNTam9ycyBQcm92b29zdCA8c2pvcnNAc3Byb3Zvb3N0Lm5sPokCQAQTAQoAKgIb
+AwUJB4YfgAULCQgHAwUVCgkICwUWAgMBAAIeAQIXgAUCWWXcoAIZAQAKCRBX/5vb
+zDAQCeYJD/47XDMfEMg4g4spo7k92XsNkvjlAhWvvxd+kxow/V8c64WQXody32FZ
+HRSmK8dVjf9mIJMKkX4lpKpim7cQxsdTcorcdu+yk4TK+Wah61vsMhbSSllfHs1U
++q8jYMGnXTD+CY0aeTMrTfJcR2yN98jmNSWIL1qWmJ51RSTL6BQKb6eYtR7pWRkW
+uMR6oFC09Db4fiKa4zhH81+/t0g+6pMY391gSluaS+OfNqGORCo+/IdG5IDzh5Vp
+f19qXjd5oMsZQf6/P4b4XUktgl8RVRcNzdYGoXpcd8LpeHtEOh5I93ODmCwqd67b
+YDlhDNN7iGhPndPEF6P4CNO/rXLPCZyMhRyt1dflu0KPCr+0AgR31cdhH/p7eCyj
+FTE9gUgUHOG9OHdRoVXrwHYXwAiDBr2pp2giLpBsAwa4d2hXNDJ6wfMMCSOXKQlS
+lHq06y/v/049DammkqW0XnEsU4qvsdteZ0jQu7Ob3LyGoytBIj8fn1OioT21W7wc
+ns3/Tt4cQsn2ICBYB4PzqwkvGUp7fDwwHYw7rq6kvCEVDUDWMtVgQ8kjsh2OoU75
+eeteM1Q1fV06Wfn2Qct9bn0NKRGrA8mm3lrCWYCeGqJeBvC6kna1QgV53vYRLJod
+w3Ql4+M9tUIi9uiGLvVaGZWO9wU1EwL+EAO+6D85h6QiJN7H8gcwUokCPQQTAQoA
+JwUCVZLAygIbAwUJB4YfgAULCQgHAwUVCgkICwUWAgMBAAIeAQIXgAAKCRBX/5vb
+zDAQCauuD/9IDWhf/fTseA1Rt5i4gwK+8dCQjTlRS2cZtGc2aMX8w5XruDWnna1P
+Mj/aVUncDrprRx9rxgEqIDyPheuJ6r7v6D8GjrpAjcG/BPNFtPaxQccbZbAYdzoj
+Rrs+ttVIqS+wO7qLmQkKA4oGRMmgYh3VX8EBZNcvxaGCcJx0PfoqS8cPXTnCRHcg
+Wx6kaFyuWtrTX+kCpDraB1KGtxedR4rzuOtUOLoqFOOfsQuOxPlKNNr9Zjc8x2o4
+5TtwbuoEog8FIEttY6NOywpsSsvYvNB4gq1fxO49H0pQopmJlOMatMH6IRT7BJJZ
+cOoHOh4X/zItOJZtuCOT4u+Y2XOuyLcW83X5ymIR3ZCxedsLzjyiCWm61/znJVON
+Ws8I+gShbvauahBCB9rOHqwM0QioJMc36hUPB21KghQS8RJpGwmtk1WhFFMtAsSJ
+w+wRfy2d6u+lSGdlA+2hEyKVm/DNQMDCQVFx3lQ6YBwAwkSiLMylrPKvs56fUjRr
+74qoPyDxuRMC+q+TThHsy5O9r31G+Dc3+H5k4iTk354Jshjltx/k2O732e9Vxyar
+/U5P7UZqHHuJKXDihUFrcJZq+gk8sGEWzGG/wocce7ezrTnHqR8YA04BTA4PXQqZ
+4N42f422YYGIH/3Nm6drQkbigekLw6wx+NrxtTsYg4eCtSsaUd/RjLQhU2pvcnMg
+UHJvdm9vc3QgPHNqb3JzQGZyZWVkb20ubmw+iQI9BBMBCgAnBQJZZdyfAhsDBQkH
+hh+ABQsJCAcDBRUKCQgLBRYCAwEAAh4BAheAAAoJEFf/m9vMMBAJEsIQAK4ihgRB
+05QqETpWNeV/XSGBHQINuwwEDz/k8dAJ5Uo6OoSpDULa16fs/EgAV46wTSxfWuci
+n2Fc1AWLeLDWOax/NlycL00VDHEwT2PCjcc5uMuwR4RUTciKyByT1u7BFToZ6PyL
+mbU6u6whcQejl6Ci2kw0Mu4n4bKTS7OL4/w/EbdfMSpRi8wWmTPMB/aMjtS2Mxi/
+N+yQhJ9pReHADeCBoAjq0cUy+QbzvBwDCK4XWRzF7kiFuA7UW2r7/dX6l31mPfi/
+GLA5+ftPxJ6EH8cxToF70OWiSfhOTleaqZaHUOG0V7wV2lr/bwAYzpVlxeZSCIta
+lAA9ZLzUD2hiHYcei6kc/YjIhmlml7O0FK1eBk7+bt5wr0nvWt4Lbha4y5LxBX8C
+d7InvB3xUYHz+S5Ul4vp0Rzx97MBL4oX2ltBEDpc1CcOgzv4dcWMG9bbh9/SaI/G
+RehAzwkbpVUl9AEUNKO0dNlZUdu8CkehHdPdz5sJyS/9zE0A7yIECDFP9Nrht0nK
+MahBijm4K+jOiLOZ2xyfOX1pVWLqIXGQHKjfcD3oI3qvGrQYtxB5Dffb9ACFMpZO
+z3jM8h2UAa2/KqA4MZiZG9N6uWHKkIAMMuXWs1s439WePvbQ+5aw/qPUAMyqA3XZ
+dkfn8QWaJPR4nRM+McYBYuS4fKK9HRJWQgcQuQINBFWSwMoBEACzmkabZ8oHWJUE
+beU7rJF/TMbwV1IFtFxJ/QlY8rE4VnHekPMvkLi/gjx3WY5nmMe+d4JYoK/uPNdt
+y5u0QYgH2MB/jebk4gYXCAHIPpU38h9UgHRb6qV8OaqHhmoXvKwyz+1QPzyJpmgg
+oCUN+OAroNjl7zhunE7w7EEddFQftfPoGKEUnTjv84QOCuAb46JsYyiNAc3h6okq
+74hY7PKCv8IRGclMPjemhBT2LEenn1t4yi7a8W/hjIe44PmQiqQEXR17keqcP/ls
+EH9xSST1v/70ieiPqb6zbHGWzjQxqpFUJxRU6OluBCy5pHVd8wfFGYrrbTpoxaUC
+jyA2SLr1oZZ9gaGprt6X7FC5gpE5LV9essq3O5wwvoPbyMe1F5uFaxIPhlt55oEu
+rwVWecFJ8tSjniF/WSkTcILrOmiQZ4mylXfOP9Wk38seZReCs799KEfKFlXHk89a
+Sj3ZvaJQxwVCnvsAsbVKmmHZ5wPt+G2KfhOkkv2A1I/UyeTT7aXvt2vxDqGuG0su
+Eo6QknM/2Sr5Uv7BwBeSIQ6llH5ZnqKz34+HjriP8YPWzvsC959GXsxS01dCSvUM
+92j5PvTZzf5dt1CWHMeufAY5XIH+nftkRniuScRhJ7xK3tJ7wngg7UvdeZwJWqmK
+lJ7GI38V8HIMnd2x28yiGpj1ue6T+QARAQABiQIlBBgBCgAPBQJVksDKAhsMBQkH
+hh+AAAoJEFf/m9vMMBAJjeIP/1UBCi6gSXzpGJBLD2u4PcZJjXBJAImZdf1aCqfS
+YZBCaA65UrM3uaVa7h8MGAJc9kDjpqHurjDmG3YWf33KvHWYmReQvX43pZmfF12s
+X7FZgcCfgZJKKj+ri6oHQonZzUMrecEcAJLLaQoD3Du3iZpETiyRLL7sJ1lZSaCJ
+gYKnN4WV5GypvdFvb8vSUBST2h0D6AewGKMNh8ruRlkIxI+YSlywgYIH+O0qNKqW
+wBlZc/5f+JZ3hu+cjx/+Zn+w+saIb6SgySg0UzN35b2WM2YzrfQep4ah3NIxuC7e
+qzmfV6GnRtuUrBLVJ8qyjif1JSM9tZfinnmAB4/U5Qfc+YYViIXMTljmHWvbokas
+tTBfVAw74yWnkv4ZuXf5SkTmGwEMJUOat0TSr085Ck5y394bRepdI1Y+1cdqpwMQ
+QmkKyvcBlREQ7Xk1UnDDR3o/2ieVuGGHRp8jmoWBWGq4Cm43fYOlVe+PcaX0tDns
+Tmmh2uwEU/TXe5qGil51OlSM7qhAMqhWUIYphSOcdvApNXuiWMfnTdjsNygE4HVh
+Jq4efJ/nlx5N+PNAK2GpzeUJQGyxiVsXybq+h8UlvytBsdz1X6ZYzBv1yYwANThU
+rMB1s4tMaEugX0aNByLcsxuS4ixd2qzwkYVz25Aeko/U1v2/j2cIRtrTNgja3BKE
+N5Ug
+=80Es
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/contrib/init/bitcoind.service b/contrib/init/bitcoind.service
index 9132957c38..ee113d7615 100644
--- a/contrib/init/bitcoind.service
+++ b/contrib/init/bitcoind.service
@@ -1,22 +1,25 @@
+# It is not recommended to modify this file in-place, because it will
+# be overwritten during package upgrades. If you want to add further
+# options or overwrite existing ones then use
+# $ systemctl edit bitcoind.service
+# See "man systemd.service" for details.
+
+# Note that almost all daemon options could be specified in
+# /etc/bitcoin/bitcoin.conf
+
[Unit]
-Description=Bitcoin's distributed currency daemon
+Description=Bitcoin daemon
After=network.target
[Service]
+ExecStart=/usr/bin/bitcoind -daemon -conf=/etc/bitcoin/bitcoin.conf -pid=/run/bitcoind/bitcoind.pid
+# Creates /run/bitcoind owned by bitcoin
+RuntimeDirectory=bitcoind
User=bitcoin
-Group=bitcoin
-
Type=forking
-PIDFile=/var/lib/bitcoind/bitcoind.pid
-ExecStart=/usr/bin/bitcoind -daemon -pid=/var/lib/bitcoind/bitcoind.pid \
--conf=/etc/bitcoin/bitcoin.conf -datadir=/var/lib/bitcoind -disablewallet
-
-Restart=always
+PIDFile=/run/bitcoind/bitcoind.pid
+Restart=on-failure
PrivateTmp=true
-TimeoutStopSec=60s
-TimeoutStartSec=2s
-StartLimitInterval=120s
-StartLimitBurst=5
[Install]
WantedBy=multi-user.target
diff --git a/doc/build-openbsd.md b/doc/build-openbsd.md
index 4ea8e30b53..760bb69b15 100644
--- a/doc/build-openbsd.md
+++ b/doc/build-openbsd.md
@@ -1,10 +1,10 @@
OpenBSD build guide
======================
-(updated for OpenBSD 6.1)
+(updated for OpenBSD 6.2)
This guide describes how to build bitcoind and command-line utilities on OpenBSD.
-As OpenBSD is most common as a server OS, we will not bother with the GUI.
+OpenBSD is most commonly used as a server OS, so this guide does not contain instructions for building the GUI.
Preparation
-------------
@@ -12,10 +12,13 @@ Preparation
Run the following as root to install the base dependencies for building:
```bash
-pkg_add gmake libtool libevent
+pkg_add git gmake libevent libtool
pkg_add autoconf # (select highest version, e.g. 2.69)
pkg_add automake # (select highest version, e.g. 1.15)
-pkg_add python # (select highest version, e.g. 3.5)
+pkg_add python # (select highest version, e.g. 3.6)
+pkg_add boost
+
+git clone https://github.com/bitcoin/bitcoin.git
```
See [dependencies.md](dependencies.md) for a complete overview.
@@ -23,54 +26,19 @@ See [dependencies.md](dependencies.md) for a complete overview.
GCC
-------
-The default C++ compiler that comes with OpenBSD 5.9 is g++ 4.2. This version is old (from 2007), and is not able to compile the current version of Bitcoin Core, primarily as it has no C++11 support, but even before there were issues. So here we will be installing a newer compiler:
+The default C++ compiler that comes with OpenBSD 6.2 is g++ 4.2.1. This version is old (from 2007), and is not able to compile the current version of Bitcoin Core because it has no C++11 support. We'll install a newer version of GCC:
```bash
-pkg_add g++ # (select newest 4.x version, e.g. 4.9.3)
-```
-
-This compiler will not overwrite the system compiler, it will be installed as `egcc` and `eg++` in `/usr/local/bin`.
-
-### Building boost
-
-Do not use `pkg_add boost`! The boost version installed thus is compiled using the `g++` compiler not `eg++`, which will result in a conflict between `/usr/local/lib/libestdc++.so.XX.0` and `/usr/lib/libstdc++.so.XX.0`, resulting in a test crash:
-
- test_bitcoin:/usr/lib/libstdc++.so.57.0: /usr/local/lib/libestdc++.so.17.0 : WARNING: symbol(_ZN11__gnu_debug17_S_debug_me ssagesE) size mismatch, relink your program
- ...
- Segmentation fault (core dumped)
+ pkg_add g++
+ ```
-This makes it necessary to build boost, or at least the parts used by Bitcoin Core, manually:
-
-```
-# Pick some path to install boost to, here we create a directory within the bitcoin directory
-BITCOIN_ROOT=$(pwd)
-BOOST_PREFIX="${BITCOIN_ROOT}/boost"
-mkdir -p $BOOST_PREFIX
-
-# Fetch the source and verify that it is not tampered with
-curl -o boost_1_64_0.tar.bz2 https://netcologne.dl.sourceforge.net/project/boost/boost/1.64.0/boost_1_64_0.tar.bz2
-echo '7bcc5caace97baa948931d712ea5f37038dbb1c5d89b43ad4def4ed7cb683332 boost_1_64_0.tar.bz2' | sha256 -c
-# MUST output: (SHA256) boost_1_64_0.tar.bz2: OK
-tar -xjf boost_1_64_0.tar.bz2
-
-# Boost 1.64 needs one small patch for OpenBSD
-cd boost_1_64_0
-# Also here: https://gist.githubusercontent.com/laanwj/bf359281dc319b8ff2e1/raw/92250de8404b97bb99d72ab898f4a8cb35ae1ea3/patch-boost_test_impl_execution_monitor_ipp.patch
-patch -p0 < /usr/ports/devel/boost/patches/patch-boost_test_impl_execution_monitor_ipp
-
-# Build w/ minimum configuration necessary for bitcoin
-echo 'using gcc : : eg++ : <cxxflags>"-fvisibility=hidden -fPIC" <linkflags>"" <archiver>"ar" <striper>"strip" <ranlib>"ranlib" <rc>"" : ;' > user-config.jam
-config_opts="runtime-link=shared threadapi=pthread threading=multi link=static variant=release --layout=tagged --build-type=complete --user-config=user-config.jam -sNO_BZIP2=1"
-./bootstrap.sh --without-icu --with-libraries=chrono,filesystem,program_options,system,thread,test
-./b2 -d2 -j2 -d1 ${config_opts} --prefix=${BOOST_PREFIX} stage
-./b2 -d0 -j4 ${config_opts} --prefix=${BOOST_PREFIX} install
-```
+ This compiler will not overwrite the system compiler, it will be installed as `egcc` and `eg++` in `/usr/local/bin`.
### Building BerkeleyDB
BerkeleyDB is only necessary for the wallet functionality. To skip this, pass `--disable-wallet` to `./configure`.
-See "Berkeley DB" in [build_unix.md](build_unix.md) for instructions on how to build BerkeleyDB 4.8.
+See "Berkeley DB" in [build-unix.md](build-unix.md#berkeley-db) for instructions on how to build BerkeleyDB 4.8.
You cannot use the BerkeleyDB library from ports, for the same reason as boost above (g++/libstd++ incompatibility).
```bash
@@ -98,8 +66,8 @@ The standard ulimit restrictions in OpenBSD are very strict:
data(kbytes) 1572864
-This is, unfortunately, no longer enough to compile some `.cpp` files in the project,
-at least with gcc 4.9.3 (see issue https://github.com/bitcoin/bitcoin/issues/6658).
+This, unfortunately, may no longer be enough to compile some `.cpp` files in the project,
+at least with GCC 4.9.4 (see issue [#6658](https://github.com/bitcoin/bitcoin/issues/6658)).
If your user is in the `staff` group the limit can be raised with:
ulimit -d 3000000
@@ -118,59 +86,32 @@ export AUTOCONF_VERSION=2.69 # replace this with the autoconf version that you i
export AUTOMAKE_VERSION=1.15 # replace this with the automake version that you installed
./autogen.sh
```
-Make sure `BDB_PREFIX` and `BOOST_PREFIX` are set to the appropriate paths from the above steps.
+Make sure `BDB_PREFIX` is set to the appropriate path from the above steps.
To configure with wallet:
```bash
-./configure --with-gui=no --with-boost=$BOOST_PREFIX \
- CC=egcc CXX=eg++ CPP=ecpp \
+./configure --with-gui=no CC=egcc CXX=eg++ CPP=ecpp \
BDB_LIBS="-L${BDB_PREFIX}/lib -ldb_cxx-4.8" BDB_CFLAGS="-I${BDB_PREFIX}/include"
```
To configure without wallet:
```bash
-./configure --disable-wallet --with-gui=no --with-boost=$BOOST_PREFIX \
- CC=egcc CXX=eg++ CPP=ecpp
+./configure --disable-wallet --with-gui=no CC=egcc CXX=eg++ CPP=ecpp
```
Build and run the tests:
```bash
-gmake # can use -jX here for parallelism
+gmake # use -jX here for parallelism
gmake check
```
-Clang (not currently working)
+Clang
------------------------------
-WARNING: This is outdated, needs to be updated for OpenBSD 6.0 and re-tried.
-
-Using a newer g++ results in linking the new code to a new libstdc++.
-Libraries built with the old g++, will still import the old library.
-This gives conflicts, necessitating rebuild of all C++ dependencies of the application.
-
-With clang this can - at least theoretically - be avoided because it uses the
-base system's libstdc++.
-
```bash
-pkg_add llvm boost
-```
+pkg_add llvm
-```bash
./configure --disable-wallet --with-gui=no CC=clang CXX=clang++
-gmake
+gmake # use -jX here for parallelism
+gmake check
```
-
-However, this does not appear to work. Compilation succeeds, but link fails
-with many 'local symbol discarded' errors:
-
- local symbol 150: discarded in section `.text._ZN10tinyformat6detail14FormatIterator6finishEv' from libbitcoin_util.a(libbitcoin_util_a-random.o)
- local symbol 151: discarded in section `.text._ZN10tinyformat6detail14FormatIterator21streamStateFromFormatERSoRjPKcii' from libbitcoin_util.a(libbitcoin_util_a-random.o)
- local symbol 152: discarded in section `.text._ZN10tinyformat6detail12convertToIntIA13_cLb0EE6invokeERA13_Kc' from libbitcoin_util.a(libbitcoin_util_a-random.o)
-
-According to similar reported errors this is a binutils (ld) issue in 2.15, the
-version installed by OpenBSD 5.7:
-
-- http://openbsd-archive.7691.n7.nabble.com/UPDATE-cppcheck-1-65-td248900.html
-- https://llvm.org/bugs/show_bug.cgi?id=9758
-
-There is no known workaround for this.
diff --git a/doc/release-notes.md b/doc/release-notes.md
index 4ecca7897c..23414666ce 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -76,6 +76,9 @@ will only create hierarchical deterministic (HD) wallets.
Low-level RPC changes
----------------------
+- `listsinceblock` will now throw an error if an unknown `blockhash` argument
+ value is passed, instead of returning a list of all wallet transactions since
+ the genesis block.
- The "currentblocksize" value in getmininginfo has been removed.
- The deprecated RPC `getinfo` was removed. It is recommended that the more specific RPCs are used:
* `getblockchaininfo`
diff --git a/src/addrman.h b/src/addrman.h
index 18f3062287..f347cba6ca 100644
--- a/src/addrman.h
+++ b/src/addrman.h
@@ -455,6 +455,7 @@ public:
void Clear()
{
+ LOCK(cs);
std::vector<int>().swap(vRandom);
nKey = GetRandHash();
for (size_t bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; bucket++) {
diff --git a/src/bench/bench.cpp b/src/bench/bench.cpp
index 7b307d6f42..dd4ba5ab0e 100644
--- a/src/bench/bench.cpp
+++ b/src/bench/bench.cpp
@@ -8,29 +8,22 @@
#include <assert.h>
#include <iostream>
#include <iomanip>
-#include <sys/time.h>
benchmark::BenchRunner::BenchmarkMap &benchmark::BenchRunner::benchmarks() {
static std::map<std::string, benchmark::BenchFunction> benchmarks_map;
return benchmarks_map;
}
-static double gettimedouble(void) {
- struct timeval tv;
- gettimeofday(&tv, nullptr);
- return tv.tv_usec * 0.000001 + tv.tv_sec;
-}
-
benchmark::BenchRunner::BenchRunner(std::string name, benchmark::BenchFunction func)
{
benchmarks().insert(std::make_pair(name, func));
}
void
-benchmark::BenchRunner::RunAll(double elapsedTimeForOne)
+benchmark::BenchRunner::RunAll(benchmark::duration elapsedTimeForOne)
{
perf_init();
- std::cout << "#Benchmark" << "," << "count" << "," << "min" << "," << "max" << "," << "average" << ","
+ std::cout << "#Benchmark" << "," << "count" << "," << "min(ns)" << "," << "max(ns)" << "," << "average(ns)" << ","
<< "min_cycles" << "," << "max_cycles" << "," << "average_cycles" << "\n";
for (const auto &p: benchmarks()) {
@@ -46,16 +39,17 @@ bool benchmark::State::KeepRunning()
++count;
return true;
}
- double now;
+ time_point now;
+
uint64_t nowCycles;
if (count == 0) {
- lastTime = beginTime = now = gettimedouble();
+ lastTime = beginTime = now = clock::now();
lastCycles = beginCycles = nowCycles = perf_cpucycles();
}
else {
- now = gettimedouble();
- double elapsed = now - lastTime;
- double elapsedOne = elapsed / (countMask + 1);
+ now = clock::now();
+ auto elapsed = now - lastTime;
+ auto elapsedOne = elapsed / (countMask + 1);
if (elapsedOne < minTime) minTime = elapsedOne;
if (elapsedOne > maxTime) maxTime = elapsedOne;
@@ -70,8 +64,8 @@ bool benchmark::State::KeepRunning()
// The restart avoids including the overhead of this code in the measurement.
countMask = ((countMask<<3)|7) & ((1LL<<60)-1);
count = 0;
- minTime = std::numeric_limits<double>::max();
- maxTime = std::numeric_limits<double>::min();
+ minTime = duration::max();
+ maxTime = duration::zero();
minCycles = std::numeric_limits<uint64_t>::max();
maxCycles = std::numeric_limits<uint64_t>::min();
return true;
@@ -94,9 +88,13 @@ bool benchmark::State::KeepRunning()
assert(count != 0 && "count == 0 => (now == 0 && beginTime == 0) => return above");
// Output results
- double average = (now-beginTime)/count;
+ // Duration casts are only necessary here because hardware with sub-nanosecond clocks
+ // will lose precision.
+ int64_t min_elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(minTime).count();
+ int64_t max_elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(maxTime).count();
+ int64_t avg_elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>((now-beginTime)/count).count();
int64_t averageCycles = (nowCycles-beginCycles)/count;
- std::cout << std::fixed << std::setprecision(15) << name << "," << count << "," << minTime << "," << maxTime << "," << average << ","
+ std::cout << std::fixed << std::setprecision(15) << name << "," << count << "," << min_elapsed << "," << max_elapsed << "," << avg_elapsed << ","
<< minCycles << "," << maxCycles << "," << averageCycles << "\n";
std::cout.copyfmt(std::ios(nullptr));
diff --git a/src/bench/bench.h b/src/bench/bench.h
index 79109eaa56..d276f4ee91 100644
--- a/src/bench/bench.h
+++ b/src/bench/bench.h
@@ -9,6 +9,7 @@
#include <limits>
#include <map>
#include <string>
+#include <chrono>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/stringize.hpp>
@@ -36,12 +37,23 @@ BENCHMARK(CODE_TO_TIME);
*/
namespace benchmark {
+ // On many systems, the high_resolution_clock offers no better resolution than the steady_clock.
+ // If that's the case, prefer the steady_clock.
+ struct best_clock {
+ using hi_res_clock = std::chrono::high_resolution_clock;
+ using steady_clock = std::chrono::steady_clock;
+ static constexpr bool steady_is_high_res = std::ratio_less_equal<steady_clock::period, hi_res_clock::period>::value;
+ using type = std::conditional<steady_is_high_res, steady_clock, hi_res_clock>::type;
+ };
+ using clock = best_clock::type;
+ using time_point = clock::time_point;
+ using duration = clock::duration;
class State {
std::string name;
- double maxElapsed;
- double beginTime;
- double lastTime, minTime, maxTime;
+ duration maxElapsed;
+ time_point beginTime, lastTime;
+ duration minTime, maxTime;
uint64_t count;
uint64_t countMask;
uint64_t beginCycles;
@@ -49,9 +61,9 @@ namespace benchmark {
uint64_t minCycles;
uint64_t maxCycles;
public:
- State(std::string _name, double _maxElapsed) : name(_name), maxElapsed(_maxElapsed), count(0) {
- minTime = std::numeric_limits<double>::max();
- maxTime = std::numeric_limits<double>::min();
+ State(std::string _name, duration _maxElapsed) : name(_name), maxElapsed(_maxElapsed), count(0) {
+ minTime = duration::max();
+ maxTime = duration::zero();
minCycles = std::numeric_limits<uint64_t>::max();
maxCycles = std::numeric_limits<uint64_t>::min();
countMask = 1;
@@ -69,7 +81,7 @@ namespace benchmark {
public:
BenchRunner(std::string name, BenchFunction func);
- static void RunAll(double elapsedTimeForOne=1.0);
+ static void RunAll(duration elapsedTimeForOne = std::chrono::seconds(1));
};
}
diff --git a/src/bench/rollingbloom.cpp b/src/bench/rollingbloom.cpp
index 73c02cf718..a93d0fb0a5 100644
--- a/src/bench/rollingbloom.cpp
+++ b/src/bench/rollingbloom.cpp
@@ -6,7 +6,6 @@
#include "bench.h"
#include "bloom.h"
-#include "utiltime.h"
static void RollingBloom(benchmark::State& state)
{
@@ -23,10 +22,10 @@ static void RollingBloom(benchmark::State& state)
data[2] = count >> 16;
data[3] = count >> 24;
if (countnow == nEntriesPerGeneration) {
- int64_t b = GetTimeMicros();
+ auto b = benchmark::clock::now();
filter.insert(data);
- int64_t e = GetTimeMicros();
- std::cout << "RollingBloom-refresh,1," << (e-b)*0.000001 << "," << (e-b)*0.000001 << "," << (e-b)*0.000001 << "\n";
+ auto total = std::chrono::duration_cast<std::chrono::nanoseconds>(benchmark::clock::now() - b).count();
+ std::cout << "RollingBloom-refresh,1," << total << "," << total << "," << total << "\n";
countnow = 0;
} else {
filter.insert(data);
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index a20222d05c..b499b15507 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -387,6 +387,10 @@ static void MutateTxAddOutMultiSig(CMutableTransaction& tx, const std::string& s
scriptPubKey = GetScriptForWitness(scriptPubKey);
}
if (bScriptHash) {
+ if (scriptPubKey.size() > MAX_SCRIPT_ELEMENT_SIZE) {
+ throw std::runtime_error(strprintf(
+ "redeemScript exceeds size limit: %d > %d", scriptPubKey.size(), MAX_SCRIPT_ELEMENT_SIZE));
+ }
// Get the ID for the script, and then construct a P2SH destination for it.
scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey));
}
@@ -447,10 +451,19 @@ static void MutateTxAddOutScript(CMutableTransaction& tx, const std::string& str
bScriptHash = (flags.find("S") != std::string::npos);
}
+ if (scriptPubKey.size() > MAX_SCRIPT_SIZE) {
+ throw std::runtime_error(strprintf(
+ "script exceeds size limit: %d > %d", scriptPubKey.size(), MAX_SCRIPT_SIZE));
+ }
+
if (bSegWit) {
scriptPubKey = GetScriptForWitness(scriptPubKey);
}
if (bScriptHash) {
+ if (scriptPubKey.size() > MAX_SCRIPT_ELEMENT_SIZE) {
+ throw std::runtime_error(strprintf(
+ "redeemScript exceeds size limit: %d > %d", scriptPubKey.size(), MAX_SCRIPT_ELEMENT_SIZE));
+ }
scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey));
}
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index 543eba0e69..5f88c35dbd 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -120,7 +120,7 @@ bool AppInit(int argc, char* argv[])
for (int i = 1; i < argc; i++) {
if (!IsSwitchChar(argv[i][0])) {
fprintf(stderr, "Error: Command line contains unexpected token '%s', see bitcoind -h for a list of options.\n", argv[i]);
- exit(EXIT_FAILURE);
+ return false;
}
}
@@ -132,17 +132,17 @@ bool AppInit(int argc, char* argv[])
if (!AppInitBasicSetup())
{
// InitError will have been called with detailed error, which ends up on console
- exit(EXIT_FAILURE);
+ return false;
}
if (!AppInitParameterInteraction())
{
// InitError will have been called with detailed error, which ends up on console
- exit(EXIT_FAILURE);
+ return false;
}
if (!AppInitSanityChecks())
{
// InitError will have been called with detailed error, which ends up on console
- exit(EXIT_FAILURE);
+ return false;
}
if (gArgs.GetBoolArg("-daemon", false))
{
@@ -163,7 +163,7 @@ bool AppInit(int argc, char* argv[])
if (!AppInitLockDataDirectory())
{
// If locking the data directory failed, exit immediately
- exit(EXIT_FAILURE);
+ return false;
}
fRet = AppInitMain(threadGroup, scheduler);
}
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index afdac16da4..950bdd945c 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -75,6 +75,7 @@ public:
CMainParams() {
strNetworkID = "main";
consensus.nSubsidyHalvingInterval = 210000;
+ consensus.BIP16Height = 173805; // 00000000000000ce80a7e057163a4db1d5ad7b20fb6f598c9597b9665c8fb0d4 - April 1, 2012
consensus.BIP34Height = 227931;
consensus.BIP34Hash = uint256S("0x000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8");
consensus.BIP65Height = 388381; // 000000000000000004c2b624ed5d7756c508d90fd0da2c7c679febfa6c4735f0
@@ -181,6 +182,7 @@ public:
CTestNetParams() {
strNetworkID = "test";
consensus.nSubsidyHalvingInterval = 210000;
+ consensus.BIP16Height = 514; // 00000000040b4e986385315e14bee30ad876d8b47f748025b26683116d21aa65
consensus.BIP34Height = 21111;
consensus.BIP34Hash = uint256S("0x0000000023b3a96d3484e5abb3755c413e7d41500f8e2a5c3f0dd01299cd8ef8");
consensus.BIP65Height = 581885; // 00000000007f6655f22f98e72ed80d8b06dc761d5da09df0fa1dc4be4f861eb6
@@ -270,6 +272,7 @@ public:
CRegTestParams() {
strNetworkID = "regtest";
consensus.nSubsidyHalvingInterval = 150;
+ consensus.BIP16Height = 0; // always enforce P2SH BIP16 on regtest
consensus.BIP34Height = 100000000; // BIP34 has not activated on regtest (far in the future so block v1 are not rejected in tests)
consensus.BIP34Hash = uint256();
consensus.BIP65Height = 1351; // BIP65 activated on regtest (Used in rpc activation tests)
@@ -283,13 +286,13 @@ public:
consensus.nMinerConfirmationWindow = 144; // Faster than normal for regtest (144 instead of 2016)
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28;
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 0;
- consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 999999999999ULL;
+ consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
consensus.vDeployments[Consensus::DEPLOYMENT_CSV].bit = 0;
consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nStartTime = 0;
- consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nTimeout = 999999999999ULL;
+ consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].bit = 1;
- consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nStartTime = 0;
- consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout = 999999999999ULL;
+ consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE;
+ consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
// The best chain should have at least this much work.
consensus.nMinimumChainWork = uint256S("0x00");
diff --git a/src/consensus/params.h b/src/consensus/params.h
index 6240e82857..fd0946a612 100644
--- a/src/consensus/params.h
+++ b/src/consensus/params.h
@@ -7,6 +7,7 @@
#define BITCOIN_CONSENSUS_PARAMS_H
#include "uint256.h"
+#include <limits>
#include <map>
#include <string>
@@ -31,6 +32,15 @@ struct BIP9Deployment {
int64_t nStartTime;
/** Timeout/expiry MedianTime for the deployment attempt. */
int64_t nTimeout;
+
+ /** Constant for nTimeout very far in the future. */
+ static constexpr int64_t NO_TIMEOUT = std::numeric_limits<int64_t>::max();
+
+ /** Special value for nStartTime indicating that the deployment is always active.
+ * This is useful for testing, as it means tests don't need to deal with the activation
+ * process (which takes at least 3 BIP9 intervals). Only tests that specifically test the
+ * behaviour during activation cannot use this. */
+ static constexpr int64_t ALWAYS_ACTIVE = -1;
};
/**
@@ -39,6 +49,8 @@ struct BIP9Deployment {
struct Params {
uint256 hashGenesisBlock;
int nSubsidyHalvingInterval;
+ /** Block height at which BIP16 becomes active */
+ int BIP16Height;
/** Block height and hash at which BIP34 becomes active */
int BIP34Height;
uint256 BIP34Hash;
diff --git a/src/httpserver.cpp b/src/httpserver.cpp
index 31b6a3705b..f6cbaa20b7 100644
--- a/src/httpserver.cpp
+++ b/src/httpserver.cpp
@@ -24,6 +24,7 @@
#include <event2/thread.h>
#include <event2/buffer.h>
+#include <event2/bufferevent.h>
#include <event2/util.h>
#include <event2/keyvalq_struct.h>
@@ -239,6 +240,16 @@ static std::string RequestMethodString(HTTPRequest::RequestMethod m)
/** HTTP request callback */
static void http_request_cb(struct evhttp_request* req, void* arg)
{
+ // Disable reading to work around a libevent bug, fixed in 2.2.0.
+ if (event_get_version_number() >= 0x02010600 && event_get_version_number() < 0x02020001) {
+ evhttp_connection* conn = evhttp_request_get_connection(req);
+ if (conn) {
+ bufferevent* bev = evhttp_connection_get_bufferevent(conn);
+ if (bev) {
+ bufferevent_disable(bev, EV_READ);
+ }
+ }
+ }
std::unique_ptr<HTTPRequest> hreq(new HTTPRequest(req));
LogPrint(BCLog::HTTP, "Received a %s request for %s from %s\n",
@@ -601,8 +612,21 @@ void HTTPRequest::WriteReply(int nStatus, const std::string& strReply)
struct evbuffer* evb = evhttp_request_get_output_buffer(req);
assert(evb);
evbuffer_add(evb, strReply.data(), strReply.size());
- HTTPEvent* ev = new HTTPEvent(eventBase, true,
- std::bind(evhttp_send_reply, req, nStatus, (const char*)nullptr, (struct evbuffer *)nullptr));
+ auto req_copy = req;
+ HTTPEvent* ev = new HTTPEvent(eventBase, true, [req_copy, nStatus]{
+ evhttp_send_reply(req_copy, nStatus, nullptr, nullptr);
+ // Re-enable reading from the socket. This is the second part of the libevent
+ // workaround above.
+ if (event_get_version_number() >= 0x02010600 && event_get_version_number() < 0x02020001) {
+ evhttp_connection* conn = evhttp_request_get_connection(req_copy);
+ if (conn) {
+ bufferevent* bev = evhttp_connection_get_bufferevent(conn);
+ if (bev) {
+ bufferevent_enable(bev, EV_READ | EV_WRITE);
+ }
+ }
+ }
+ });
ev->trigger(nullptr);
replySent = true;
req = nullptr; // transferred back to main thread
diff --git a/src/init.cpp b/src/init.cpp
index 6557434880..ddac606a39 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -544,14 +544,14 @@ static void BlockNotifyCallback(bool initialSync, const CBlockIndex *pBlockIndex
}
static bool fHaveGenesis = false;
-static boost::mutex cs_GenesisWait;
+static CWaitableCriticalSection cs_GenesisWait;
static CConditionVariable condvar_GenesisWait;
static void BlockNotifyGenesisWait(bool, const CBlockIndex *pBlockIndex)
{
if (pBlockIndex != nullptr) {
{
- boost::unique_lock<boost::mutex> lock_GenesisWait(cs_GenesisWait);
+ WaitableLock lock_GenesisWait(cs_GenesisWait);
fHaveGenesis = true;
}
condvar_GenesisWait.notify_all();
@@ -1270,7 +1270,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
g_connman = std::unique_ptr<CConnman>(new CConnman(GetRand(std::numeric_limits<uint64_t>::max()), GetRand(std::numeric_limits<uint64_t>::max())));
CConnman& connman = *g_connman;
- peerLogic.reset(new PeerLogicValidation(&connman));
+ peerLogic.reset(new PeerLogicValidation(&connman, scheduler));
RegisterValidationInterface(peerLogic.get());
// sanitize comments per BIP-0014, format user agent and check total size
@@ -1630,7 +1630,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
// Wait for genesis block to be processed
{
- boost::unique_lock<boost::mutex> lock(cs_GenesisWait);
+ WaitableLock lock(cs_GenesisWait);
while (!fHaveGenesis) {
condvar_GenesisWait.wait(lock);
}
diff --git a/src/net.cpp b/src/net.cpp
index 258599747a..d5bd4c162b 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -961,6 +961,16 @@ static bool CompareNodeTXTime(const NodeEvictionCandidate &a, const NodeEviction
return a.nTimeConnected > b.nTimeConnected;
}
+
+//! Sort an array by the specified comparator, then erase the last K elements.
+template<typename T, typename Comparator>
+static void EraseLastKElements(std::vector<T> &elements, Comparator comparator, size_t k)
+{
+ std::sort(elements.begin(), elements.end(), comparator);
+ size_t eraseSize = std::min(k, elements.size());
+ elements.erase(elements.end() - eraseSize, elements.end());
+}
+
/** Try to find a connection to evict when the node is full.
* Extreme care must be taken to avoid opening the node to attacker
* triggered network partitioning.
@@ -990,42 +1000,23 @@ bool CConnman::AttemptToEvictConnection()
}
}
- if (vEvictionCandidates.empty()) return false;
-
// Protect connections with certain characteristics
// Deterministically select 4 peers to protect by netgroup.
// An attacker cannot predict which netgroups will be protected
- std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareNetGroupKeyed);
- vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(4, static_cast<int>(vEvictionCandidates.size())), vEvictionCandidates.end());
-
- if (vEvictionCandidates.empty()) return false;
-
+ EraseLastKElements(vEvictionCandidates, CompareNetGroupKeyed, 4);
// Protect the 8 nodes with the lowest minimum ping time.
// An attacker cannot manipulate this metric without physically moving nodes closer to the target.
- std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), ReverseCompareNodeMinPingTime);
- vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(8, static_cast<int>(vEvictionCandidates.size())), vEvictionCandidates.end());
-
- if (vEvictionCandidates.empty()) return false;
-
+ EraseLastKElements(vEvictionCandidates, ReverseCompareNodeMinPingTime, 8);
// Protect 4 nodes that most recently sent us transactions.
// An attacker cannot manipulate this metric without performing useful work.
- std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareNodeTXTime);
- vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(4, static_cast<int>(vEvictionCandidates.size())), vEvictionCandidates.end());
-
- if (vEvictionCandidates.empty()) return false;
-
+ EraseLastKElements(vEvictionCandidates, CompareNodeTXTime, 4);
// Protect 4 nodes that most recently sent us blocks.
// An attacker cannot manipulate this metric without performing useful work.
- std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareNodeBlockTime);
- vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(4, static_cast<int>(vEvictionCandidates.size())), vEvictionCandidates.end());
-
- if (vEvictionCandidates.empty()) return false;
-
+ EraseLastKElements(vEvictionCandidates, CompareNodeBlockTime, 4);
// Protect the half of the remaining nodes which have been connected the longest.
// This replicates the non-eviction implicit behavior, and precludes attacks that start later.
- std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), ReverseCompareNodeTimeConnected);
- vEvictionCandidates.erase(vEvictionCandidates.end() - static_cast<int>(vEvictionCandidates.size() / 2), vEvictionCandidates.end());
+ EraseLastKElements(vEvictionCandidates, ReverseCompareNodeTimeConnected, vEvictionCandidates.size() / 2);
if (vEvictionCandidates.empty()) return false;
@@ -1036,12 +1027,12 @@ bool CConnman::AttemptToEvictConnection()
int64_t nMostConnectionsTime = 0;
std::map<uint64_t, std::vector<NodeEvictionCandidate> > mapNetGroupNodes;
for (const NodeEvictionCandidate &node : vEvictionCandidates) {
- mapNetGroupNodes[node.nKeyedNetGroup].push_back(node);
- int64_t grouptime = mapNetGroupNodes[node.nKeyedNetGroup][0].nTimeConnected;
- size_t groupsize = mapNetGroupNodes[node.nKeyedNetGroup].size();
+ std::vector<NodeEvictionCandidate> &group = mapNetGroupNodes[node.nKeyedNetGroup];
+ group.push_back(node);
+ int64_t grouptime = group[0].nTimeConnected;
- if (groupsize > nMostConnections || (groupsize == nMostConnections && grouptime > nMostConnectionsTime)) {
- nMostConnections = groupsize;
+ if (group.size() > nMostConnections || (group.size() == nMostConnections && grouptime > nMostConnectionsTime)) {
+ nMostConnections = group.size();
nMostConnectionsTime = grouptime;
naMostConnections = node.nKeyedNetGroup;
}
@@ -1693,6 +1684,37 @@ void CConnman::ProcessOneShot()
}
}
+bool CConnman::GetTryNewOutboundPeer()
+{
+ return m_try_another_outbound_peer;
+}
+
+void CConnman::SetTryNewOutboundPeer(bool flag)
+{
+ m_try_another_outbound_peer = flag;
+ LogPrint(BCLog::NET, "net: setting try another outbound peer=%s\n", flag ? "true" : "false");
+}
+
+// Return the number of peers we have over our outbound connection limit
+// Exclude peers that are marked for disconnect, or are going to be
+// disconnected soon (eg one-shots and feelers)
+// Also exclude peers that haven't finished initial connection handshake yet
+// (so that we don't decide we're over our desired connection limit, and then
+// evict some peer that has finished the handshake)
+int CConnman::GetExtraOutboundCount()
+{
+ int nOutbound = 0;
+ {
+ LOCK(cs_vNodes);
+ for (CNode* pnode : vNodes) {
+ if (!pnode->fInbound && !pnode->m_manual_connection && !pnode->fFeeler && !pnode->fDisconnect && !pnode->fOneShot && pnode->fSuccessfullyConnected) {
+ ++nOutbound;
+ }
+ }
+ }
+ return std::max(nOutbound - nMaxOutbound, 0);
+}
+
void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
{
// Connect to specific addresses
@@ -1781,7 +1803,8 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
// * Only make a feeler connection once every few minutes.
//
bool fFeeler = false;
- if (nOutbound >= nMaxOutbound) {
+
+ if (nOutbound >= nMaxOutbound && !GetTryNewOutboundPeer()) {
int64_t nTime = GetTimeMicros(); // The current time right now (in microseconds).
if (nTime > nNextFeeler) {
nNextFeeler = PoissonNextSend(nTime, FEELER_INTERVAL);
@@ -2204,6 +2227,7 @@ CConnman::CConnman(uint64_t nSeed0In, uint64_t nSeed1In) : nSeed0(nSeed0In), nSe
semOutbound = nullptr;
semAddnode = nullptr;
flagInterruptMsgProc = false;
+ SetTryNewOutboundPeer(false);
Options connOptions;
Init(connOptions);
@@ -2754,8 +2778,7 @@ CNode::~CNode()
{
CloseSocket(hSocket);
- if (pfilter)
- delete pfilter;
+ delete pfilter;
}
void CNode::AskFor(const CInv& inv)
diff --git a/src/net.h b/src/net.h
index f373ab0cf1..edca1171ab 100644
--- a/src/net.h
+++ b/src/net.h
@@ -251,6 +251,19 @@ public:
void GetBanned(banmap_t &banmap);
void SetBanned(const banmap_t &banmap);
+ // This allows temporarily exceeding nMaxOutbound, with the goal of finding
+ // a peer that is better than all our current peers.
+ void SetTryNewOutboundPeer(bool flag);
+ bool GetTryNewOutboundPeer();
+
+ // Return the number of outbound peers we have in excess of our target (eg,
+ // if we previously called SetTryNewOutboundPeer(true), and have since set
+ // to false, we may have extra peers that we wish to disconnect). This may
+ // return a value less than (num_outbound_connections - num_outbound_slots)
+ // in cases where some outbound connections are not yet fully connected, or
+ // not yet fully disconnected.
+ int GetExtraOutboundCount();
+
bool AddNode(const std::string& node);
bool RemoveAddedNode(const std::string& node);
std::vector<AddedNodeInfo> GetAddedNodeInfo();
@@ -413,6 +426,13 @@ private:
std::thread threadOpenAddedConnections;
std::thread threadOpenConnections;
std::thread threadMessageHandler;
+
+ /** flag for deciding to connect to an extra outbound peer,
+ * in excess of nMaxOutbound
+ * This takes the place of a feeler connection */
+ std::atomic_bool m_try_another_outbound_peer;
+
+ friend struct CConnmanTest;
};
extern std::unique_ptr<CConnman> g_connman;
void Discover(boost::thread_group& threadGroup);
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index b26caf377f..52387b32f4 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -23,6 +23,7 @@
#include "primitives/transaction.h"
#include "random.h"
#include "reverse_iterator.h"
+#include "scheduler.h"
#include "tinyformat.h"
#include "txmempool.h"
#include "ui_interface.h"
@@ -127,6 +128,9 @@ namespace {
/** Number of outbound peers with m_chain_sync.m_protect. */
int g_outbound_peers_with_protect_from_disconnect = 0;
+ /** When our tip was last updated. */
+ int64_t g_last_tip_update = 0;
+
/** Relay map, protected by cs_main. */
typedef std::map<uint256, CTransactionRef> MapRelay;
MapRelay mapRelay;
@@ -231,6 +235,9 @@ struct CNodeState {
ChainSyncTimeoutState m_chain_sync;
+ //! Time of last new block announcement
+ int64_t m_last_block_announcement;
+
CNodeState(CAddress addrIn, std::string addrNameIn) : address(addrIn), name(addrNameIn) {
fCurrentlyConnected = false;
nMisbehavior = 0;
@@ -254,6 +261,7 @@ struct CNodeState {
fWantsCmpctWitness = false;
fSupportsDesiredCmpctVersion = false;
m_chain_sync = { 0, nullptr, false, false };
+ m_last_block_announcement = 0;
}
};
@@ -427,6 +435,15 @@ void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman* connman) {
}
}
+bool TipMayBeStale(const Consensus::Params &consensusParams)
+{
+ AssertLockHeld(cs_main);
+ if (g_last_tip_update == 0) {
+ g_last_tip_update = GetTime();
+ }
+ return g_last_tip_update < GetTime() - consensusParams.nPowTargetSpacing * 3 && mapBlocksInFlight.empty();
+}
+
// Requires cs_main
bool CanDirectFetch(const Consensus::Params &consensusParams)
{
@@ -533,6 +550,15 @@ void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<con
} // namespace
+// This function is used for testing the stale tip eviction logic, see
+// DoS_tests.cpp
+void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds)
+{
+ LOCK(cs_main);
+ CNodeState *state = State(node);
+ if (state) state->m_last_block_announcement = time_in_seconds;
+}
+
// Returns true for outbound peers, excluding manual connections, feelers, and
// one-shots
bool IsOutboundDisconnectionCandidate(const CNode *node)
@@ -607,7 +633,7 @@ bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) {
// mapOrphanTransactions
//
-void AddToCompactExtraTransactions(const CTransactionRef& tx)
+void AddToCompactExtraTransactions(const CTransactionRef& tx) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
size_t max_extra_txn = gArgs.GetArg("-blockreconstructionextratxn", DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN);
if (max_extra_txn <= 0)
@@ -766,9 +792,17 @@ static bool BlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Para
(GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, consensusParams) < STALE_RELAY_AGE_LIMIT);
}
-PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn) : connman(connmanIn) {
+PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn, CScheduler &scheduler) : connman(connmanIn), m_stale_tip_check_time(0) {
// Initialize global variables that cannot be constructed at startup.
recentRejects.reset(new CRollingBloomFilter(120000, 0.000001));
+
+ const Consensus::Params& consensusParams = Params().GetConsensus();
+ // Stale tip checking and peer eviction are on two different timers, but we
+ // don't want them to get out of sync due to drift in the scheduler, so we
+ // combine them in one function and schedule at the quicker (peer-eviction)
+ // timer.
+ static_assert(EXTRA_PEER_CHECK_INTERVAL < STALE_CHECK_INTERVAL, "peer eviction timer should be less than stale tip check timer");
+ scheduler.scheduleEvery(std::bind(&PeerLogicValidation::CheckForStaleTipAndEvictPeers, this, consensusParams), EXTRA_PEER_CHECK_INTERVAL * 1000);
}
void PeerLogicValidation::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex, const std::vector<CTransactionRef>& vtxConflicted) {
@@ -799,6 +833,8 @@ void PeerLogicValidation::BlockConnected(const std::shared_ptr<const CBlock>& pb
}
LogPrint(BCLog::MEMPOOL, "Erased %d orphan tx included or conflicted by block\n", nErased);
}
+
+ g_last_tip_update = GetTime();
}
// All of the following cache a recent block, and are protected by cs_most_recent_block
@@ -1212,6 +1248,7 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve
return true;
}
+ bool received_new_header = false;
const CBlockIndex *pindexLast = nullptr;
{
LOCK(cs_main);
@@ -1252,6 +1289,12 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve
}
hashLastBlock = header.GetHash();
}
+
+ // If we don't have the last header, then they'll have given us
+ // something new (if these headers are valid).
+ if (mapBlockIndex.find(hashLastBlock) == mapBlockIndex.end()) {
+ received_new_header = true;
+ }
}
CValidationState state;
@@ -1259,8 +1302,8 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve
if (!ProcessNewBlockHeaders(headers, state, chainparams, &pindexLast, &first_invalid_header)) {
int nDoS;
if (state.IsInvalid(nDoS)) {
+ LOCK(cs_main);
if (nDoS > 0) {
- LOCK(cs_main);
Misbehaving(pfrom->GetId(), nDoS);
}
if (punish_duplicate_invalid && mapBlockIndex.find(first_invalid_header.GetHash()) != mapBlockIndex.end()) {
@@ -1316,6 +1359,10 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve
// because it is set in UpdateBlockAvailability. Some nullptr checks
// are still present, however, as belt-and-suspenders.
+ if (received_new_header && pindexLast->nChainWork > chainActive.Tip()->nChainWork) {
+ nodestate->m_last_block_announcement = GetTime();
+ }
+
if (nCount == MAX_HEADERS_RESULTS) {
// Headers message had its maximum size; the peer may have more headers.
// TODO: optimize: if pindexLast is an ancestor of chainActive.Tip or pindexBestHeader, continue
@@ -1400,6 +1447,7 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve
// If this is an outbound peer, check to see if we should protect
// it from the bad/lagging chain logic.
if (g_outbound_peers_with_protect_from_disconnect < MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT && nodestate->pindexBestKnownBlock->nChainWork >= chainActive.Tip()->nChainWork && !nodestate->m_chain_sync.m_protect) {
+ LogPrint(BCLog::NET, "Protecting outbound peer=%d from eviction\n", pfrom->GetId());
nodestate->m_chain_sync.m_protect = true;
++g_outbound_peers_with_protect_from_disconnect;
}
@@ -2215,6 +2263,8 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
CBlockHeaderAndShortTxIDs cmpctblock;
vRecv >> cmpctblock;
+ bool received_new_header = false;
+
{
LOCK(cs_main);
@@ -2224,6 +2274,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), uint256()));
return true;
}
+
+ if (mapBlockIndex.find(cmpctblock.header.GetHash()) == mapBlockIndex.end()) {
+ received_new_header = true;
+ }
}
const CBlockIndex *pindex = nullptr;
@@ -2262,6 +2316,14 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
assert(pindex);
UpdateBlockAvailability(pfrom->GetId(), pindex->GetBlockHash());
+ CNodeState *nodestate = State(pfrom->GetId());
+
+ // If this was a new header with more work than our tip, update the
+ // peer's last block announcement time
+ if (received_new_header && pindex->nChainWork > chainActive.Tip()->nChainWork) {
+ nodestate->m_last_block_announcement = GetTime();
+ }
+
std::map<uint256, std::pair<NodeId, std::list<QueuedBlock>::iterator> >::iterator blockInFlightIt = mapBlocksInFlight.find(pindex->GetBlockHash());
bool fAlreadyInFlight = blockInFlightIt != mapBlocksInFlight.end();
@@ -2284,8 +2346,6 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (!fAlreadyInFlight && !CanDirectFetch(chainparams.GetConsensus()))
return true;
- CNodeState *nodestate = State(pfrom->GetId());
-
if (IsWitnessEnabled(pindex->pprev, chainparams.GetConsensus()) && !nodestate->fSupportsDesiredCmpctVersion) {
// Don't bother trying to process compact blocks from v1 peers
// after segwit activates.
@@ -2527,11 +2587,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
LogPrint(BCLog::NET, "received block %s peer=%d\n", pblock->GetHash().ToString(), pfrom->GetId());
- // Process all blocks from whitelisted peers, even if not requested,
- // unless we're still syncing with the network.
- // Such an unrequested block may still be processed, subject to the
- // conditions in AcceptBlock().
- bool forceProcessing = pfrom->fWhitelisted && !IsInitialBlockDownload();
+ bool forceProcessing = false;
const uint256 hash(pblock->GetHash());
{
LOCK(cs_main);
@@ -2967,6 +3023,83 @@ void PeerLogicValidation::ConsiderEviction(CNode *pto, int64_t time_in_seconds)
}
}
+void PeerLogicValidation::EvictExtraOutboundPeers(int64_t time_in_seconds)
+{
+ // Check whether we have too many outbound peers
+ int extra_peers = connman->GetExtraOutboundCount();
+ if (extra_peers > 0) {
+ // If we have more outbound peers than we target, disconnect one.
+ // Pick the outbound peer that least recently announced
+ // us a new block, with ties broken by choosing the more recent
+ // connection (higher node id)
+ NodeId worst_peer = -1;
+ int64_t oldest_block_announcement = std::numeric_limits<int64_t>::max();
+
+ LOCK(cs_main);
+
+ connman->ForEachNode([&](CNode* pnode) {
+ // Ignore non-outbound peers, or nodes marked for disconnect already
+ if (!IsOutboundDisconnectionCandidate(pnode) || pnode->fDisconnect) return;
+ CNodeState *state = State(pnode->GetId());
+ if (state == nullptr) return; // shouldn't be possible, but just in case
+ // Don't evict our protected peers
+ if (state->m_chain_sync.m_protect) return;
+ if (state->m_last_block_announcement < oldest_block_announcement || (state->m_last_block_announcement == oldest_block_announcement && pnode->GetId() > worst_peer)) {
+ worst_peer = pnode->GetId();
+ oldest_block_announcement = state->m_last_block_announcement;
+ }
+ });
+ if (worst_peer != -1) {
+ bool disconnected = connman->ForNode(worst_peer, [&](CNode *pnode) {
+ // Only disconnect a peer that has been connected to us for
+ // some reasonable fraction of our check-frequency, to give
+ // it time for new information to have arrived.
+ // Also don't disconnect any peer we're trying to download a
+ // block from.
+ CNodeState &state = *State(pnode->GetId());
+ if (time_in_seconds - pnode->nTimeConnected > MINIMUM_CONNECT_TIME && state.nBlocksInFlight == 0) {
+ LogPrint(BCLog::NET, "disconnecting extra outbound peer=%d (last block announcement received at time %d)\n", pnode->GetId(), oldest_block_announcement);
+ pnode->fDisconnect = true;
+ return true;
+ } else {
+ LogPrint(BCLog::NET, "keeping outbound peer=%d chosen for eviction (connect time: %d, blocks_in_flight: %d)\n", pnode->GetId(), pnode->nTimeConnected, state.nBlocksInFlight);
+ return false;
+ }
+ });
+ if (disconnected) {
+ // If we disconnected an extra peer, that means we successfully
+ // connected to at least one peer after the last time we
+ // detected a stale tip. Don't try any more extra peers until
+ // we next detect a stale tip, to limit the load we put on the
+ // network from these extra connections.
+ connman->SetTryNewOutboundPeer(false);
+ }
+ }
+ }
+}
+
+void PeerLogicValidation::CheckForStaleTipAndEvictPeers(const Consensus::Params &consensusParams)
+{
+ if (connman == nullptr) return;
+
+ int64_t time_in_seconds = GetTime();
+
+ EvictExtraOutboundPeers(time_in_seconds);
+
+ if (time_in_seconds > m_stale_tip_check_time) {
+ LOCK(cs_main);
+ // Check whether our tip is stale, and if so, allow using an extra
+ // outbound peer
+ if (TipMayBeStale(consensusParams)) {
+ LogPrintf("Potential stale tip detected, will try using extra outbound peer (last tip update: %d seconds ago)\n", time_in_seconds - g_last_tip_update);
+ connman->SetTryNewOutboundPeer(true);
+ } else if (connman->GetTryNewOutboundPeer()) {
+ connman->SetTryNewOutboundPeer(false);
+ }
+ m_stale_tip_check_time = time_in_seconds + STALE_CHECK_INTERVAL;
+ }
+}
+
class CompareInvMempoolOrder
{
CTxMemPool *mp;
diff --git a/src/net_processing.h b/src/net_processing.h
index 656324bba0..0a49972eed 100644
--- a/src/net_processing.h
+++ b/src/net_processing.h
@@ -8,6 +8,7 @@
#include "net.h"
#include "validationinterface.h"
+#include "consensus/params.h"
/** Default for -maxorphantx, maximum number of orphan transactions kept in memory */
static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS = 100;
@@ -27,13 +28,19 @@ static constexpr int64_t HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER = 1000; // 1ms/head
static constexpr int32_t MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT = 4;
/** Timeout for (unprotected) outbound peers to sync to our chainwork, in seconds */
static constexpr int64_t CHAIN_SYNC_TIMEOUT = 20 * 60; // 20 minutes
+/** How frequently to check for stale tips, in seconds */
+static constexpr int64_t STALE_CHECK_INTERVAL = 10 * 60; // 10 minutes
+/** How frequently to check for extra outbound peers and disconnect, in seconds */
+static constexpr int64_t EXTRA_PEER_CHECK_INTERVAL = 45;
+/** Minimum time an outbound-peer-eviction candidate must be connected for, in order to evict, in seconds */
+static constexpr int64_t MINIMUM_CONNECT_TIME = 30;
class PeerLogicValidation : public CValidationInterface, public NetEventsInterface {
private:
- CConnman* connman;
+ CConnman* const connman;
public:
- explicit PeerLogicValidation(CConnman* connman);
+ explicit PeerLogicValidation(CConnman* connman, CScheduler &scheduler);
void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexConnected, const std::vector<CTransactionRef>& vtxConflicted) override;
void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) override;
@@ -55,6 +62,11 @@ public:
bool SendMessages(CNode* pto, std::atomic<bool>& interrupt) override;
void ConsiderEviction(CNode *pto, int64_t time_in_seconds);
+ void CheckForStaleTipAndEvictPeers(const Consensus::Params &consensusParams);
+ void EvictExtraOutboundPeers(int64_t time_in_seconds);
+
+private:
+ int64_t m_stale_tip_check_time; //! Next time to check for stale tip
};
struct CNodeStateStats {
diff --git a/src/qt/askpassphrasedialog.cpp b/src/qt/askpassphrasedialog.cpp
index e9f5c77a5b..d6cce09e8d 100644
--- a/src/qt/askpassphrasedialog.cpp
+++ b/src/qt/askpassphrasedialog.cpp
@@ -70,6 +70,7 @@ AskPassphraseDialog::AskPassphraseDialog(Mode _mode, QWidget *parent) :
break;
}
textChanged();
+ connect(ui->toggleShowPasswordButton, SIGNAL(toggled(bool)), this, SLOT(toggleShowPassword(bool)));
connect(ui->passEdit1, SIGNAL(textChanged(QString)), this, SLOT(textChanged()));
connect(ui->passEdit2, SIGNAL(textChanged(QString)), this, SLOT(textChanged()));
connect(ui->passEdit3, SIGNAL(textChanged(QString)), this, SLOT(textChanged()));
@@ -234,6 +235,15 @@ bool AskPassphraseDialog::event(QEvent *event)
return QWidget::event(event);
}
+void AskPassphraseDialog::toggleShowPassword(bool show)
+{
+ ui->toggleShowPasswordButton->setDown(show);
+ const auto mode = show ? QLineEdit::Normal : QLineEdit::Password;
+ ui->passEdit1->setEchoMode(mode);
+ ui->passEdit2->setEchoMode(mode);
+ ui->passEdit3->setEchoMode(mode);
+}
+
bool AskPassphraseDialog::eventFilter(QObject *object, QEvent *event)
{
/* Detect Caps Lock.
diff --git a/src/qt/askpassphrasedialog.h b/src/qt/askpassphrasedialog.h
index 34bf7ccb31..7c6acc4650 100644
--- a/src/qt/askpassphrasedialog.h
+++ b/src/qt/askpassphrasedialog.h
@@ -43,6 +43,7 @@ private:
private Q_SLOTS:
void textChanged();
void secureClearPassFields();
+ void toggleShowPassword(bool);
protected:
bool event(QEvent *event);
diff --git a/src/qt/forms/askpassphrasedialog.ui b/src/qt/forms/askpassphrasedialog.ui
index a2105ecd0a..69803989cd 100644
--- a/src/qt/forms/askpassphrasedialog.ui
+++ b/src/qt/forms/askpassphrasedialog.ui
@@ -93,6 +93,13 @@
</widget>
</item>
<item row="3" column="1">
+ <widget class="QCheckBox" name="toggleShowPasswordButton">
+ <property name="text">
+ <string>Show password</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
<widget class="QLabel" name="capsLabel">
<property name="font">
<font>
diff --git a/src/qt/paymentrequestplus.cpp b/src/qt/paymentrequestplus.cpp
index d3799f59ab..c7f92a0921 100644
--- a/src/qt/paymentrequestplus.cpp
+++ b/src/qt/paymentrequestplus.cpp
@@ -194,8 +194,7 @@ bool PaymentRequestPlus::getMerchant(X509_STORE* certStore, QString& merchant) c
qWarning() << "PaymentRequestPlus::getMerchant: SSL error: " << err.what();
}
- if (website)
- delete[] website;
+ delete[] website;
X509_STORE_CTX_free(store_ctx);
for (unsigned int i = 0; i < certs.size(); i++)
X509_free(certs[i]);
diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp
index 169684cf6d..506e49af0d 100644
--- a/src/qt/paymentserver.cpp
+++ b/src/qt/paymentserver.cpp
@@ -362,8 +362,7 @@ void PaymentServer::initNetManager()
{
if (!optionsModel)
return;
- if (netManager != nullptr)
- delete netManager;
+ delete netManager;
// netManager is used to fetch paymentrequests given in bitcoin: URIs
netManager = new QNetworkAccessManager(this);
diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp
index 74f5c774a0..41ee89de09 100644
--- a/src/qt/transactiondesc.cpp
+++ b/src/qt/transactiondesc.cpp
@@ -24,7 +24,7 @@
QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx)
{
AssertLockHeld(cs_main);
- if (!CheckFinalTx(wtx))
+ if (!CheckFinalTx(*wtx.tx))
{
if (wtx.tx->nLockTime < LOCKTIME_THRESHOLD)
return tr("Open for %n more block(s)", "", wtx.tx->nLockTime - chainActive.Height());
diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp
index d40ffd22cd..8ac00ac4cf 100644
--- a/src/qt/transactionrecord.cpp
+++ b/src/qt/transactionrecord.cpp
@@ -182,7 +182,7 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx)
status.depth = wtx.GetDepthInMainChain();
status.cur_num_blocks = chainActive.Height();
- if (!CheckFinalTx(wtx))
+ if (!CheckFinalTx(*wtx.tx))
{
if (wtx.tx->nLockTime < LOCKTIME_THRESHOLD)
{
diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp
index 59cef555b1..e83d824a6a 100644
--- a/src/qt/transactiontablemodel.cpp
+++ b/src/qt/transactiontablemodel.cpp
@@ -230,7 +230,7 @@ public:
std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(rec->hash);
if(mi != wallet->mapWallet.end())
{
- std::string strHex = EncodeHexTx(static_cast<CTransaction>(mi->second));
+ std::string strHex = EncodeHexTx(*mi->second.tx);
return QString::fromStdString(strHex);
}
return QString();
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index 53b1c2967c..e1d0660627 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -661,7 +661,7 @@ bool WalletModel::transactionCanBeBumped(uint256 hash) const
{
LOCK2(cs_main, wallet->cs_wallet);
const CWalletTx *wtx = wallet->GetWalletTx(hash);
- return wtx && SignalsOptInRBF(*wtx) && !wtx->mapValue.count("replaced_by_txid");
+ return wtx && SignalsOptInRBF(*(wtx->tx)) && !wtx->mapValue.count("replaced_by_txid");
}
bool WalletModel::bumpFee(uint256 hash)
diff --git a/src/qt/walletmodeltransaction.cpp b/src/qt/walletmodeltransaction.cpp
index eae2c27f8a..a2290535c7 100644
--- a/src/qt/walletmodeltransaction.cpp
+++ b/src/qt/walletmodeltransaction.cpp
@@ -34,7 +34,7 @@ CWalletTx *WalletModelTransaction::getTransaction() const
unsigned int WalletModelTransaction::getTransactionSize()
{
- return (!walletTransaction ? 0 : ::GetVirtualTransactionSize(*walletTransaction));
+ return (!walletTransaction ? 0 : ::GetVirtualTransactionSize(*walletTransaction->tx));
}
CAmount WalletModelTransaction::getTransactionFee() const
diff --git a/src/rest.cpp b/src/rest.cpp
index 4d2cdfdf08..b1fc96bdf5 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -178,8 +178,11 @@ static bool rest_headers(HTTPRequest* req,
}
case RF_JSON: {
UniValue jsonHeaders(UniValue::VARR);
- for (const CBlockIndex *pindex : headers) {
- jsonHeaders.push_back(blockheaderToJSON(pindex));
+ {
+ LOCK(cs_main);
+ for (const CBlockIndex *pindex : headers) {
+ jsonHeaders.push_back(blockheaderToJSON(pindex));
+ }
}
std::string strJSON = jsonHeaders.write() + "\n";
req->WriteHeader("Content-Type", "application/json");
@@ -239,7 +242,11 @@ static bool rest_block(HTTPRequest* req,
}
case RF_JSON: {
- UniValue objBlock = blockToJSON(block, pblockindex, showTxDetails);
+ UniValue objBlock;
+ {
+ LOCK(cs_main);
+ objBlock = blockToJSON(block, pblockindex, showTxDetails);
+ }
std::string strJSON = objBlock.write() + "\n";
req->WriteHeader("Content-Type", "application/json");
req->WriteReply(HTTP_OK, strJSON);
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 68af376f35..8d01d8ba9c 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -78,6 +78,7 @@ double GetDifficulty(const CBlockIndex* blockindex)
UniValue blockheaderToJSON(const CBlockIndex* blockindex)
{
+ AssertLockHeld(cs_main);
UniValue result(UniValue::VOBJ);
result.push_back(Pair("hash", blockindex->GetBlockHash().GetHex()));
int confirmations = -1;
@@ -106,6 +107,7 @@ UniValue blockheaderToJSON(const CBlockIndex* blockindex)
UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails)
{
+ AssertLockHeld(cs_main);
UniValue result(UniValue::VOBJ);
result.push_back(Pair("hash", blockindex->GetBlockHash().GetHex()));
int confirmations = -1;
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index f79439f038..0ba0e968a7 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -455,7 +455,7 @@ UniValue getblocktemplate(const JSONRPCRequest& request)
{
// Wait to respond until either the best block changes, OR a minute has passed and there are more transactions
uint256 hashWatchedChain;
- boost::system_time checktxtime;
+ std::chrono::steady_clock::time_point checktxtime;
unsigned int nTransactionsUpdatedLastLP;
if (lpval.isStr())
@@ -476,17 +476,17 @@ UniValue getblocktemplate(const JSONRPCRequest& request)
// Release the wallet and main lock while waiting
LEAVE_CRITICAL_SECTION(cs_main);
{
- checktxtime = boost::get_system_time() + boost::posix_time::minutes(1);
+ checktxtime = std::chrono::steady_clock::now() + std::chrono::minutes(1);
- boost::unique_lock<boost::mutex> lock(csBestBlock);
+ WaitableLock lock(csBestBlock);
while (chainActive.Tip()->GetBlockHash() == hashWatchedChain && IsRPCRunning())
{
- if (!cvBlockChange.timed_wait(lock, checktxtime))
+ if (cvBlockChange.wait_until(lock, checktxtime) == std::cv_status::timeout)
{
// Timeout: Check transactions for update
if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLastLP)
break;
- checktxtime += boost::posix_time::seconds(10);
+ checktxtime += std::chrono::seconds(10);
}
}
}
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index 521b49e2a7..d042fa31d5 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -608,6 +608,7 @@ static const CRPCCommand commands[] =
{ // category name actor (function) argNames
// --------------------- ------------------------ ----------------------- ----------
{ "control", "getmemoryinfo", &getmemoryinfo, {"mode"} },
+ { "control", "logging", &logging, {"include", "exclude"}},
{ "util", "validateaddress", &validateaddress, {"address"} }, /* uses wallet if enabled */
{ "util", "createmultisig", &createmultisig, {"nrequired","keys"} },
{ "util", "verifymessage", &verifymessage, {"address","signature","message"} },
@@ -617,7 +618,6 @@ static const CRPCCommand commands[] =
{ "hidden", "setmocktime", &setmocktime, {"timestamp"}},
{ "hidden", "echo", &echo, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}},
{ "hidden", "echojson", &echo, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}},
- { "hidden", "logging", &logging, {"include", "exclude"}},
};
void RegisterMiscRPCCommands(CRPCTable &t)
diff --git a/src/streams.h b/src/streams.h
index 9a3badea57..5dbeaac9a5 100644
--- a/src/streams.h
+++ b/src/streams.h
@@ -345,18 +345,16 @@ public:
// Read from the beginning of the buffer
unsigned int nReadPosNext = nReadPos + nSize;
- if (nReadPosNext >= vch.size())
+ if (nReadPosNext > vch.size()) {
+ throw std::ios_base::failure("CDataStream::read(): end of data");
+ }
+ memcpy(pch, &vch[nReadPos], nSize);
+ if (nReadPosNext == vch.size())
{
- if (nReadPosNext > vch.size())
- {
- throw std::ios_base::failure("CDataStream::read(): end of data");
- }
- memcpy(pch, &vch[nReadPos], nSize);
nReadPos = 0;
vch.clear();
return;
}
- memcpy(pch, &vch[nReadPos], nSize);
nReadPos = nReadPosNext;
}
diff --git a/src/sync.h b/src/sync.h
index 0871c5fb4d..20556af890 100644
--- a/src/sync.h
+++ b/src/sync.h
@@ -10,7 +10,9 @@
#include <boost/thread/condition_variable.hpp>
#include <boost/thread/mutex.hpp>
-#include <boost/thread/recursive_mutex.hpp>
+#include <condition_variable>
+#include <thread>
+#include <mutex>
////////////////////////////////////////////////
@@ -21,17 +23,17 @@
/*
CCriticalSection mutex;
- boost::recursive_mutex mutex;
+ std::recursive_mutex mutex;
LOCK(mutex);
- boost::unique_lock<boost::recursive_mutex> criticalblock(mutex);
+ std::unique_lock<std::recursive_mutex> criticalblock(mutex);
LOCK2(mutex1, mutex2);
- boost::unique_lock<boost::recursive_mutex> criticalblock1(mutex1);
- boost::unique_lock<boost::recursive_mutex> criticalblock2(mutex2);
+ std::unique_lock<std::recursive_mutex> criticalblock1(mutex1);
+ std::unique_lock<std::recursive_mutex> criticalblock2(mutex2);
TRY_LOCK(mutex, name);
- boost::unique_lock<boost::recursive_mutex> name(mutex, boost::try_to_lock_t);
+ std::unique_lock<std::recursive_mutex> name(mutex, std::try_to_lock_t);
ENTER_CRITICAL_SECTION(mutex); // no RAII
mutex.lock();
@@ -85,10 +87,10 @@ void static inline DeleteLock(void* cs) {}
#define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs)
/**
- * Wrapped boost mutex: supports recursive locking, but no waiting
+ * Wrapped mutex: supports recursive locking, but no waiting
* TODO: We should move away from using the recursive lock by default.
*/
-class CCriticalSection : public AnnotatedMixin<boost::recursive_mutex>
+class CCriticalSection : public AnnotatedMixin<std::recursive_mutex>
{
public:
~CCriticalSection() {
@@ -96,22 +98,24 @@ public:
}
};
-/** Wrapped boost mutex: supports waiting but not recursive locking */
-typedef AnnotatedMixin<boost::mutex> CWaitableCriticalSection;
+/** Wrapped mutex: supports waiting but not recursive locking */
+typedef AnnotatedMixin<std::mutex> CWaitableCriticalSection;
-/** Just a typedef for boost::condition_variable, can be wrapped later if desired */
-typedef boost::condition_variable CConditionVariable;
+/** Just a typedef for std::condition_variable, can be wrapped later if desired */
+typedef std::condition_variable CConditionVariable;
+
+/** Just a typedef for std::unique_lock, can be wrapped later if desired */
+typedef std::unique_lock<std::mutex> WaitableLock;
#ifdef DEBUG_LOCKCONTENTION
void PrintLockContention(const char* pszName, const char* pszFile, int nLine);
#endif
-/** Wrapper around boost::unique_lock<Mutex> */
-template <typename Mutex>
-class SCOPED_LOCKABLE CMutexLock
+/** Wrapper around std::unique_lock<CCriticalSection> */
+class SCOPED_LOCKABLE CCriticalBlock
{
private:
- boost::unique_lock<Mutex> lock;
+ std::unique_lock<CCriticalSection> lock;
void Enter(const char* pszName, const char* pszFile, int nLine)
{
@@ -136,7 +140,7 @@ private:
}
public:
- CMutexLock(Mutex& mutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(mutexIn) : lock(mutexIn, boost::defer_lock)
+ CCriticalBlock(CCriticalSection& mutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(mutexIn) : lock(mutexIn, std::defer_lock)
{
if (fTry)
TryEnter(pszName, pszFile, nLine);
@@ -144,18 +148,18 @@ public:
Enter(pszName, pszFile, nLine);
}
- CMutexLock(Mutex* pmutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(pmutexIn)
+ CCriticalBlock(CCriticalSection* pmutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(pmutexIn)
{
if (!pmutexIn) return;
- lock = boost::unique_lock<Mutex>(*pmutexIn, boost::defer_lock);
+ lock = std::unique_lock<CCriticalSection>(*pmutexIn, std::defer_lock);
if (fTry)
TryEnter(pszName, pszFile, nLine);
else
Enter(pszName, pszFile, nLine);
}
- ~CMutexLock() UNLOCK_FUNCTION()
+ ~CCriticalBlock() UNLOCK_FUNCTION()
{
if (lock.owns_lock())
LeaveCritical();
@@ -167,8 +171,6 @@ public:
}
};
-typedef CMutexLock<CCriticalSection> CCriticalBlock;
-
#define PASTE(x, y) x ## y
#define PASTE2(x, y) PASTE(x, y)
diff --git a/src/test/DoS_tests.cpp b/src/test/DoS_tests.cpp
index 7bcf304833..d1f9e63ecf 100644
--- a/src/test/DoS_tests.cpp
+++ b/src/test/DoS_tests.cpp
@@ -40,6 +40,8 @@ CService ip(uint32_t i)
static NodeId id = 0;
+void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds);
+
BOOST_FIXTURE_TEST_SUITE(DoS_tests, TestingSetup)
// Test eviction of an outbound peer whose chain never advances
@@ -87,6 +89,89 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
peerLogic->FinalizeNode(dummyNode1.GetId(), dummy);
}
+void AddRandomOutboundPeer(std::vector<CNode *> &vNodes, PeerLogicValidation &peerLogic)
+{
+ CAddress addr(ip(GetRandInt(0xffffffff)), NODE_NONE);
+ vNodes.emplace_back(new CNode(id++, ServiceFlags(NODE_NETWORK|NODE_WITNESS), 0, INVALID_SOCKET, addr, 0, 0, CAddress(), "", /*fInboundIn=*/ false));
+ CNode &node = *vNodes.back();
+ node.SetSendVersion(PROTOCOL_VERSION);
+
+ peerLogic.InitializeNode(&node);
+ node.nVersion = 1;
+ node.fSuccessfullyConnected = true;
+
+ CConnmanTest::AddNode(node);
+}
+
+BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
+{
+ const Consensus::Params& consensusParams = Params().GetConsensus();
+ constexpr int nMaxOutbound = 8;
+ CConnman::Options options;
+ options.nMaxConnections = 125;
+ options.nMaxOutbound = nMaxOutbound;
+ options.nMaxFeeler = 1;
+
+ connman->Init(options);
+ std::vector<CNode *> vNodes;
+
+ // Mock some outbound peers
+ for (int i=0; i<nMaxOutbound; ++i) {
+ AddRandomOutboundPeer(vNodes, *peerLogic);
+ }
+
+ peerLogic->CheckForStaleTipAndEvictPeers(consensusParams);
+
+ // No nodes should be marked for disconnection while we have no extra peers
+ for (const CNode *node : vNodes) {
+ BOOST_CHECK(node->fDisconnect == false);
+ }
+
+ SetMockTime(GetTime() + 3*consensusParams.nPowTargetSpacing + 1);
+
+ // Now tip should definitely be stale, and we should look for an extra
+ // outbound peer
+ peerLogic->CheckForStaleTipAndEvictPeers(consensusParams);
+ BOOST_CHECK(connman->GetTryNewOutboundPeer());
+
+ // Still no peers should be marked for disconnection
+ for (const CNode *node : vNodes) {
+ BOOST_CHECK(node->fDisconnect == false);
+ }
+
+ // If we add one more peer, something should get marked for eviction
+ // on the next check (since we're mocking the time to be in the future, the
+ // required time connected check should be satisfied).
+ AddRandomOutboundPeer(vNodes, *peerLogic);
+
+ peerLogic->CheckForStaleTipAndEvictPeers(consensusParams);
+ for (int i=0; i<nMaxOutbound; ++i) {
+ BOOST_CHECK(vNodes[i]->fDisconnect == false);
+ }
+ // Last added node should get marked for eviction
+ BOOST_CHECK(vNodes.back()->fDisconnect == true);
+
+ vNodes.back()->fDisconnect = false;
+
+ // Update the last announced block time for the last
+ // peer, and check that the next newest node gets evicted.
+ UpdateLastBlockAnnounceTime(vNodes.back()->GetId(), GetTime());
+
+ peerLogic->CheckForStaleTipAndEvictPeers(consensusParams);
+ for (int i=0; i<nMaxOutbound-1; ++i) {
+ BOOST_CHECK(vNodes[i]->fDisconnect == false);
+ }
+ BOOST_CHECK(vNodes[nMaxOutbound-1]->fDisconnect == true);
+ BOOST_CHECK(vNodes.back()->fDisconnect == false);
+
+ bool dummy;
+ for (const CNode *node : vNodes) {
+ peerLogic->FinalizeNode(node->GetId(), dummy);
+ }
+
+ CConnmanTest::ClearNodes();
+}
+
BOOST_AUTO_TEST_CASE(DoS_banning)
{
std::atomic<bool> interruptDummy(false);
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index 41e0626eb9..2851808cf4 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -335,23 +335,6 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
BOOST_CHECK_THROW(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error);
mempool.clear();
- // invalid (pre-p2sh) txn in mempool, template creation fails
- tx.vin[0].prevout.hash = txFirst[0]->GetHash();
- tx.vin[0].prevout.n = 0;
- tx.vin[0].scriptSig = CScript() << OP_1;
- tx.vout[0].nValue = BLOCKSUBSIDY-LOWFEE;
- script = CScript() << OP_0;
- tx.vout[0].scriptPubKey = GetScriptForDestination(CScriptID(script));
- hash = tx.GetHash();
- mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
- tx.vin[0].prevout.hash = hash;
- tx.vin[0].scriptSig = CScript() << std::vector<unsigned char>(script.begin(), script.end());
- tx.vout[0].nValue -= LOWFEE;
- hash = tx.GetHash();
- mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
- BOOST_CHECK_THROW(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error);
- mempool.clear();
-
// double spend txn pair in mempool, template creation fails
tx.vin[0].prevout.hash = txFirst[0]->GetHash();
tx.vin[0].scriptSig = CScript() << OP_1;
@@ -391,6 +374,24 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
chainActive.SetTip(next);
}
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
+
+ // invalid p2sh txn in mempool, template creation fails
+ tx.vin[0].prevout.hash = txFirst[0]->GetHash();
+ tx.vin[0].prevout.n = 0;
+ tx.vin[0].scriptSig = CScript() << OP_1;
+ tx.vout[0].nValue = BLOCKSUBSIDY-LOWFEE;
+ script = CScript() << OP_0;
+ tx.vout[0].scriptPubKey = GetScriptForDestination(CScriptID(script));
+ hash = tx.GetHash();
+ mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
+ tx.vin[0].prevout.hash = hash;
+ tx.vin[0].scriptSig = CScript() << std::vector<unsigned char>(script.begin(), script.end());
+ tx.vout[0].nValue -= LOWFEE;
+ hash = tx.GetHash();
+ mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
+ BOOST_CHECK_THROW(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error);
+ mempool.clear();
+
// Delete the dummy blocks again.
while (chainActive.Tip()->nHeight > nHeight) {
CBlockIndex* del = chainActive.Tip();
diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp
index 79bc48a118..f9ce52c594 100644
--- a/src/test/test_bitcoin.cpp
+++ b/src/test/test_bitcoin.cpp
@@ -25,6 +25,18 @@
#include <memory>
+void CConnmanTest::AddNode(CNode& node)
+{
+ LOCK(g_connman->cs_vNodes);
+ g_connman->vNodes.push_back(&node);
+}
+
+void CConnmanTest::ClearNodes()
+{
+ LOCK(g_connman->cs_vNodes);
+ g_connman->vNodes.clear();
+}
+
uint256 insecure_rand_seed = GetRandHash();
FastRandomContext insecure_rand_ctx(insecure_rand_seed);
@@ -86,7 +98,7 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
threadGroup.create_thread(&ThreadScriptCheck);
g_connman = std::unique_ptr<CConnman>(new CConnman(0x1337, 0x1337)); // Deterministic randomness for tests.
connman = g_connman.get();
- peerLogic.reset(new PeerLogicValidation(connman));
+ peerLogic.reset(new PeerLogicValidation(connman, scheduler));
}
TestingSetup::~TestingSetup()
@@ -106,6 +118,9 @@ TestingSetup::~TestingSetup()
TestChain100Setup::TestChain100Setup() : TestingSetup(CBaseChainParams::REGTEST)
{
+ // CreateAndProcessBlock() does not support building SegWit blocks, so don't activate in these tests.
+ // TODO: fix the code to support SegWit blocks.
+ UpdateVersionBitsParameters(Consensus::DEPLOYMENT_SEGWIT, 0, Consensus::BIP9Deployment::NO_TIMEOUT);
// Generate a 100-block chain:
coinbaseKey.MakeNewKey(true);
CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h
index 2390aca342..62ded2aaf5 100644
--- a/src/test/test_bitcoin.h
+++ b/src/test/test_bitcoin.h
@@ -49,6 +49,12 @@ struct BasicTestingSetup {
* Included are data directory, coins database, script check threads setup.
*/
class CConnman;
+class CNode;
+struct CConnmanTest {
+ static void AddNode(CNode& node);
+ static void ClearNodes();
+};
+
class PeerLogicValidation;
struct TestingSetup: public BasicTestingSetup {
CCoinsViewDB *pcoinsdbview;
diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp
index 882afb2e20..db537d3932 100644
--- a/src/test/versionbits_tests.cpp
+++ b/src/test/versionbits_tests.cpp
@@ -32,6 +32,12 @@ public:
int GetStateSinceHeightFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateSinceHeightFor(pindexPrev, paramsDummy, cache); }
};
+class TestAlwaysActiveConditionChecker : public TestConditionChecker
+{
+public:
+ int64_t BeginTime(const Consensus::Params& params) const override { return Consensus::BIP9Deployment::ALWAYS_ACTIVE; }
+};
+
#define CHECKERS 6
class VersionBitsTester
@@ -43,6 +49,8 @@ class VersionBitsTester
// The first one performs all checks, the second only 50%, the third only 25%, etc...
// This is to test whether lack of cached information leads to the same results.
TestConditionChecker checker[CHECKERS];
+ // Another 6 that assume always active activation
+ TestAlwaysActiveConditionChecker checker_always[CHECKERS];
// Test counter (to identify failures)
int num;
@@ -56,6 +64,7 @@ public:
}
for (unsigned int i = 0; i < CHECKERS; i++) {
checker[i] = TestConditionChecker();
+ checker_always[i] = TestAlwaysActiveConditionChecker();
}
vpblock.clear();
return *this;
@@ -82,6 +91,7 @@ public:
for (int i = 0; i < CHECKERS; i++) {
if (InsecureRandBits(i) == 0) {
BOOST_CHECK_MESSAGE(checker[i].GetStateSinceHeightFor(vpblock.empty() ? nullptr : vpblock.back()) == height, strprintf("Test %i for StateSinceHeight", num));
+ BOOST_CHECK_MESSAGE(checker_always[i].GetStateSinceHeightFor(vpblock.empty() ? nullptr : vpblock.back()) == 0, strprintf("Test %i for StateSinceHeight (always active)", num));
}
}
num++;
@@ -92,6 +102,7 @@ public:
for (int i = 0; i < CHECKERS; i++) {
if (InsecureRandBits(i) == 0) {
BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_DEFINED, strprintf("Test %i for DEFINED", num));
+ BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_ACTIVE, strprintf("Test %i for ACTIVE (always active)", num));
}
}
num++;
@@ -102,6 +113,7 @@ public:
for (int i = 0; i < CHECKERS; i++) {
if (InsecureRandBits(i) == 0) {
BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_STARTED, strprintf("Test %i for STARTED", num));
+ BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_ACTIVE, strprintf("Test %i for ACTIVE (always active)", num));
}
}
num++;
@@ -112,6 +124,7 @@ public:
for (int i = 0; i < CHECKERS; i++) {
if (InsecureRandBits(i) == 0) {
BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_LOCKED_IN, strprintf("Test %i for LOCKED_IN", num));
+ BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_ACTIVE, strprintf("Test %i for ACTIVE (always active)", num));
}
}
num++;
@@ -122,6 +135,7 @@ public:
for (int i = 0; i < CHECKERS; i++) {
if (InsecureRandBits(i) == 0) {
BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_ACTIVE, strprintf("Test %i for ACTIVE", num));
+ BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_ACTIVE, strprintf("Test %i for ACTIVE (always active)", num));
}
}
num++;
@@ -132,6 +146,7 @@ public:
for (int i = 0; i < CHECKERS; i++) {
if (InsecureRandBits(i) == 0) {
BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_FAILED, strprintf("Test %i for FAILED", num));
+ BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_ACTIVE, strprintf("Test %i for ACTIVE (always active)", num));
}
}
num++;
diff --git a/src/tinyformat.h b/src/tinyformat.h
index 2e453e56bb..d34cfaa94f 100644
--- a/src/tinyformat.h
+++ b/src/tinyformat.h
@@ -495,7 +495,11 @@ namespace detail {
class FormatArg
{
public:
- FormatArg() {}
+ FormatArg()
+ : m_value(nullptr),
+ m_formatImpl(nullptr),
+ m_toIntImpl(nullptr)
+ { }
template<typename T>
explicit FormatArg(const T& value)
@@ -507,11 +511,15 @@ class FormatArg
void format(std::ostream& out, const char* fmtBegin,
const char* fmtEnd, int ntrunc) const
{
+ assert(m_value);
+ assert(m_formatImpl);
m_formatImpl(out, fmtBegin, fmtEnd, ntrunc, m_value);
}
int toInt() const
{
+ assert(m_value);
+ assert(m_toIntImpl);
return m_toIntImpl(m_value);
}
@@ -712,23 +720,27 @@ inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositi
break;
case 'X':
out.setf(std::ios::uppercase);
+ // Falls through
case 'x': case 'p':
out.setf(std::ios::hex, std::ios::basefield);
intConversion = true;
break;
case 'E':
out.setf(std::ios::uppercase);
+ // Falls through
case 'e':
out.setf(std::ios::scientific, std::ios::floatfield);
out.setf(std::ios::dec, std::ios::basefield);
break;
case 'F':
out.setf(std::ios::uppercase);
+ // Falls through
case 'f':
out.setf(std::ios::fixed, std::ios::floatfield);
break;
case 'G':
out.setf(std::ios::uppercase);
+ // Falls through
case 'g':
out.setf(std::ios::dec, std::ios::basefield);
// As in boost::format, let stream decide float format.
diff --git a/src/validation.cpp b/src/validation.cpp
index 78eb6d7302..efabe6293f 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -156,6 +156,26 @@ namespace {
/** chainwork for the last block that preciousblock has been applied to. */
arith_uint256 nLastPreciousChainwork = 0;
+ /** In order to efficiently track invalidity of headers, we keep the set of
+ * blocks which we tried to connect and found to be invalid here (ie which
+ * were set to BLOCK_FAILED_VALID since the last restart). We can then
+ * walk this set and check if a new header is a descendant of something in
+ * this set, preventing us from having to walk mapBlockIndex when we try
+ * to connect a bad block and fail.
+ *
+ * While this is more complicated than marking everything which descends
+ * from an invalid block as invalid at the time we discover it to be
+ * invalid, doing so would require walking all of mapBlockIndex to find all
+ * descendants. Since this case should be very rare, keeping track of all
+ * BLOCK_FAILED_VALID blocks in a set should be just fine and work just as
+ * well.
+ *
+ * Because we already walk mapBlockIndex in height-order at startup, we go
+ * ahead and mark descendants of invalid blocks as FAILED_CHILD at that time,
+ * instead of putting things in this set.
+ */
+ std::set<CBlockIndex*> g_failed_blocks;
+
/** Dirty block index entries. */
std::set<CBlockIndex*> setDirtyBlockIndex;
@@ -1180,6 +1200,7 @@ void static InvalidChainFound(CBlockIndex* pindexNew)
void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) {
if (!state.CorruptionPossible()) {
pindex->nStatus |= BLOCK_FAILED_VALID;
+ g_failed_blocks.insert(pindex);
setDirtyBlockIndex.insert(pindex);
setBlockIndexCandidates.erase(pindex);
InvalidChainFound(pindex);
@@ -1590,11 +1611,12 @@ static ThresholdConditionCache warningcache[VERSIONBITS_NUM_BITS];
static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consensus::Params& consensusparams) {
AssertLockHeld(cs_main);
- // BIP16 didn't become active until Apr 1 2012
- int64_t nBIP16SwitchTime = 1333238400;
- bool fStrictPayToScriptHash = (pindex->GetBlockTime() >= nBIP16SwitchTime);
+ unsigned int flags = SCRIPT_VERIFY_NONE;
- unsigned int flags = fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE;
+ // Start enforcing P2SH (BIP16)
+ if (pindex->nHeight >= consensusparams.BIP16Height) {
+ flags |= SCRIPT_VERIFY_P2SH;
+ }
// Start enforcing the DERSIG (BIP66) rule
if (pindex->nHeight >= consensusparams.BIP66Height) {
@@ -2533,17 +2555,18 @@ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, C
{
AssertLockHeld(cs_main);
- // Mark the block itself as invalid.
- pindex->nStatus |= BLOCK_FAILED_VALID;
- setDirtyBlockIndex.insert(pindex);
- setBlockIndexCandidates.erase(pindex);
+ // We first disconnect backwards and then mark the blocks as invalid.
+ // This prevents a case where pruned nodes may fail to invalidateblock
+ // and be left unable to start as they have no tip candidates (as there
+ // are no blocks that meet the "have data and are not invalid per
+ // nStatus" criteria for inclusion in setBlockIndexCandidates).
+
+ bool pindex_was_in_chain = false;
+ CBlockIndex *invalid_walk_tip = chainActive.Tip();
DisconnectedBlockTransactions disconnectpool;
while (chainActive.Contains(pindex)) {
- CBlockIndex *pindexWalk = chainActive.Tip();
- pindexWalk->nStatus |= BLOCK_FAILED_CHILD;
- setDirtyBlockIndex.insert(pindexWalk);
- setBlockIndexCandidates.erase(pindexWalk);
+ pindex_was_in_chain = true;
// ActivateBestChain considers blocks already in chainActive
// unconditionally valid already, so force disconnect away from it.
if (!DisconnectTip(state, chainparams, &disconnectpool)) {
@@ -2554,6 +2577,21 @@ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, C
}
}
+ // Now mark the blocks we just disconnected as descendants invalid
+ // (note this may not be all descendants).
+ while (pindex_was_in_chain && invalid_walk_tip != pindex) {
+ invalid_walk_tip->nStatus |= BLOCK_FAILED_CHILD;
+ setDirtyBlockIndex.insert(invalid_walk_tip);
+ setBlockIndexCandidates.erase(invalid_walk_tip);
+ invalid_walk_tip = invalid_walk_tip->pprev;
+ }
+
+ // Mark the block itself as invalid.
+ pindex->nStatus |= BLOCK_FAILED_VALID;
+ setDirtyBlockIndex.insert(pindex);
+ setBlockIndexCandidates.erase(pindex);
+ g_failed_blocks.insert(pindex);
+
// DisconnectTip will add transactions to disconnectpool; try to add these
// back to the mempool.
UpdateMempoolForReorg(disconnectpool, true);
@@ -2591,6 +2629,7 @@ bool ResetBlockFailureFlags(CBlockIndex *pindex) {
// Reset invalid block marker if it was pointing to one of those.
pindexBestInvalid = nullptr;
}
+ g_failed_blocks.erase(it->second);
}
it++;
}
@@ -3066,6 +3105,21 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state
return state.DoS(100, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk");
if (!ContextualCheckBlockHeader(block, state, chainparams, pindexPrev, GetAdjustedTime()))
return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state));
+
+ if (!pindexPrev->IsValid(BLOCK_VALID_SCRIPTS)) {
+ for (const CBlockIndex* failedit : g_failed_blocks) {
+ if (pindexPrev->GetAncestor(failedit->nHeight) == failedit) {
+ assert(failedit->nStatus & BLOCK_FAILED_VALID);
+ CBlockIndex* invalid_walk = pindexPrev;
+ while (invalid_walk != failedit) {
+ invalid_walk->nStatus |= BLOCK_FAILED_CHILD;
+ setDirtyBlockIndex.insert(invalid_walk);
+ invalid_walk = invalid_walk->pprev;
+ }
+ return state.DoS(100, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk");
+ }
+ }
+ }
}
if (pindex == nullptr)
pindex = AddToBlockIndex(block);
@@ -3117,7 +3171,7 @@ static bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidation
// process an unrequested block if it's new and has enough work to
// advance our tip, and isn't too many blocks ahead.
bool fAlreadyHave = pindex->nStatus & BLOCK_HAVE_DATA;
- bool fHasMoreWork = (chainActive.Tip() ? pindex->nChainWork > chainActive.Tip()->nChainWork : true);
+ bool fHasMoreOrSameWork = (chainActive.Tip() ? pindex->nChainWork >= chainActive.Tip()->nChainWork : true);
// Blocks that are too out-of-order needlessly limit the effectiveness of
// pruning, because pruning will not delete block files that contain any
// blocks which are too close in height to the tip. Apply this test
@@ -3134,9 +3188,9 @@ static bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidation
// and unrequested blocks.
if (fAlreadyHave) return true;
if (!fRequested) { // If we didn't ask for it:
- if (pindex->nTx != 0) return true; // This is a previously-processed block that was pruned
- if (!fHasMoreWork) return true; // Don't process less-work chains
- if (fTooFarAhead) return true; // Block height is too high
+ if (pindex->nTx != 0) return true; // This is a previously-processed block that was pruned
+ if (!fHasMoreOrSameWork) return true; // Don't process less-work chains
+ if (fTooFarAhead) return true; // Block height is too high
// Protect against DoS attacks from low-work chains.
// If our tip is behind, a peer could try to send us
@@ -3494,6 +3548,10 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams)
pindex->nChainTx = pindex->nTx;
}
}
+ if (!(pindex->nStatus & BLOCK_FAILED_MASK) && pindex->pprev && (pindex->pprev->nStatus & BLOCK_FAILED_MASK)) {
+ pindex->nStatus |= BLOCK_FAILED_CHILD;
+ setDirtyBlockIndex.insert(pindex);
+ }
if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && (pindex->nChainTx || pindex->pprev == nullptr))
setBlockIndexCandidates.insert(pindex);
if (pindex->nStatus & BLOCK_FAILED_MASK && (!pindexBestInvalid || pindex->nChainWork > pindexBestInvalid->nChainWork))
@@ -3884,6 +3942,7 @@ void UnloadBlockIndex()
nLastBlockFile = 0;
nBlockSequenceId = 1;
setDirtyBlockIndex.clear();
+ g_failed_blocks.clear();
setDirtyFileInfo.clear();
versionbitscache.Clear();
for (int b = 0; b < VERSIONBITS_NUM_BITS; b++) {
diff --git a/src/versionbits.cpp b/src/versionbits.cpp
index 64ae939672..fc1acb3258 100644
--- a/src/versionbits.cpp
+++ b/src/versionbits.cpp
@@ -27,6 +27,11 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex*
int64_t nTimeStart = BeginTime(params);
int64_t nTimeTimeout = EndTime(params);
+ // Check if this deployment is always active.
+ if (nTimeStart == Consensus::BIP9Deployment::ALWAYS_ACTIVE) {
+ return THRESHOLD_ACTIVE;
+ }
+
// A block's state is always the same as that of the first of its period, so it is computed based on a pindexPrev whose height equals a multiple of nPeriod - 1.
if (pindexPrev != nullptr) {
pindexPrev = pindexPrev->GetAncestor(pindexPrev->nHeight - ((pindexPrev->nHeight + 1) % nPeriod));
@@ -136,6 +141,11 @@ BIP9Stats AbstractThresholdConditionChecker::GetStateStatisticsFor(const CBlockI
int AbstractThresholdConditionChecker::GetStateSinceHeightFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const
{
+ int64_t start_time = BeginTime(params);
+ if (start_time == Consensus::BIP9Deployment::ALWAYS_ACTIVE) {
+ return 0;
+ }
+
const ThresholdState initialState = GetStateFor(pindexPrev, params, cache);
// BIP 9 about state DEFINED: "The genesis block is by definition in this state for each deployment."
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index 459d289a42..5d48b01c2e 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -705,6 +705,11 @@ bool CWalletDBWrapper::Backup(const std::string& strDest)
pathDest /= strFile;
try {
+ if (fs::equivalent(pathSrc, pathDest)) {
+ LogPrintf("cannot backup to wallet source file %s\n", pathDest.string());
+ return false;
+ }
+
fs::copy_file(pathSrc, pathDest, fs::copy_option::overwrite_if_exists);
LogPrintf("copied %s to %s\n", strFile, pathDest.string());
return true;
diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp
index 6abd060714..8b7c50b4e9 100644
--- a/src/wallet/feebumper.cpp
+++ b/src/wallet/feebumper.cpp
@@ -89,7 +89,7 @@ CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, const CCoin
return;
}
- if (!SignalsOptInRBF(wtx)) {
+ if (!SignalsOptInRBF(*wtx.tx)) {
vErrors.push_back("Transaction is not BIP 125 replaceable");
currentResult = BumpFeeResult::WALLET_ERROR;
return;
@@ -103,7 +103,7 @@ CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, const CCoin
// check that original tx consists entirely of our inputs
// if not, we can't bump the fee, because the wallet has no way of knowing the value of the other inputs (thus the fee)
- if (!pWallet->IsAllFromMe(wtx, ISMINE_SPENDABLE)) {
+ if (!pWallet->IsAllFromMe(*wtx.tx, ISMINE_SPENDABLE)) {
vErrors.push_back("Transaction contains inputs that don't belong to this wallet");
currentResult = BumpFeeResult::WALLET_ERROR;
return;
@@ -196,7 +196,13 @@ CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, const CCoin
// moment earlier. In this case, we report an error to the user, who may use totalFee to make an adjustment.
CFeeRate minMempoolFeeRate = mempool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
if (nNewFeeRate.GetFeePerK() < minMempoolFeeRate.GetFeePerK()) {
- vErrors.push_back(strprintf("New fee rate (%s) is less than the minimum fee rate (%s) to get into the mempool. totalFee value should to be at least %s or settxfee value should be at least %s to add transaction.", FormatMoney(nNewFeeRate.GetFeePerK()), FormatMoney(minMempoolFeeRate.GetFeePerK()), FormatMoney(minMempoolFeeRate.GetFee(maxNewTxSize)), FormatMoney(minMempoolFeeRate.GetFeePerK())));
+ vErrors.push_back(strprintf(
+ "New fee rate (%s) is lower than the minimum fee rate (%s) to get into the mempool -- "
+ "the totalFee value should be at least %s or the settxfee value should be at least %s to add transaction",
+ FormatMoney(nNewFeeRate.GetFeePerK()),
+ FormatMoney(minMempoolFeeRate.GetFeePerK()),
+ FormatMoney(minMempoolFeeRate.GetFee(maxNewTxSize)),
+ FormatMoney(minMempoolFeeRate.GetFeePerK())));
currentResult = BumpFeeResult::WALLET_ERROR;
return;
}
@@ -267,7 +273,7 @@ bool CFeeBumper::commit(CWallet *pWallet)
CValidationState state;
if (!pWallet->CommitTransaction(wtxBumped, reservekey, g_connman.get(), state)) {
// NOTE: CommitTransaction never returns false, so this should never happen.
- vErrors.push_back(strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason()));
+ vErrors.push_back(strprintf("The transaction was rejected: %s", state.GetRejectReason()));
return false;
}
@@ -275,7 +281,7 @@ bool CFeeBumper::commit(CWallet *pWallet)
if (state.IsInvalid()) {
// This can happen if the mempool rejected the transaction. Report
// what happened in the "errors" response.
- vErrors.push_back(strprintf("Error: The transaction was rejected: %s", FormatStateMessage(state)));
+ vErrors.push_back(strprintf("The transaction was rejected: %s", FormatStateMessage(state)));
}
// mark the original tx as bumped
@@ -284,7 +290,7 @@ bool CFeeBumper::commit(CWallet *pWallet)
// along with an exception. It would be good to return information about
// wtxBumped to the caller even if marking the original transaction
// replaced does not succeed for some reason.
- vErrors.push_back("Error: Created new bumpfee transaction but could not mark the original transaction as replaced.");
+ vErrors.push_back("Created new bumpfee transaction but could not mark the original transaction as replaced");
}
return true;
}
diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp
index c984df1df8..6b5d4cc668 100644
--- a/src/wallet/init.cpp
+++ b/src/wallet/init.cpp
@@ -53,11 +53,16 @@ std::string GetWalletHelpString(bool showDebug)
bool WalletParameterInteraction()
{
- gArgs.SoftSetArg("-wallet", DEFAULT_WALLET_DAT);
- const bool is_multiwallet = gArgs.GetArgs("-wallet").size() > 1;
+ if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
+ for (const std::string& wallet : gArgs.GetArgs("-wallet")) {
+ LogPrintf("%s: parameter interaction: -disablewallet -> ignoring -wallet=%s\n", __func__, wallet);
+ }
- if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET))
return true;
+ }
+
+ gArgs.SoftSetArg("-wallet", DEFAULT_WALLET_DAT);
+ const bool is_multiwallet = gArgs.GetArgs("-wallet").size() > 1;
if (gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY) && gArgs.SoftSetBoolArg("-walletbroadcast", false)) {
LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -walletbroadcast=0\n", __func__);
@@ -173,15 +178,18 @@ bool WalletParameterInteraction()
void RegisterWalletRPC(CRPCTable &t)
{
- if (gArgs.GetBoolArg("-disablewallet", false)) return;
+ if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
+ return;
+ }
RegisterWalletRPCCommands(t);
}
bool VerifyWallets()
{
- if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET))
+ if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
return true;
+ }
uiInterface.InitMessage(_("Verifying wallet(s)..."));
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index 3ec4a5efb4..30246a534b 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -81,7 +81,7 @@ UniValue importprivkey(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
throw std::runtime_error(
"importprivkey \"privkey\" ( \"label\" ) ( rescan )\n"
- "\nAdds a private key (as returned by dumpprivkey) to your wallet.\n"
+ "\nAdds a private key (as returned by dumpprivkey) to your wallet. Requires a new wallet backup.\n"
"\nArguments:\n"
"1. \"privkey\" (string, required) The private key (see dumpprivkey)\n"
"2. \"label\" (string, optional, default=\"\") An optional label\n"
@@ -226,7 +226,7 @@ UniValue importaddress(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 4)
throw std::runtime_error(
"importaddress \"address\" ( \"label\" rescan p2sh )\n"
- "\nAdds a script (in hex) or address that can be watched as if it were in your wallet but cannot be used to spend.\n"
+ "\nAdds a script (in hex) or address that can be watched as if it were in your wallet but cannot be used to spend. Requires a new wallet backup.\n"
"\nArguments:\n"
"1. \"script\" (string, required) The hex-encoded script (or address)\n"
"2. \"label\" (string, optional, default=\"\") An optional label\n"
@@ -340,7 +340,7 @@ UniValue importprunedfunds(const JSONRPCRequest& request)
LOCK2(cs_main, pwallet->cs_wallet);
- if (pwallet->IsMine(wtx)) {
+ if (pwallet->IsMine(*wtx.tx)) {
pwallet->AddToWallet(wtx, false);
return NullUniValue;
}
@@ -396,7 +396,7 @@ UniValue importpubkey(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 4)
throw std::runtime_error(
"importpubkey \"pubkey\" ( \"label\" rescan )\n"
- "\nAdds a public key (in hex) that can be watched as if it were in your wallet but cannot be used to spend.\n"
+ "\nAdds a public key (in hex) that can be watched as if it were in your wallet but cannot be used to spend. Requires a new wallet backup.\n"
"\nArguments:\n"
"1. \"pubkey\" (string, required) The hex-encoded public key\n"
"2. \"label\" (string, optional, default=\"\") An optional label\n"
@@ -456,7 +456,7 @@ UniValue importwallet(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
"importwallet \"filename\"\n"
- "\nImports keys from a wallet dump file (see dumpwallet).\n"
+ "\nImports keys from a wallet dump file (see dumpwallet). Requires a new wallet backup to include imported keys.\n"
"\nArguments:\n"
"1. \"filename\" (string, required) The wallet file\n"
"\nExamples:\n"
@@ -601,6 +601,9 @@ UniValue dumpwallet(const JSONRPCRequest& request)
throw std::runtime_error(
"dumpwallet \"filename\"\n"
"\nDumps all wallet keys in a human-readable format to a server-side file. This does not allow overwriting existing files.\n"
+ "Imported scripts are not currently included in wallet dumps, these must be backed up separately.\n"
+ "Note that if your wallet contains keys which are not derived from your HD seed (e.g. imported keys), these are not covered by\n"
+ "only backing up the seed itself, and must be backed up too (e.g. ensure you back up the whole dumpfile).\n"
"\nArguments:\n"
"1. \"filename\" (string, required) The filename with path (either absolute or relative to bitcoind)\n"
"\nResult:\n"
@@ -1039,7 +1042,7 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
if (mainRequest.fHelp || mainRequest.params.size() < 1 || mainRequest.params.size() > 2)
throw std::runtime_error(
"importmulti \"requests\" ( \"options\" )\n\n"
- "Import addresses/scripts (with private or public keys, redeem script (P2SH)), rescanning all addresses in one-shot-only (rescan can be disabled via options).\n\n"
+ "Import addresses/scripts (with private or public keys, redeem script (P2SH)), rescanning all addresses in one-shot-only (rescan can be disabled via options). Requires a new wallet backup.\n\n"
"Arguments:\n"
"1. requests (array, required) Data to be imported\n"
" [ (array of json objects)\n"
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index d6989add89..d4015f6c89 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -108,7 +108,7 @@ void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry)
std::string rbfStatus = "no";
if (confirms <= 0) {
LOCK(mempool.cs);
- RBFTransactionState rbfState = IsRBFOptIn(wtx, mempool);
+ RBFTransactionState rbfState = IsRBFOptIn(*wtx.tx, mempool);
if (rbfState == RBF_TRANSACTIONSTATE_UNKNOWN)
rbfStatus = "unknown";
else if (rbfState == RBF_TRANSACTIONSTATE_REPLACEABLE_BIP125)
@@ -1110,7 +1110,7 @@ UniValue addmultisigaddress(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 2 || request.params.size() > 3)
{
std::string msg = "addmultisigaddress nrequired [\"key\",...] ( \"account\" )\n"
- "\nAdd a nrequired-to-sign multisignature address to the wallet.\n"
+ "\nAdd a nrequired-to-sign multisignature address to the wallet. Requires a new wallet backup.\n"
"Each key is a Bitcoin address or hex-encoded public key.\n"
"If 'account' is specified (DEPRECATED), assign address to that account.\n"
@@ -1228,7 +1228,7 @@ UniValue addwitnessaddress(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
{
std::string msg = "addwitnessaddress \"address\" ( p2sh )\n"
- "\nAdd a witness address for a script (with pubkey or redeemscript known).\n"
+ "\nAdd a witness address for a script (with pubkey or redeemscript known). Requires a new wallet backup.\n"
"It returns the witness script.\n"
"\nArguments:\n"
@@ -1893,19 +1893,20 @@ UniValue listsinceblock(const JSONRPCRequest& request)
int target_confirms = 1;
isminefilter filter = ISMINE_SPENDABLE;
- if (!request.params[0].isNull()) {
+ if (!request.params[0].isNull() && !request.params[0].get_str().empty()) {
uint256 blockId;
blockId.SetHex(request.params[0].get_str());
BlockMap::iterator it = mapBlockIndex.find(blockId);
- if (it != mapBlockIndex.end()) {
- paltindex = pindex = it->second;
- if (chainActive[pindex->nHeight] != pindex) {
- // the block being asked for is a part of a deactivated chain;
- // we don't want to depend on its perceived height in the block
- // chain, we want to instead use the last common ancestor
- pindex = chainActive.FindFork(pindex);
- }
+ if (it == mapBlockIndex.end()) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
+ }
+ paltindex = pindex = it->second;
+ if (chainActive[pindex->nHeight] != pindex) {
+ // the block being asked for is a part of a deactivated chain;
+ // we don't want to depend on its perceived height in the block
+ // chain, we want to instead use the last common ancestor
+ pindex = chainActive.FindFork(pindex);
}
}
@@ -2050,7 +2051,7 @@ UniValue gettransaction(const JSONRPCRequest& request)
ListTransactions(pwallet, wtx, "*", 0, false, details, filter);
entry.push_back(Pair("details", details));
- std::string strHex = EncodeHexTx(static_cast<CTransaction>(wtx), RPCSerializationFlags());
+ std::string strHex = EncodeHexTx(*wtx.tx, RPCSerializationFlags());
entry.push_back(Pair("hex", strHex));
return entry;
@@ -2179,7 +2180,7 @@ UniValue walletpassphrase(const JSONRPCRequest& request)
return NullUniValue;
}
- if (pwallet->IsCrypted() && (request.fHelp || request.params.size() != 2)) {
+ if (request.fHelp || request.params.size() != 2) {
throw std::runtime_error(
"walletpassphrase \"passphrase\" timeout\n"
"\nStores the wallet decryption key in memory for 'timeout' seconds.\n"
@@ -2243,7 +2244,7 @@ UniValue walletpassphrasechange(const JSONRPCRequest& request)
return NullUniValue;
}
- if (pwallet->IsCrypted() && (request.fHelp || request.params.size() != 2)) {
+ if (request.fHelp || request.params.size() != 2) {
throw std::runtime_error(
"walletpassphrasechange \"oldpassphrase\" \"newpassphrase\"\n"
"\nChanges the wallet passphrase from 'oldpassphrase' to 'newpassphrase'.\n"
@@ -2294,7 +2295,7 @@ UniValue walletlock(const JSONRPCRequest& request)
return NullUniValue;
}
- if (pwallet->IsCrypted() && (request.fHelp || request.params.size() != 0)) {
+ if (request.fHelp || request.params.size() != 0) {
throw std::runtime_error(
"walletlock\n"
"\nRemoves the wallet encryption key from memory, locking the wallet.\n"
@@ -2334,7 +2335,7 @@ UniValue encryptwallet(const JSONRPCRequest& request)
return NullUniValue;
}
- if (!pwallet->IsCrypted() && (request.fHelp || request.params.size() != 1)) {
+ if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"encryptwallet \"passphrase\"\n"
"\nEncrypts the wallet with 'passphrase'. This is for first time encryption.\n"
diff --git a/src/wallet/test/accounting_tests.cpp b/src/wallet/test/accounting_tests.cpp
index 330878ceb5..64244dceea 100644
--- a/src/wallet/test/accounting_tests.cpp
+++ b/src/wallet/test/accounting_tests.cpp
@@ -83,7 +83,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
wtx.mapValue["comment"] = "y";
{
- CMutableTransaction tx(wtx);
+ CMutableTransaction tx(*wtx.tx);
--tx.nLockTime; // Just to change the hash :)
wtx.SetTx(MakeTransactionRef(std::move(tx)));
}
@@ -93,7 +93,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
wtx.mapValue["comment"] = "x";
{
- CMutableTransaction tx(wtx);
+ CMutableTransaction tx(*wtx.tx);
--tx.nLockTime; // Just to change the hash :)
wtx.SetTx(MakeTransactionRef(std::move(tx)));
}
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 543bef32ad..a357224a86 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -532,6 +532,9 @@ void CWallet::SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator> ran
copyFrom = &mapWallet[hash];
}
}
+
+ assert(copyFrom);
+
// Now copy data from copyFrom to rest:
for (TxSpends::iterator it = range.first; it != range.second; ++it)
{
@@ -1718,7 +1721,7 @@ CAmount CWalletTx::GetDebit(const isminefilter& filter) const
debit += nDebitCached;
else
{
- nDebitCached = pwallet->GetDebit(*this, ISMINE_SPENDABLE);
+ nDebitCached = pwallet->GetDebit(*tx, ISMINE_SPENDABLE);
fDebitCached = true;
debit += nDebitCached;
}
@@ -1729,7 +1732,7 @@ CAmount CWalletTx::GetDebit(const isminefilter& filter) const
debit += nWatchDebitCached;
else
{
- nWatchDebitCached = pwallet->GetDebit(*this, ISMINE_WATCH_ONLY);
+ nWatchDebitCached = pwallet->GetDebit(*tx, ISMINE_WATCH_ONLY);
fWatchDebitCached = true;
debit += nWatchDebitCached;
}
@@ -1751,7 +1754,7 @@ CAmount CWalletTx::GetCredit(const isminefilter& filter) const
credit += nCreditCached;
else
{
- nCreditCached = pwallet->GetCredit(*this, ISMINE_SPENDABLE);
+ nCreditCached = pwallet->GetCredit(*tx, ISMINE_SPENDABLE);
fCreditCached = true;
credit += nCreditCached;
}
@@ -1762,7 +1765,7 @@ CAmount CWalletTx::GetCredit(const isminefilter& filter) const
credit += nWatchCreditCached;
else
{
- nWatchCreditCached = pwallet->GetCredit(*this, ISMINE_WATCH_ONLY);
+ nWatchCreditCached = pwallet->GetCredit(*tx, ISMINE_WATCH_ONLY);
fWatchCreditCached = true;
credit += nWatchCreditCached;
}
@@ -1776,7 +1779,7 @@ CAmount CWalletTx::GetImmatureCredit(bool fUseCache) const
{
if (fUseCache && fImmatureCreditCached)
return nImmatureCreditCached;
- nImmatureCreditCached = pwallet->GetCredit(*this, ISMINE_SPENDABLE);
+ nImmatureCreditCached = pwallet->GetCredit(*tx, ISMINE_SPENDABLE);
fImmatureCreditCached = true;
return nImmatureCreditCached;
}
@@ -1820,7 +1823,7 @@ CAmount CWalletTx::GetImmatureWatchOnlyCredit(const bool& fUseCache) const
{
if (fUseCache && fImmatureWatchCreditCached)
return nImmatureWatchCreditCached;
- nImmatureWatchCreditCached = pwallet->GetCredit(*this, ISMINE_WATCH_ONLY);
+ nImmatureWatchCreditCached = pwallet->GetCredit(*tx, ISMINE_WATCH_ONLY);
fImmatureWatchCreditCached = true;
return nImmatureWatchCreditCached;
}
@@ -1861,7 +1864,7 @@ CAmount CWalletTx::GetChange() const
{
if (fChangeCached)
return nChangeCached;
- nChangeCached = pwallet->GetChange(*this);
+ nChangeCached = pwallet->GetChange(*tx);
fChangeCached = true;
return nChangeCached;
}
@@ -1875,7 +1878,7 @@ bool CWalletTx::InMempool() const
bool CWalletTx::IsTrusted() const
{
// Quick answer in most cases
- if (!CheckFinalTx(*this))
+ if (!CheckFinalTx(*tx))
return false;
int nDepth = GetDepthInMainChain();
if (nDepth >= 1)
@@ -2133,7 +2136,7 @@ void CWallet::AvailableCoins(std::vector<COutput> &vCoins, bool fOnlySafe, const
const uint256& wtxid = it->first;
const CWalletTx* pcoin = &(*it).second;
- if (!CheckFinalTx(*pcoin))
+ if (!CheckFinalTx(*pcoin->tx))
continue;
if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0)
@@ -2915,7 +2918,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
wtxNew.SetTx(MakeTransactionRef(std::move(txNew)));
// Limit size
- if (GetTransactionWeight(wtxNew) >= MAX_STANDARD_TX_WEIGHT)
+ if (GetTransactionWeight(*wtxNew.tx) >= MAX_STANDARD_TX_WEIGHT)
{
strFailReason = _("Transaction too large");
return false;
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 8315bbf3da..db2861c043 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -214,10 +214,6 @@ public:
Init();
}
- /** Helper conversion operator to allow passing CMerkleTx where CTransaction is expected.
- * TODO: adapt callers and remove this operator. */
- operator const CTransaction&() const { return *tx; }
-
void Init()
{
hashBlock = uint256();
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index b7f873c1e4..180706b1ed 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -268,7 +268,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
CWalletTx wtx;
ssValue >> wtx;
CValidationState state;
- if (!(CheckTransaction(wtx, state) && (wtx.GetHash() == hash) && state.IsValid()))
+ if (!(CheckTransaction(*wtx.tx, state) && (wtx.GetHash() == hash) && state.IsValid()))
return false;
// Undo serialize changes in 31600
diff --git a/test/functional/assumevalid.py b/test/functional/assumevalid.py
index 65685c48b7..36761d359e 100755
--- a/test/functional/assumevalid.py
+++ b/test/functional/assumevalid.py
@@ -39,13 +39,12 @@ from test_framework.mininode import (CBlockHeader,
CTxIn,
CTxOut,
NetworkThread,
- NodeConn,
NodeConnCB,
msg_block,
msg_headers)
from test_framework.script import (CScript, OP_TRUE)
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import (p2p_port, assert_equal)
+from test_framework.util import assert_equal
class BaseNode(NodeConnCB):
def send_header_for_blocks(self, new_blocks):
@@ -65,13 +64,13 @@ class AssumeValidTest(BitcoinTestFramework):
# signature so we can pass in the block hash as assumevalid.
self.start_node(0)
- def send_blocks_until_disconnected(self, node):
+ def send_blocks_until_disconnected(self, p2p_conn):
"""Keep sending blocks to the node until we're disconnected."""
for i in range(len(self.blocks)):
- if not node.connection:
+ if not p2p_conn.connection:
break
try:
- node.send_message(msg_block(self.blocks[i]))
+ p2p_conn.send_message(msg_block(self.blocks[i]))
except IOError as e:
assert str(e) == 'Not connected, no pushbuf'
break
@@ -97,13 +96,10 @@ class AssumeValidTest(BitcoinTestFramework):
def run_test(self):
# Connect to node0
- node0 = BaseNode()
- connections = []
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node0))
- node0.add_connection(connections[0])
+ p2p0 = self.nodes[0].add_p2p_connection(BaseNode())
NetworkThread().start() # Start up network handling in another thread
- node0.wait_for_verack()
+ self.nodes[0].p2p.wait_for_verack()
# Build the blockchain
self.tip = int(self.nodes[0].getbestblockhash(), 16)
@@ -165,37 +161,33 @@ class AssumeValidTest(BitcoinTestFramework):
# Start node1 and node2 with assumevalid so they accept a block with a bad signature.
self.start_node(1, extra_args=["-assumevalid=" + hex(block102.sha256)])
- node1 = BaseNode() # connects to node1
- connections.append(NodeConn('127.0.0.1', p2p_port(1), self.nodes[1], node1))
- node1.add_connection(connections[1])
- node1.wait_for_verack()
+ p2p1 = self.nodes[1].add_p2p_connection(BaseNode())
+ p2p1.wait_for_verack()
self.start_node(2, extra_args=["-assumevalid=" + hex(block102.sha256)])
- node2 = BaseNode() # connects to node2
- connections.append(NodeConn('127.0.0.1', p2p_port(2), self.nodes[2], node2))
- node2.add_connection(connections[2])
- node2.wait_for_verack()
+ p2p2 = self.nodes[2].add_p2p_connection(BaseNode())
+ p2p2.wait_for_verack()
# send header lists to all three nodes
- node0.send_header_for_blocks(self.blocks[0:2000])
- node0.send_header_for_blocks(self.blocks[2000:])
- node1.send_header_for_blocks(self.blocks[0:2000])
- node1.send_header_for_blocks(self.blocks[2000:])
- node2.send_header_for_blocks(self.blocks[0:200])
+ p2p0.send_header_for_blocks(self.blocks[0:2000])
+ p2p0.send_header_for_blocks(self.blocks[2000:])
+ p2p1.send_header_for_blocks(self.blocks[0:2000])
+ p2p1.send_header_for_blocks(self.blocks[2000:])
+ p2p2.send_header_for_blocks(self.blocks[0:200])
# Send blocks to node0. Block 102 will be rejected.
- self.send_blocks_until_disconnected(node0)
+ self.send_blocks_until_disconnected(p2p0)
self.assert_blockchain_height(self.nodes[0], 101)
# Send all blocks to node1. All blocks will be accepted.
for i in range(2202):
- node1.send_message(msg_block(self.blocks[i]))
+ 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.
- node1.sync_with_ping(120)
+ p2p1.sync_with_ping(120)
assert_equal(self.nodes[1].getblock(self.nodes[1].getbestblockhash())['height'], 2202)
# Send blocks to node2. Block 102 will be rejected.
- self.send_blocks_until_disconnected(node2)
+ self.send_blocks_until_disconnected(p2p2)
self.assert_blockchain_height(self.nodes[2], 101)
if __name__ == '__main__':
diff --git a/test/functional/bip65-cltv-p2p.py b/test/functional/bip65-cltv-p2p.py
index 2cd6df6e37..3073324798 100755
--- a/test/functional/bip65-cltv-p2p.py
+++ b/test/functional/bip65-cltv-p2p.py
@@ -66,15 +66,12 @@ class BIP65Test(BitcoinTestFramework):
self.setup_clean_chain = True
def run_test(self):
- node0 = NodeConnCB()
- connections = []
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node0))
- node0.add_connection(connections[0])
+ self.nodes[0].add_p2p_connection(NodeConnCB())
NetworkThread().start() # Start up network handling in another thread
# wait_for_verack ensures that the P2P connection is fully up.
- node0.wait_for_verack()
+ self.nodes[0].p2p.wait_for_verack()
self.log.info("Mining %d blocks", CLTV_HEIGHT - 2)
self.coinbase_blocks = self.nodes[0].generate(CLTV_HEIGHT - 2)
@@ -95,7 +92,7 @@ class BIP65Test(BitcoinTestFramework):
block.hashMerkleRoot = block.calc_merkle_root()
block.solve()
- node0.send_and_ping(msg_block(block))
+ self.nodes[0].p2p.send_and_ping(msg_block(block))
assert_equal(self.nodes[0].getbestblockhash(), block.hash)
self.log.info("Test that blocks must now be at least version 4")
@@ -104,15 +101,15 @@ class BIP65Test(BitcoinTestFramework):
block = create_block(tip, create_coinbase(CLTV_HEIGHT), block_time)
block.nVersion = 3
block.solve()
- node0.send_and_ping(msg_block(block))
+ self.nodes[0].p2p.send_and_ping(msg_block(block))
assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)
- wait_until(lambda: "reject" in node0.last_message.keys(), lock=mininode_lock)
+ wait_until(lambda: "reject" in self.nodes[0].p2p.last_message.keys(), lock=mininode_lock)
with mininode_lock:
- assert_equal(node0.last_message["reject"].code, REJECT_OBSOLETE)
- assert_equal(node0.last_message["reject"].reason, b'bad-version(0x00000003)')
- assert_equal(node0.last_message["reject"].data, block.sha256)
- del node0.last_message["reject"]
+ assert_equal(self.nodes[0].p2p.last_message["reject"].code, REJECT_OBSOLETE)
+ assert_equal(self.nodes[0].p2p.last_message["reject"].reason, b'bad-version(0x00000003)')
+ assert_equal(self.nodes[0].p2p.last_message["reject"].data, block.sha256)
+ del self.nodes[0].p2p.last_message["reject"]
self.log.info("Test that invalid-according-to-cltv transactions cannot appear in a block")
block.nVersion = 4
@@ -125,7 +122,7 @@ class BIP65Test(BitcoinTestFramework):
# First we show that this tx is valid except for CLTV by getting it
# accepted to the mempool (which we can achieve with
# -promiscuousmempoolflags).
- node0.send_and_ping(msg_tx(spendtx))
+ self.nodes[0].p2p.send_and_ping(msg_tx(spendtx))
assert spendtx.hash in self.nodes[0].getrawmempool()
# Now we verify that a block with this transaction is invalid.
@@ -133,18 +130,18 @@ class BIP65Test(BitcoinTestFramework):
block.hashMerkleRoot = block.calc_merkle_root()
block.solve()
- node0.send_and_ping(msg_block(block))
+ self.nodes[0].p2p.send_and_ping(msg_block(block))
assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)
- wait_until(lambda: "reject" in node0.last_message.keys(), lock=mininode_lock)
+ wait_until(lambda: "reject" in self.nodes[0].p2p.last_message.keys(), lock=mininode_lock)
with mininode_lock:
- assert node0.last_message["reject"].code in [REJECT_INVALID, REJECT_NONSTANDARD]
- assert_equal(node0.last_message["reject"].data, block.sha256)
- if node0.last_message["reject"].code == REJECT_INVALID:
+ assert self.nodes[0].p2p.last_message["reject"].code in [REJECT_INVALID, REJECT_NONSTANDARD]
+ assert_equal(self.nodes[0].p2p.last_message["reject"].data, block.sha256)
+ if self.nodes[0].p2p.last_message["reject"].code == REJECT_INVALID:
# Generic rejection when a block is invalid
- assert_equal(node0.last_message["reject"].reason, b'block-validation-failed')
+ assert_equal(self.nodes[0].p2p.last_message["reject"].reason, b'block-validation-failed')
else:
- assert b'Negative locktime' in node0.last_message["reject"].reason
+ assert b'Negative locktime' in self.nodes[0].p2p.last_message["reject"].reason
self.log.info("Test that a version 4 block with a valid-according-to-CLTV transaction is accepted")
spendtx = cltv_validate(self.nodes[0], spendtx, CLTV_HEIGHT - 1)
@@ -155,7 +152,7 @@ class BIP65Test(BitcoinTestFramework):
block.hashMerkleRoot = block.calc_merkle_root()
block.solve()
- node0.send_and_ping(msg_block(block))
+ self.nodes[0].p2p.send_and_ping(msg_block(block))
assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
diff --git a/test/functional/bipdersig-p2p.py b/test/functional/bipdersig-p2p.py
index c620d3e155..e5febde42d 100755
--- a/test/functional/bipdersig-p2p.py
+++ b/test/functional/bipdersig-p2p.py
@@ -54,14 +54,12 @@ class BIP66Test(BitcoinTestFramework):
self.setup_clean_chain = True
def run_test(self):
- node0 = NodeConnCB()
- connections = []
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node0))
- node0.add_connection(connections[0])
+ self.nodes[0].add_p2p_connection(NodeConnCB())
+
NetworkThread().start() # Start up network handling in another thread
# wait_for_verack ensures that the P2P connection is fully up.
- node0.wait_for_verack()
+ self.nodes[0].p2p.wait_for_verack()
self.log.info("Mining %d blocks", DERSIG_HEIGHT - 2)
self.coinbase_blocks = self.nodes[0].generate(DERSIG_HEIGHT - 2)
@@ -83,7 +81,7 @@ class BIP66Test(BitcoinTestFramework):
block.rehash()
block.solve()
- node0.send_and_ping(msg_block(block))
+ self.nodes[0].p2p.send_and_ping(msg_block(block))
assert_equal(self.nodes[0].getbestblockhash(), block.hash)
self.log.info("Test that blocks must now be at least version 3")
@@ -93,15 +91,15 @@ class BIP66Test(BitcoinTestFramework):
block.nVersion = 2
block.rehash()
block.solve()
- node0.send_and_ping(msg_block(block))
+ self.nodes[0].p2p.send_and_ping(msg_block(block))
assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)
- wait_until(lambda: "reject" in node0.last_message.keys(), lock=mininode_lock)
+ wait_until(lambda: "reject" in self.nodes[0].p2p.last_message.keys(), lock=mininode_lock)
with mininode_lock:
- assert_equal(node0.last_message["reject"].code, REJECT_OBSOLETE)
- assert_equal(node0.last_message["reject"].reason, b'bad-version(0x00000002)')
- assert_equal(node0.last_message["reject"].data, block.sha256)
- del node0.last_message["reject"]
+ assert_equal(self.nodes[0].p2p.last_message["reject"].code, REJECT_OBSOLETE)
+ assert_equal(self.nodes[0].p2p.last_message["reject"].reason, b'bad-version(0x00000002)')
+ assert_equal(self.nodes[0].p2p.last_message["reject"].data, block.sha256)
+ del self.nodes[0].p2p.last_message["reject"]
self.log.info("Test that transactions with non-DER signatures cannot appear in a block")
block.nVersion = 3
@@ -114,7 +112,7 @@ class BIP66Test(BitcoinTestFramework):
# First we show that this tx is valid except for DERSIG by getting it
# accepted to the mempool (which we can achieve with
# -promiscuousmempoolflags).
- node0.send_and_ping(msg_tx(spendtx))
+ self.nodes[0].p2p.send_and_ping(msg_tx(spendtx))
assert spendtx.hash in self.nodes[0].getrawmempool()
# Now we verify that a block with this transaction is invalid.
@@ -123,23 +121,23 @@ class BIP66Test(BitcoinTestFramework):
block.rehash()
block.solve()
- node0.send_and_ping(msg_block(block))
+ self.nodes[0].p2p.send_and_ping(msg_block(block))
assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)
- wait_until(lambda: "reject" in node0.last_message.keys(), lock=mininode_lock)
+ wait_until(lambda: "reject" in self.nodes[0].p2p.last_message.keys(), lock=mininode_lock)
with mininode_lock:
# We can receive different reject messages depending on whether
# bitcoind is running with multiple script check threads. If script
# check threads are not in use, then transaction script validation
# happens sequentially, and bitcoind produces more specific reject
# reasons.
- assert node0.last_message["reject"].code in [REJECT_INVALID, REJECT_NONSTANDARD]
- assert_equal(node0.last_message["reject"].data, block.sha256)
- if node0.last_message["reject"].code == REJECT_INVALID:
+ assert self.nodes[0].p2p.last_message["reject"].code in [REJECT_INVALID, REJECT_NONSTANDARD]
+ assert_equal(self.nodes[0].p2p.last_message["reject"].data, block.sha256)
+ if self.nodes[0].p2p.last_message["reject"].code == REJECT_INVALID:
# Generic rejection when a block is invalid
- assert_equal(node0.last_message["reject"].reason, b'block-validation-failed')
+ assert_equal(self.nodes[0].p2p.last_message["reject"].reason, b'block-validation-failed')
else:
- assert b'Non-canonical DER signature' in node0.last_message["reject"].reason
+ assert b'Non-canonical DER signature' in self.nodes[0].p2p.last_message["reject"].reason
self.log.info("Test that a version 3 block with a DERSIG-compliant transaction is accepted")
block.vtx[1] = create_transaction(self.nodes[0],
@@ -148,7 +146,7 @@ class BIP66Test(BitcoinTestFramework):
block.rehash()
block.solve()
- node0.send_and_ping(msg_block(block))
+ self.nodes[0].p2p.send_and_ping(msg_block(block))
assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
if __name__ == '__main__':
diff --git a/test/functional/example_test.py b/test/functional/example_test.py
index 87d73ad14a..ba40f33016 100755
--- a/test/functional/example_test.py
+++ b/test/functional/example_test.py
@@ -18,7 +18,6 @@ from test_framework.blocktools import (create_block, create_coinbase)
from test_framework.mininode import (
CInv,
NetworkThread,
- NodeConn,
NodeConnCB,
mininode_lock,
msg_block,
@@ -28,7 +27,6 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
connect_nodes,
- p2p_port,
wait_until,
)
@@ -134,16 +132,13 @@ class ExampleTest(BitcoinTestFramework):
"""Main test logic"""
# Create a P2P connection to one of the nodes
- node0 = BaseNode()
- connections = []
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node0))
- node0.add_connection(connections[0])
+ self.nodes[0].add_p2p_connection(BaseNode())
# Start up network handling in another thread. This needs to be called
# after the P2P connections have been created.
NetworkThread().start()
# wait_for_verack ensures that the P2P connection is fully up.
- node0.wait_for_verack()
+ self.nodes[0].p2p.wait_for_verack()
# Generating a block on one of the nodes will get us out of IBD
blocks = [int(self.nodes[0].generate(nblocks=1)[0], 16)]
@@ -180,7 +175,7 @@ class ExampleTest(BitcoinTestFramework):
block.solve()
block_message = msg_block(block)
# Send message is used to send a P2P message to the node over our NodeConn connection
- node0.send_message(block_message)
+ self.nodes[0].p2p.send_message(block_message)
self.tip = block.sha256
blocks.append(self.tip)
self.block_time += 1
@@ -193,28 +188,26 @@ class ExampleTest(BitcoinTestFramework):
connect_nodes(self.nodes[1], 2)
self.log.info("Add P2P connection to node2")
- node2 = BaseNode()
- connections.append(NodeConn('127.0.0.1', p2p_port(2), self.nodes[2], node2))
- node2.add_connection(connections[1])
- node2.wait_for_verack()
+ self.nodes[2].add_p2p_connection(BaseNode())
+ self.nodes[2].p2p.wait_for_verack()
self.log.info("Wait for node2 reach current tip. Test that it has propagated all the blocks to us")
getdata_request = msg_getdata()
for block in blocks:
getdata_request.inv.append(CInv(2, block))
- node2.send_message(getdata_request)
+ self.nodes[2].p2p.send_message(getdata_request)
# wait_until() will loop until a predicate condition is met. Use it to test properties of the
# NodeConnCB objects.
- wait_until(lambda: sorted(blocks) == sorted(list(node2.block_receive_map.keys())), timeout=5, lock=mininode_lock)
+ wait_until(lambda: sorted(blocks) == sorted(list(self.nodes[2].p2p.block_receive_map.keys())), timeout=5, lock=mininode_lock)
self.log.info("Check that each block was received only once")
# The network thread uses a global lock on data access to the NodeConn objects when sending and receiving
# messages. The test thread should acquire the global lock before accessing any NodeConn data to avoid locking
# and synchronization issues. Note wait_until() acquires this global lock when testing the predicate.
with mininode_lock:
- for block in node2.block_receive_map.values():
+ for block in self.nodes[2].p2p.block_receive_map.values():
assert_equal(block, 1)
if __name__ == '__main__':
diff --git a/test/functional/listsinceblock.py b/test/functional/listsinceblock.py
index 6f428388ec..67e7744bf8 100755
--- a/test/functional/listsinceblock.py
+++ b/test/functional/listsinceblock.py
@@ -5,7 +5,7 @@
"""Test the listsincelast RPC."""
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal
+from test_framework.util import assert_equal, assert_array_result, assert_raises_rpc_error
class ListSinceBlockTest (BitcoinTestFramework):
def set_test_params(self):
@@ -16,10 +16,43 @@ class ListSinceBlockTest (BitcoinTestFramework):
self.nodes[2].generate(101)
self.sync_all()
+ self.test_no_blockhash()
+ self.test_invalid_blockhash()
self.test_reorg()
self.test_double_spend()
self.test_double_send()
+ def test_no_blockhash(self):
+ txid = self.nodes[2].sendtoaddress(self.nodes[0].getnewaddress(), 1)
+ blockhash, = self.nodes[2].generate(1)
+ self.sync_all()
+
+ txs = self.nodes[0].listtransactions()
+ assert_array_result(txs, {"txid": txid}, {
+ "category": "receive",
+ "amount": 1,
+ "blockhash": blockhash,
+ "confirmations": 1,
+ })
+ assert_equal(
+ self.nodes[0].listsinceblock(),
+ {"lastblock": blockhash,
+ "removed": [],
+ "transactions": txs})
+ assert_equal(
+ self.nodes[0].listsinceblock(""),
+ {"lastblock": blockhash,
+ "removed": [],
+ "transactions": txs})
+
+ def test_invalid_blockhash(self):
+ assert_raises_rpc_error(-5, "Block not found", self.nodes[0].listsinceblock,
+ "42759cde25462784395a337460bde75f58e73d3f08bd31fdc3507cbac856a2c4")
+ assert_raises_rpc_error(-5, "Block not found", self.nodes[0].listsinceblock,
+ "0000000000000000000000000000000000000000000000000000000000000000")
+ assert_raises_rpc_error(-5, "Block not found", self.nodes[0].listsinceblock,
+ "invalid-hex")
+
def test_reorg(self):
'''
`listsinceblock` did not behave correctly when handed a block that was
diff --git a/test/functional/maxuploadtarget.py b/test/functional/maxuploadtarget.py
index 1f402798e7..9c92aa1dc0 100755
--- a/test/functional/maxuploadtarget.py
+++ b/test/functional/maxuploadtarget.py
@@ -49,19 +49,17 @@ class MaxUploadTest(BitcoinTestFramework):
# Generate some old blocks
self.nodes[0].generate(130)
- # test_nodes[0] will only request old blocks
- # test_nodes[1] will only request new blocks
- # test_nodes[2] will test resetting the counters
- test_nodes = []
- connections = []
+ # p2p_conns[0] will only request old blocks
+ # p2p_conns[1] will only request new blocks
+ # p2p_conns[2] will test resetting the counters
+ p2p_conns = []
for i in range(3):
- test_nodes.append(TestNode())
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_nodes[i]))
- test_nodes[i].add_connection(connections[i])
+ p2p_conns.append(self.nodes[0].add_p2p_connection(TestNode()))
NetworkThread().start() # Start up network handling in another thread
- [x.wait_for_verack() for x in test_nodes]
+ for p2pc in p2p_conns:
+ p2pc.wait_for_verack()
# Test logic begins here
@@ -83,7 +81,7 @@ class MaxUploadTest(BitcoinTestFramework):
big_new_block = self.nodes[0].getbestblockhash()
big_new_block = int(big_new_block, 16)
- # test_nodes[0] will test what happens if we just keep requesting the
+ # p2p_conns[0] will test what happens if we just keep requesting the
# the same big old block too many times (expect: disconnect)
getdata_request = msg_getdata()
@@ -97,34 +95,34 @@ class MaxUploadTest(BitcoinTestFramework):
# 576MB will be reserved for relaying new blocks, so expect this to
# succeed for ~235 tries.
for i in range(success_count):
- test_nodes[0].send_message(getdata_request)
- test_nodes[0].sync_with_ping()
- assert_equal(test_nodes[0].block_receive_map[big_old_block], i+1)
+ p2p_conns[0].send_message(getdata_request)
+ p2p_conns[0].sync_with_ping()
+ assert_equal(p2p_conns[0].block_receive_map[big_old_block], i+1)
assert_equal(len(self.nodes[0].getpeerinfo()), 3)
# At most a couple more tries should succeed (depending on how long
# the test has been running so far).
for i in range(3):
- test_nodes[0].send_message(getdata_request)
- test_nodes[0].wait_for_disconnect()
+ p2p_conns[0].send_message(getdata_request)
+ p2p_conns[0].wait_for_disconnect()
assert_equal(len(self.nodes[0].getpeerinfo()), 2)
self.log.info("Peer 0 disconnected after downloading old block too many times")
- # Requesting the current block on test_nodes[1] should succeed indefinitely,
+ # Requesting the current block on p2p_conns[1] should succeed indefinitely,
# even when over the max upload target.
# We'll try 800 times
getdata_request.inv = [CInv(2, big_new_block)]
for i in range(800):
- test_nodes[1].send_message(getdata_request)
- test_nodes[1].sync_with_ping()
- assert_equal(test_nodes[1].block_receive_map[big_new_block], i+1)
+ p2p_conns[1].send_message(getdata_request)
+ p2p_conns[1].sync_with_ping()
+ assert_equal(p2p_conns[1].block_receive_map[big_new_block], i+1)
self.log.info("Peer 1 able to repeatedly download new block")
- # But if test_nodes[1] tries for an old block, it gets disconnected too.
+ # But if p2p_conns[1] tries for an old block, it gets disconnected too.
getdata_request.inv = [CInv(2, big_old_block)]
- test_nodes[1].send_message(getdata_request)
- test_nodes[1].wait_for_disconnect()
+ p2p_conns[1].send_message(getdata_request)
+ p2p_conns[1].wait_for_disconnect()
assert_equal(len(self.nodes[0].getpeerinfo()), 1)
self.log.info("Peer 1 disconnected after trying to download old block")
@@ -132,39 +130,38 @@ class MaxUploadTest(BitcoinTestFramework):
self.log.info("Advancing system time on node to clear counters...")
# If we advance the time by 24 hours, then the counters should reset,
- # and test_nodes[2] should be able to retrieve the old block.
+ # and p2p_conns[2] should be able to retrieve the old block.
self.nodes[0].setmocktime(int(time.time()))
- test_nodes[2].sync_with_ping()
- test_nodes[2].send_message(getdata_request)
- test_nodes[2].sync_with_ping()
- assert_equal(test_nodes[2].block_receive_map[big_old_block], 1)
+ p2p_conns[2].sync_with_ping()
+ p2p_conns[2].send_message(getdata_request)
+ p2p_conns[2].sync_with_ping()
+ assert_equal(p2p_conns[2].block_receive_map[big_old_block], 1)
self.log.info("Peer 2 able to download old block")
- [c.disconnect_node() for c in connections]
+ for i in range(3):
+ self.nodes[0].disconnect_p2p()
#stop and start node 0 with 1MB maxuploadtarget, whitelist 127.0.0.1
self.log.info("Restarting nodes with -whitelist=127.0.0.1")
self.stop_node(0)
self.start_node(0, ["-whitelist=127.0.0.1", "-maxuploadtarget=1", "-blockmaxsize=999000"])
- #recreate/reconnect a test node
- test_nodes = [TestNode()]
- connections = [NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_nodes[0])]
- test_nodes[0].add_connection(connections[0])
+ # Reconnect to self.nodes[0]
+ self.nodes[0].add_p2p_connection(TestNode())
NetworkThread().start() # Start up network handling in another thread
- test_nodes[0].wait_for_verack()
+ self.nodes[0].p2p.wait_for_verack()
#retrieve 20 blocks which should be enough to break the 1MB limit
getdata_request.inv = [CInv(2, big_new_block)]
for i in range(20):
- test_nodes[0].send_message(getdata_request)
- test_nodes[0].sync_with_ping()
- assert_equal(test_nodes[0].block_receive_map[big_new_block], i+1)
+ self.nodes[0].p2p.send_message(getdata_request)
+ self.nodes[0].p2p.sync_with_ping()
+ assert_equal(self.nodes[0].p2p.block_receive_map[big_new_block], i+1)
getdata_request.inv = [CInv(2, big_old_block)]
- test_nodes[0].send_and_ping(getdata_request)
+ self.nodes[0].p2p.send_and_ping(getdata_request)
assert_equal(len(self.nodes[0].getpeerinfo()), 1) #node is still connected because of the whitelist
self.log.info("Peer still connected after trying to download old block (whitelisted)")
diff --git a/test/functional/nulldummy.py b/test/functional/nulldummy.py
index 91c4550653..7bc7c168f4 100755
--- a/test/functional/nulldummy.py
+++ b/test/functional/nulldummy.py
@@ -40,7 +40,9 @@ class NULLDUMMYTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.setup_clean_chain = True
- self.extra_args = [['-whitelist=127.0.0.1', '-walletprematurewitness']]
+ # This script tests NULLDUMMY activation, which is part of the 'segwit' deployment, so we go through
+ # normal segwit activation here (and don't use the default always-on behaviour).
+ self.extra_args = [['-whitelist=127.0.0.1', '-walletprematurewitness', '-vbparams=segwit:0:999999999999']]
def run_test(self):
self.address = self.nodes[0].getnewaddress()
diff --git a/test/functional/p2p-acceptblock.py b/test/functional/p2p-acceptblock.py
index 27ae0c27e1..fbe5a78029 100755
--- a/test/functional/p2p-acceptblock.py
+++ b/test/functional/p2p-acceptblock.py
@@ -4,42 +4,32 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test processing of unrequested blocks.
-Since behavior differs when receiving unrequested blocks from whitelisted peers
-versus non-whitelisted peers, this tests the behavior of both (effectively two
-separate tests running in parallel).
+Setup: two nodes, node0+node1, not connected to each other. Node1 will have
+nMinimumChainWork set to 0x10, so it won't process low-work unrequested blocks.
-Setup: three nodes, node0+node1+node2, not connected to each other. Node0 does not
-whitelist localhost, but node1 does. They will each be on their own chain for
-this test. Node2 will have nMinimumChainWork set to 0x10, so it won't process
-low-work unrequested blocks.
-
-We have one NodeConn connection to each, test_node, white_node, and min_work_node,
-respectively.
+We have one NodeConn connection to node0 called test_node, and one to node1
+called min_work_node.
The test:
1. Generate one block on each node, to leave IBD.
2. Mine a new block on each tip, and deliver to each node from node's peer.
- The tip should advance for node0 and node1, but node2 should skip processing
- due to nMinimumChainWork.
+ The tip should advance for node0, but node1 should skip processing due to
+ nMinimumChainWork.
-Node2 is unused in tests 3-7:
+Node1 is unused in tests 3-7:
-3. Mine a block that forks the previous block, and deliver to each node from
- corresponding peer.
- Node0 should not process this block (just accept the header), because it is
- unrequested and doesn't have more work than the tip.
- Node1 should process because this is coming from a whitelisted peer.
+3. Mine a block that forks from the genesis block, and deliver to test_node.
+ Node0 should not process this block (just accept the header), because it
+ is unrequested and doesn't have more or equal work to the tip.
-4. Send another block that builds on the forking block.
- Node0 should process this block but be stuck on the shorter chain, because
- it's missing an intermediate block.
- Node1 should reorg to this longer chain.
+4a,b. Send another two blocks that build on the forking block.
+ Node0 should process the second block but be stuck on the shorter chain,
+ because it's missing an intermediate block.
-4b.Send 288 more blocks on the longer chain.
+4c.Send 288 more blocks on the longer chain (the number of blocks ahead
+ we currently store).
Node0 should process all but the last block (too far ahead in height).
- Send all headers to Node1, and then send the last block in that chain.
- Node1 should accept the block because it's coming from a whitelisted peer.
5. Send a duplicate of the block in #3 to Node0.
Node0 should not process the block because it is unrequested, and stay on
@@ -52,16 +42,20 @@ Node2 is unused in tests 3-7:
7. Send Node0 the missing block again.
Node0 should process and the tip should advance.
-8. Test Node2 is able to sync when connected to node0 (which should have sufficient
-work on its chain).
+8. Create a fork which is invalid at a height longer than the current chain
+ (ie to which the node will try to reorg) but which has headers built on top
+ of the invalid block. Check that we get disconnected if we send more headers
+ on the chain the node now knows to be invalid.
+9. Test Node1 is able to sync when connected to node0 (which should have sufficient
+ work on its chain).
"""
from test_framework.mininode import *
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
import time
-from test_framework.blocktools import create_block, create_coinbase
+from test_framework.blocktools import create_block, create_coinbase, create_transaction
class AcceptBlockTest(BitcoinTestFramework):
def add_options(self, parser):
@@ -71,8 +65,8 @@ class AcceptBlockTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
- self.num_nodes = 3
- self.extra_args = [[], ["-whitelist=127.0.0.1"], ["-minimumchainwork=0x10"]]
+ self.num_nodes = 2
+ self.extra_args = [[], ["-minimumchainwork=0x10"]]
def setup_network(self):
# Node0 will be used to test behavior of processing unrequested blocks
@@ -84,132 +78,140 @@ class AcceptBlockTest(BitcoinTestFramework):
def run_test(self):
# Setup the p2p connections and start up the network thread.
- test_node = NodeConnCB() # connects to node0 (not whitelisted)
- white_node = NodeConnCB() # connects to node1 (whitelisted)
- min_work_node = NodeConnCB() # connects to node2 (not whitelisted)
-
- connections = []
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_node))
- connections.append(NodeConn('127.0.0.1', p2p_port(1), self.nodes[1], white_node))
- connections.append(NodeConn('127.0.0.1', p2p_port(2), self.nodes[2], min_work_node))
- test_node.add_connection(connections[0])
- white_node.add_connection(connections[1])
- min_work_node.add_connection(connections[2])
+ # test_node connects to node0 (not whitelisted)
+ test_node = self.nodes[0].add_p2p_connection(NodeConnCB())
+ # min_work_node connects to node1
+ min_work_node = self.nodes[1].add_p2p_connection(NodeConnCB())
NetworkThread().start() # Start up network handling in another thread
# Test logic begins here
test_node.wait_for_verack()
- white_node.wait_for_verack()
min_work_node.wait_for_verack()
- # 1. Have nodes mine a block (nodes1/2 leave IBD)
+ # 1. Have nodes mine a block (leave IBD)
[ n.generate(1) for n in self.nodes ]
tips = [ int("0x" + n.getbestblockhash(), 0) for n in self.nodes ]
# 2. Send one block that builds on each tip.
- # This should be accepted by nodes 1/2
+ # This should be accepted by node0
blocks_h2 = [] # the height 2 blocks on each node's chain
block_time = int(time.time()) + 1
- for i in range(3):
+ for i in range(2):
blocks_h2.append(create_block(tips[i], create_coinbase(2), block_time))
blocks_h2[i].solve()
block_time += 1
test_node.send_message(msg_block(blocks_h2[0]))
- white_node.send_message(msg_block(blocks_h2[1]))
- min_work_node.send_message(msg_block(blocks_h2[2]))
+ min_work_node.send_message(msg_block(blocks_h2[1]))
- for x in [test_node, white_node, min_work_node]:
+ for x in [test_node, min_work_node]:
x.sync_with_ping()
assert_equal(self.nodes[0].getblockcount(), 2)
- assert_equal(self.nodes[1].getblockcount(), 2)
- assert_equal(self.nodes[2].getblockcount(), 1)
- self.log.info("First height 2 block accepted by node0/node1; correctly rejected by node2")
+ assert_equal(self.nodes[1].getblockcount(), 1)
+ self.log.info("First height 2 block accepted by node0; correctly rejected by node1")
- # 3. Send another block that builds on the original tip.
- blocks_h2f = [] # Blocks at height 2 that fork off the main chain
- for i in range(2):
- blocks_h2f.append(create_block(tips[i], create_coinbase(2), blocks_h2[i].nTime+1))
- blocks_h2f[i].solve()
- test_node.send_message(msg_block(blocks_h2f[0]))
- white_node.send_message(msg_block(blocks_h2f[1]))
+ # 3. Send another block that builds on genesis.
+ block_h1f = create_block(int("0x" + self.nodes[0].getblockhash(0), 0), create_coinbase(1), block_time)
+ block_time += 1
+ block_h1f.solve()
+ test_node.send_message(msg_block(block_h1f))
- for x in [test_node, white_node]:
- x.sync_with_ping()
+ test_node.sync_with_ping()
+ tip_entry_found = False
for x in self.nodes[0].getchaintips():
- if x['hash'] == blocks_h2f[0].hash:
+ if x['hash'] == block_h1f.hash:
assert_equal(x['status'], "headers-only")
+ tip_entry_found = True
+ assert(tip_entry_found)
+ assert_raises_rpc_error(-1, "Block not found on disk", self.nodes[0].getblock, block_h1f.hash)
- for x in self.nodes[1].getchaintips():
- if x['hash'] == blocks_h2f[1].hash:
- assert_equal(x['status'], "valid-headers")
+ # 4. Send another two block that build on the fork.
+ block_h2f = create_block(block_h1f.sha256, create_coinbase(2), block_time)
+ block_time += 1
+ block_h2f.solve()
+ test_node.send_message(msg_block(block_h2f))
- self.log.info("Second height 2 block accepted only from whitelisted peer")
+ test_node.sync_with_ping()
+ # Since the earlier block was not processed by node, the new block
+ # can't be fully validated.
+ tip_entry_found = False
+ for x in self.nodes[0].getchaintips():
+ if x['hash'] == block_h2f.hash:
+ assert_equal(x['status'], "headers-only")
+ tip_entry_found = True
+ assert(tip_entry_found)
- # 4. Now send another block that builds on the forking chain.
- blocks_h3 = []
- for i in range(2):
- blocks_h3.append(create_block(blocks_h2f[i].sha256, create_coinbase(3), blocks_h2f[i].nTime+1))
- blocks_h3[i].solve()
- test_node.send_message(msg_block(blocks_h3[0]))
- white_node.send_message(msg_block(blocks_h3[1]))
+ # But this block should be accepted by node since it has equal work.
+ self.nodes[0].getblock(block_h2f.hash)
+ self.log.info("Second height 2 block accepted, but not reorg'ed to")
- for x in [test_node, white_node]:
- x.sync_with_ping()
- # Since the earlier block was not processed by node0, the new block
+ # 4b. Now send another block that builds on the forking chain.
+ block_h3 = create_block(block_h2f.sha256, create_coinbase(3), block_h2f.nTime+1)
+ block_h3.solve()
+ test_node.send_message(msg_block(block_h3))
+
+ test_node.sync_with_ping()
+ # Since the earlier block was not processed by node, the new block
# can't be fully validated.
+ tip_entry_found = False
for x in self.nodes[0].getchaintips():
- if x['hash'] == blocks_h3[0].hash:
+ if x['hash'] == block_h3.hash:
assert_equal(x['status'], "headers-only")
+ tip_entry_found = True
+ assert(tip_entry_found)
+ self.nodes[0].getblock(block_h3.hash)
+
+ # But this block should be accepted by node since it has more work.
+ self.nodes[0].getblock(block_h3.hash)
+ self.log.info("Unrequested more-work block accepted")
+
+ # 4c. Now mine 288 more blocks and deliver; all should be processed but
+ # the last (height-too-high) on node (as long as its not missing any headers)
+ tip = block_h3
+ all_blocks = []
+ for i in range(288):
+ next_block = create_block(tip.sha256, create_coinbase(i + 4), tip.nTime+1)
+ next_block.solve()
+ all_blocks.append(next_block)
+ tip = next_block
+
+ # Now send the block at height 5 and check that it wasn't accepted (missing header)
+ test_node.send_message(msg_block(all_blocks[1]))
+ test_node.sync_with_ping()
+ assert_raises_rpc_error(-5, "Block not found", self.nodes[0].getblock, all_blocks[1].hash)
+ assert_raises_rpc_error(-5, "Block not found", self.nodes[0].getblockheader, all_blocks[1].hash)
- # But this block should be accepted by node0 since it has more work.
- self.nodes[0].getblock(blocks_h3[0].hash)
- self.log.info("Unrequested more-work block accepted from non-whitelisted peer")
+ # The block at height 5 should be accepted if we provide the missing header, though
+ headers_message = msg_headers()
+ headers_message.headers.append(CBlockHeader(all_blocks[0]))
+ test_node.send_message(headers_message)
+ test_node.send_message(msg_block(all_blocks[1]))
+ test_node.sync_with_ping()
+ self.nodes[0].getblock(all_blocks[1].hash)
- # Node1 should have accepted and reorged.
- assert_equal(self.nodes[1].getblockcount(), 3)
- self.log.info("Successfully reorged to length 3 chain from whitelisted peer")
+ # Now send the blocks in all_blocks
+ for i in range(288):
+ test_node.send_message(msg_block(all_blocks[i]))
+ test_node.sync_with_ping()
- # 4b. Now mine 288 more blocks and deliver; all should be processed but
- # the last (height-too-high) on node0. Node1 should process the tip if
- # we give it the headers chain leading to the tip.
- tips = blocks_h3
- headers_message = msg_headers()
- all_blocks = [] # node0's blocks
- for j in range(2):
- for i in range(288):
- next_block = create_block(tips[j].sha256, create_coinbase(i + 4), tips[j].nTime+1)
- next_block.solve()
- if j==0:
- test_node.send_message(msg_block(next_block))
- all_blocks.append(next_block)
- else:
- headers_message.headers.append(CBlockHeader(next_block))
- tips[j] = next_block
-
- time.sleep(2)
# Blocks 1-287 should be accepted, block 288 should be ignored because it's too far ahead
for x in all_blocks[:-1]:
self.nodes[0].getblock(x.hash)
assert_raises_rpc_error(-1, "Block not found on disk", self.nodes[0].getblock, all_blocks[-1].hash)
- headers_message.headers.pop() # Ensure the last block is unrequested
- white_node.send_message(headers_message) # Send headers leading to tip
- white_node.send_message(msg_block(tips[1])) # Now deliver the tip
- white_node.sync_with_ping()
- self.nodes[1].getblock(tips[1].hash)
- self.log.info("Unrequested block far ahead of tip accepted from whitelisted peer")
-
# 5. Test handling of unrequested block on the node that didn't process
# Should still not be processed (even though it has a child that has more
# work).
- test_node.send_message(msg_block(blocks_h2f[0]))
- # Here, if the sleep is too short, the test could falsely succeed (if the
- # node hasn't processed the block by the time the sleep returns, and then
- # the node processes it and incorrectly advances the tip).
- # But this would be caught later on, when we verify that an inv triggers
- # a getdata request for this block.
+ # The node should have requested the blocks at some point, so
+ # disconnect/reconnect first
+
+ self.nodes[0].disconnect_p2p()
+ test_node = self.nodes[0].add_p2p_connection(NodeConnCB())
+
+ test_node.wait_for_verack()
+ test_node.send_message(msg_block(block_h1f))
+
test_node.sync_with_ping()
assert_equal(self.nodes[0].getblockcount(), 2)
self.log.info("Unrequested block that would complete more-work chain was ignored")
@@ -220,29 +222,98 @@ class AcceptBlockTest(BitcoinTestFramework):
with mininode_lock:
# Clear state so we can check the getdata request
test_node.last_message.pop("getdata", None)
- test_node.send_message(msg_inv([CInv(2, blocks_h3[0].sha256)]))
+ test_node.send_message(msg_inv([CInv(2, block_h3.sha256)]))
test_node.sync_with_ping()
with mininode_lock:
getdata = test_node.last_message["getdata"]
# Check that the getdata includes the right block
- assert_equal(getdata.inv[0].hash, blocks_h2f[0].sha256)
+ assert_equal(getdata.inv[0].hash, block_h1f.sha256)
self.log.info("Inv at tip triggered getdata for unprocessed block")
# 7. Send the missing block for the third time (now it is requested)
- test_node.send_message(msg_block(blocks_h2f[0]))
+ test_node.send_message(msg_block(block_h1f))
test_node.sync_with_ping()
assert_equal(self.nodes[0].getblockcount(), 290)
+ self.nodes[0].getblock(all_blocks[286].hash)
+ assert_equal(self.nodes[0].getbestblockhash(), all_blocks[286].hash)
+ assert_raises_rpc_error(-1, "Block not found on disk", self.nodes[0].getblock, all_blocks[287].hash)
self.log.info("Successfully reorged to longer chain from non-whitelisted peer")
- # 8. Connect node2 to node0 and ensure it is able to sync
- connect_nodes(self.nodes[0], 2)
- sync_blocks([self.nodes[0], self.nodes[2]])
- self.log.info("Successfully synced nodes 2 and 0")
+ # 8. Create a chain which is invalid at a height longer than the
+ # current chain, but which has more blocks on top of that
+ block_289f = create_block(all_blocks[284].sha256, create_coinbase(289), all_blocks[284].nTime+1)
+ block_289f.solve()
+ block_290f = create_block(block_289f.sha256, create_coinbase(290), block_289f.nTime+1)
+ block_290f.solve()
+ block_291 = create_block(block_290f.sha256, create_coinbase(291), block_290f.nTime+1)
+ # block_291 spends a coinbase below maturity!
+ block_291.vtx.append(create_transaction(block_290f.vtx[0], 0, b"42", 1))
+ block_291.hashMerkleRoot = block_291.calc_merkle_root()
+ block_291.solve()
+ block_292 = create_block(block_291.sha256, create_coinbase(292), block_291.nTime+1)
+ block_292.solve()
+
+ # Now send all the headers on the chain and enough blocks to trigger reorg
+ headers_message = msg_headers()
+ headers_message.headers.append(CBlockHeader(block_289f))
+ headers_message.headers.append(CBlockHeader(block_290f))
+ headers_message.headers.append(CBlockHeader(block_291))
+ headers_message.headers.append(CBlockHeader(block_292))
+ test_node.send_message(headers_message)
+
+ test_node.sync_with_ping()
+ tip_entry_found = False
+ for x in self.nodes[0].getchaintips():
+ if x['hash'] == block_292.hash:
+ assert_equal(x['status'], "headers-only")
+ tip_entry_found = True
+ assert(tip_entry_found)
+ assert_raises_rpc_error(-1, "Block not found on disk", self.nodes[0].getblock, block_292.hash)
+
+ test_node.send_message(msg_block(block_289f))
+ test_node.send_message(msg_block(block_290f))
- [ c.disconnect_node() for c in connections ]
+ test_node.sync_with_ping()
+ self.nodes[0].getblock(block_289f.hash)
+ self.nodes[0].getblock(block_290f.hash)
+
+ test_node.send_message(msg_block(block_291))
+
+ # At this point we've sent an obviously-bogus block, wait for full processing
+ # without assuming whether we will be disconnected or not
+ try:
+ # Only wait a short while so the test doesn't take forever if we do get
+ # disconnected
+ test_node.sync_with_ping(timeout=1)
+ except AssertionError:
+ test_node.wait_for_disconnect()
+
+ self.nodes[0].disconnect_p2p()
+ test_node = self.nodes[0].add_p2p_connection(NodeConnCB())
+
+ NetworkThread().start() # Start up network handling in another thread
+ test_node.wait_for_verack()
+
+ # We should have failed reorg and switched back to 290 (but have block 291)
+ assert_equal(self.nodes[0].getblockcount(), 290)
+ assert_equal(self.nodes[0].getbestblockhash(), all_blocks[286].hash)
+ assert_equal(self.nodes[0].getblock(block_291.hash)["confirmations"], -1)
+
+ # Now send a new header on the invalid chain, indicating we're forked off, and expect to get disconnected
+ block_293 = create_block(block_292.sha256, create_coinbase(293), block_292.nTime+1)
+ block_293.solve()
+ headers_message = msg_headers()
+ headers_message.headers.append(CBlockHeader(block_293))
+ test_node.send_message(headers_message)
+ test_node.wait_for_disconnect()
+
+ # 9. Connect node1 to node0 and ensure it is able to sync
+ connect_nodes(self.nodes[0], 1)
+ sync_blocks([self.nodes[0], self.nodes[1]])
+ self.log.info("Successfully synced nodes 1 and 0")
if __name__ == '__main__':
AcceptBlockTest().main()
diff --git a/test/functional/p2p-compactblocks.py b/test/functional/p2p-compactblocks.py
index 94513d3f43..d2c4d39305 100755
--- a/test/functional/p2p-compactblocks.py
+++ b/test/functional/p2p-compactblocks.py
@@ -93,7 +93,9 @@ class CompactBlocksTest(BitcoinTestFramework):
self.setup_clean_chain = True
# Node0 = pre-segwit, node1 = segwit-aware
self.num_nodes = 2
- self.extra_args = [["-vbparams=segwit:0:0"], ["-txindex"]]
+ # This test was written assuming SegWit is activated using BIP9 at height 432 (3x confirmation window).
+ # TODO: Rewrite this test to support SegWit being always active.
+ self.extra_args = [["-vbparams=segwit:0:0"], ["-vbparams=segwit:0:999999999999", "-txindex"]]
self.utxos = []
def build_block_on_tip(self, node, segwit=False):
@@ -786,23 +788,12 @@ class CompactBlocksTest(BitcoinTestFramework):
def run_test(self):
# Setup the p2p connections and start up the network thread.
- self.test_node = TestNode()
- self.segwit_node = TestNode()
- self.old_node = TestNode() # version 1 peer <--> segwit node
-
- connections = []
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], self.test_node))
- connections.append(NodeConn('127.0.0.1', p2p_port(1), self.nodes[1],
- self.segwit_node, services=NODE_NETWORK|NODE_WITNESS))
- connections.append(NodeConn('127.0.0.1', p2p_port(1), self.nodes[1],
- self.old_node, services=NODE_NETWORK))
- self.test_node.add_connection(connections[0])
- self.segwit_node.add_connection(connections[1])
- self.old_node.add_connection(connections[2])
+ self.test_node = self.nodes[0].add_p2p_connection(TestNode())
+ self.segwit_node = self.nodes[1].add_p2p_connection(TestNode(), services=NODE_NETWORK|NODE_WITNESS)
+ self.old_node = self.nodes[1].add_p2p_connection(TestNode(), services=NODE_NETWORK)
NetworkThread().start() # Start up network handling in another thread
- # Test logic begins here
self.test_node.wait_for_verack()
# We will need UTXOs to construct transactions in later tests.
diff --git a/test/functional/p2p-feefilter.py b/test/functional/p2p-feefilter.py
index 8c92365ced..624278df40 100755
--- a/test/functional/p2p-feefilter.py
+++ b/test/functional/p2p-feefilter.py
@@ -48,25 +48,23 @@ class FeeFilterTest(BitcoinTestFramework):
sync_blocks(self.nodes)
# Setup the p2p connections and start up the network thread.
- test_node = TestNode()
- connection = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_node)
- test_node.add_connection(connection)
+ self.nodes[0].add_p2p_connection(TestNode())
NetworkThread().start()
- test_node.wait_for_verack()
+ self.nodes[0].p2p.wait_for_verack()
# Test that invs are received for all txs at feerate of 20 sat/byte
node1.settxfee(Decimal("0.00020000"))
txids = [node1.sendtoaddress(node1.getnewaddress(), 1) for x in range(3)]
- assert(allInvsMatch(txids, test_node))
- test_node.clear_invs()
+ assert(allInvsMatch(txids, self.nodes[0].p2p))
+ self.nodes[0].p2p.clear_invs()
# Set a filter of 15 sat/byte
- test_node.send_and_ping(msg_feefilter(15000))
+ self.nodes[0].p2p.send_and_ping(msg_feefilter(15000))
# Test that txs are still being received (paying 20 sat/byte)
txids = [node1.sendtoaddress(node1.getnewaddress(), 1) for x in range(3)]
- assert(allInvsMatch(txids, test_node))
- test_node.clear_invs()
+ assert(allInvsMatch(txids, self.nodes[0].p2p))
+ self.nodes[0].p2p.clear_invs()
# Change tx fee rate to 10 sat/byte and test they are no longer received
node1.settxfee(Decimal("0.00010000"))
@@ -82,14 +80,14 @@ class FeeFilterTest(BitcoinTestFramework):
# as well.
node0.settxfee(Decimal("0.00020000"))
txids = [node0.sendtoaddress(node0.getnewaddress(), 1)]
- assert(allInvsMatch(txids, test_node))
- test_node.clear_invs()
+ assert(allInvsMatch(txids, self.nodes[0].p2p))
+ self.nodes[0].p2p.clear_invs()
# Remove fee filter and check that txs are received again
- test_node.send_and_ping(msg_feefilter(0))
+ self.nodes[0].p2p.send_and_ping(msg_feefilter(0))
txids = [node1.sendtoaddress(node1.getnewaddress(), 1) for x in range(3)]
- assert(allInvsMatch(txids, test_node))
- test_node.clear_invs()
+ assert(allInvsMatch(txids, self.nodes[0].p2p))
+ self.nodes[0].p2p.clear_invs()
if __name__ == '__main__':
FeeFilterTest().main()
diff --git a/test/functional/p2p-fingerprint.py b/test/functional/p2p-fingerprint.py
index fe60c6cd46..4b6446fc5b 100755
--- a/test/functional/p2p-fingerprint.py
+++ b/test/functional/p2p-fingerprint.py
@@ -14,7 +14,6 @@ from test_framework.blocktools import (create_block, create_coinbase)
from test_framework.mininode import (
CInv,
NetworkThread,
- NodeConn,
NodeConnCB,
msg_headers,
msg_block,
@@ -77,11 +76,7 @@ class P2PFingerprintTest(BitcoinTestFramework):
# This does not currently test that stale blocks timestamped within the
# last month but that have over a month's worth of work are also withheld.
def run_test(self):
- node0 = NodeConnCB()
-
- connections = []
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node0))
- node0.add_connection(connections[0])
+ node0 = self.nodes[0].add_p2p_connection(NodeConnCB())
NetworkThread().start()
node0.wait_for_verack()
diff --git a/test/functional/p2p-leaktests.py b/test/functional/p2p-leaktests.py
index 1dc8f72cd6..a6e47b5df6 100755
--- a/test/functional/p2p-leaktests.py
+++ b/test/functional/p2p-leaktests.py
@@ -39,7 +39,6 @@ class CLazyNode(NodeConnCB):
def on_reject(self, conn, message): self.bad_message(message)
def on_inv(self, conn, message): self.bad_message(message)
def on_addr(self, conn, message): self.bad_message(message)
- def on_alert(self, conn, message): self.bad_message(message)
def on_getdata(self, conn, message): self.bad_message(message)
def on_getblocks(self, conn, message): self.bad_message(message)
def on_tx(self, conn, message): self.bad_message(message)
@@ -97,24 +96,13 @@ class P2PLeakTest(BitcoinTestFramework):
self.extra_args = [['-banscore='+str(banscore)]]
def run_test(self):
- no_version_bannode = CNodeNoVersionBan()
- no_version_idlenode = CNodeNoVersionIdle()
- no_verack_idlenode = CNodeNoVerackIdle()
- unsupported_service_bit5_node = CLazyNode()
- unsupported_service_bit7_node = CLazyNode()
-
self.nodes[0].setmocktime(1501545600) # August 1st 2017
- connections = []
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], no_version_bannode, send_version=False))
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], no_version_idlenode, send_version=False))
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], no_verack_idlenode))
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], unsupported_service_bit5_node, services=NODE_NETWORK|NODE_UNSUPPORTED_SERVICE_BIT_5))
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], unsupported_service_bit7_node, services=NODE_NETWORK|NODE_UNSUPPORTED_SERVICE_BIT_7))
- no_version_bannode.add_connection(connections[0])
- no_version_idlenode.add_connection(connections[1])
- no_verack_idlenode.add_connection(connections[2])
- unsupported_service_bit5_node.add_connection(connections[3])
- unsupported_service_bit7_node.add_connection(connections[4])
+
+ no_version_bannode = self.nodes[0].add_p2p_connection(CNodeNoVersionBan(), send_version=False)
+ no_version_idlenode = self.nodes[0].add_p2p_connection(CNodeNoVersionIdle(), send_version=False)
+ no_verack_idlenode = self.nodes[0].add_p2p_connection(CNodeNoVerackIdle())
+ unsupported_service_bit5_node = self.nodes[0].add_p2p_connection(CLazyNode(), services=NODE_NETWORK|NODE_UNSUPPORTED_SERVICE_BIT_5)
+ unsupported_service_bit7_node = self.nodes[0].add_p2p_connection(CLazyNode(), services=NODE_NETWORK|NODE_UNSUPPORTED_SERVICE_BIT_7)
NetworkThread().start() # Start up network handling in another thread
@@ -137,7 +125,8 @@ class P2PLeakTest(BitcoinTestFramework):
assert not unsupported_service_bit5_node.connected
assert not unsupported_service_bit7_node.connected
- [conn.disconnect_node() for conn in connections]
+ for _ in range(5):
+ self.nodes[0].disconnect_p2p()
# Wait until all connections are closed
wait_until(lambda: len(self.nodes[0].getpeerinfo()) == 0)
@@ -152,13 +141,8 @@ class P2PLeakTest(BitcoinTestFramework):
self.log.info("Service bits 5 and 7 are allowed after August 1st 2018")
self.nodes[0].setmocktime(1533168000) # August 2nd 2018
- allowed_service_bit5_node = NodeConnCB()
- allowed_service_bit7_node = NodeConnCB()
-
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], allowed_service_bit5_node, services=NODE_NETWORK|NODE_UNSUPPORTED_SERVICE_BIT_5))
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], allowed_service_bit7_node, services=NODE_NETWORK|NODE_UNSUPPORTED_SERVICE_BIT_7))
- allowed_service_bit5_node.add_connection(connections[5])
- allowed_service_bit7_node.add_connection(connections[6])
+ allowed_service_bit5_node = self.nodes[0].add_p2p_connection(NodeConnCB(), services=NODE_NETWORK|NODE_UNSUPPORTED_SERVICE_BIT_5)
+ allowed_service_bit7_node = self.nodes[0].add_p2p_connection(NodeConnCB(), services=NODE_NETWORK|NODE_UNSUPPORTED_SERVICE_BIT_7)
NetworkThread().start() # Network thread stopped when all previous NodeConnCBs disconnected. Restart it
diff --git a/test/functional/p2p-mempool.py b/test/functional/p2p-mempool.py
index 40fcde2605..be467c4223 100755
--- a/test/functional/p2p-mempool.py
+++ b/test/functional/p2p-mempool.py
@@ -19,16 +19,14 @@ class P2PMempoolTests(BitcoinTestFramework):
self.extra_args = [["-peerbloomfilters=0"]]
def run_test(self):
- #connect a mininode
- aTestNode = NodeConnCB()
- node = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], aTestNode)
- aTestNode.add_connection(node)
+ # Add a p2p connection
+ self.nodes[0].add_p2p_connection(NodeConnCB())
NetworkThread().start()
- aTestNode.wait_for_verack()
+ self.nodes[0].p2p.wait_for_verack()
#request mempool
- aTestNode.send_message(msg_mempool())
- aTestNode.wait_for_disconnect()
+ self.nodes[0].p2p.send_message(msg_mempool())
+ self.nodes[0].p2p.wait_for_disconnect()
#mininode must be disconnected at this point
assert_equal(len(self.nodes[0].getpeerinfo()), 0)
diff --git a/test/functional/p2p-segwit.py b/test/functional/p2p-segwit.py
index f803367668..22da7f2db1 100755
--- a/test/functional/p2p-segwit.py
+++ b/test/functional/p2p-segwit.py
@@ -111,7 +111,8 @@ class SegWitTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 3
- self.extra_args = [["-whitelist=127.0.0.1"], ["-whitelist=127.0.0.1", "-acceptnonstdtxn=0"], ["-whitelist=127.0.0.1", "-vbparams=segwit:0:0"]]
+ # This test tests SegWit both pre and post-activation, so use the normal BIP9 activation.
+ self.extra_args = [["-whitelist=127.0.0.1", "-vbparams=segwit:0:999999999999"], ["-whitelist=127.0.0.1", "-acceptnonstdtxn=0", "-vbparams=segwit:0:999999999999"], ["-whitelist=127.0.0.1", "-vbparams=segwit:0:0"]]
def setup_network(self):
self.setup_nodes()
@@ -1493,7 +1494,7 @@ class SegWitTest(BitcoinTestFramework):
# Restart with the new binary
self.stop_node(node_id)
- self.start_node(node_id, extra_args=[])
+ self.start_node(node_id, extra_args=["-vbparams=segwit:0:999999999999"])
connect_nodes(self.nodes[0], node_id)
sync_blocks(self.nodes)
@@ -1867,19 +1868,12 @@ class SegWitTest(BitcoinTestFramework):
def run_test(self):
# Setup the p2p connections and start up the network thread.
- self.test_node = TestNode() # sets NODE_WITNESS|NODE_NETWORK
- self.old_node = TestNode() # only NODE_NETWORK
- self.std_node = TestNode() # for testing node1 (fRequireStandard=true)
-
- self.p2p_connections = [self.test_node, self.old_node]
-
- self.connections = []
- self.connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], self.test_node, services=NODE_NETWORK|NODE_WITNESS))
- self.connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], self.old_node, services=NODE_NETWORK))
- self.connections.append(NodeConn('127.0.0.1', p2p_port(1), self.nodes[1], self.std_node, services=NODE_NETWORK|NODE_WITNESS))
- self.test_node.add_connection(self.connections[0])
- self.old_node.add_connection(self.connections[1])
- self.std_node.add_connection(self.connections[2])
+ # self.test_node sets NODE_WITNESS|NODE_NETWORK
+ self.test_node = self.nodes[0].add_p2p_connection(TestNode(), services=NODE_NETWORK|NODE_WITNESS)
+ # self.old_node sets only NODE_NETWORK
+ self.old_node = self.nodes[0].add_p2p_connection(TestNode(), services=NODE_NETWORK)
+ # self.std_node is for testing node1 (fRequireStandard=true)
+ self.std_node = self.nodes[1].add_p2p_connection(TestNode(), services=NODE_NETWORK|NODE_WITNESS)
NetworkThread().start() # Start up network handling in another thread
diff --git a/test/functional/p2p-timeouts.py b/test/functional/p2p-timeouts.py
index 51d4769efc..14a3bf48fb 100755
--- a/test/functional/p2p-timeouts.py
+++ b/test/functional/p2p-timeouts.py
@@ -39,46 +39,37 @@ class TimeoutsTest(BitcoinTestFramework):
def run_test(self):
# Setup the p2p connections and start up the network thread.
- self.no_verack_node = TestNode() # never send verack
- self.no_version_node = TestNode() # never send version (just ping)
- self.no_send_node = TestNode() # never send anything
-
- connections = []
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], self.no_verack_node))
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], self.no_version_node, send_version=False))
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], self.no_send_node, send_version=False))
- self.no_verack_node.add_connection(connections[0])
- self.no_version_node.add_connection(connections[1])
- self.no_send_node.add_connection(connections[2])
+ no_verack_node = self.nodes[0].add_p2p_connection(TestNode())
+ no_version_node = self.nodes[0].add_p2p_connection(TestNode(), send_version=False)
+ no_send_node = self.nodes[0].add_p2p_connection(TestNode(), send_version=False)
NetworkThread().start() # Start up network handling in another thread
sleep(1)
- assert(self.no_verack_node.connected)
- assert(self.no_version_node.connected)
- assert(self.no_send_node.connected)
+ assert no_verack_node.connected
+ assert no_version_node.connected
+ assert no_send_node.connected
- ping_msg = msg_ping()
- connections[0].send_message(ping_msg)
- connections[1].send_message(ping_msg)
+ no_verack_node.send_message(msg_ping())
+ no_version_node.send_message(msg_ping())
sleep(30)
- assert "version" in self.no_verack_node.last_message
+ assert "version" in no_verack_node.last_message
- assert(self.no_verack_node.connected)
- assert(self.no_version_node.connected)
- assert(self.no_send_node.connected)
+ assert no_verack_node.connected
+ assert no_version_node.connected
+ assert no_send_node.connected
- connections[0].send_message(ping_msg)
- connections[1].send_message(ping_msg)
+ no_verack_node.send_message(msg_ping())
+ no_version_node.send_message(msg_ping())
sleep(31)
- assert(not self.no_verack_node.connected)
- assert(not self.no_version_node.connected)
- assert(not self.no_send_node.connected)
+ assert not no_verack_node.connected
+ assert not no_version_node.connected
+ assert not no_send_node.connected
if __name__ == '__main__':
TimeoutsTest().main()
diff --git a/test/functional/p2p-versionbits-warning.py b/test/functional/p2p-versionbits-warning.py
index f9bef2580a..464ca5a312 100755
--- a/test/functional/p2p-versionbits-warning.py
+++ b/test/functional/p2p-versionbits-warning.py
@@ -64,16 +64,12 @@ class VersionBitsWarningTest(BitcoinTestFramework):
def run_test(self):
# Setup the p2p connection and start up the network thread.
- test_node = TestNode()
-
- connections = []
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_node))
- test_node.add_connection(connections[0])
+ self.nodes[0].add_p2p_connection(TestNode())
NetworkThread().start() # Start up network handling in another thread
# Test logic begins here
- test_node.wait_for_verack()
+ self.nodes[0].p2p.wait_for_verack()
# 1. Have the node mine one period worth of blocks
self.nodes[0].generate(VB_PERIOD)
@@ -81,7 +77,7 @@ class VersionBitsWarningTest(BitcoinTestFramework):
# 2. Now build one period of blocks on the tip, with < VB_THRESHOLD
# blocks signaling some unknown bit.
nVersion = VB_TOP_BITS | (1<<VB_UNKNOWN_BIT)
- self.send_blocks_with_version(test_node, VB_THRESHOLD-1, nVersion)
+ self.send_blocks_with_version(self.nodes[0].p2p, VB_THRESHOLD-1, nVersion)
# Fill rest of period with regular version blocks
self.nodes[0].generate(VB_PERIOD - VB_THRESHOLD + 1)
@@ -92,7 +88,7 @@ class VersionBitsWarningTest(BitcoinTestFramework):
# 3. Now build one period of blocks with >= VB_THRESHOLD blocks signaling
# some unknown bit
- self.send_blocks_with_version(test_node, VB_THRESHOLD, nVersion)
+ self.send_blocks_with_version(self.nodes[0].p2p, VB_THRESHOLD, nVersion)
self.nodes[0].generate(VB_PERIOD - VB_THRESHOLD)
# Might not get a versionbits-related alert yet, as we should
# have gotten a different alert due to more than 51/100 blocks
diff --git a/test/functional/segwit.py b/test/functional/segwit.py
index 6ecade7cb6..338fa1bc52 100755
--- a/test/functional/segwit.py
+++ b/test/functional/segwit.py
@@ -77,9 +77,10 @@ class SegWitTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 3
- self.extra_args = [["-walletprematurewitness", "-rpcserialversion=0"],
- ["-blockversion=4", "-promiscuousmempoolflags=517", "-prematurewitness", "-walletprematurewitness", "-rpcserialversion=1"],
- ["-blockversion=536870915", "-promiscuousmempoolflags=517", "-prematurewitness", "-walletprematurewitness"]]
+ # This test tests SegWit both pre and post-activation, so use the normal BIP9 activation.
+ self.extra_args = [["-walletprematurewitness", "-rpcserialversion=0", "-vbparams=segwit:0:999999999999"],
+ ["-blockversion=4", "-promiscuousmempoolflags=517", "-prematurewitness", "-walletprematurewitness", "-rpcserialversion=1", "-vbparams=segwit:0:999999999999"],
+ ["-blockversion=536870915", "-promiscuousmempoolflags=517", "-prematurewitness", "-walletprematurewitness", "-vbparams=segwit:0:999999999999"]]
def setup_network(self):
super().setup_network()
diff --git a/test/functional/sendheaders.py b/test/functional/sendheaders.py
index 159dfd3004..55bb80ea00 100755
--- a/test/functional/sendheaders.py
+++ b/test/functional/sendheaders.py
@@ -192,7 +192,7 @@ class SendHeadersTest(BitcoinTestFramework):
# mine count blocks and return the new tip
def mine_blocks(self, count):
# Clear out last block announcement from each p2p listener
- [ x.clear_last_announcement() for x in self.p2p_connections ]
+ [x.clear_last_announcement() for x in self.nodes[0].p2ps]
self.nodes[0].generate(count)
return int(self.nodes[0].getbestblockhash(), 16)
@@ -204,7 +204,7 @@ class SendHeadersTest(BitcoinTestFramework):
def mine_reorg(self, length):
self.nodes[0].generate(length) # make sure all invalidated blocks are node0's
sync_blocks(self.nodes, wait=0.1)
- for x in self.p2p_connections:
+ for x in self.nodes[0].p2ps:
x.wait_for_block_announcement(int(self.nodes[0].getbestblockhash(), 16))
x.clear_last_announcement()
@@ -217,18 +217,10 @@ class SendHeadersTest(BitcoinTestFramework):
def run_test(self):
# Setup the p2p connections and start up the network thread.
- inv_node = TestNode()
- test_node = TestNode()
-
- self.p2p_connections = [inv_node, test_node]
-
- connections = []
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], inv_node))
+ inv_node = self.nodes[0].add_p2p_connection(TestNode())
# Set nServices to 0 for test_node, so no block download will occur outside of
# direct fetching
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_node, services=0))
- inv_node.add_connection(connections[0])
- test_node.add_connection(connections[1])
+ test_node = self.nodes[0].add_p2p_connection(TestNode(), services=NODE_WITNESS)
NetworkThread().start() # Start up network handling in another thread
diff --git a/test/functional/test_framework/blockstore.py b/test/functional/test_framework/blockstore.py
index ad04722488..051c57a6c7 100644
--- a/test/functional/test_framework/blockstore.py
+++ b/test/functional/test_framework/blockstore.py
@@ -100,7 +100,7 @@ class BlockStore():
def get_blocks(self, inv):
responses = []
for i in inv:
- if (i.type == 2): # MSG_BLOCK
+ if (i.type == 2 or i.type == (2 | (1 << 30))): # MSG_BLOCK or MSG_WITNESS_BLOCK
data = self.get(i.hash)
if data is not None:
# Use msg_generic to avoid re-serialization
@@ -153,7 +153,7 @@ class TxStore():
def get_transactions(self, inv):
responses = []
for i in inv:
- if (i.type == 1): # MSG_TX
+ if (i.type == 1 or i.type == (1 | (1 << 30))): # MSG_TX or MSG_WITNESS_TX
tx = self.get(i.hash)
if tx is not None:
responses.append(msg_generic(b"tx", tx))
diff --git a/test/functional/test_framework/comptool.py b/test/functional/test_framework/comptool.py
index b0417e02d8..39a81a267f 100755
--- a/test/functional/test_framework/comptool.py
+++ b/test/functional/test_framework/comptool.py
@@ -80,9 +80,9 @@ class TestNode(NodeConnCB):
[conn.send_message(r) for r in self.tx_store.get_transactions(message.inv)]
for i in message.inv:
- if i.type == 1:
+ if i.type == 1 or i.type == 1 | (1 << 30): # MSG_TX or MSG_WITNESS_TX
self.tx_request_map[i.hash] = True
- elif i.type == 2:
+ elif i.type == 2 or i.type == 2 | (1 << 30): # MSG_BLOCK or MSG_WITNESS_BLOCK
self.block_request_map[i.hash] = True
def on_inv(self, conn, message):
diff --git a/test/functional/test_framework/mininode.py b/test/functional/test_framework/mininode.py
index 345ecfe76d..8fbc63fba4 100755
--- a/test/functional/test_framework/mininode.py
+++ b/test/functional/test_framework/mininode.py
@@ -37,7 +37,7 @@ from threading import RLock, Thread
from test_framework.siphash import siphash256
from test_framework.util import hex_str_to_bytes, bytes_to_hex_str, wait_until
-BIP0031_VERSION = 60000
+MIN_VERSION_SUPPORTED = 60001
MY_VERSION = 70014 # past bip-31 for ping/pong
MY_SUBVERSION = b"/python-mininode-tester:0.0.3/"
MY_RELAY = 1 # from version 70001 onwards, fRelay should be appended to version messages (BIP37)
@@ -666,81 +666,6 @@ class CBlock(CBlockHeader):
time.ctime(self.nTime), self.nBits, self.nNonce, repr(self.vtx))
-class CUnsignedAlert():
- def __init__(self):
- self.nVersion = 1
- self.nRelayUntil = 0
- self.nExpiration = 0
- self.nID = 0
- self.nCancel = 0
- self.setCancel = []
- self.nMinVer = 0
- self.nMaxVer = 0
- self.setSubVer = []
- self.nPriority = 0
- self.strComment = b""
- self.strStatusBar = b""
- self.strReserved = b""
-
- def deserialize(self, f):
- self.nVersion = struct.unpack("<i", f.read(4))[0]
- self.nRelayUntil = struct.unpack("<q", f.read(8))[0]
- self.nExpiration = struct.unpack("<q", f.read(8))[0]
- self.nID = struct.unpack("<i", f.read(4))[0]
- self.nCancel = struct.unpack("<i", f.read(4))[0]
- self.setCancel = deser_int_vector(f)
- self.nMinVer = struct.unpack("<i", f.read(4))[0]
- self.nMaxVer = struct.unpack("<i", f.read(4))[0]
- self.setSubVer = deser_string_vector(f)
- self.nPriority = struct.unpack("<i", f.read(4))[0]
- self.strComment = deser_string(f)
- self.strStatusBar = deser_string(f)
- self.strReserved = deser_string(f)
-
- def serialize(self):
- r = b""
- r += struct.pack("<i", self.nVersion)
- r += struct.pack("<q", self.nRelayUntil)
- r += struct.pack("<q", self.nExpiration)
- r += struct.pack("<i", self.nID)
- r += struct.pack("<i", self.nCancel)
- r += ser_int_vector(self.setCancel)
- r += struct.pack("<i", self.nMinVer)
- r += struct.pack("<i", self.nMaxVer)
- r += ser_string_vector(self.setSubVer)
- r += struct.pack("<i", self.nPriority)
- r += ser_string(self.strComment)
- r += ser_string(self.strStatusBar)
- r += ser_string(self.strReserved)
- return r
-
- def __repr__(self):
- return "CUnsignedAlert(nVersion %d, nRelayUntil %d, nExpiration %d, nID %d, nCancel %d, nMinVer %d, nMaxVer %d, nPriority %d, strComment %s, strStatusBar %s, strReserved %s)" \
- % (self.nVersion, self.nRelayUntil, self.nExpiration, self.nID,
- self.nCancel, self.nMinVer, self.nMaxVer, self.nPriority,
- self.strComment, self.strStatusBar, self.strReserved)
-
-
-class CAlert():
- def __init__(self):
- self.vchMsg = b""
- self.vchSig = b""
-
- def deserialize(self, f):
- self.vchMsg = deser_string(f)
- self.vchSig = deser_string(f)
-
- def serialize(self):
- r = b""
- r += ser_string(self.vchMsg)
- r += ser_string(self.vchSig)
- return r
-
- def __repr__(self):
- return "CAlert(vchMsg.sz %d, vchSig.sz %d)" \
- % (len(self.vchMsg), len(self.vchSig))
-
-
class PrefilledTransaction():
def __init__(self, index=0, tx = None):
self.index = index
@@ -949,7 +874,7 @@ class msg_version():
def __init__(self):
self.nVersion = MY_VERSION
- self.nServices = 1
+ self.nServices = NODE_NETWORK | NODE_WITNESS
self.nTime = int(time.time())
self.addrTo = CAddress()
self.addrFrom = CAddress()
@@ -1044,25 +969,6 @@ class msg_addr():
return "msg_addr(addrs=%s)" % (repr(self.addrs))
-class msg_alert():
- command = b"alert"
-
- def __init__(self):
- self.alert = CAlert()
-
- def deserialize(self, f):
- self.alert = CAlert()
- self.alert.deserialize(f)
-
- def serialize(self):
- r = b""
- r += self.alert.serialize()
- return r
-
- def __repr__(self):
- return "msg_alert(alert=%s)" % (repr(self.alert), )
-
-
class msg_inv():
command = b"inv"
@@ -1195,22 +1101,6 @@ class msg_getaddr():
return "msg_getaddr()"
-class msg_ping_prebip31():
- command = b"ping"
-
- def __init__(self):
- pass
-
- def deserialize(self, f):
- pass
-
- def serialize(self):
- return b""
-
- def __repr__(self):
- return "msg_ping() (pre-bip31)"
-
-
class msg_ping():
command = b"ping"
@@ -1458,9 +1348,7 @@ class NodeConnCB():
"""Callback and helper functions for P2P connection to a bitcoind node.
Individual testcases should subclass this and override the on_* methods
- if they want to alter message handling behaviour.
- """
-
+ if they want to alter message handling behaviour."""
def __init__(self):
# Track whether we have a P2P connection open to the node
self.connected = False
@@ -1474,25 +1362,13 @@ class NodeConnCB():
# A count of the number of ping messages we've sent to the node
self.ping_counter = 1
- # deliver_sleep_time is helpful for debugging race conditions in p2p
- # tests; it causes message delivery to sleep for the specified time
- # before acquiring the global lock and delivering the next message.
- self.deliver_sleep_time = None
-
# Message receiving methods
def deliver(self, conn, message):
"""Receive message and dispatch message to appropriate callback.
We keep a count of how many of each message type has been received
- and the most recent message of each type.
-
- Optionally waits for deliver_sleep_time before dispatching message.
- """
-
- deliver_sleep = self.get_deliver_sleep_time()
- if deliver_sleep is not None:
- time.sleep(deliver_sleep)
+ and the most recent message of each type."""
with mininode_lock:
try:
command = message.command.decode('ascii')
@@ -1504,10 +1380,6 @@ class NodeConnCB():
sys.exc_info()[0]))
raise
- def get_deliver_sleep_time(self):
- with mininode_lock:
- return self.deliver_sleep_time
-
# Callback methods. Can be overridden by subclasses in individual test
# cases to provide custom message handling behaviour.
@@ -1519,7 +1391,6 @@ class NodeConnCB():
self.connection = None
def on_addr(self, conn, message): pass
- def on_alert(self, conn, message): pass
def on_block(self, conn, message): pass
def on_blocktxn(self, conn, message): pass
def on_cmpctblock(self, conn, message): pass
@@ -1546,19 +1417,15 @@ class NodeConnCB():
conn.send_message(want)
def on_ping(self, conn, message):
- if conn.ver_send > BIP0031_VERSION:
- conn.send_message(msg_pong(message.nonce))
+ conn.send_message(msg_pong(message.nonce))
def on_verack(self, conn, message):
conn.ver_recv = conn.ver_send
self.verack_received = True
def on_version(self, conn, message):
- if message.nVersion >= 209:
- conn.send_message(msg_verack())
- conn.ver_send = min(MY_VERSION, message.nVersion)
- if message.nVersion < 209:
- conn.ver_recv = conn.ver_send
+ assert message.nVersion >= MIN_VERSION_SUPPORTED, "Version {} received. Test framework only supports versions greater than {}".format(message.nVersion, MIN_VERSION_SUPPORTED)
+ conn.send_message(msg_verack())
conn.nServices = message.nServices
# Connection helper methods
@@ -1616,14 +1483,14 @@ class NodeConnCB():
wait_until(test_function, timeout=timeout, lock=mininode_lock)
self.ping_counter += 1
-# The actual NodeConn class
-# This class provides an interface for a p2p connection to a specified node
class NodeConn(asyncore.dispatcher):
+ """The actual NodeConn class
+
+ This class provides an interface for a p2p connection to a specified node."""
messagemap = {
b"version": msg_version,
b"verack": msg_verack,
b"addr": msg_addr,
- b"alert": msg_alert,
b"inv": msg_inv,
b"getdata": msg_getdata,
b"getblocks": msg_getblocks,
@@ -1649,7 +1516,7 @@ class NodeConn(asyncore.dispatcher):
"regtest": b"\xfa\xbf\xb5\xda", # regtest
}
- def __init__(self, dstaddr, dstport, rpc, callback, net="regtest", services=NODE_NETWORK, send_version=True):
+ def __init__(self, dstaddr, dstport, rpc, callback, net="regtest", services=NODE_NETWORK|NODE_WITNESS, send_version=True):
asyncore.dispatcher.__init__(self, map=mininode_socket_map)
self.dstaddr = dstaddr
self.dstport = dstport
@@ -1740,40 +1607,27 @@ class NodeConn(asyncore.dispatcher):
return
if self.recvbuf[:4] != self.MAGIC_BYTES[self.network]:
raise ValueError("got garbage %s" % repr(self.recvbuf))
- if self.ver_recv < 209:
- if len(self.recvbuf) < 4 + 12 + 4:
- return
- command = self.recvbuf[4:4+12].split(b"\x00", 1)[0]
- msglen = struct.unpack("<i", self.recvbuf[4+12:4+12+4])[0]
- checksum = None
- if len(self.recvbuf) < 4 + 12 + 4 + msglen:
- return
- msg = self.recvbuf[4+12+4:4+12+4+msglen]
- self.recvbuf = self.recvbuf[4+12+4+msglen:]
- else:
- if len(self.recvbuf) < 4 + 12 + 4 + 4:
- return
- command = self.recvbuf[4:4+12].split(b"\x00", 1)[0]
- msglen = struct.unpack("<i", self.recvbuf[4+12:4+12+4])[0]
- checksum = self.recvbuf[4+12+4:4+12+4+4]
- if len(self.recvbuf) < 4 + 12 + 4 + 4 + msglen:
- return
- msg = self.recvbuf[4+12+4+4:4+12+4+4+msglen]
- th = sha256(msg)
- h = sha256(th)
- if checksum != h[:4]:
- raise ValueError("got bad checksum " + repr(self.recvbuf))
- self.recvbuf = self.recvbuf[4+12+4+4+msglen:]
- if command in self.messagemap:
- f = BytesIO(msg)
- t = self.messagemap[command]()
- t.deserialize(f)
- self.got_message(t)
- else:
- logger.warning("Received unknown command from %s:%d: '%s' %s" % (self.dstaddr, self.dstport, command, repr(msg)))
- raise ValueError("Unknown command: '%s'" % (command))
+ if len(self.recvbuf) < 4 + 12 + 4 + 4:
+ return
+ command = self.recvbuf[4:4+12].split(b"\x00", 1)[0]
+ msglen = struct.unpack("<i", self.recvbuf[4+12:4+12+4])[0]
+ checksum = self.recvbuf[4+12+4:4+12+4+4]
+ if len(self.recvbuf) < 4 + 12 + 4 + 4 + msglen:
+ return
+ msg = self.recvbuf[4+12+4+4:4+12+4+4+msglen]
+ th = sha256(msg)
+ h = sha256(th)
+ if checksum != h[:4]:
+ raise ValueError("got bad checksum " + repr(self.recvbuf))
+ self.recvbuf = self.recvbuf[4+12+4+4+msglen:]
+ if command not in self.messagemap:
+ raise ValueError("Received unknown command from %s:%d: '%s' %s" % (self.dstaddr, self.dstport, command, repr(msg)))
+ f = BytesIO(msg)
+ t = self.messagemap[command]()
+ t.deserialize(f)
+ self.got_message(t)
except Exception as e:
- logger.exception('got_data:', repr(e))
+ logger.exception('Error reading message:', repr(e))
raise
def send_message(self, message, pushbuf=False):
@@ -1786,10 +1640,9 @@ class NodeConn(asyncore.dispatcher):
tmsg += command
tmsg += b"\x00" * (12 - len(command))
tmsg += struct.pack("<I", len(data))
- if self.ver_send >= 209:
- th = sha256(data)
- h = sha256(th)
- tmsg += h[:4]
+ th = sha256(data)
+ h = sha256(th)
+ tmsg += h[:4]
tmsg += data
with mininode_lock:
if (len(self.sendbuf) == 0 and not pushbuf):
@@ -1803,9 +1656,6 @@ class NodeConn(asyncore.dispatcher):
self.last_sent = time.time()
def got_message(self, message):
- if message.command == b"version":
- if message.nVersion <= BIP0031_VERSION:
- self.messagemap[b'ping'] = msg_ping_prebip31
if self.last_sent + 30 * 60 < time.time():
self.send_message(self.messagemap[b'ping']())
self._log_message("receive", message)
@@ -1838,13 +1688,3 @@ class NetworkThread(Thread):
[ obj.handle_close() for obj in disconnected ]
asyncore.loop(0.1, use_poll=True, map=mininode_socket_map, count=1)
logger.debug("Network thread closing")
-
-
-# An exception we can raise if we detect a potential disconnect
-# (p2p or rpc) before the test is complete
-class EarlyDisconnectError(Exception):
- def __init__(self, value):
- self.value = value
-
- def __str__(self):
- return repr(self.value)
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index 12dab57a02..8b28064c46 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -13,13 +13,15 @@ import os
import subprocess
import time
+from .authproxy import JSONRPCException
+from .mininode import NodeConn
from .util import (
assert_equal,
get_rpc_proxy,
rpc_url,
wait_until,
+ p2p_port,
)
-from .authproxy import JSONRPCException
BITCOIND_PROC_WAIT_TIMEOUT = 60
@@ -31,9 +33,11 @@ class TestNode():
- state about the node (whether it's running, etc)
- a Python subprocess.Popen object representing the running process
- an RPC connection to the node
+ - one or more P2P connections to the node
+
- To make things easier for the test writer, a bit of magic is happening under the covers.
- Any unrecognised messages will be dispatched to the RPC connection."""
+ To make things easier for the test writer, any unrecognised messages will
+ be dispatched to the RPC connection."""
def __init__(self, i, dirname, extra_args, rpchost, timewait, binary, stderr, mocktime, coverage_dir):
self.index = i
@@ -63,10 +67,12 @@ class TestNode():
self.url = None
self.log = logging.getLogger('TestFramework.node%d' % i)
- def __getattr__(self, *args, **kwargs):
+ self.p2ps = []
+
+ def __getattr__(self, name):
"""Dispatches any unrecognised messages to the RPC connection."""
assert self.rpc_connected and self.rpc is not None, "Error: no RPC connection"
- return self.rpc.__getattr__(*args, **kwargs)
+ return getattr(self.rpc, name)
def start(self, extra_args=None, stderr=None):
"""Start the node."""
@@ -119,6 +125,7 @@ class TestNode():
self.stop()
except http.client.CannotSendRequest:
self.log.exception("Unable to stop node.")
+ del self.p2ps[:]
def is_node_stopped(self):
"""Checks whether the node has stopped.
@@ -151,6 +158,38 @@ class TestNode():
self.encryptwallet(passphrase)
self.wait_until_stopped()
+ def add_p2p_connection(self, p2p_conn, **kwargs):
+ """Add a p2p connection to the node.
+
+ This method adds the p2p connection to the self.p2ps list and also
+ returns the connection to the caller."""
+ if 'dstport' not in kwargs:
+ kwargs['dstport'] = p2p_port(self.index)
+ if 'dstaddr' not in kwargs:
+ kwargs['dstaddr'] = '127.0.0.1'
+ self.p2ps.append(p2p_conn)
+ kwargs.update({'rpc': self.rpc, 'callback': p2p_conn})
+ p2p_conn.add_connection(NodeConn(**kwargs))
+
+ return p2p_conn
+
+ @property
+ def p2p(self):
+ """Return the first p2p connection
+
+ Convenience property - most tests only use a single p2p connection to each
+ node, so this saves having to write node.p2ps[0] many times."""
+ assert self.p2ps, "No p2p connection"
+ return self.p2ps[0]
+
+ def disconnect_p2p(self, index=0):
+ """Close the p2p connection to the node."""
+ # Connection could have already been closed by other end. Calling disconnect_p2p()
+ # on an already disconnected p2p connection is not an error.
+ if self.p2ps[index].connection is not None:
+ self.p2ps[index].connection.disconnect_node()
+ del self.p2ps[index]
+
class TestNodeCLI():
"""Interface to bitcoin-cli for an individual node"""
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 80a5ffefb4..ca36426a0a 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -125,6 +125,7 @@ BASE_SCRIPTS= [
'minchainwork.py',
'p2p-fingerprint.py',
'uacomment.py',
+ 'p2p-acceptblock.py',
]
EXTENDED_SCRIPTS = [
@@ -152,7 +153,6 @@ EXTENDED_SCRIPTS = [
'txn_clone.py --mineblock',
'notifications.py',
'invalidateblock.py',
- 'p2p-acceptblock.py',
'replace-by-fee.py',
]
diff --git a/test/functional/wallet-accounts.py b/test/functional/wallet-accounts.py
index 40726d2a76..bc1efaee15 100755
--- a/test/functional/wallet-accounts.py
+++ b/test/functional/wallet-accounts.py
@@ -72,62 +72,135 @@ class WalletAccountsTest(BitcoinTestFramework):
# otherwise we're off by exactly the fee amount as that's mined
# and matures in the next 100 blocks
node.sendfrom("", common_address, fee)
- accounts = ["a", "b", "c", "d", "e"]
amount_to_send = 1.0
- account_addresses = dict()
+
+ # Create accounts and make sure subsequent account API calls
+ # recognize the account/address associations.
+ accounts = [Account(name) for name in ("a", "b", "c", "d", "e")]
for account in accounts:
- address = node.getaccountaddress(account)
- account_addresses[account] = address
-
- node.getnewaddress(account)
- assert_equal(node.getaccount(address), account)
- assert(address in node.getaddressesbyaccount(account))
-
- node.sendfrom("", address, amount_to_send)
-
+ account.add_receive_address(node.getaccountaddress(account.name))
+ account.verify(node)
+
+ # Send a transaction to each account, and make sure this forces
+ # getaccountaddress to generate a new receiving address.
+ for account in accounts:
+ node.sendtoaddress(account.receive_address, amount_to_send)
+ account.add_receive_address(node.getaccountaddress(account.name))
+ account.verify(node)
+
+ # Check the amounts received.
node.generate(1)
+ for account in accounts:
+ assert_equal(
+ node.getreceivedbyaddress(account.addresses[0]), amount_to_send)
+ assert_equal(node.getreceivedbyaccount(account.name), amount_to_send)
- for i in range(len(accounts)):
- from_account = accounts[i]
+ # Check that sendfrom account reduces listaccounts balances.
+ for i, account in enumerate(accounts):
to_account = accounts[(i+1) % len(accounts)]
- to_address = account_addresses[to_account]
- node.sendfrom(from_account, to_address, amount_to_send)
-
+ node.sendfrom(account.name, to_account.receive_address, amount_to_send)
node.generate(1)
-
for account in accounts:
- address = node.getaccountaddress(account)
- assert(address != account_addresses[account])
- assert_equal(node.getreceivedbyaccount(account), 2)
- node.move(account, "", node.getbalance(account))
-
+ account.add_receive_address(node.getaccountaddress(account.name))
+ account.verify(node)
+ assert_equal(node.getreceivedbyaccount(account.name), 2)
+ node.move(account.name, "", node.getbalance(account.name))
+ account.verify(node)
node.generate(101)
-
expected_account_balances = {"": 5200}
for account in accounts:
- expected_account_balances[account] = 0
-
+ expected_account_balances[account.name] = 0
assert_equal(node.listaccounts(), expected_account_balances)
-
assert_equal(node.getbalance(""), 5200)
+ # Check that setaccount can assign an account to a new unused address.
for account in accounts:
address = node.getaccountaddress("")
- node.setaccount(address, account)
- assert(address in node.getaddressesbyaccount(account))
+ node.setaccount(address, account.name)
+ account.add_address(address)
+ account.verify(node)
assert(address not in node.getaddressesbyaccount(""))
+ # Check that addmultisigaddress can assign accounts.
for account in accounts:
addresses = []
for x in range(10):
addresses.append(node.getnewaddress())
- multisig_address = node.addmultisigaddress(5, addresses, account)
+ multisig_address = node.addmultisigaddress(5, addresses, account.name)
+ account.add_address(multisig_address)
+ account.verify(node)
node.sendfrom("", multisig_address, 50)
-
node.generate(101)
-
for account in accounts:
- assert_equal(node.getbalance(account), 50)
+ assert_equal(node.getbalance(account.name), 50)
+
+ # Check that setaccount can change the account of an address from a
+ # different account.
+ change_account(node, accounts[0].addresses[0], accounts[0], accounts[1])
+
+ # Check that setaccount can change the account of an address which
+ # is the receiving address of a different account.
+ change_account(node, accounts[0].receive_address, accounts[0], accounts[1])
+
+ # Check that setaccount can set the account of an address already
+ # in the account. This is a no-op.
+ change_account(node, accounts[2].addresses[0], accounts[2], accounts[2])
+
+ # Check that setaccount can set the account of an address which is
+ # already the receiving address of the account. It would probably make
+ # sense for this to be a no-op, but right now it resets the receiving
+ # address, causing getaccountaddress to return a brand new address.
+ change_account(node, accounts[2].receive_address, accounts[2], accounts[2])
+
+class Account:
+ def __init__(self, name):
+ # Account name
+ self.name = name
+ # Current receiving address associated with this account.
+ self.receive_address = None
+ # List of all addresses assigned with this account
+ self.addresses = []
+
+ def add_address(self, address):
+ assert_equal(address not in self.addresses, True)
+ self.addresses.append(address)
+
+ def add_receive_address(self, address):
+ self.add_address(address)
+ self.receive_address = address
+
+ def verify(self, node):
+ if self.receive_address is not None:
+ assert self.receive_address in self.addresses
+ assert_equal(node.getaccountaddress(self.name), self.receive_address)
+
+ for address in self.addresses:
+ assert_equal(node.getaccount(address), self.name)
+
+ assert_equal(
+ set(node.getaddressesbyaccount(self.name)), set(self.addresses))
+
+
+def change_account(node, address, old_account, new_account):
+ assert_equal(address in old_account.addresses, True)
+ node.setaccount(address, new_account.name)
+
+ old_account.addresses.remove(address)
+ new_account.add_address(address)
+
+ # Calling setaccount on an address which was previously the receiving
+ # address of a different account should reset the receiving address of
+ # the old account, causing getaccountaddress to return a brand new
+ # address.
+ if address == old_account.receive_address:
+ new_address = node.getaccountaddress(old_account.name)
+ assert_equal(new_address not in old_account.addresses, True)
+ assert_equal(new_address not in new_account.addresses, True)
+ old_account.add_receive_address(new_address)
+
+ old_account.verify(node)
+ new_account.verify(node)
+
if __name__ == '__main__':
WalletAccountsTest().main()
diff --git a/test/functional/walletbackup.py b/test/functional/walletbackup.py
index 15ea26afa1..85a149793e 100755
--- a/test/functional/walletbackup.py
+++ b/test/functional/walletbackup.py
@@ -190,6 +190,16 @@ class WalletBackupTest(BitcoinTestFramework):
assert_equal(self.nodes[1].getbalance(), balance1)
assert_equal(self.nodes[2].getbalance(), balance2)
+ # Backup to source wallet file must fail
+ sourcePaths = [
+ tmpdir + "/node0/regtest/wallet.dat",
+ tmpdir + "/node0/./regtest/wallet.dat",
+ tmpdir + "/node0/regtest/",
+ tmpdir + "/node0/regtest"]
+
+ for sourcePath in sourcePaths:
+ assert_raises_rpc_error(-4, "backup failed", self.nodes[0].backupwallet, sourcePath)
+
if __name__ == '__main__':
WalletBackupTest().main()
diff --git a/test/functional/zmq_test.py b/test/functional/zmq_test.py
index 165f9192dd..fa30318416 100755
--- a/test/functional/zmq_test.py
+++ b/test/functional/zmq_test.py
@@ -8,10 +8,12 @@ import os
import struct
from test_framework.test_framework import BitcoinTestFramework, SkipTest
+from test_framework.mininode import CTransaction
from test_framework.util import (assert_equal,
bytes_to_hex_str,
hash256,
)
+from io import BytesIO
class ZMQSubscriber:
def __init__(self, socket, topic):
@@ -93,7 +95,10 @@ class ZMQTest (BitcoinTestFramework):
# Should receive the coinbase raw transaction.
hex = self.rawtx.receive()
- assert_equal(hash256(hex), txid)
+ tx = CTransaction()
+ tx.deserialize(BytesIO(hex))
+ tx.calc_sha256()
+ assert_equal(tx.hash, bytes_to_hex_str(txid))
# Should receive the generated block hash.
hash = bytes_to_hex_str(self.hashblock.receive())