aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml1
-rw-r--r--CONTRIBUTING.md35
-rw-r--r--Makefile.am3
-rw-r--r--build-aux/m4/bitcoin_qt.m42
-rw-r--r--configure.ac7
-rwxr-xr-xcontrib/devtools/github-merge.py55
-rw-r--r--contrib/linearize/README.md3
-rw-r--r--contrib/linearize/example-linearize.cfg1
-rwxr-xr-xcontrib/linearize/linearize-hashes.py23
-rwxr-xr-xcontrib/macdeploy/macdeployqtplus6
-rw-r--r--doc/.gitignore1
-rw-r--r--doc/Doxyfile.in (renamed from doc/Doxyfile)2
-rw-r--r--doc/README.md4
-rw-r--r--doc/README_windows.txt4
-rw-r--r--doc/build-osx.md6
-rw-r--r--doc/build-unix.md6
-rw-r--r--doc/developer-notes.md73
-rw-r--r--doc/release-notes/release-notes-0.14.1.md143
-rw-r--r--doc/release-process.md21
-rw-r--r--src/Makefile.am14
-rw-r--r--src/Makefile.test.include4
-rw-r--r--src/addrdb.cpp18
-rw-r--r--src/addrdb.h6
-rw-r--r--src/addrman.cpp8
-rw-r--r--src/addrman.h11
-rw-r--r--src/bench/checkblock.cpp4
-rw-r--r--src/bench/checkqueue.cpp2
-rw-r--r--src/bench/coin_selection.cpp2
-rw-r--r--src/bench/crypto_hash.cpp25
-rw-r--r--src/bitcoin-cli.cpp4
-rw-r--r--src/bitcoin-tx.cpp8
-rw-r--r--src/bitcoind.cpp4
-rw-r--r--src/chainparamsbase.cpp5
-rw-r--r--src/chainparamsbase.h6
-rw-r--r--src/checkqueue.h2
-rw-r--r--src/clientversion.h26
-rw-r--r--src/coins.h5
-rw-r--r--src/core_write.cpp2
-rw-r--r--src/crypto/chacha20.cpp180
-rw-r--r--src/crypto/chacha20.h26
-rw-r--r--src/crypto/common.h21
-rw-r--r--src/dbwrapper.cpp5
-rw-r--r--src/dbwrapper.h5
-rw-r--r--src/fs.cpp17
-rw-r--r--src/fs.h24
-rw-r--r--src/httpserver.cpp28
-rw-r--r--src/httpserver.h4
-rw-r--r--src/init.cpp66
-rw-r--r--src/key.cpp8
-rw-r--r--src/key.h3
-rw-r--r--src/memusage.h20
-rw-r--r--src/merkleblock.cpp2
-rw-r--r--src/merkleblock.h2
-rw-r--r--src/miner.cpp4
-rw-r--r--src/net.cpp112
-rw-r--r--src/net.h12
-rw-r--r--src/net_processing.cpp244
-rw-r--r--src/net_processing.h8
-rw-r--r--src/netaddress.cpp6
-rw-r--r--src/netaddress.h1
-rw-r--r--src/policy/fees.cpp209
-rw-r--r--src/policy/fees.h129
-rw-r--r--src/policy/policy.cpp1
-rw-r--r--src/primitives/transaction.h4
-rw-r--r--src/qt/bantablemodel.cpp4
-rw-r--r--src/qt/bitcoin.cpp4
-rw-r--r--src/qt/bitcoingui.cpp3
-rw-r--r--src/qt/clientmodel.cpp34
-rw-r--r--src/qt/clientmodel.h6
-rw-r--r--src/qt/coincontroldialog.cpp5
-rw-r--r--src/qt/forms/optionsdialog.ui102
-rw-r--r--src/qt/forms/sendcoinsdialog.ui2
-rw-r--r--src/qt/guiutil.cpp67
-rw-r--r--src/qt/guiutil.h10
-rw-r--r--src/qt/intro.cpp5
-rw-r--r--src/qt/optionsdialog.cpp12
-rw-r--r--src/qt/optionsdialog.h1
-rw-r--r--src/qt/peertablemodel.cpp2
-rw-r--r--src/qt/rpcconsole.cpp6
-rw-r--r--src/qt/sendcoinsdialog.cpp7
-rw-r--r--src/qt/test/rpcnestedtests.cpp5
-rw-r--r--src/qt/test/test_main.cpp8
-rw-r--r--src/qt/test/wallettests.cpp15
-rw-r--r--src/qt/transactionrecord.cpp10
-rw-r--r--src/qt/walletmodel.cpp1
-rw-r--r--src/random.cpp33
-rw-r--r--src/random.h78
-rw-r--r--src/rest.cpp9
-rw-r--r--src/rpc/blockchain.cpp6
-rw-r--r--src/rpc/client.cpp3
-rw-r--r--src/rpc/mining.cpp14
-rw-r--r--src/rpc/misc.cpp72
-rw-r--r--src/rpc/net.cpp34
-rw-r--r--src/rpc/protocol.cpp12
-rw-r--r--src/rpc/protocol.h5
-rw-r--r--src/rpc/rawtransaction.cpp78
-rw-r--r--src/rpc/server.cpp6
-rw-r--r--src/script/interpreter.cpp6
-rw-r--r--src/script/sigcache.cpp22
-rw-r--r--src/script/sigcache.h21
-rw-r--r--src/serialize.h11
-rw-r--r--src/streams.h8
-rw-r--r--src/test/addrman_tests.cpp9
-rw-r--r--src/test/amount_tests.cpp38
-rw-r--r--src/test/crypto_tests.cpp68
-rw-r--r--src/test/cuckoocache_tests.cpp25
-rw-r--r--src/test/dbwrapper_tests.cpp18
-rw-r--r--src/test/net_tests.cpp13
-rw-r--r--src/test/policyestimator_tests.cpp45
-rw-r--r--src/test/prevector_tests.cpp8
-rw-r--r--src/test/random_tests.cpp36
-rw-r--r--src/test/test_bitcoin.cpp9
-rw-r--r--src/test/test_bitcoin.h4
-rw-r--r--src/test/test_random.h8
-rw-r--r--src/test/testutil.cpp6
-rw-r--r--src/test/testutil.h4
-rw-r--r--src/torcontrol.cpp20
-rw-r--r--src/txdb.h8
-rw-r--r--src/txmempool.cpp71
-rw-r--r--src/txmempool.h16
-rw-r--r--src/util.cpp97
-rw-r--r--src/util.h29
-rw-r--r--src/utiltime.cpp12
-rw-r--r--src/validation.cpp221
-rw-r--r--src/validation.h22
-rw-r--r--src/validationinterface.cpp18
-rw-r--r--src/validationinterface.h38
-rw-r--r--src/wallet/db.cpp163
-rw-r--r--src/wallet/db.h71
-rw-r--r--src/wallet/feebumper.cpp284
-rw-r--r--src/wallet/feebumper.h56
-rw-r--r--src/wallet/rpcdump.cpp27
-rw-r--r--src/wallet/rpcwallet.cpp310
-rw-r--r--src/wallet/test/wallet_test_fixture.cpp3
-rw-r--r--src/wallet/test/wallet_tests.cpp13
-rw-r--r--src/wallet/wallet.cpp565
-rw-r--r--src/wallet/wallet.h120
-rw-r--r--src/wallet/walletdb.cpp119
-rw-r--r--src/wallet/walletdb.h43
-rw-r--r--src/zmq/zmqnotificationinterface.cpp22
-rw-r--r--src/zmq/zmqnotificationinterface.h7
-rw-r--r--src/zmq/zmqpublishnotifier.cpp1
-rw-r--r--test/README.md2
-rw-r--r--test/functional/README.md2
-rwxr-xr-xtest/functional/abandonconflict.py7
-rwxr-xr-xtest/functional/bip65-cltv-p2p.py7
-rwxr-xr-xtest/functional/bip65-cltv.py7
-rwxr-xr-xtest/functional/bip68-112-113-p2p.py7
-rwxr-xr-xtest/functional/bip68-sequence.py9
-rwxr-xr-xtest/functional/bip9-softforks.py6
-rwxr-xr-xtest/functional/bipdersig-p2p.py6
-rwxr-xr-xtest/functional/bipdersig.py7
-rwxr-xr-xtest/functional/blockchain.py20
-rwxr-xr-xtest/functional/bumpfee.py1
-rwxr-xr-xtest/functional/decodescript.py4
-rwxr-xr-xtest/functional/disablewallet.py6
-rwxr-xr-xtest/functional/disconnect_ban.py105
-rwxr-xr-xtest/functional/forknotify.py3
-rwxr-xr-xtest/functional/fundrawtransaction.py19
-rwxr-xr-xtest/functional/getblocktemplate_proposals.py4
-rwxr-xr-xtest/functional/getchaintips.py2
-rwxr-xr-xtest/functional/httpbasics.py2
-rwxr-xr-xtest/functional/import-abort-rescan.py66
-rwxr-xr-xtest/functional/importmulti.py5
-rwxr-xr-xtest/functional/importprunedfunds.py6
-rwxr-xr-xtest/functional/invalidateblock.py11
-rwxr-xr-xtest/functional/keypool.py3
-rwxr-xr-xtest/functional/listsinceblock.py4
-rwxr-xr-xtest/functional/listtransactions.py2
-rwxr-xr-xtest/functional/maxblocksinflight.py93
-rwxr-xr-xtest/functional/maxuploadtarget.py82
-rwxr-xr-xtest/functional/mempool_limit.py19
-rwxr-xr-xtest/functional/mempool_packages.py21
-rwxr-xr-xtest/functional/mempool_reorg.py10
-rwxr-xr-xtest/functional/mempool_resurrect_test.py7
-rwxr-xr-xtest/functional/mempool_spendcoinbase.py8
-rwxr-xr-xtest/functional/merkle_blocks.py11
-rwxr-xr-xtest/functional/multi_rpc.py3
-rwxr-xr-xtest/functional/net.py58
-rwxr-xr-xtest/functional/nodehandling.py80
-rwxr-xr-xtest/functional/nulldummy.py6
-rwxr-xr-xtest/functional/p2p-acceptblock.py50
-rwxr-xr-xtest/functional/p2p-compactblocks.py174
-rwxr-xr-xtest/functional/p2p-feefilter.py18
-rwxr-xr-xtest/functional/p2p-fullblocktest.py4
-rwxr-xr-xtest/functional/p2p-leaktests.py24
-rwxr-xr-xtest/functional/p2p-mempool.py66
-rwxr-xr-xtest/functional/p2p-segwit.py122
-rwxr-xr-xtest/functional/p2p-timeouts.py21
-rwxr-xr-xtest/functional/p2p-versionbits-warning.py20
-rwxr-xr-xtest/functional/preciousblock.py2
-rwxr-xr-xtest/functional/prioritise_transaction.py9
-rwxr-xr-xtest/functional/proxy_test.py2
-rwxr-xr-xtest/functional/pruning.py42
-rwxr-xr-xtest/functional/rawtransactions.py14
-rwxr-xr-xtest/functional/receivedby.py2
-rwxr-xr-xtest/functional/reindex.py3
-rwxr-xr-xtest/functional/replace-by-fee.py17
-rwxr-xr-xtest/functional/rest.py8
-rwxr-xr-xtest/functional/rpcnamedargs.py5
-rwxr-xr-xtest/functional/segwit.py11
-rwxr-xr-xtest/functional/sendheaders.py104
-rwxr-xr-xtest/functional/signmessages.py4
-rwxr-xr-xtest/functional/signrawtransactions.py4
-rwxr-xr-xtest/functional/smartfees.py1
-rw-r--r--test/functional/test_framework/blocktools.py11
-rwxr-xr-xtest/functional/test_framework/comptool.py4
-rwxr-xr-xtest/functional/test_framework/mininode.py157
-rwxr-xr-xtest/functional/test_framework/test_framework.py51
-rw-r--r--test/functional/test_framework/util.py15
-rwxr-xr-xtest/functional/test_runner.py166
-rwxr-xr-xtest/functional/txn_clone.py4
-rwxr-xr-xtest/functional/txn_doublespend.py4
-rwxr-xr-xtest/functional/wallet-accounts.py66
-rwxr-xr-xtest/functional/wallet-hd.py13
-rwxr-xr-xtest/functional/wallet.py31
-rwxr-xr-xtest/functional/walletbackup.py4
-rwxr-xr-xtest/functional/zapwallettxes.py8
-rwxr-xr-xtest/functional/zmq_test.py23
-rw-r--r--test/util/data/blanktxv1.json2
-rw-r--r--test/util/data/blanktxv2.json2
-rw-r--r--test/util/data/tt-delin1-out.json2
-rw-r--r--test/util/data/tt-delout1-out.json2
-rw-r--r--test/util/data/tt-locktime317000-out.json2
-rw-r--r--test/util/data/txcreate1.json2
-rw-r--r--test/util/data/txcreate2.json2
-rw-r--r--test/util/data/txcreatedata1.json2
-rw-r--r--test/util/data/txcreatedata2.json2
-rw-r--r--test/util/data/txcreatedata_seq0.json2
-rw-r--r--test/util/data/txcreatedata_seq1.json2
-rw-r--r--test/util/data/txcreatemultisig1.json2
-rw-r--r--test/util/data/txcreatemultisig2.json2
-rw-r--r--test/util/data/txcreatemultisig3.json2
-rw-r--r--test/util/data/txcreatemultisig4.json2
-rw-r--r--test/util/data/txcreateoutpubkey1.json2
-rw-r--r--test/util/data/txcreateoutpubkey2.json2
-rw-r--r--test/util/data/txcreateoutpubkey3.json2
-rw-r--r--test/util/data/txcreatescript1.json2
-rw-r--r--test/util/data/txcreatescript2.json2
-rw-r--r--test/util/data/txcreatescript3.json2
-rw-r--r--test/util/data/txcreatescript4.json2
-rw-r--r--test/util/data/txcreatesignv1.json2
242 files changed, 4086 insertions, 3032 deletions
diff --git a/.travis.yml b/.travis.yml
index d8395255bc..a479e46f44 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -39,7 +39,6 @@ env:
before_install:
- export PATH=$(echo $PATH | tr ':' "\n" | sed '/\/opt\/python/d' | tr "\n" ":" | sed "s|::|:|g")
install:
- - if [ -n "$PPA" ]; then travis_retry sudo add-apt-repository "$PPA" -y; fi
- if [ -n "$DPKG_ADD_ARCH" ]; then sudo dpkg --add-architecture "$DPKG_ADD_ARCH" ; fi
- if [ -n "$PACKAGES" ]; then travis_retry sudo apt-get update; fi
- if [ -n "$PACKAGES" ]; then travis_retry sudo apt-get install --no-install-recommends --no-upgrade -qq $PACKAGES; fi
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 0766d89f55..f5d63517b1 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -37,8 +37,8 @@ fixes or code moves with actual code changes.
Commit messages should be verbose by default consisting of a short subject line
(50 chars max), a blank line and detailed explanatory text as separate
-paragraph(s); unless the title alone is self-explanatory (like "Corrected typo
-in init.cpp") then a single title line is sufficient. Commit messages should be
+paragraph(s), unless the title alone is self-explanatory (like "Corrected typo
+in init.cpp") in which case a single title line is sufficient. Commit messages should be
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/).
@@ -225,6 +225,37 @@ discussed extensively on the mailing list and IRC, be accompanied by a widely
discussed BIP and have a generally widely perceived technical consensus of being
a worthwhile change based on the judgement of the maintainers.
+### Finding Reviewers
+
+As most reviewers are themselves developers with their own projects, the review
+process can be quite lengthy, and some amount of patience is required. If you find
+that you've been waiting for a pull request to be given attention for several
+months, there may be a number of reasons for this, some of which you can do something
+about:
+
+ - It may be because of a feature freeze due to an upcoming release. During this time,
+ only bug fixes are taken into consideration. If your pull request is a new feature,
+ it will not be prioritized until the release is over. Wait for release.
+ - It may be because the changes you are suggesting do not appeal to people. Rather than
+ nits and critique, which require effort and means they care enough to spend time on your
+ contribution, thundering silence is a good sign of widespread (mild) dislike of a given change
+ (because people don't assume *others* won't actually like the proposal). Don't take
+ that personally, though! Instead, take another critical look at what you are suggesting
+ and see if it: changes too much, is too broad, doesn't adhere to the
+ [developer notes](doc/developer-notes.md), is dangerous or insecure, is messily written, etc.
+ Identify and address any of the issues you find. Then ask e.g. on IRC if someone could give
+ their opinion on the concept itself.
+ - It may be because your code is too complex for all but a few people. And those people
+ may not have realized your pull request even exists. A great way to find people who
+ are qualified and care about the code you are touching is the
+ [Git Blame feature](https://help.github.com/articles/tracing-changes-in-a-file/). Simply
+ find the person touching the code you are touching before you and see if you can find
+ them and give them a nudge. Don't be incessant about the nudging though.
+ - Finally, if all else fails, ask on IRC or elsewhere for someone to give your pull request
+ a look. If you think you've been waiting an unreasonably long amount of time (month+) for
+ no particular reason (few lines changed, etc), this is totally fine. Try to return the favor
+ when someone else is asking for feedback on their code, and universe balances out.
+
Release Policy
--------------
diff --git a/Makefile.am b/Makefile.am
index e82704dbdf..3a56eea0c0 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -76,9 +76,6 @@ $(BITCOIN_WIN_INSTALLER): all-recursive
echo error: could not build $@
@echo built $@
-$(if $(findstring src/,$(MAKECMDGOALS)),$(MAKECMDGOALS), none): FORCE
- $(MAKE) -C src $(patsubst src/%,%,$@)
-
$(OSX_APP)/Contents/PkgInfo:
$(MKDIR_P) $(@D)
@echo "APPL????" > $@
diff --git a/build-aux/m4/bitcoin_qt.m4 b/build-aux/m4/bitcoin_qt.m4
index f43fc3037f..1b339559fc 100644
--- a/build-aux/m4/bitcoin_qt.m4
+++ b/build-aux/m4/bitcoin_qt.m4
@@ -130,6 +130,8 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[
if test "x$bitcoin_cv_need_acc_widget" = "xyes"; then
_BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(AccessibleFactory)], [-lqtaccessiblewidgets])
fi
+ _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QMinimalIntegrationPlugin)],[-lqminimal])
+ AC_DEFINE(QT_QPA_PLATFORM_MINIMAL, 1, [Define this symbol if the minimal qt platform exists])
if test x$TARGET_OS = xwindows; then
_BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin)],[-lqwindows])
AC_DEFINE(QT_QPA_PLATFORM_WINDOWS, 1, [Define this symbol if the qt platform is windows])
diff --git a/configure.ac b/configure.ac
index 20e528e904..3672700488 100644
--- a/configure.ac
+++ b/configure.ac
@@ -549,6 +549,8 @@ AC_CHECK_DECLS([bswap_16, bswap_32, bswap_64],,,
#include <byteswap.h>
#endif])
+AC_CHECK_DECLS([__builtin_clz, __builtin_clzl, __builtin_clzll])
+
dnl Check for MSG_NOSIGNAL
AC_MSG_CHECKING(for MSG_NOSIGNAL)
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <sys/socket.h>]],
@@ -676,6 +678,10 @@ AX_BOOST_PROGRAM_OPTIONS
AX_BOOST_THREAD
AX_BOOST_CHRONO
+dnl Boost 1.56 through 1.62 allow using std::atomic instead of its own atomic
+dnl counter implementations. In 1.63 and later the std::atomic approach is default.
+m4_pattern_allow(DBOOST_AC_USE_STD_ATOMIC) dnl otherwise it's treated like a macro
+BOOST_CPPFLAGS="-DBOOST_SP_USE_STD_ATOMIC -DBOOST_AC_USE_STD_ATOMIC $BOOST_CPPFLAGS"
if test x$use_reduce_exports = xyes; then
AC_MSG_CHECKING([for working boost reduced exports])
@@ -1157,6 +1163,7 @@ AC_SUBST(QR_LIBS)
AC_CONFIG_FILES([Makefile src/Makefile doc/man/Makefile share/setup.nsi share/qt/Info.plist test/functional/config.ini])
AC_CONFIG_FILES([test/util/buildenv.py],[chmod +x test/util/buildenv.py])
AC_CONFIG_FILES([contrib/devtools/split-debug.sh],[chmod +x contrib/devtools/split-debug.sh])
+AC_CONFIG_FILES([doc/Doxyfile])
AC_CONFIG_LINKS([test/functional/test_runner.py:test/functional/test_runner.py])
AC_CONFIG_LINKS([test/util/bitcoin-util-test.py:test/util/bitcoin-util-test.py])
AC_CONFIG_LINKS([test/util/bctest.py:test/util/bctest.py])
diff --git a/contrib/devtools/github-merge.py b/contrib/devtools/github-merge.py
index 3fee39143d..03ccf5b624 100755
--- a/contrib/devtools/github-merge.py
+++ b/contrib/devtools/github-merge.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2016 The Bitcoin Core developers
+# Copyright (c) 2016-2017 Bitcoin Core Developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -15,7 +15,7 @@
# In case of a clean merge that is accepted by the user, the local branch with
# name $BRANCH is overwritten with the merged result, and optionally pushed.
from __future__ import division,print_function,unicode_literals
-import os
+import os,sys
from sys import stdin,stdout,stderr
import argparse
import hashlib
@@ -127,6 +127,9 @@ def tree_sha512sum(commit='HEAD'):
raise IOError('Non-zero return value executing git cat-file')
return overall.hexdigest()
+def print_merge_details(pull, title, branch, base_branch, head_branch):
+ print('%s#%s%s %s %sinto %s%s' % (ATTR_RESET+ATTR_PR,pull,ATTR_RESET,title,ATTR_RESET+ATTR_PR,branch,ATTR_RESET))
+ subprocess.check_call([GIT,'log','--graph','--topo-order','--pretty=format:'+COMMIT_FORMAT,base_branch+'..'+head_branch])
def parse_arguments():
epilog = '''
@@ -171,7 +174,7 @@ def main():
info = retrieve_pr_info(repo,pull)
if info is None:
exit(1)
- title = info['title']
+ title = info['title'].strip()
# precedence order for destination branch argument:
# - command line argument
# - githubmerge.branch setting
@@ -256,8 +259,7 @@ def main():
printf("ERROR: Cannot update message.",file=stderr)
exit(4)
- print('%s#%s%s %s %sinto %s%s' % (ATTR_RESET+ATTR_PR,pull,ATTR_RESET,title,ATTR_RESET+ATTR_PR,branch,ATTR_RESET))
- subprocess.check_call([GIT,'log','--graph','--topo-order','--pretty=format:'+COMMIT_FORMAT,base_branch+'..'+head_branch])
+ print_merge_details(pull, title, branch, base_branch, head_branch)
print()
# Run test command if configured.
@@ -276,12 +278,6 @@ def main():
print("Difference with github ignored.",file=stderr)
else:
exit(6)
- reply = ask_prompt("Press 'd' to accept the diff.")
- if reply.lower() == 'd':
- print("Diff accepted.",file=stderr)
- else:
- print("ERROR: Diff rejected.",file=stderr)
- exit(6)
else:
# Verify the result manually.
print("Dropping you on a shell so you can try building/testing the merged source.",file=stderr)
@@ -290,12 +286,6 @@ def main():
if os.path.isfile('/etc/debian_version'): # Show pull number on Debian default prompt
os.putenv('debian_chroot',pull)
subprocess.call([BASH,'-i'])
- reply = ask_prompt("Type 'm' to accept the merge.")
- if reply.lower() == 'm':
- print("Merge accepted.",file=stderr)
- else:
- print("ERROR: Merge rejected.",file=stderr)
- exit(7)
second_sha512 = tree_sha512sum()
if first_sha512 != second_sha512:
@@ -303,16 +293,19 @@ def main():
exit(8)
# Sign the merge commit.
- reply = ask_prompt("Type 's' to sign off on the merge.")
- if reply == 's':
- try:
- subprocess.check_call([GIT,'commit','-q','--gpg-sign','--amend','--no-edit'])
- except subprocess.CalledProcessError as e:
- print("Error signing, exiting.",file=stderr)
+ print_merge_details(pull, title, branch, base_branch, head_branch)
+ while True:
+ reply = ask_prompt("Type 's' to sign off on the above merge, or 'x' to reject and exit.").lower()
+ if reply == 's':
+ try:
+ subprocess.check_call([GIT,'commit','-q','--gpg-sign','--amend','--no-edit'])
+ break
+ except subprocess.CalledProcessError as e:
+ print("Error signing, exiting.",file=stderr)
+ exit(1)
+ elif reply == 'x':
+ print("Not signing off on merge, exiting.",file=stderr)
exit(1)
- else:
- print("Not signing off on merge, exiting.",file=stderr)
- exit(1)
# Put the result in branch.
subprocess.check_call([GIT,'checkout','-q',branch])
@@ -326,9 +319,13 @@ def main():
subprocess.call([GIT,'branch','-q','-D',local_merge_branch],stderr=devnull)
# Push the result.
- reply = ask_prompt("Type 'push' to push the result to %s, branch %s." % (host_repo,branch))
- if reply.lower() == 'push':
- subprocess.check_call([GIT,'push',host_repo,'refs/heads/'+branch])
+ while True:
+ reply = ask_prompt("Type 'push' to push the result to %s, branch %s, or 'x' to exit without pushing." % (host_repo,branch)).lower()
+ if reply == 'push':
+ subprocess.check_call([GIT,'push',host_repo,'refs/heads/'+branch])
+ break
+ elif reply == 'x':
+ exit(1)
if __name__ == '__main__':
main()
diff --git a/contrib/linearize/README.md b/contrib/linearize/README.md
index 0971e7816b..f2a2ab2768 100644
--- a/contrib/linearize/README.md
+++ b/contrib/linearize/README.md
@@ -7,7 +7,8 @@ run using Python 3 but are compatible with Python 2.
$ ./linearize-hashes.py linearize.cfg > hashlist.txt
Required configuration file settings for linearize-hashes:
-* RPC: `rpcuser`, `rpcpassword`
+* RPC: `datadir` (Required if `rpcuser` and `rpcpassword` are not specified)
+* RPC: `rpcuser`, `rpcpassword` (Required if `datadir` is not specified)
Optional config file setting for linearize-hashes:
* RPC: `host` (Default: `127.0.0.1`)
diff --git a/contrib/linearize/example-linearize.cfg b/contrib/linearize/example-linearize.cfg
index 2cc910edfe..d019b06b6c 100644
--- a/contrib/linearize/example-linearize.cfg
+++ b/contrib/linearize/example-linearize.cfg
@@ -1,6 +1,7 @@
# bitcoind RPC settings (linearize-hashes)
rpcuser=someuser
rpcpassword=somepassword
+#datadir=~/.bitcoin
host=127.0.0.1
port=8332
#port=18332
diff --git a/contrib/linearize/linearize-hashes.py b/contrib/linearize/linearize-hashes.py
index 00a54d0820..db8eb7021e 100755
--- a/contrib/linearize/linearize-hashes.py
+++ b/contrib/linearize/linearize-hashes.py
@@ -16,6 +16,8 @@ import json
import re
import base64
import sys
+import os
+import os.path
settings = {}
@@ -93,6 +95,14 @@ def get_block_hashes(settings, max_blocks_per_call=10000):
height += num_blocks
+def get_rpc_cookie():
+ # Open the cookie file
+ with open(os.path.join(os.path.expanduser(settings['datadir']), '.cookie'), 'r') as f:
+ combined = f.readline()
+ combined_split = combined.split(":")
+ settings['rpcuser'] = combined_split[0]
+ settings['rpcpassword'] = combined_split[1]
+
if __name__ == '__main__':
if len(sys.argv) != 2:
print("Usage: linearize-hashes.py CONFIG-FILE")
@@ -122,8 +132,15 @@ if __name__ == '__main__':
settings['max_height'] = 313000
if 'rev_hash_bytes' not in settings:
settings['rev_hash_bytes'] = 'false'
+
+ use_userpass = True
+ use_datadir = False
if 'rpcuser' not in settings or 'rpcpassword' not in settings:
- print("Missing username and/or password in cfg file", file=stderr)
+ use_userpass = False
+ if 'datadir' in settings and not use_userpass:
+ use_datadir = True
+ if not use_userpass and not use_datadir:
+ print("Missing datadir or username and/or password in cfg file", file=stderr)
sys.exit(1)
settings['port'] = int(settings['port'])
@@ -133,4 +150,8 @@ if __name__ == '__main__':
# Force hash byte format setting to be lowercase to make comparisons easier.
settings['rev_hash_bytes'] = settings['rev_hash_bytes'].lower()
+ # Get the rpc user and pass from the cookie if the datadir is set
+ if use_datadir:
+ get_rpc_cookie()
+
get_block_hashes(settings)
diff --git a/contrib/macdeploy/macdeployqtplus b/contrib/macdeploy/macdeployqtplus
index 5995f9f438..23a568ad13 100755
--- a/contrib/macdeploy/macdeployqtplus
+++ b/contrib/macdeploy/macdeployqtplus
@@ -302,7 +302,6 @@ def copyFramework(framework, path, verbose):
if os.path.exists(fromContentsDir):
toContentsDir = os.path.join(path, framework.destinationVersionContentsDirectory)
shutil.copytree(fromContentsDir, toContentsDir, symlinks=True)
- contentslinkfrom = os.path.join(path, framework.destinationContentsDirectory)
if verbose >= 3:
print("Copied Contents:", fromContentsDir)
print(" to:", toContentsDir)
@@ -675,9 +674,8 @@ else:
if verbose >= 2:
print("+ Installing qt.conf +")
-f = open(os.path.join(applicationBundle.resourcesPath, "qt.conf"), "wb")
-f.write(qt_conf.encode())
-f.close()
+with open(os.path.join(applicationBundle.resourcesPath, "qt.conf"), "wb") as f:
+ f.write(qt_conf.encode())
# ------------------------------------------------
diff --git a/doc/.gitignore b/doc/.gitignore
new file mode 100644
index 0000000000..38498103bb
--- /dev/null
+++ b/doc/.gitignore
@@ -0,0 +1 @@
+Doxyfile
diff --git a/doc/Doxyfile b/doc/Doxyfile.in
index 45436a6b15..58c65fb7e2 100644
--- a/doc/Doxyfile
+++ b/doc/Doxyfile.in
@@ -38,7 +38,7 @@ PROJECT_NAME = "Bitcoin Core"
# could be handy for archiving the generated documentation or if some version
# control system is used.
-PROJECT_NUMBER = 0.14.99
+PROJECT_NUMBER = @PACKAGE_VERSION@
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
diff --git a/doc/README.md b/doc/README.md
index 5c00ab9150..275ae67e54 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -1,5 +1,5 @@
-Bitcoin Core 0.14.99
-=====================
+Bitcoin Core
+=============
Setup
---------------------
diff --git a/doc/README_windows.txt b/doc/README_windows.txt
index 0e4c9ce04f..07d61b3bda 100644
--- a/doc/README_windows.txt
+++ b/doc/README_windows.txt
@@ -1,5 +1,5 @@
-Bitcoin Core 0.14.99
-=====================
+Bitcoin Core
+=============
Intro
-----
diff --git a/doc/build-osx.md b/doc/build-osx.md
index a15bcd012c..32d7dbd69e 100644
--- a/doc/build-osx.md
+++ b/doc/build-osx.md
@@ -11,14 +11,14 @@ Install the OS X command line tools:
When the popup appears, click `Install`.
-Then install [Homebrew](http://brew.sh).
+Then install [Homebrew](https://brew.sh).
Dependencies
----------------------
- brew install automake berkeley-db4 libtool boost --c++11 miniupnpc openssl pkg-config protobuf --c++11 qt5 libevent
+ brew install automake berkeley-db4 libtool boost --c++11 miniupnpc openssl pkg-config protobuf qt libevent
-In case you want to build the disk image with `make deploy` (.dmg / optional), you need RSVG
+If you want to build the disk image with `make deploy` (.dmg / optional), you need RSVG
brew install librsvg
diff --git a/doc/build-unix.md b/doc/build-unix.md
index 31a88a1b18..b7eae2a630 100644
--- a/doc/build-unix.md
+++ b/doc/build-unix.md
@@ -321,8 +321,10 @@ Clang is installed by default as `cc` compiler, this makes it easier to get
started than on [OpenBSD](build-openbsd.md). Installing dependencies:
pkg install autoconf automake libtool pkgconf
- pkg install boost-libs openssl libevent2
+ pkg install boost-libs openssl libevent
+ pkg install gmake
+You need to use GNU make (`gmake`) instead of `make`.
(`libressl` instead of `openssl` will also work)
For the wallet (optional):
@@ -338,7 +340,7 @@ Then build using:
./autogen.sh
./configure --with-incompatible-bdb BDB_CFLAGS="-I/usr/local/include/db5" BDB_LIBS="-L/usr/local/lib -ldb_cxx-5"
- make
+ gmake
*Note on debugging*: The version of `gdb` installed by default is [ancient and considered harmful](https://wiki.freebsd.org/GdbRetirement).
It is not suitable for debugging a multi-threaded C++ program, not even for getting backtraces. Please install the package `gdb` and
diff --git a/doc/developer-notes.md b/doc/developer-notes.md
index 797507cd3e..fd75ada79f 100644
--- a/doc/developer-notes.md
+++ b/doc/developer-notes.md
@@ -495,3 +495,76 @@ Git and GitHub tips
This will add an `upstream-pull` remote to your git repository, which can be fetched using `git fetch --all`
or `git fetch upstream-pull`. Afterwards, you can use `upstream-pull/NUMBER/head` in arguments to `git show`,
`git checkout` and anywhere a commit id would be acceptable to see the changes from pull request NUMBER.
+
+RPC interface guidelines
+--------------------------
+
+A few guidelines for introducing and reviewing new RPC interfaces:
+
+- Method naming: use consecutive lower-case names such as `getrawtransaction` and `submitblock`
+
+ - *Rationale*: Consistency with existing interface.
+
+- Argument naming: use snake case `fee_delta` (and not, e.g. camel case `feeDelta`)
+
+ - *Rationale*: Consistency with existing interface.
+
+- Use the JSON parser for parsing, don't manually parse integers or strings from
+ arguments unless absolutely necessary.
+
+ - *Rationale*: Introduces hand-rolled string manipulation code at both the caller and callee sites,
+ which is error prone, and it is easy to get things such as escaping wrong.
+ JSON already supports nested data structures, no need to re-invent the wheel.
+
+ - *Exception*: AmountToValue can parse amounts as string. This was introduced because many JSON
+ parsers and formatters hard-code handling decimal numbers as floating point
+ values, resulting in potential loss of precision. This is unacceptable for
+ monetary values. **Always** use `AmountToValue` and `ValueToAmount` when
+ inputting or outputting monetary values. The only exceptions to this are
+ `prioritisetransaction` and `getblocktemplate` because their interface
+ is specified as-is in BIP22.
+
+- Missing arguments and 'null' should be treated the same: as default values. If there is no
+ default value, both cases should fail in the same way.
+
+ - *Rationale*: Avoids surprises when switching to name-based arguments. Missing name-based arguments
+ are passed as 'null'.
+
+ - *Exception*: Many legacy exceptions to this exist, one of the worst ones is
+ `getbalance` which follows a completely different code path based on the
+ number of arguments. We are still in the process of cleaning these up. Do not introduce
+ new ones.
+
+- Try not to overload methods on argument type. E.g. don't make `getblock(true)` and `getblock("hash")`
+ do different things.
+
+ - *Rationale*: This is impossible to use with `bitcoin-cli`, and can be surprising to users.
+
+ - *Exception*: Some RPC calls can take both an `int` and `bool`, most notably when a bool was switched
+ to a multi-value, or due to other historical reasons. **Always** have false map to 0 and
+ true to 1 in this case.
+
+- Don't forget to fill in the argument names correctly in the RPC command table.
+
+ - *Rationale*: If not, the call can not be used with name-based arguments.
+
+- Set okSafeMode in the RPC command table to a sensible value: safe mode is when the
+ blockchain is regarded to be in a confused state, and the client deems it unsafe to
+ do anything irreversible such as send. Anything that just queries should be permitted.
+
+ - *Rationale*: Troubleshooting a node in safe mode is difficult if half the
+ RPCs don't work.
+
+- Add every non-string RPC argument `(method, idx, name)` to the table `vRPCConvertParams` in `rpc/client.cpp`.
+
+ - *Rationale*: `bitcoin-cli` and the GUI debug console use this table to determine how to
+ convert a plaintext command line to JSON. If the types don't match, the method can be unusable
+ from there.
+
+- A RPC method must either be a wallet method or a non-wallet method. Do not
+ introduce new methods such as `getinfo` and `signrawtransaction` that differ
+ in behavior based on presence of a wallet.
+
+ - *Rationale*: as well as complicating the implementation and interfering
+ with the introduction of multi-wallet, wallet and non-wallet code should be
+ separated to avoid introducing circular dependencies between code units.
diff --git a/doc/release-notes/release-notes-0.14.1.md b/doc/release-notes/release-notes-0.14.1.md
new file mode 100644
index 0000000000..ef072afd4d
--- /dev/null
+++ b/doc/release-notes/release-notes-0.14.1.md
@@ -0,0 +1,143 @@
+Bitcoin Core version 0.14.1 is now available from:
+
+ <https://bitcoin.org/bin/bitcoin-core-0.14.1/>
+
+This is a new minor version release, including various bugfixes and
+performance improvements, as well as updated translations.
+
+Please report bugs using the issue tracker at github:
+
+ <https://github.com/bitcoin/bitcoin/issues>
+
+To receive security and update notifications, please subscribe to:
+
+ <https://bitcoincore.org/en/list/announcements/join/>
+
+Compatibility
+==============
+
+Bitcoin Core is extensively tested on multiple operating systems using
+the Linux kernel, macOS 10.8+, and Windows Vista and later.
+
+Microsoft ended support for Windows XP on [April 8th, 2014](https://www.microsoft.com/en-us/WindowsForBusiness/end-of-xp-support),
+No attempt is made to prevent installing or running the software on Windows XP, you
+can still do so at your own risk but be aware that there are known instabilities and issues.
+Please do not report issues about Windows XP to the issue tracker.
+
+Bitcoin Core should also work on most other Unix-like systems but is not
+frequently tested on them.
+
+Notable changes
+===============
+
+RPC changes
+-----------
+
+- The first positional argument of `createrawtransaction` was renamed from
+ `transactions` to `inputs`.
+
+- The argument of `disconnectnode` was renamed from `node` to `address`.
+
+These interface changes break compatibility with 0.14.0, when the named
+arguments functionality, introduced in 0.14.0, is used. Client software
+using these calls with named arguments needs to be updated.
+
+Mining
+------
+
+In previous versions, getblocktemplate required segwit support from downstream
+clients/miners once the feature activated on the network. In this version, it
+now supports non-segwit clients even after activation, by removing all segwit
+transactions from the returned block template. This allows non-segwit miners to
+continue functioning correctly even after segwit has activated.
+
+Due to the limitations in previous versions, getblocktemplate also recommended
+non-segwit clients to not signal for the segwit version-bit. Since this is no
+longer an issue, getblocktemplate now always recommends signalling segwit for
+all miners. This is safe because ability to enforce the rule is the only
+required criteria for safe activation, not actually producing segwit-enabled
+blocks.
+
+UTXO memory accounting
+----------------------
+
+Memory usage for the UTXO cache is being calculated more accurately, so that
+the configured limit (`-dbcache`) will be respected when memory usage peaks
+during cache flushes. The memory accounting in prior releases is estimated to
+only account for half the actual peak utilization.
+
+The default `-dbcache` has also been changed in this release to 450MiB. Users
+who currently set `-dbcache` to a high value (e.g. to keep the UTXO more fully
+cached in memory) should consider increasing this setting in order to achieve
+the same cache performance as prior releases. Users on low-memory systems
+(such as systems with 1GB or less) should consider specifying a lower value for
+this parameter.
+
+Additional information relating to running on low-memory systems can be found
+here:
+[reducing-bitcoind-memory-usage.md](https://gist.github.com/laanwj/efe29c7661ce9b6620a7).
+
+0.14.1 Change log
+=================
+
+Detailed release notes follow. This overview includes changes that affect
+behavior, not code moves, refactors and string updates. For convenience in locating
+the code changes and accompanying discussion, both the pull request and
+git merge commit are mentioned.
+
+### RPC and other APIs
+- #10084 `142fbb2` Rename first named arg of createrawtransaction (MarcoFalke)
+- #10139 `f15268d` Remove auth cookie on shutdown (practicalswift)
+- #10146 `2fea10a` Better error handling for submitblock (rawodb, gmaxwell)
+- #10144 `d947afc` Prioritisetransaction wasn't always updating ancestor fee (sdaftuar)
+- #10204 `3c79602` Rename disconnectnode argument (jnewbery)
+
+### Block and transaction handling
+- #10126 `0b5e162` Compensate for memory peak at flush time (sipa)
+- #9912 `fc3d7db` Optimize GetWitnessHash() for non-segwit transactions (sdaftuar)
+- #10133 `ab864d3` Clean up calculations of pcoinsTip memory usage (morcos)
+
+### P2P protocol and network code
+- #9953/#10013 `d2548a4` Fix shutdown hang with >= 8 -addnodes set (TheBlueMatt)
+- #10176 `30fa231` net: gracefully handle NodeId wrapping (theuni)
+
+### Build system
+- #9973 `e9611d1` depends: fix zlib build on osx (theuni)
+
+### GUI
+- #10060 `ddc2dd1` Ensure an item exists on the rpcconsole stack before adding (achow101)
+
+### Mining
+- #9955/#10006 `569596c` Don't require segwit in getblocktemplate for segwit signalling or mining (sdaftuar)
+- #9959/#10127 `b5c3440` Prevent slowdown in CreateNewBlock on large mempools (sdaftuar)
+
+### Tests and QA
+- #10157 `55f641c` Fix the `mempool_packages.py` test (sdaftuar)
+
+### Miscellaneous
+- #10037 `4d8e660` Trivial: Fix typo in help getrawtransaction RPC (keystrike)
+- #10120 `e4c9a90` util: Work around (virtual) memory exhaustion on 32-bit w/ glibc (laanwj)
+- #10130 `ecc5232` bitcoin-tx input verification (awemany, jnewbery)
+
+Credits
+=======
+
+Thanks to everyone who directly contributed to this release:
+
+- Alex Morcos
+- Andrew Chow
+- Awemany
+- Cory Fields
+- Gregory Maxwell
+- James Evans
+- John Newbery
+- MarcoFalke
+- Matt Corallo
+- Pieter Wuille
+- practicalswift
+- rawodb
+- Suhas Daftuar
+- Wladimir J. van der Laan
+
+As well as everyone that helped translating on [Transifex](https://www.transifex.com/projects/p/bitcoin/).
+
diff --git a/doc/release-process.md b/doc/release-process.md
index 44d5757989..5a99b726f1 100644
--- a/doc/release-process.md
+++ b/doc/release-process.md
@@ -10,7 +10,7 @@ Before every release candidate:
Before every minor and major release:
* Update [bips.md](bips.md) to account for changes since the last release.
-* Update version in sources (see below)
+* Update version in `configure.ac` (don't forget to set `CLIENT_VERSION_IS_RELEASE` to `true`)
* Write release notes (see below)
* Update `src/chainparams.cpp` nMinimumChainWork with information from the getblockchaininfo rpc.
* Update `src/chainparams.cpp` defaultAssumeValid with information from the getblockhash rpc.
@@ -24,6 +24,7 @@ Before every major release:
* Update hardcoded [seeds](/contrib/seeds/README.md), see [this pull request](https://github.com/bitcoin/bitcoin/pull/7415) for an example.
* Update [`BLOCK_CHAIN_SIZE`](/src/qt/intro.cpp) to the current size plus some overhead.
* Update `src/chainparams.cpp` chainTxData with statistics about the transaction count and rate.
+* Update version of `contrib/gitian-descriptors/*.yml`: usually one'd want to do this on master after branching off the release - but be sure to at least do it before a new major release
### First time / New builders
@@ -37,23 +38,7 @@ Check out the source code in the following directory hierarchy.
git clone https://github.com/devrandom/gitian-builder.git
git clone https://github.com/bitcoin/bitcoin.git
-### Bitcoin maintainers/release engineers, update version in sources
-
-Update the following:
-
-- `configure.ac`:
- - `_CLIENT_VERSION_MAJOR`
- - `_CLIENT_VERSION_MINOR`
- - `_CLIENT_VERSION_REVISION`
- - Don't forget to set `_CLIENT_VERSION_IS_RELEASE` to `true`
-- `src/clientversion.h`: (this mirrors `configure.ac` - see issue #3539)
- - `CLIENT_VERSION_MAJOR`
- - `CLIENT_VERSION_MINOR`
- - `CLIENT_VERSION_REVISION`
- - Don't forget to set `CLIENT_VERSION_IS_RELEASE` to `true`
-- `doc/README.md` and `doc/README_windows.txt`
-- `doc/Doxyfile`: `PROJECT_NUMBER` contains the full version
-- `contrib/gitian-descriptors/*.yml`: usually one'd want to do this on master after branching off the release - but be sure to at least do it before a new major release
+### Bitcoin maintainers/release engineers, suggestion for writing release notes
Write release notes. git shortlog helps a lot, for example:
diff --git a/src/Makefile.am b/src/Makefile.am
index 30d027315a..cb88171348 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -98,6 +98,7 @@ BITCOIN_CORE_H = \
core_io.h \
core_memusage.h \
cuckoocache.h \
+ fs.h \
httprpc.h \
httpserver.h \
indirectmap.h \
@@ -156,6 +157,7 @@ BITCOIN_CORE_H = \
wallet/coincontrol.h \
wallet/crypter.h \
wallet/db.h \
+ wallet/feebumper.h \
wallet/rpcwallet.h \
wallet/wallet.h \
wallet/walletdb.h \
@@ -230,6 +232,7 @@ libbitcoin_wallet_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_wallet_a_SOURCES = \
wallet/crypter.cpp \
wallet/db.cpp \
+ wallet/feebumper.cpp \
wallet/rpcdump.cpp \
wallet/rpcwallet.cpp \
wallet/wallet.cpp \
@@ -243,6 +246,8 @@ crypto_libbitcoin_crypto_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
crypto_libbitcoin_crypto_a_SOURCES = \
crypto/aes.cpp \
crypto/aes.h \
+ crypto/chacha20.h \
+ crypto/chacha20.cpp \
crypto/common.h \
crypto/hmac_sha256.cpp \
crypto/hmac_sha256.h \
@@ -326,6 +331,7 @@ libbitcoin_util_a_SOURCES = \
compat/glibc_sanity.cpp \
compat/glibcxx_sanity.cpp \
compat/strnlen.cpp \
+ fs.cpp \
random.cpp \
rpc/protocol.cpp \
support/cleanse.cpp \
@@ -458,6 +464,14 @@ DISTCLEANFILES = obj/build.h
EXTRA_DIST = $(CTAES_DIST)
+
+config/bitcoin-config.h: config/stamp-h1
+ @$(MAKE) -C $(top_builddir) $(subdir)/$(@)
+config/stamp-h1: $(top_srcdir)/$(subdir)/config/bitcoin-config.h.in $(top_builddir)/config.status
+ $(AM_V_at)$(MAKE) -C $(top_builddir) $(subdir)/$(@)
+$(top_srcdir)/$(subdir)/config/bitcoin-config.h.in: $(am__configure_deps)
+ $(AM_V_at)$(MAKE) -C $(top_srcdir) $(subdir)/config/bitcoin-config.h.in
+
clean-local:
-$(MAKE) -C secp256k1 clean
-$(MAKE) -C univalue clean
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index fe0ed59fe2..10cb7e775a 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -57,8 +57,8 @@ BITCOIN_TESTS =\
test/policyestimator_tests.cpp \
test/pow_tests.cpp \
test/prevector_tests.cpp \
- test/random_tests.cpp \
test/raii_event_tests.cpp \
+ test/random_tests.cpp \
test/reverselock_tests.cpp \
test/rpc_tests.cpp \
test/sanity_tests.cpp \
@@ -97,7 +97,7 @@ endif
test_test_bitcoin_SOURCES = $(BITCOIN_TESTS) $(JSON_TEST_FILES) $(RAW_TEST_FILES)
test_test_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -I$(builddir)/test/ $(TESTDEFS) $(EVENT_CFLAGS)
test_test_bitcoin_LDADD = $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) $(LIBMEMENV) \
- $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) $(EVENT_LIBS)
+ $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) $(EVENT_LIBS) $(EVENT_PTHREADS_LIBS)
test_test_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
if ENABLE_WALLET
test_test_bitcoin_LDADD += $(LIBBITCOIN_WALLET)
diff --git a/src/addrdb.cpp b/src/addrdb.cpp
index 224d3921cc..a3743cd0d4 100644
--- a/src/addrdb.cpp
+++ b/src/addrdb.cpp
@@ -8,13 +8,13 @@
#include "addrman.h"
#include "chainparams.h"
#include "clientversion.h"
+#include "fs.h"
#include "hash.h"
#include "random.h"
#include "streams.h"
#include "tinyformat.h"
#include "util.h"
-#include <boost/filesystem.hpp>
CBanDB::CBanDB()
{
@@ -36,8 +36,8 @@ bool CBanDB::Write(const banmap_t& banSet)
ssBanlist << hash;
// open temp output file, and associate with CAutoFile
- boost::filesystem::path pathTmp = GetDataDir() / tmpfn;
- FILE *file = fopen(pathTmp.string().c_str(), "wb");
+ fs::path pathTmp = GetDataDir() / tmpfn;
+ FILE *file = fsbridge::fopen(pathTmp, "wb");
CAutoFile fileout(file, SER_DISK, CLIENT_VERSION);
if (fileout.IsNull())
return error("%s: Failed to open file %s", __func__, pathTmp.string());
@@ -62,13 +62,13 @@ bool CBanDB::Write(const banmap_t& banSet)
bool CBanDB::Read(banmap_t& banSet)
{
// open input file, and associate with CAutoFile
- FILE *file = fopen(pathBanlist.string().c_str(), "rb");
+ FILE *file = fsbridge::fopen(pathBanlist, "rb");
CAutoFile filein(file, SER_DISK, CLIENT_VERSION);
if (filein.IsNull())
return error("%s: Failed to open file %s", __func__, pathBanlist.string());
// use file size to size memory buffer
- uint64_t fileSize = boost::filesystem::file_size(pathBanlist);
+ uint64_t fileSize = fs::file_size(pathBanlist);
uint64_t dataSize = 0;
// Don't try to resize to a negative number if file is small
if (fileSize >= sizeof(uint256))
@@ -133,8 +133,8 @@ bool CAddrDB::Write(const CAddrMan& addr)
ssPeers << hash;
// open temp output file, and associate with CAutoFile
- boost::filesystem::path pathTmp = GetDataDir() / tmpfn;
- FILE *file = fopen(pathTmp.string().c_str(), "wb");
+ fs::path pathTmp = GetDataDir() / tmpfn;
+ FILE *file = fsbridge::fopen(pathTmp, "wb");
CAutoFile fileout(file, SER_DISK, CLIENT_VERSION);
if (fileout.IsNull())
return error("%s: Failed to open file %s", __func__, pathTmp.string());
@@ -159,13 +159,13 @@ bool CAddrDB::Write(const CAddrMan& addr)
bool CAddrDB::Read(CAddrMan& addr)
{
// open input file, and associate with CAutoFile
- FILE *file = fopen(pathAddr.string().c_str(), "rb");
+ FILE *file = fsbridge::fopen(pathAddr, "rb");
CAutoFile filein(file, SER_DISK, CLIENT_VERSION);
if (filein.IsNull())
return error("%s: Failed to open file %s", __func__, pathAddr.string());
// use file size to size memory buffer
- uint64_t fileSize = boost::filesystem::file_size(pathAddr);
+ uint64_t fileSize = fs::file_size(pathAddr);
uint64_t dataSize = 0;
// Don't try to resize to a negative number if file is small
if (fileSize >= sizeof(uint256))
diff --git a/src/addrdb.h b/src/addrdb.h
index ab985b10cb..c3d509bd3a 100644
--- a/src/addrdb.h
+++ b/src/addrdb.h
@@ -6,11 +6,11 @@
#ifndef BITCOIN_ADDRDB_H
#define BITCOIN_ADDRDB_H
+#include "fs.h"
#include "serialize.h"
#include <string>
#include <map>
-#include <boost/filesystem/path.hpp>
class CSubNet;
class CAddrMan;
@@ -80,7 +80,7 @@ typedef std::map<CSubNet, CBanEntry> banmap_t;
class CAddrDB
{
private:
- boost::filesystem::path pathAddr;
+ fs::path pathAddr;
public:
CAddrDB();
bool Write(const CAddrMan& addr);
@@ -92,7 +92,7 @@ public:
class CBanDB
{
private:
- boost::filesystem::path pathBanlist;
+ fs::path pathBanlist;
public:
CBanDB();
bool Write(const banmap_t& banSet);
diff --git a/src/addrman.cpp b/src/addrman.cpp
index ed9c128eb2..4a408b9beb 100644
--- a/src/addrman.cpp
+++ b/src/addrman.cpp
@@ -351,8 +351,8 @@ CAddrInfo CAddrMan::Select_(bool newOnly)
int nKBucket = RandomInt(ADDRMAN_TRIED_BUCKET_COUNT);
int nKBucketPos = RandomInt(ADDRMAN_BUCKET_SIZE);
while (vvTried[nKBucket][nKBucketPos] == -1) {
- nKBucket = (nKBucket + insecure_rand.rand32()) % ADDRMAN_TRIED_BUCKET_COUNT;
- nKBucketPos = (nKBucketPos + insecure_rand.rand32()) % ADDRMAN_BUCKET_SIZE;
+ nKBucket = (nKBucket + insecure_rand.randbits(ADDRMAN_TRIED_BUCKET_COUNT_LOG2)) % ADDRMAN_TRIED_BUCKET_COUNT;
+ nKBucketPos = (nKBucketPos + insecure_rand.randbits(ADDRMAN_BUCKET_SIZE_LOG2)) % ADDRMAN_BUCKET_SIZE;
}
int nId = vvTried[nKBucket][nKBucketPos];
assert(mapInfo.count(nId) == 1);
@@ -368,8 +368,8 @@ CAddrInfo CAddrMan::Select_(bool newOnly)
int nUBucket = RandomInt(ADDRMAN_NEW_BUCKET_COUNT);
int nUBucketPos = RandomInt(ADDRMAN_BUCKET_SIZE);
while (vvNew[nUBucket][nUBucketPos] == -1) {
- nUBucket = (nUBucket + insecure_rand.rand32()) % ADDRMAN_NEW_BUCKET_COUNT;
- nUBucketPos = (nUBucketPos + insecure_rand.rand32()) % ADDRMAN_BUCKET_SIZE;
+ nUBucket = (nUBucket + insecure_rand.randbits(ADDRMAN_NEW_BUCKET_COUNT_LOG2)) % ADDRMAN_NEW_BUCKET_COUNT;
+ nUBucketPos = (nUBucketPos + insecure_rand.randbits(ADDRMAN_BUCKET_SIZE_LOG2)) % ADDRMAN_BUCKET_SIZE;
}
int nId = vvNew[nUBucket][nUBucketPos];
assert(mapInfo.count(nId) == 1);
diff --git a/src/addrman.h b/src/addrman.h
index f123b20b1b..70d907488f 100644
--- a/src/addrman.h
+++ b/src/addrman.h
@@ -136,13 +136,13 @@ public:
*/
//! total number of buckets for tried addresses
-#define ADDRMAN_TRIED_BUCKET_COUNT 256
+#define ADDRMAN_TRIED_BUCKET_COUNT_LOG2 8
//! total number of buckets for new addresses
-#define ADDRMAN_NEW_BUCKET_COUNT 1024
+#define ADDRMAN_NEW_BUCKET_COUNT_LOG2 10
//! maximum allowed number of entries in buckets for new and tried addresses
-#define ADDRMAN_BUCKET_SIZE 64
+#define ADDRMAN_BUCKET_SIZE_LOG2 6
//! over how many buckets entries with tried addresses from a single group (/16 for IPv4) are spread
#define ADDRMAN_TRIED_BUCKETS_PER_GROUP 8
@@ -171,6 +171,11 @@ public:
//! the maximum number of nodes to return in a getaddr call
#define ADDRMAN_GETADDR_MAX 2500
+//! Convenience
+#define ADDRMAN_TRIED_BUCKET_COUNT (1 << ADDRMAN_TRIED_BUCKET_COUNT_LOG2)
+#define ADDRMAN_NEW_BUCKET_COUNT (1 << ADDRMAN_NEW_BUCKET_COUNT_LOG2)
+#define ADDRMAN_BUCKET_SIZE (1 << ADDRMAN_BUCKET_SIZE_LOG2)
+
/**
* Stochastical (IP) address manager
*/
diff --git a/src/bench/checkblock.cpp b/src/bench/checkblock.cpp
index 230e4ca773..c6c932454a 100644
--- a/src/bench/checkblock.cpp
+++ b/src/bench/checkblock.cpp
@@ -22,7 +22,7 @@ static void DeserializeBlockTest(benchmark::State& state)
CDataStream stream((const char*)block_bench::block413567,
(const char*)&block_bench::block413567[sizeof(block_bench::block413567)],
SER_NETWORK, PROTOCOL_VERSION);
- char a;
+ char a = '\0';
stream.write(&a, 1); // Prevent compaction
while (state.KeepRunning()) {
@@ -37,7 +37,7 @@ static void DeserializeAndCheckBlockTest(benchmark::State& state)
CDataStream stream((const char*)block_bench::block413567,
(const char*)&block_bench::block413567[sizeof(block_bench::block413567)],
SER_NETWORK, PROTOCOL_VERSION);
- char a;
+ char a = '\0';
stream.write(&a, 1); // Prevent compaction
Consensus::Params params = Params(CBaseChainParams::MAIN).GetConsensus();
diff --git a/src/bench/checkqueue.cpp b/src/bench/checkqueue.cpp
index 6fa9fe4fe8..88a2a570f9 100644
--- a/src/bench/checkqueue.cpp
+++ b/src/bench/checkqueue.cpp
@@ -68,7 +68,7 @@ static void CCheckQueueSpeedPrevectorJob(benchmark::State& state)
PrevectorJob(){
}
PrevectorJob(FastRandomContext& insecure_rand){
- p.resize(insecure_rand.rand32() % (PREVECTOR_SIZE*2));
+ p.resize(insecure_rand.randrange(PREVECTOR_SIZE*2));
}
bool operator()()
{
diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp
index 06882f1514..42891f345b 100644
--- a/src/bench/coin_selection.cpp
+++ b/src/bench/coin_selection.cpp
@@ -48,7 +48,7 @@ static void CoinSelection(benchmark::State& state)
addCoin(1000 * COIN, wallet, vCoins);
addCoin(3 * COIN, wallet, vCoins);
- std::set<std::pair<const CWalletTx*, unsigned int> > setCoinsRet;
+ std::set<CInputCoin> setCoinsRet;
CAmount nValueRet;
bool success = wallet.SelectCoinsMinConf(1003 * COIN, 1, 6, 0, vCoins, setCoinsRet, nValueRet);
assert(success);
diff --git a/src/bench/crypto_hash.cpp b/src/bench/crypto_hash.cpp
index 737d3572ae..2914a36c7b 100644
--- a/src/bench/crypto_hash.cpp
+++ b/src/bench/crypto_hash.cpp
@@ -7,6 +7,7 @@
#include "bench.h"
#include "bloom.h"
#include "hash.h"
+#include "random.h"
#include "uint256.h"
#include "utiltime.h"
#include "crypto/ripemd160.h"
@@ -69,6 +70,28 @@ static void SipHash_32b(benchmark::State& state)
}
}
+static void FastRandom_32bit(benchmark::State& state)
+{
+ FastRandomContext rng(true);
+ uint32_t x = 0;
+ while (state.KeepRunning()) {
+ for (int i = 0; i < 1000000; i++) {
+ x += rng.rand32();
+ }
+ }
+}
+
+static void FastRandom_1bit(benchmark::State& state)
+{
+ FastRandomContext rng(true);
+ uint32_t x = 0;
+ while (state.KeepRunning()) {
+ for (int i = 0; i < 1000000; i++) {
+ x += rng.randbool();
+ }
+ }
+}
+
BENCHMARK(RIPEMD160);
BENCHMARK(SHA1);
BENCHMARK(SHA256);
@@ -76,3 +99,5 @@ BENCHMARK(SHA512);
BENCHMARK(SHA256_32b);
BENCHMARK(SipHash_32b);
+BENCHMARK(FastRandom_32bit);
+BENCHMARK(FastRandom_1bit);
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index ed8ca7e14c..5edd43d41e 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -9,12 +9,12 @@
#include "chainparamsbase.h"
#include "clientversion.h"
+#include "fs.h"
#include "rpc/client.h"
#include "rpc/protocol.h"
#include "util.h"
#include "utilstrencodings.h"
-#include <boost/filesystem/operations.hpp>
#include <stdio.h>
#include <event2/buffer.h>
@@ -96,7 +96,7 @@ static int AppInitRPC(int argc, char* argv[])
}
return EXIT_SUCCESS;
}
- if (!boost::filesystem::is_directory(GetDataDir(false))) {
+ if (!fs::is_directory(GetDataDir(false))) {
fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", GetArg("-datadir", "").c_str());
return EXIT_FAILURE;
}
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index 83b855cbcf..45738b5df8 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -657,11 +657,13 @@ static void MutateTx(CMutableTransaction& tx, const std::string& command,
MutateTxDelOutput(tx, commandVal);
else if (command == "outaddr")
MutateTxAddOutAddr(tx, commandVal);
- else if (command == "outpubkey")
+ else if (command == "outpubkey") {
+ if (!ecc) { ecc.reset(new Secp256k1Init()); }
MutateTxAddOutPubKey(tx, commandVal);
- else if (command == "outmultisig")
+ } else if (command == "outmultisig") {
+ if (!ecc) { ecc.reset(new Secp256k1Init()); }
MutateTxAddOutMultiSig(tx, commandVal);
- else if (command == "outscript")
+ } else if (command == "outscript")
MutateTxAddOutScript(tx, commandVal);
else if (command == "outdata")
MutateTxAddOutData(tx, commandVal);
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index a3d02afcdb..31680a8ec7 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -10,6 +10,7 @@
#include "chainparams.h"
#include "clientversion.h"
#include "compat.h"
+#include "fs.h"
#include "rpc/server.h"
#include "init.h"
#include "noui.h"
@@ -20,7 +21,6 @@
#include "utilstrencodings.h"
#include <boost/algorithm/string/predicate.hpp>
-#include <boost/filesystem.hpp>
#include <boost/thread.hpp>
#include <stdio.h>
@@ -97,7 +97,7 @@ bool AppInit(int argc, char* argv[])
try
{
- if (!boost::filesystem::is_directory(GetDataDir(false)))
+ if (!fs::is_directory(GetDataDir(false)))
{
fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", GetArg("-datadir", "").c_str());
return false;
diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp
index cb71a8b550..d013cc1450 100644
--- a/src/chainparamsbase.cpp
+++ b/src/chainparamsbase.cpp
@@ -103,8 +103,3 @@ std::string ChainNameFromCommandLine()
return CBaseChainParams::TESTNET;
return CBaseChainParams::MAIN;
}
-
-bool AreBaseParamsConfigured()
-{
- return pCurrentBaseParams != NULL;
-}
diff --git a/src/chainparamsbase.h b/src/chainparamsbase.h
index 59493afb9b..84350cf65b 100644
--- a/src/chainparamsbase.h
+++ b/src/chainparamsbase.h
@@ -53,10 +53,4 @@ void SelectBaseParams(const std::string& chain);
*/
std::string ChainNameFromCommandLine();
-/**
- * Return true if SelectBaseParamsFromCommandLine() has been called to select
- * a network.
- */
-bool AreBaseParamsConfigured();
-
#endif // BITCOIN_CHAINPARAMSBASE_H
diff --git a/src/checkqueue.h b/src/checkqueue.h
index ea12df66dd..63c104c02a 100644
--- a/src/checkqueue.h
+++ b/src/checkqueue.h
@@ -5,6 +5,8 @@
#ifndef BITCOIN_CHECKQUEUE_H
#define BITCOIN_CHECKQUEUE_H
+#include "sync.h"
+
#include <algorithm>
#include <vector>
diff --git a/src/clientversion.h b/src/clientversion.h
index 69154d546d..3d5392619b 100644
--- a/src/clientversion.h
+++ b/src/clientversion.h
@@ -7,29 +7,13 @@
#if defined(HAVE_CONFIG_H)
#include "config/bitcoin-config.h"
-#else
-
-/**
- * client versioning and copyright year
- */
-
-//! These need to be macros, as clientversion.cpp's and bitcoin*-res.rc's voodoo requires it
-#define CLIENT_VERSION_MAJOR 0
-#define CLIENT_VERSION_MINOR 14
-#define CLIENT_VERSION_REVISION 99
-#define CLIENT_VERSION_BUILD 0
-
-//! Set to true for release, false for prerelease or test build
-#define CLIENT_VERSION_IS_RELEASE false
-
-/**
- * Copyright year (2009-this)
- * Todo: update this when changing our copyright comments in the source
- */
-#define COPYRIGHT_YEAR 2017
-
#endif //HAVE_CONFIG_H
+// Check that required client information is defined
+#if !defined(CLIENT_VERSION_MAJOR) || !defined(CLIENT_VERSION_MINOR) || !defined(CLIENT_VERSION_REVISION) || !defined(CLIENT_VERSION_BUILD) || !defined(CLIENT_VERSION_IS_RELEASE) || !defined(COPYRIGHT_YEAR)
+#error Client version information missing: version is not defined by bitcoin-config.h or in any other way
+#endif
+
/**
* Converts the parameter X to a string after macro replacement on X has been performed.
* Don't merge these into one macro!
diff --git a/src/coins.h b/src/coins.h
index 8ee49b33ae..065bae56e9 100644
--- a/src/coins.h
+++ b/src/coins.h
@@ -6,6 +6,7 @@
#ifndef BITCOIN_COINS_H
#define BITCOIN_COINS_H
+#include "primitives/transaction.h"
#include "compressor.h"
#include "core_memusage.h"
#include "hash.h"
@@ -17,7 +18,7 @@
#include <stdint.h>
#include <boost/foreach.hpp>
-#include <boost/unordered_map.hpp>
+#include <unordered_map>
/**
* Pruned version of CTransaction: only retains metadata and unspent transaction outputs
@@ -279,7 +280,7 @@ struct CCoinsCacheEntry
CCoinsCacheEntry() : coins(), flags(0) {}
};
-typedef boost::unordered_map<uint256, CCoinsCacheEntry, SaltedTxidHasher> CCoinsMap;
+typedef std::unordered_map<uint256, CCoinsCacheEntry, SaltedTxidHasher> CCoinsMap;
/** Cursor for iterating over CoinsView state */
class CCoinsViewCursor
diff --git a/src/core_write.cpp b/src/core_write.cpp
index a3ca87c8b5..d116e617ee 100644
--- a/src/core_write.cpp
+++ b/src/core_write.cpp
@@ -151,6 +151,8 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry)
entry.pushKV("txid", tx.GetHash().GetHex());
entry.pushKV("hash", tx.GetWitnessHash().GetHex());
entry.pushKV("version", tx.nVersion);
+ entry.pushKV("size", (int)::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION));
+ entry.pushKV("vsize", (GetTransactionWeight(tx) + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR);
entry.pushKV("locktime", (int64_t)tx.nLockTime);
UniValue vin(UniValue::VARR);
diff --git a/src/crypto/chacha20.cpp b/src/crypto/chacha20.cpp
new file mode 100644
index 0000000000..816ae870e1
--- /dev/null
+++ b/src/crypto/chacha20.cpp
@@ -0,0 +1,180 @@
+// Copyright (c) 2017 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+// Based on the public domain implementation 'merged' by D. J. Bernstein
+// See https://cr.yp.to/chacha.html.
+
+#include "crypto/common.h"
+#include "crypto/chacha20.h"
+
+#include <string.h>
+
+constexpr static inline uint32_t rotl32(uint32_t v, int c) { return (v << c) | (v >> (32 - c)); }
+
+#define QUARTERROUND(a,b,c,d) \
+ a += b; d = rotl32(d ^ a, 16); \
+ c += d; b = rotl32(b ^ c, 12); \
+ a += b; d = rotl32(d ^ a, 8); \
+ c += d; b = rotl32(b ^ c, 7);
+
+static const unsigned char sigma[] = "expand 32-byte k";
+static const unsigned char tau[] = "expand 16-byte k";
+
+void ChaCha20::SetKey(const unsigned char* k, size_t keylen)
+{
+ const unsigned char *constants;
+
+ input[4] = ReadLE32(k + 0);
+ input[5] = ReadLE32(k + 4);
+ input[6] = ReadLE32(k + 8);
+ input[7] = ReadLE32(k + 12);
+ if (keylen == 32) { /* recommended */
+ k += 16;
+ constants = sigma;
+ } else { /* keylen == 16 */
+ constants = tau;
+ }
+ input[8] = ReadLE32(k + 0);
+ input[9] = ReadLE32(k + 4);
+ input[10] = ReadLE32(k + 8);
+ input[11] = ReadLE32(k + 12);
+ input[0] = ReadLE32(constants + 0);
+ input[1] = ReadLE32(constants + 4);
+ input[2] = ReadLE32(constants + 8);
+ input[3] = ReadLE32(constants + 12);
+ input[12] = 0;
+ input[13] = 0;
+ input[14] = 0;
+ input[15] = 0;
+}
+
+ChaCha20::ChaCha20()
+{
+ memset(input, 0, sizeof(input));
+}
+
+ChaCha20::ChaCha20(const unsigned char* k, size_t keylen)
+{
+ SetKey(k, keylen);
+}
+
+void ChaCha20::SetIV(uint64_t iv)
+{
+ input[14] = iv;
+ input[15] = iv >> 32;
+}
+
+void ChaCha20::Seek(uint64_t pos)
+{
+ input[12] = pos;
+ input[13] = pos >> 32;
+}
+
+void ChaCha20::Output(unsigned char* c, size_t bytes)
+{
+ uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
+ uint32_t j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15;
+ unsigned char *ctarget = NULL;
+ unsigned char tmp[64];
+ unsigned int i;
+
+ if (!bytes) return;
+
+ j0 = input[0];
+ j1 = input[1];
+ j2 = input[2];
+ j3 = input[3];
+ j4 = input[4];
+ j5 = input[5];
+ j6 = input[6];
+ j7 = input[7];
+ j8 = input[8];
+ j9 = input[9];
+ j10 = input[10];
+ j11 = input[11];
+ j12 = input[12];
+ j13 = input[13];
+ j14 = input[14];
+ j15 = input[15];
+
+ for (;;) {
+ if (bytes < 64) {
+ ctarget = c;
+ c = tmp;
+ }
+ x0 = j0;
+ x1 = j1;
+ x2 = j2;
+ x3 = j3;
+ x4 = j4;
+ x5 = j5;
+ x6 = j6;
+ x7 = j7;
+ x8 = j8;
+ x9 = j9;
+ x10 = j10;
+ x11 = j11;
+ x12 = j12;
+ x13 = j13;
+ x14 = j14;
+ x15 = j15;
+ for (i = 20;i > 0;i -= 2) {
+ QUARTERROUND( x0, x4, x8,x12)
+ QUARTERROUND( x1, x5, x9,x13)
+ QUARTERROUND( x2, x6,x10,x14)
+ QUARTERROUND( x3, x7,x11,x15)
+ QUARTERROUND( x0, x5,x10,x15)
+ QUARTERROUND( x1, x6,x11,x12)
+ QUARTERROUND( x2, x7, x8,x13)
+ QUARTERROUND( x3, x4, x9,x14)
+ }
+ x0 += j0;
+ x1 += j1;
+ x2 += j2;
+ x3 += j3;
+ x4 += j4;
+ x5 += j5;
+ x6 += j6;
+ x7 += j7;
+ x8 += j8;
+ x9 += j9;
+ x10 += j10;
+ x11 += j11;
+ x12 += j12;
+ x13 += j13;
+ x14 += j14;
+ x15 += j15;
+
+ ++j12;
+ if (!j12) ++j13;
+
+ WriteLE32(c + 0, x0);
+ WriteLE32(c + 4, x1);
+ WriteLE32(c + 8, x2);
+ WriteLE32(c + 12, x3);
+ WriteLE32(c + 16, x4);
+ WriteLE32(c + 20, x5);
+ WriteLE32(c + 24, x6);
+ WriteLE32(c + 28, x7);
+ WriteLE32(c + 32, x8);
+ WriteLE32(c + 36, x9);
+ WriteLE32(c + 40, x10);
+ WriteLE32(c + 44, x11);
+ WriteLE32(c + 48, x12);
+ WriteLE32(c + 52, x13);
+ WriteLE32(c + 56, x14);
+ WriteLE32(c + 60, x15);
+
+ if (bytes <= 64) {
+ if (bytes < 64) {
+ for (i = 0;i < bytes;++i) ctarget[i] = c[i];
+ }
+ input[12] = j12;
+ input[13] = j13;
+ return;
+ }
+ bytes -= 64;
+ c += 64;
+ }
+}
diff --git a/src/crypto/chacha20.h b/src/crypto/chacha20.h
new file mode 100644
index 0000000000..a305977bcd
--- /dev/null
+++ b/src/crypto/chacha20.h
@@ -0,0 +1,26 @@
+// Copyright (c) 2017 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_CRYPTO_CHACHA20_H
+#define BITCOIN_CRYPTO_CHACHA20_H
+
+#include <stdint.h>
+#include <stdlib.h>
+
+/** A PRNG class for ChaCha20. */
+class ChaCha20
+{
+private:
+ uint32_t input[16];
+
+public:
+ ChaCha20();
+ ChaCha20(const unsigned char* key, size_t keylen);
+ void SetKey(const unsigned char* key, size_t keylen);
+ void SetIV(uint64_t iv);
+ void Seek(uint64_t pos);
+ void Output(unsigned char* output, size_t bytes);
+};
+
+#endif // BITCOIN_CRYPTO_CHACHA20_H
diff --git a/src/crypto/common.h b/src/crypto/common.h
index 4a9d1150b6..bcca3d30ea 100644
--- a/src/crypto/common.h
+++ b/src/crypto/common.h
@@ -79,4 +79,25 @@ void static inline WriteBE64(unsigned char* ptr, uint64_t x)
memcpy(ptr, (char*)&v, 8);
}
+/** Return the smallest number n such that (x >> n) == 0 (or 64 if the highest bit in x is set. */
+uint64_t static inline CountBits(uint64_t x)
+{
+#ifdef HAVE_DECL___BUILTIN_CLZL
+ if (sizeof(unsigned long) >= sizeof(uint64_t)) {
+ return x ? 8 * sizeof(unsigned long) - __builtin_clzl(x) : 0;
+ }
+#endif
+#ifdef HAVE_DECL___BUILTIN_CLZLL
+ if (sizeof(unsigned long long) >= sizeof(uint64_t)) {
+ return x ? 8 * sizeof(unsigned long long) - __builtin_clzll(x) : 0;
+ }
+#endif
+ int ret = 0;
+ while (x) {
+ x >>= 1;
+ ++ret;
+ }
+ return ret;
+}
+
#endif // BITCOIN_CRYPTO_COMMON_H
diff --git a/src/dbwrapper.cpp b/src/dbwrapper.cpp
index 01fcd07420..3d2098c059 100644
--- a/src/dbwrapper.cpp
+++ b/src/dbwrapper.cpp
@@ -4,11 +4,10 @@
#include "dbwrapper.h"
+#include "fs.h"
#include "util.h"
#include "random.h"
-#include <boost/filesystem.hpp>
-
#include <leveldb/cache.h>
#include <leveldb/env.h>
#include <leveldb/filter_policy.h>
@@ -91,7 +90,7 @@ static leveldb::Options GetOptions(size_t nCacheSize)
return options;
}
-CDBWrapper::CDBWrapper(const boost::filesystem::path& path, size_t nCacheSize, bool fMemory, bool fWipe, bool obfuscate)
+CDBWrapper::CDBWrapper(const fs::path& path, size_t nCacheSize, bool fMemory, bool fWipe, bool obfuscate)
{
penv = NULL;
readoptions.verify_checksums = true;
diff --git a/src/dbwrapper.h b/src/dbwrapper.h
index 414df76a7c..b13e98b7a4 100644
--- a/src/dbwrapper.h
+++ b/src/dbwrapper.h
@@ -6,14 +6,13 @@
#define BITCOIN_DBWRAPPER_H
#include "clientversion.h"
+#include "fs.h"
#include "serialize.h"
#include "streams.h"
#include "util.h"
#include "utilstrencodings.h"
#include "version.h"
-#include <boost/filesystem/path.hpp>
-
#include <leveldb/db.h>
#include <leveldb/write_batch.h>
@@ -195,7 +194,7 @@ public:
* @param[in] obfuscate If true, store data obfuscated via simple XOR. If false, XOR
* with a zero'd byte array.
*/
- CDBWrapper(const boost::filesystem::path& path, size_t nCacheSize, bool fMemory = false, bool fWipe = false, bool obfuscate = false);
+ CDBWrapper(const fs::path& path, size_t nCacheSize, bool fMemory = false, bool fWipe = false, bool obfuscate = false);
~CDBWrapper();
template <typename K, typename V>
diff --git a/src/fs.cpp b/src/fs.cpp
new file mode 100644
index 0000000000..6f2b768de3
--- /dev/null
+++ b/src/fs.cpp
@@ -0,0 +1,17 @@
+#include "fs.h"
+
+#include <boost/filesystem.hpp>
+
+namespace fsbridge {
+
+FILE *fopen(const fs::path& p, const char *mode)
+{
+ return ::fopen(p.string().c_str(), mode);
+}
+
+FILE *freopen(const fs::path& p, const char *mode, FILE *stream)
+{
+ return ::freopen(p.string().c_str(), mode, stream);
+}
+
+} // fsbridge
diff --git a/src/fs.h b/src/fs.h
new file mode 100644
index 0000000000..585cbf9c38
--- /dev/null
+++ b/src/fs.h
@@ -0,0 +1,24 @@
+// Copyright (c) 2017 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_FS_H
+#define BITCOIN_FS_H
+
+#include <stdio.h>
+#include <string>
+
+#include <boost/filesystem.hpp>
+#include <boost/filesystem/fstream.hpp>
+#include <boost/filesystem/detail/utf8_codecvt_facet.hpp>
+
+/** Filesystem operations and types */
+namespace fs = boost::filesystem;
+
+/** Bridge operations to C stdio */
+namespace fsbridge {
+ FILE *fopen(const fs::path& p, const char *mode);
+ FILE *freopen(const fs::path& p, const char *mode, FILE *stream);
+};
+
+#endif
diff --git a/src/httpserver.cpp b/src/httpserver.cpp
index 347433eb11..e7df23295d 100644
--- a/src/httpserver.cpp
+++ b/src/httpserver.cpp
@@ -384,15 +384,13 @@ bool InitHTTPServer()
// Redirect libevent's logging to our own log
event_set_log_callback(&libevent_log_cb);
-#if LIBEVENT_VERSION_NUMBER >= 0x02010100
- // If -debug=libevent, set full libevent debugging.
- // Otherwise, disable all libevent debugging.
- if (LogAcceptCategory(BCLog::LIBEVENT)) {
- event_enable_debug_logging(EVENT_DBG_ALL);
- } else {
- event_enable_debug_logging(EVENT_DBG_NONE);
+ // Update libevent's log handling. Returns false if our version of
+ // libevent doesn't support debug logging, in which case we should
+ // clear the BCLog::LIBEVENT flag.
+ if (!UpdateHTTPServerLogging(logCategories & BCLog::LIBEVENT)) {
+ logCategories &= ~BCLog::LIBEVENT;
}
-#endif
+
#ifdef WIN32
evthread_use_windows_threads();
#else
@@ -435,6 +433,20 @@ bool InitHTTPServer()
return true;
}
+bool UpdateHTTPServerLogging(bool enable) {
+#if LIBEVENT_VERSION_NUMBER >= 0x02010100
+ if (enable) {
+ event_enable_debug_logging(EVENT_DBG_ALL);
+ } else {
+ event_enable_debug_logging(EVENT_DBG_NONE);
+ }
+ return true;
+#else
+ // Can't update libevent logging if version < 02010100
+ return false;
+#endif
+}
+
std::thread threadHTTP;
std::future<bool> threadResult;
diff --git a/src/httpserver.h b/src/httpserver.h
index b55b253bea..6be9950682 100644
--- a/src/httpserver.h
+++ b/src/httpserver.h
@@ -32,6 +32,10 @@ void InterruptHTTPServer();
/** Stop HTTP server */
void StopHTTPServer();
+/** Change logging level for libevent. Removes BCLog::LIBEVENT from logCategories if
+ * libevent doesn't support debug logging.*/
+bool UpdateHTTPServerLogging(bool enable);
+
/** Handler for requests to a certain HTTP path */
typedef std::function<bool(HTTPRequest* req, const std::string &)> HTTPRequestHandler;
/** Register handler for prefix.
diff --git a/src/init.cpp b/src/init.cpp
index 59e5a4a207..64f571f284 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -16,6 +16,7 @@
#include "checkpoints.h"
#include "compat/sanity.h"
#include "consensus/validation.h"
+#include "fs.h"
#include "httpserver.h"
#include "httprpc.h"
#include "key.h"
@@ -24,6 +25,7 @@
#include "netbase.h"
#include "net.h"
#include "net_processing.h"
+#include "policy/fees.h"
#include "policy/policy.h"
#include "rpc/server.h"
#include "rpc/register.h"
@@ -56,7 +58,6 @@
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/bind.hpp>
-#include <boost/filesystem.hpp>
#include <boost/function.hpp>
#include <boost/interprocess/sync/file_lock.hpp>
#include <boost/thread.hpp>
@@ -212,10 +213,10 @@ void Shutdown()
if (fFeeEstimatesInitialized)
{
- boost::filesystem::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME;
- CAutoFile est_fileout(fopen(est_path.string().c_str(), "wb"), SER_DISK, CLIENT_VERSION);
+ fs::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME;
+ CAutoFile est_fileout(fsbridge::fopen(est_path, "wb"), SER_DISK, CLIENT_VERSION);
if (!est_fileout.IsNull())
- mempool.WriteFeeEstimates(est_fileout);
+ ::feeEstimator.Write(est_fileout);
else
LogPrintf("%s: Failed to write fee estimates to %s\n", __func__, est_path.string());
fFeeEstimatesInitialized = false;
@@ -250,8 +251,8 @@ void Shutdown()
#ifndef WIN32
try {
- boost::filesystem::remove(GetPidFile());
- } catch (const boost::filesystem::filesystem_error& e) {
+ fs::remove(GetPidFile());
+ } catch (const fs::filesystem_error& e) {
LogPrintf("%s: Unable to remove pidfile: %s\n", __func__, e.what());
}
#endif
@@ -435,6 +436,8 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-dropmessagestest=<n>", "Randomly drop 1 of every <n> network messages");
strUsage += HelpMessageOpt("-fuzzmessagestest=<n>", "Randomly fuzz 1 of every <n> network messages");
strUsage += HelpMessageOpt("-stopafterblockimport", strprintf("Stop running after importing blocks from disk (default: %u)", DEFAULT_STOPAFTERBLOCKIMPORT));
+ strUsage += HelpMessageOpt("-stopatheight", strprintf("Stop running after reaching the given height in the main chain (default: %u)", DEFAULT_STOPATHEIGHT));
+
strUsage += HelpMessageOpt("-limitancestorcount=<n>", strprintf("Do not accept transactions if number of in-mempool ancestors is <n> or more (default: %u)", DEFAULT_ANCESTOR_LIMIT));
strUsage += HelpMessageOpt("-limitancestorsize=<n>", strprintf("Do not accept transactions whose size with all in-mempool ancestors exceeds <n> kilobytes (default: %u)", DEFAULT_ANCESTOR_SIZE_LIMIT));
strUsage += HelpMessageOpt("-limitdescendantcount=<n>", strprintf("Do not accept transactions if any ancestor would have <n> or more in-mempool descendants (default: %u)", DEFAULT_DESCENDANT_LIMIT));
@@ -444,8 +447,6 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-debug=<category>", strprintf(_("Output debugging information (default: %u, supplying <category> is optional)"), 0) + ". " +
_("If <category> is not supplied or if <category> = 1, output all debugging information.") + " " + _("<category> can be:") + " " + ListLogCategories() + ".");
strUsage += HelpMessageOpt("-debugexclude=<category>", strprintf(_("Exclude debugging information for a category. Can be used in conjunction with -debug=1 to output debug logs for all categories except one or more specified categories.")));
- if (showDebug)
- strUsage += HelpMessageOpt("-nodebug", "Turn off debugging messages, same as -debug=0");
strUsage += HelpMessageOpt("-help-debug", _("Show all debugging options (usage: --help -help-debug)"));
strUsage += HelpMessageOpt("-logips", strprintf(_("Include IP addresses in debug output (default: %u)"), DEFAULT_LOGIPS));
strUsage += HelpMessageOpt("-logtimestamps", strprintf(_("Prepend debug output with timestamp (default: %u)"), DEFAULT_LOGTIMESTAMPS));
@@ -579,14 +580,14 @@ struct CImportingNow
// works correctly.
void CleanupBlockRevFiles()
{
- std::map<std::string, boost::filesystem::path> mapBlockFiles;
+ std::map<std::string, fs::path> mapBlockFiles;
// Glob all blk?????.dat and rev?????.dat files from the blocks directory.
// Remove the rev files immediately and insert the blk file paths into an
// ordered map keyed by block file index.
LogPrintf("Removing unusable blk?????.dat and rev?????.dat files for -reindex with -prune\n");
- boost::filesystem::path blocksdir = GetDataDir() / "blocks";
- for (boost::filesystem::directory_iterator it(blocksdir); it != boost::filesystem::directory_iterator(); it++) {
+ fs::path blocksdir = GetDataDir() / "blocks";
+ for (fs::directory_iterator it(blocksdir); it != fs::directory_iterator(); it++) {
if (is_regular_file(*it) &&
it->path().filename().string().length() == 12 &&
it->path().filename().string().substr(8,4) == ".dat")
@@ -603,7 +604,7 @@ void CleanupBlockRevFiles()
// keeping a separate counter. Once we hit a gap (or if 0 doesn't exist)
// start removing block files.
int nContigCounter = 0;
- BOOST_FOREACH(const PAIRTYPE(std::string, boost::filesystem::path)& item, mapBlockFiles) {
+ BOOST_FOREACH(const PAIRTYPE(std::string, fs::path)& item, mapBlockFiles) {
if (atoi(item.first) == nContigCounter) {
nContigCounter++;
continue;
@@ -612,7 +613,7 @@ void CleanupBlockRevFiles()
}
}
-void ThreadImport(std::vector<boost::filesystem::path> vImportFiles)
+void ThreadImport(std::vector<fs::path> vImportFiles)
{
const CChainParams& chainparams = Params();
RenameThread("bitcoin-loadblk");
@@ -625,7 +626,7 @@ void ThreadImport(std::vector<boost::filesystem::path> vImportFiles)
int nFile = 0;
while (true) {
CDiskBlockPos pos(nFile, 0);
- if (!boost::filesystem::exists(GetBlockPosFilename(pos, "blk")))
+ if (!fs::exists(GetBlockPosFilename(pos, "blk")))
break; // No block files left to reindex
FILE *file = OpenBlockFile(pos, true);
if (!file)
@@ -642,11 +643,11 @@ void ThreadImport(std::vector<boost::filesystem::path> vImportFiles)
}
// hardcoded $DATADIR/bootstrap.dat
- boost::filesystem::path pathBootstrap = GetDataDir() / "bootstrap.dat";
- if (boost::filesystem::exists(pathBootstrap)) {
- FILE *file = fopen(pathBootstrap.string().c_str(), "rb");
+ fs::path pathBootstrap = GetDataDir() / "bootstrap.dat";
+ if (fs::exists(pathBootstrap)) {
+ FILE *file = fsbridge::fopen(pathBootstrap, "rb");
if (file) {
- boost::filesystem::path pathBootstrapOld = GetDataDir() / "bootstrap.dat.old";
+ fs::path pathBootstrapOld = GetDataDir() / "bootstrap.dat.old";
LogPrintf("Importing bootstrap.dat...\n");
LoadExternalBlockFile(chainparams, file);
RenameOver(pathBootstrap, pathBootstrapOld);
@@ -656,8 +657,8 @@ void ThreadImport(std::vector<boost::filesystem::path> vImportFiles)
}
// -loadblock=
- BOOST_FOREACH(const boost::filesystem::path& path, vImportFiles) {
- FILE *file = fopen(path.string().c_str(), "rb");
+ BOOST_FOREACH(const fs::path& path, vImportFiles) {
+ FILE *file = fsbridge::fopen(path, "rb");
if (file) {
LogPrintf("Importing blocks file %s...\n", path.string());
LoadExternalBlockFile(chainparams, file);
@@ -906,16 +907,16 @@ bool AppInitParameterInteraction()
InitWarning(strprintf(_("Reducing -maxconnections from %d to %d, because of system limitations."), nUserMaxConnections, nMaxConnections));
// ********************************************************* Step 3: parameter-to-internal-flags
-
if (mapMultiArgs.count("-debug") > 0) {
// Special-case: if -debug=0/-nodebug is set, turn off debugging messages
const std::vector<std::string>& categories = mapMultiArgs.at("-debug");
- if (!(GetBoolArg("-nodebug", false) || find(categories.begin(), categories.end(), std::string("0")) != categories.end())) {
+ if (find(categories.begin(), categories.end(), std::string("0")) == categories.end()) {
for (const auto& cat : categories) {
- uint32_t flag;
+ uint32_t flag = 0;
if (!GetLogCategory(&flag, &cat)) {
InitWarning(strprintf(_("Unsupported logging category %s=%s."), "-debug", cat));
+ continue;
}
logCategories |= flag;
}
@@ -926,9 +927,10 @@ bool AppInitParameterInteraction()
if (mapMultiArgs.count("-debugexclude") > 0) {
const std::vector<std::string>& excludedCategories = mapMultiArgs.at("-debugexclude");
for (const auto& cat : excludedCategories) {
- uint32_t flag;
+ uint32_t flag = 0;
if (!GetLogCategory(&flag, &cat)) {
InitWarning(strprintf(_("Unsupported logging category %s=%s."), "-debugexclude", cat));
+ continue;
}
logCategories &= ~flag;
}
@@ -1136,8 +1138,8 @@ static bool LockDataDirectory(bool probeOnly)
std::string strDataDir = GetDataDir().string();
// Make sure only a single Bitcoin process is using the data directory.
- boost::filesystem::path pathLockFile = GetDataDir() / ".lock";
- FILE* file = fopen(pathLockFile.string().c_str(), "a"); // empty lock file; created if it doesn't exist.
+ fs::path pathLockFile = GetDataDir() / ".lock";
+ FILE* file = fsbridge::fopen(pathLockFile, "a"); // empty lock file; created if it doesn't exist.
if (file) fclose(file);
try {
@@ -1185,7 +1187,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
#ifndef WIN32
CreatePidFile(GetPidFile(), getpid());
#endif
- if (GetBoolArg("-shrinkdebugfile", logCategories != BCLog::NONE)) {
+ if (GetBoolArg("-shrinkdebugfile", logCategories == BCLog::NONE)) {
// Do this first since it both loads a bunch of debug.log into memory,
// and because this needs to happen before any other debug.log printing
ShrinkDebugFile();
@@ -1401,7 +1403,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
fReindex = GetBoolArg("-reindex", false);
bool fReindexChainState = GetBoolArg("-reindex-chainstate", false);
- boost::filesystem::create_directories(GetDataDir() / "blocks");
+ fs::create_directories(GetDataDir() / "blocks");
// cache size calculations
int64_t nTotalCache = (GetArg("-dbcache", nDefaultDbCache) << 20);
@@ -1547,11 +1549,11 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
}
LogPrintf(" block index %15dms\n", GetTimeMillis() - nStart);
- boost::filesystem::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME;
- CAutoFile est_filein(fopen(est_path.string().c_str(), "rb"), SER_DISK, CLIENT_VERSION);
+ fs::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME;
+ CAutoFile est_filein(fsbridge::fopen(est_path, "rb"), SER_DISK, CLIENT_VERSION);
// Allowed to fail as this file IS missing on first startup.
if (!est_filein.IsNull())
- mempool.ReadFeeEstimates(est_filein);
+ ::feeEstimator.Read(est_filein);
fFeeEstimatesInitialized = true;
// ********************************************************* Step 8: load wallet
@@ -1603,7 +1605,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
if (IsArgSet("-blocknotify"))
uiInterface.NotifyBlockTip.connect(BlockNotifyCallback);
- std::vector<boost::filesystem::path> vImportFiles;
+ std::vector<fs::path> vImportFiles;
if (mapMultiArgs.count("-loadblock"))
{
BOOST_FOREACH(const std::string& strFile, mapMultiArgs.at("-loadblock"))
diff --git a/src/key.cpp b/src/key.cpp
index b4f0dc8202..5a75647f1a 100644
--- a/src/key.cpp
+++ b/src/key.cpp
@@ -131,14 +131,6 @@ void CKey::MakeNewKey(bool fCompressedIn) {
fCompressed = fCompressedIn;
}
-bool CKey::SetPrivKey(const CPrivKey &privkey, bool fCompressedIn) {
- if (!ec_privkey_import_der(secp256k1_context_sign, (unsigned char*)begin(), &privkey[0], privkey.size()))
- return false;
- fCompressed = fCompressedIn;
- fValid = true;
- return true;
-}
-
CPrivKey CKey::GetPrivKey() const {
assert(fValid);
CPrivKey privkey;
diff --git a/src/key.h b/src/key.h
index 925a8d186d..2c6f151727 100644
--- a/src/key.h
+++ b/src/key.h
@@ -94,9 +94,6 @@ public:
//! Check whether the public key corresponding to this private key is (to be) compressed.
bool IsCompressed() const { return fCompressed; }
- //! Initialize from a CPrivKey (serialized OpenSSL private key data).
- bool SetPrivKey(const CPrivKey& vchPrivKey, bool fCompressed);
-
//! Generate a new private key using a cryptographic PRNG.
void MakeNewKey(bool fCompressed);
diff --git a/src/memusage.h b/src/memusage.h
index 81e8702954..b69acafffd 100644
--- a/src/memusage.h
+++ b/src/memusage.h
@@ -12,6 +12,8 @@
#include <map>
#include <set>
#include <vector>
+#include <unordered_map>
+#include <unordered_set>
#include <boost/foreach.hpp>
#include <boost/unordered_set.hpp>
@@ -149,7 +151,7 @@ static inline size_t DynamicUsage(const std::shared_ptr<X>& p)
// Boost data structures
template<typename X>
-struct boost_unordered_node : private X
+struct unordered_node : private X
{
private:
void* ptr;
@@ -158,13 +160,25 @@ private:
template<typename X, typename Y>
static inline size_t DynamicUsage(const boost::unordered_set<X, Y>& s)
{
- return MallocUsage(sizeof(boost_unordered_node<X>)) * s.size() + MallocUsage(sizeof(void*) * s.bucket_count());
+ return MallocUsage(sizeof(unordered_node<X>)) * s.size() + MallocUsage(sizeof(void*) * s.bucket_count());
}
template<typename X, typename Y, typename Z>
static inline size_t DynamicUsage(const boost::unordered_map<X, Y, Z>& m)
{
- return MallocUsage(sizeof(boost_unordered_node<std::pair<const X, Y> >)) * m.size() + MallocUsage(sizeof(void*) * m.bucket_count());
+ return MallocUsage(sizeof(unordered_node<std::pair<const X, Y> >)) * m.size() + MallocUsage(sizeof(void*) * m.bucket_count());
+}
+
+template<typename X, typename Y>
+static inline size_t DynamicUsage(const std::unordered_set<X, Y>& s)
+{
+ return MallocUsage(sizeof(unordered_node<X>)) * s.size() + MallocUsage(sizeof(void*) * s.bucket_count());
+}
+
+template<typename X, typename Y, typename Z>
+static inline size_t DynamicUsage(const std::unordered_map<X, Y, Z>& m)
+{
+ return MallocUsage(sizeof(unordered_node<std::pair<const X, Y> >)) * m.size() + MallocUsage(sizeof(void*) * m.bucket_count());
}
}
diff --git a/src/merkleblock.cpp b/src/merkleblock.cpp
index e3f3e4621a..78d7cd6001 100644
--- a/src/merkleblock.cpp
+++ b/src/merkleblock.cpp
@@ -65,7 +65,7 @@ uint256 CPartialMerkleTree::CalcHash(int height, unsigned int pos, const std::ve
} else {
// calculate left hash
uint256 left = CalcHash(height-1, pos*2, vTxid), right;
- // calculate right hash if not beyond the end of the array - copy left hash otherwise1
+ // calculate right hash if not beyond the end of the array - copy left hash otherwise
if (pos*2+1 < CalcTreeWidth(height-1))
right = CalcHash(height-1, pos*2+1, vTxid);
else
diff --git a/src/merkleblock.h b/src/merkleblock.h
index 73cbf670ee..de4c5c8d29 100644
--- a/src/merkleblock.h
+++ b/src/merkleblock.h
@@ -23,7 +23,7 @@
* storing a bit for each traversed node, signifying whether the node is the
* parent of at least one matched leaf txid (or a matched txid itself). In
* case we are at the leaf level, or this bit is 0, its merkle node hash is
- * stored, and its children are not explorer further. Otherwise, no hash is
+ * stored, and its children are not explored further. Otherwise, no hash is
* stored, but we recurse into both (or the only) child branch. During
* decoding, the same depth-first traversal is performed, consuming bits and
* hashes as they written during encoding.
diff --git a/src/miner.cpp b/src/miner.cpp
index 386d75c4be..69a89bd617 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -317,9 +317,7 @@ int BlockAssembler::UpdatePackagesForAdded(const CTxMemPool::setEntries& already
bool BlockAssembler::SkipMapTxEntry(CTxMemPool::txiter it, indexed_modified_transaction_set &mapModifiedTx, CTxMemPool::setEntries &failedTx)
{
assert (it != mempool.mapTx.end());
- if (mapModifiedTx.count(it) || inBlock.count(it) || failedTx.count(it))
- return true;
- return false;
+ return mapModifiedTx.count(it) || inBlock.count(it) || failedTx.count(it);
}
void BlockAssembler::SortForBlock(const CTxMemPool::setEntries& package, CTxMemPool::txiter entry, std::vector<CTxMemPool::txiter>& sortedEntries)
diff --git a/src/net.cpp b/src/net.cpp
index cf94faf854..ed4c752606 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -418,10 +418,10 @@ void CConnman::DumpBanlist()
CBanDB bandb;
banmap_t banmap;
- SetBannedSetDirty(false);
GetBanned(banmap);
- if (!bandb.Write(banmap))
- SetBannedSetDirty(true);
+ if (bandb.Write(banmap)) {
+ SetBannedSetDirty(false);
+ }
LogPrint(BCLog::NET, "Flushed %d banned node ips/subnets to banlist.dat %dms\n",
banmap.size(), GetTimeMillis() - nStart);
@@ -541,6 +541,8 @@ bool CConnman::Unban(const CSubNet &subNet) {
void CConnman::GetBanned(banmap_t &banMap)
{
LOCK(cs_setBanned);
+ // Sweep the banlist so expired bans are not returned
+ SweepBanned();
banMap = setBanned; //create a thread safe copy
}
@@ -708,7 +710,7 @@ bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes, bool& complete
handled = msg.readData(pch, nBytes);
if (handled < 0)
- return false;
+ return false;
if (msg.in_data && msg.hdr.nMessageSize > MAX_PROTOCOL_MESSAGE_LENGTH) {
LogPrint(BCLog::NET, "Oversized message from peer=%i, disconnecting\n", GetId());
@@ -786,7 +788,7 @@ int CNetMessage::readHeader(const char *pch, unsigned int nBytes)
// reject messages larger than MAX_SIZE
if (hdr.nMessageSize > MAX_SIZE)
- return -1;
+ return -1;
// switch state to reading message data
in_data = true;
@@ -1299,59 +1301,55 @@ void CConnman::ThreadSocketHandler()
}
if (recvSet || errorSet)
{
+ // typical socket buffer is 8K-64K
+ char pchBuf[0x10000];
+ int nBytes = 0;
{
- {
- // typical socket buffer is 8K-64K
- char pchBuf[0x10000];
- int nBytes = 0;
- {
- LOCK(pnode->cs_hSocket);
- if (pnode->hSocket == INVALID_SOCKET)
- continue;
- nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT);
- }
- if (nBytes > 0)
- {
- bool notify = false;
- if (!pnode->ReceiveMsgBytes(pchBuf, nBytes, notify))
- pnode->CloseSocketDisconnect();
- RecordBytesRecv(nBytes);
- if (notify) {
- size_t nSizeAdded = 0;
- auto it(pnode->vRecvMsg.begin());
- for (; it != pnode->vRecvMsg.end(); ++it) {
- if (!it->complete())
- break;
- nSizeAdded += it->vRecv.size() + CMessageHeader::HEADER_SIZE;
- }
- {
- LOCK(pnode->cs_vProcessMsg);
- pnode->vProcessMsg.splice(pnode->vProcessMsg.end(), pnode->vRecvMsg, pnode->vRecvMsg.begin(), it);
- pnode->nProcessQueueSize += nSizeAdded;
- pnode->fPauseRecv = pnode->nProcessQueueSize > nReceiveFloodSize;
- }
- WakeMessageHandler();
- }
- }
- else if (nBytes == 0)
- {
- // socket closed gracefully
- if (!pnode->fDisconnect) {
- LogPrint(BCLog::NET, "socket closed\n");
- }
- pnode->CloseSocketDisconnect();
+ LOCK(pnode->cs_hSocket);
+ if (pnode->hSocket == INVALID_SOCKET)
+ continue;
+ nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT);
+ }
+ if (nBytes > 0)
+ {
+ bool notify = false;
+ if (!pnode->ReceiveMsgBytes(pchBuf, nBytes, notify))
+ pnode->CloseSocketDisconnect();
+ RecordBytesRecv(nBytes);
+ if (notify) {
+ size_t nSizeAdded = 0;
+ auto it(pnode->vRecvMsg.begin());
+ for (; it != pnode->vRecvMsg.end(); ++it) {
+ if (!it->complete())
+ break;
+ nSizeAdded += it->vRecv.size() + CMessageHeader::HEADER_SIZE;
}
- else if (nBytes < 0)
{
- // error
- int nErr = WSAGetLastError();
- if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS)
- {
- if (!pnode->fDisconnect)
- LogPrintf("socket recv error %s\n", NetworkErrorString(nErr));
- pnode->CloseSocketDisconnect();
- }
+ LOCK(pnode->cs_vProcessMsg);
+ pnode->vProcessMsg.splice(pnode->vProcessMsg.end(), pnode->vRecvMsg, pnode->vRecvMsg.begin(), it);
+ pnode->nProcessQueueSize += nSizeAdded;
+ pnode->fPauseRecv = pnode->nProcessQueueSize > nReceiveFloodSize;
}
+ WakeMessageHandler();
+ }
+ }
+ else if (nBytes == 0)
+ {
+ // socket closed gracefully
+ if (!pnode->fDisconnect) {
+ LogPrint(BCLog::NET, "socket closed\n");
+ }
+ pnode->CloseSocketDisconnect();
+ }
+ else if (nBytes < 0)
+ {
+ // error
+ int nErr = WSAGetLastError();
+ if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS)
+ {
+ if (!pnode->fDisconnect)
+ LogPrintf("socket recv error %s\n", NetworkErrorString(nErr));
+ pnode->CloseSocketDisconnect();
}
}
}
@@ -1585,6 +1583,9 @@ void CConnman::ThreadDNSAddressSeed()
LogPrintf("Loading addresses from DNS seeds (could take a while)\n");
BOOST_FOREACH(const CDNSSeedData &seed, vSeeds) {
+ if (interruptNet) {
+ return;
+ }
if (HaveNameProxy()) {
AddOneShot(seed.host);
} else {
@@ -1602,6 +1603,9 @@ void CConnman::ThreadDNSAddressSeed()
found++;
}
}
+ if (interruptNet) {
+ return;
+ }
// TODO: The seed name resolve may fail, yielding an IP of [::], which results in
// addrman assigning the same source to results from different seeds.
// This should switch to a hard-coded stable dummy IP for each seed name, so that the
diff --git a/src/net.h b/src/net.h
index 3bbe386173..4a63ef84e7 100644
--- a/src/net.h
+++ b/src/net.h
@@ -32,11 +32,9 @@
#include <arpa/inet.h>
#endif
-#include <boost/filesystem/path.hpp>
#include <boost/foreach.hpp>
#include <boost/signals2/signal.hpp>
-class CAddrMan;
class CScheduler;
class CNode;
@@ -92,7 +90,7 @@ static const ServiceFlags REQUIRED_SERVICES = NODE_NETWORK;
// NOTE: When adjusting this, update rpcnet:setban's help ("24h")
static const unsigned int DEFAULT_MISBEHAVING_BANTIME = 60 * 60 * 24; // Default 24-hour ban
-typedef int NodeId;
+typedef int64_t NodeId;
struct AddedNodeInfo
{
@@ -701,15 +699,15 @@ private:
public:
NodeId GetId() const {
- return id;
+ return id;
}
uint64_t GetLocalNonce() const {
- return nLocalHostNonce;
+ return nLocalHostNonce;
}
int GetMyStartingHeight() const {
- return nMyStartingHeight;
+ return nMyStartingHeight;
}
int GetRefCount()
@@ -760,7 +758,7 @@ public:
// after addresses were pushed.
if (_addr.IsValid() && !addrKnown.contains(_addr.GetKey())) {
if (vAddrToSend.size() >= MAX_ADDR_TO_SEND) {
- vAddrToSend[insecure_rand.rand32() % vAddrToSend.size()] = _addr;
+ vAddrToSend[insecure_rand.randrange(vAddrToSend.size())] = _addr;
} else {
vAddrToSend.push_back(_addr);
}
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 17653f542d..718a7de031 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -744,21 +744,23 @@ PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn) : connman(connmanI
recentRejects.reset(new CRollingBloomFilter(120000, 0.000001));
}
-void PeerLogicValidation::SyncTransaction(const CTransaction& tx, const CBlockIndex* pindex, int nPosInBlock) {
- if (nPosInBlock == CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK)
- return;
-
+void PeerLogicValidation::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex, const std::vector<CTransactionRef>& vtxConflicted) {
LOCK(cs_main);
std::vector<uint256> vOrphanErase;
- // Which orphan pool entries must we evict?
- for (size_t j = 0; j < tx.vin.size(); j++) {
- auto itByPrev = mapOrphanTransactionsByPrev.find(tx.vin[j].prevout);
- if (itByPrev == mapOrphanTransactionsByPrev.end()) continue;
- for (auto mi = itByPrev->second.begin(); mi != itByPrev->second.end(); ++mi) {
- const CTransaction& orphanTx = *(*mi)->second.tx;
- const uint256& orphanHash = orphanTx.GetHash();
- vOrphanErase.push_back(orphanHash);
+
+ for (const CTransactionRef& ptx : pblock->vtx) {
+ const CTransaction& tx = *ptx;
+
+ // Which orphan pool entries must we evict?
+ for (size_t j = 0; j < tx.vin.size(); j++) {
+ auto itByPrev = mapOrphanTransactionsByPrev.find(tx.vin[j].prevout);
+ if (itByPrev == mapOrphanTransactionsByPrev.end()) continue;
+ for (auto mi = itByPrev->second.begin(); mi != itByPrev->second.end(); ++mi) {
+ const CTransaction& orphanTx = *(*mi)->second.tx;
+ const uint256& orphanHash = orphanTx.GetHash();
+ vOrphanErase.push_back(orphanHash);
+ }
}
}
@@ -772,10 +774,12 @@ void PeerLogicValidation::SyncTransaction(const CTransaction& tx, const CBlockIn
}
}
+// All of the following cache a recent block, and are protected by cs_most_recent_block
static CCriticalSection cs_most_recent_block;
static std::shared_ptr<const CBlock> most_recent_block;
static std::shared_ptr<const CBlockHeaderAndShortTxIDs> most_recent_compact_block;
static uint256 most_recent_block_hash;
+static bool fWitnessesPresentInMostRecentCompactBlock;
void PeerLogicValidation::NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& pblock) {
std::shared_ptr<const CBlockHeaderAndShortTxIDs> pcmpctblock = std::make_shared<const CBlockHeaderAndShortTxIDs> (*pblock, true);
@@ -796,6 +800,7 @@ void PeerLogicValidation::NewPoWValidBlock(const CBlockIndex *pindex, const std:
most_recent_block_hash = hashBlock;
most_recent_block = pblock;
most_recent_compact_block = pcmpctblock;
+ fWitnessesPresentInMostRecentCompactBlock = fWitnessEnabled;
}
connman->ForEachNode([this, &pcmpctblock, pindex, &msgMaker, fWitnessEnabled, &hashBlock](CNode* pnode) {
@@ -856,8 +861,8 @@ void PeerLogicValidation::BlockChecked(const CBlock& block, const CValidationSta
int nDoS = 0;
if (state.IsInvalid(nDoS)) {
- if (it != mapBlockSource.end() && State(it->second.first)) {
- assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes
+ // Don't send reject message with code 0 or an internal reject code.
+ if (it != mapBlockSource.end() && State(it->second.first) && state.GetRejectCode() > 0 && state.GetRejectCode() < REJECT_INTERNAL) {
CBlockReject reject = {(unsigned char)state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), hash};
State(it->second.first)->rejects.push_back(reject);
if (nDoS > 0 && it->second.second)
@@ -988,6 +993,15 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
{
bool send = false;
BlockMap::iterator mi = mapBlockIndex.find(inv.hash);
+ std::shared_ptr<const CBlock> a_recent_block;
+ std::shared_ptr<const CBlockHeaderAndShortTxIDs> a_recent_compact_block;
+ bool fWitnessesPresentInARecentCompactBlock;
+ {
+ LOCK(cs_most_recent_block);
+ a_recent_block = most_recent_block;
+ a_recent_compact_block = most_recent_compact_block;
+ fWitnessesPresentInARecentCompactBlock = fWitnessesPresentInMostRecentCompactBlock;
+ }
if (mi != mapBlockIndex.end())
{
if (mi->second->nChainTx && !mi->second->IsValid(BLOCK_VALID_SCRIPTS) &&
@@ -997,11 +1011,6 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
// before ActivateBestChain but after AcceptBlock).
// In this case, we need to run ActivateBestChain prior to checking the relay
// conditions below.
- std::shared_ptr<const CBlock> a_recent_block;
- {
- LOCK(cs_most_recent_block);
- a_recent_block = most_recent_block;
- }
CValidationState dummy;
ActivateBestChain(dummy, Params(), a_recent_block);
}
@@ -1035,14 +1044,20 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
// it's available before trying to send.
if (send && (mi->second->nStatus & BLOCK_HAVE_DATA))
{
- // Send block from disk
- CBlock block;
- if (!ReadBlockFromDisk(block, (*mi).second, consensusParams))
- assert(!"cannot load block from disk");
+ std::shared_ptr<const CBlock> pblock;
+ if (a_recent_block && a_recent_block->GetHash() == (*mi).second->GetBlockHash()) {
+ pblock = a_recent_block;
+ } else {
+ // Send block from disk
+ std::shared_ptr<CBlock> pblockRead = std::make_shared<CBlock>();
+ if (!ReadBlockFromDisk(*pblockRead, (*mi).second, consensusParams))
+ assert(!"cannot load block from disk");
+ pblock = pblockRead;
+ }
if (inv.type == MSG_BLOCK)
- connman.PushMessage(pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCK, block));
+ connman.PushMessage(pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCK, *pblock));
else if (inv.type == MSG_WITNESS_BLOCK)
- connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::BLOCK, block));
+ connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::BLOCK, *pblock));
else if (inv.type == MSG_FILTERED_BLOCK)
{
bool sendMerkleBlock = false;
@@ -1051,7 +1066,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
LOCK(pfrom->cs_filter);
if (pfrom->pfilter) {
sendMerkleBlock = true;
- merkleBlock = CMerkleBlock(block, *pfrom->pfilter);
+ merkleBlock = CMerkleBlock(*pblock, *pfrom->pfilter);
}
}
if (sendMerkleBlock) {
@@ -1064,7 +1079,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
// however we MUST always provide at least what the remote peer needs
typedef std::pair<unsigned int, uint256> PairType;
BOOST_FOREACH(PairType& pair, merkleBlock.vMatchedTxn)
- connman.PushMessage(pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::TX, *block.vtx[pair.first]));
+ connman.PushMessage(pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::TX, *pblock->vtx[pair.first]));
}
// else
// no response
@@ -1078,10 +1093,15 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
bool fPeerWantsWitness = State(pfrom->GetId())->fWantsCmpctWitness;
int nSendFlags = fPeerWantsWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS;
if (CanDirectFetch(consensusParams) && mi->second->nHeight >= chainActive.Height() - MAX_CMPCTBLOCK_DEPTH) {
- CBlockHeaderAndShortTxIDs cmpctblock(block, fPeerWantsWitness);
- connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock));
- } else
- connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCK, block));
+ if ((fPeerWantsWitness || !fWitnessesPresentInARecentCompactBlock) && a_recent_compact_block && a_recent_compact_block->header.GetHash() == mi->second->GetBlockHash()) {
+ connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *a_recent_compact_block));
+ } else {
+ CBlockHeaderAndShortTxIDs cmpctblock(*pblock, fPeerWantsWitness);
+ connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock));
+ }
+ } else {
+ connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCK, *pblock));
+ }
}
// Trigger the peer node to send a getblocks request for the next batch of inventory
@@ -1945,7 +1965,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
LogPrint(BCLog::MEMPOOLREJ, "%s from peer=%d was not accepted: %s\n", tx.GetHash().ToString(),
pfrom->id,
FormatStateMessage(state));
- if (state.GetRejectCode() < REJECT_INTERNAL) // Never send AcceptToMemoryPool's internal codes over P2P
+ if (state.GetRejectCode() > 0 && state.GetRejectCode() < REJECT_INTERNAL) // Never send AcceptToMemoryPool's internal codes over P2P
connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::REJECT, strCommand, (unsigned char)state.GetRejectCode(),
state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash));
if (nDoS > 0) {
@@ -2661,100 +2681,100 @@ bool ProcessMessages(CNode* pfrom, CConnman& connman, const std::atomic<bool>& i
// this maintains the order of responses
if (!pfrom->vRecvGetData.empty()) return true;
- // Don't bother if send buffer is too full to respond anyway
- if (pfrom->fPauseSend)
- return false;
+ // Don't bother if send buffer is too full to respond anyway
+ if (pfrom->fPauseSend)
+ return false;
- std::list<CNetMessage> msgs;
- {
- LOCK(pfrom->cs_vProcessMsg);
- if (pfrom->vProcessMsg.empty())
- return false;
- // Just take one message
- msgs.splice(msgs.begin(), pfrom->vProcessMsg, pfrom->vProcessMsg.begin());
- pfrom->nProcessQueueSize -= msgs.front().vRecv.size() + CMessageHeader::HEADER_SIZE;
- pfrom->fPauseRecv = pfrom->nProcessQueueSize > connman.GetReceiveFloodSize();
- fMoreWork = !pfrom->vProcessMsg.empty();
- }
- CNetMessage& msg(msgs.front());
-
- msg.SetVersion(pfrom->GetRecvVersion());
- // Scan for message start
- if (memcmp(msg.hdr.pchMessageStart, chainparams.MessageStart(), CMessageHeader::MESSAGE_START_SIZE) != 0) {
- LogPrintf("PROCESSMESSAGE: INVALID MESSAGESTART %s peer=%d\n", SanitizeString(msg.hdr.GetCommand()), pfrom->id);
- pfrom->fDisconnect = true;
+ std::list<CNetMessage> msgs;
+ {
+ LOCK(pfrom->cs_vProcessMsg);
+ if (pfrom->vProcessMsg.empty())
return false;
- }
+ // Just take one message
+ msgs.splice(msgs.begin(), pfrom->vProcessMsg, pfrom->vProcessMsg.begin());
+ pfrom->nProcessQueueSize -= msgs.front().vRecv.size() + CMessageHeader::HEADER_SIZE;
+ pfrom->fPauseRecv = pfrom->nProcessQueueSize > connman.GetReceiveFloodSize();
+ fMoreWork = !pfrom->vProcessMsg.empty();
+ }
+ CNetMessage& msg(msgs.front());
+
+ msg.SetVersion(pfrom->GetRecvVersion());
+ // Scan for message start
+ if (memcmp(msg.hdr.pchMessageStart, chainparams.MessageStart(), CMessageHeader::MESSAGE_START_SIZE) != 0) {
+ LogPrintf("PROCESSMESSAGE: INVALID MESSAGESTART %s peer=%d\n", SanitizeString(msg.hdr.GetCommand()), pfrom->id);
+ pfrom->fDisconnect = true;
+ return false;
+ }
- // Read header
- CMessageHeader& hdr = msg.hdr;
- if (!hdr.IsValid(chainparams.MessageStart()))
- {
- LogPrintf("PROCESSMESSAGE: ERRORS IN HEADER %s peer=%d\n", SanitizeString(hdr.GetCommand()), pfrom->id);
- return fMoreWork;
- }
- std::string strCommand = hdr.GetCommand();
+ // Read header
+ CMessageHeader& hdr = msg.hdr;
+ if (!hdr.IsValid(chainparams.MessageStart()))
+ {
+ LogPrintf("PROCESSMESSAGE: ERRORS IN HEADER %s peer=%d\n", SanitizeString(hdr.GetCommand()), pfrom->id);
+ return fMoreWork;
+ }
+ std::string strCommand = hdr.GetCommand();
+
+ // Message size
+ unsigned int nMessageSize = hdr.nMessageSize;
- // Message size
- unsigned int nMessageSize = hdr.nMessageSize;
+ // Checksum
+ CDataStream& vRecv = msg.vRecv;
+ const uint256& hash = msg.GetMessageHash();
+ if (memcmp(hash.begin(), hdr.pchChecksum, CMessageHeader::CHECKSUM_SIZE) != 0)
+ {
+ LogPrintf("%s(%s, %u bytes): CHECKSUM ERROR expected %s was %s\n", __func__,
+ SanitizeString(strCommand), nMessageSize,
+ HexStr(hash.begin(), hash.begin()+CMessageHeader::CHECKSUM_SIZE),
+ HexStr(hdr.pchChecksum, hdr.pchChecksum+CMessageHeader::CHECKSUM_SIZE));
+ return fMoreWork;
+ }
- // Checksum
- CDataStream& vRecv = msg.vRecv;
- const uint256& hash = msg.GetMessageHash();
- if (memcmp(hash.begin(), hdr.pchChecksum, CMessageHeader::CHECKSUM_SIZE) != 0)
+ // Process message
+ bool fRet = false;
+ try
+ {
+ fRet = ProcessMessage(pfrom, strCommand, vRecv, msg.nTime, chainparams, connman, interruptMsgProc);
+ if (interruptMsgProc)
+ return false;
+ if (!pfrom->vRecvGetData.empty())
+ fMoreWork = true;
+ }
+ catch (const std::ios_base::failure& e)
+ {
+ connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_MALFORMED, std::string("error parsing message")));
+ if (strstr(e.what(), "end of data"))
{
- LogPrintf("%s(%s, %u bytes): CHECKSUM ERROR expected %s was %s\n", __func__,
- SanitizeString(strCommand), nMessageSize,
- HexStr(hash.begin(), hash.begin()+CMessageHeader::CHECKSUM_SIZE),
- HexStr(hdr.pchChecksum, hdr.pchChecksum+CMessageHeader::CHECKSUM_SIZE));
- return fMoreWork;
+ // Allow exceptions from under-length message on vRecv
+ LogPrintf("%s(%s, %u bytes): Exception '%s' caught, normally caused by a message being shorter than its stated length\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
}
-
- // Process message
- bool fRet = false;
- try
+ else if (strstr(e.what(), "size too large"))
{
- fRet = ProcessMessage(pfrom, strCommand, vRecv, msg.nTime, chainparams, connman, interruptMsgProc);
- if (interruptMsgProc)
- return false;
- if (!pfrom->vRecvGetData.empty())
- fMoreWork = true;
+ // Allow exceptions from over-long size
+ LogPrintf("%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
}
- catch (const std::ios_base::failure& e)
+ else if (strstr(e.what(), "non-canonical ReadCompactSize()"))
{
- connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_MALFORMED, std::string("error parsing message")));
- if (strstr(e.what(), "end of data"))
- {
- // Allow exceptions from under-length message on vRecv
- LogPrintf("%s(%s, %u bytes): Exception '%s' caught, normally caused by a message being shorter than its stated length\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
- }
- else if (strstr(e.what(), "size too large"))
- {
- // Allow exceptions from over-long size
- LogPrintf("%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
- }
- else if (strstr(e.what(), "non-canonical ReadCompactSize()"))
- {
- // Allow exceptions from non-canonical encoding
- LogPrintf("%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
- }
- else
- {
- PrintExceptionContinue(&e, "ProcessMessages()");
- }
+ // Allow exceptions from non-canonical encoding
+ LogPrintf("%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
}
- catch (const std::exception& e) {
+ else
+ {
PrintExceptionContinue(&e, "ProcessMessages()");
- } catch (...) {
- PrintExceptionContinue(NULL, "ProcessMessages()");
}
+ }
+ catch (const std::exception& e) {
+ PrintExceptionContinue(&e, "ProcessMessages()");
+ } catch (...) {
+ PrintExceptionContinue(NULL, "ProcessMessages()");
+ }
- if (!fRet) {
- LogPrintf("%s(%s, %u bytes) FAILED peer=%d\n", __func__, SanitizeString(strCommand), nMessageSize, pfrom->id);
- }
+ if (!fRet) {
+ LogPrintf("%s(%s, %u bytes) FAILED peer=%d\n", __func__, SanitizeString(strCommand), nMessageSize, pfrom->id);
+ }
- LOCK(cs_main);
- SendRejectsAndCheckIfBanned(pfrom, connman);
+ LOCK(cs_main);
+ SendRejectsAndCheckIfBanned(pfrom, connman);
return fMoreWork;
}
@@ -2972,7 +2992,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr
{
LOCK(cs_most_recent_block);
if (most_recent_block_hash == pBestIndex->GetBlockHash()) {
- if (state.fWantsCmpctWitness)
+ if (state.fWantsCmpctWitness || !fWitnessesPresentInMostRecentCompactBlock)
connman.PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *most_recent_compact_block));
else {
CBlockHeaderAndShortTxIDs cmpctblock(*most_recent_block, state.fWantsCmpctWitness);
diff --git a/src/net_processing.h b/src/net_processing.h
index 9e3f1b7156..f460595bc1 100644
--- a/src/net_processing.h
+++ b/src/net_processing.h
@@ -30,10 +30,10 @@ private:
public:
PeerLogicValidation(CConnman* connmanIn);
- virtual void SyncTransaction(const CTransaction& tx, const CBlockIndex* pindex, int nPosInBlock);
- virtual void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload);
- virtual void BlockChecked(const CBlock& block, const CValidationState& state);
- virtual void NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& pblock);
+ 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;
+ void BlockChecked(const CBlock& block, const CValidationState& state) override;
+ void NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& pblock) override;
};
struct CNodeStateStats {
diff --git a/src/netaddress.cpp b/src/netaddress.cpp
index ab07270f3f..34a7029862 100644
--- a/src/netaddress.cpp
+++ b/src/netaddress.cpp
@@ -179,12 +179,6 @@ bool CNetAddr::IsLocal() const
return false;
}
-bool CNetAddr::IsMulticast() const
-{
- return (IsIPv4() && (GetByte(3) & 0xF0) == 0xE0)
- || (GetByte(15) == 0xFF);
-}
-
bool CNetAddr::IsValid() const
{
// Cleanup 3-byte shifted addresses caused by garbage in size field
diff --git a/src/netaddress.h b/src/netaddress.h
index a85c2b7452..fbc4d1a65f 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -65,7 +65,6 @@ class CNetAddr
bool IsLocal() const;
bool IsRoutable() const;
bool IsValid() const;
- bool IsMulticast() const;
enum Network GetNetwork() const;
std::string ToString() const;
std::string ToStringIP() const;
diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp
index 38e07dc345..bd169f875a 100644
--- a/src/policy/fees.cpp
+++ b/src/policy/fees.cpp
@@ -7,14 +7,121 @@
#include "policy/policy.h"
#include "amount.h"
+#include "clientversion.h"
#include "primitives/transaction.h"
#include "random.h"
#include "streams.h"
#include "txmempool.h"
#include "util.h"
-void TxConfirmStats::Initialize(std::vector<double>& defaultBuckets,
- unsigned int maxConfirms, double _decay)
+/**
+ * We will instantiate an instance of this class to track transactions that were
+ * included in a block. We will lump transactions into a bucket according to their
+ * approximate feerate and then track how long it took for those txs to be included in a block
+ *
+ * The tracking of unconfirmed (mempool) transactions is completely independent of the
+ * historical tracking of transactions that have been confirmed in a block.
+ */
+class TxConfirmStats
+{
+private:
+ //Define the buckets we will group transactions into
+ std::vector<double> buckets; // The upper-bound of the range for the bucket (inclusive)
+ std::map<double, unsigned int> bucketMap; // Map of bucket upper-bound to index into all vectors by bucket
+
+ // For each bucket X:
+ // Count the total # of txs in each bucket
+ // Track the historical moving average of this total over blocks
+ std::vector<double> txCtAvg;
+ // and calculate the total for the current block to update the moving average
+ std::vector<int> curBlockTxCt;
+
+ // Count the total # of txs confirmed within Y blocks in each bucket
+ // Track the historical moving average of theses totals over blocks
+ std::vector<std::vector<double> > confAvg; // confAvg[Y][X]
+ // and calculate the totals for the current block to update the moving averages
+ std::vector<std::vector<int> > curBlockConf; // curBlockConf[Y][X]
+
+ // Sum the total feerate of all tx's in each bucket
+ // Track the historical moving average of this total over blocks
+ std::vector<double> avg;
+ // and calculate the total for the current block to update the moving average
+ std::vector<double> curBlockVal;
+
+ // Combine the conf counts with tx counts to calculate the confirmation % for each Y,X
+ // Combine the total value with the tx counts to calculate the avg feerate per bucket
+
+ double decay;
+
+ // Mempool counts of outstanding transactions
+ // For each bucket X, track the number of transactions in the mempool
+ // that are unconfirmed for each possible confirmation value Y
+ std::vector<std::vector<int> > unconfTxs; //unconfTxs[Y][X]
+ // transactions still unconfirmed after MAX_CONFIRMS for each bucket
+ std::vector<int> oldUnconfTxs;
+
+public:
+ /**
+ * Create new TxConfirmStats. This is called by BlockPolicyEstimator's
+ * constructor with default values.
+ * @param defaultBuckets contains the upper limits for the bucket boundaries
+ * @param maxConfirms max number of confirms to track
+ * @param decay how much to decay the historical moving average per block
+ */
+ TxConfirmStats(const std::vector<double>& defaultBuckets, unsigned int maxConfirms, double decay);
+
+ /** Clear the state of the curBlock variables to start counting for the new block */
+ void ClearCurrent(unsigned int nBlockHeight);
+
+ /**
+ * Record a new transaction data point in the current block stats
+ * @param blocksToConfirm the number of blocks it took this transaction to confirm
+ * @param val the feerate of the transaction
+ * @warning blocksToConfirm is 1-based and has to be >= 1
+ */
+ void Record(int blocksToConfirm, double val);
+
+ /** Record a new transaction entering the mempool*/
+ unsigned int NewTx(unsigned int nBlockHeight, double val);
+
+ /** Remove a transaction from mempool tracking stats*/
+ void removeTx(unsigned int entryHeight, unsigned int nBestSeenHeight,
+ unsigned int bucketIndex);
+
+ /** Update our estimates by decaying our historical moving average and updating
+ with the data gathered from the current block */
+ void UpdateMovingAverages();
+
+ /**
+ * Calculate a feerate estimate. Find the lowest value bucket (or range of buckets
+ * to make sure we have enough data points) whose transactions still have sufficient likelihood
+ * of being confirmed within the target number of confirmations
+ * @param confTarget target number of confirmations
+ * @param sufficientTxVal required average number of transactions per block in a bucket range
+ * @param minSuccess the success probability we require
+ * @param requireGreater return the lowest feerate such that all higher values pass minSuccess OR
+ * return the highest feerate such that all lower values fail minSuccess
+ * @param nBlockHeight the current block height
+ */
+ double EstimateMedianVal(int confTarget, double sufficientTxVal,
+ double minSuccess, bool requireGreater, unsigned int nBlockHeight) const;
+
+ /** Return the max number of confirms we're tracking */
+ unsigned int GetMaxConfirms() const { return confAvg.size(); }
+
+ /** Write state of estimation data to a file*/
+ void Write(CAutoFile& fileout) const;
+
+ /**
+ * Read saved state of estimation data from a file and replace all internal data structures and
+ * variables with this state.
+ */
+ void Read(CAutoFile& filein);
+};
+
+
+TxConfirmStats::TxConfirmStats(const std::vector<double>& defaultBuckets,
+ unsigned int maxConfirms, double _decay)
{
decay = _decay;
for (unsigned int i = 0; i < defaultBuckets.size(); i++) {
@@ -77,7 +184,7 @@ void TxConfirmStats::UpdateMovingAverages()
// returns -1 on error conditions
double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal,
double successBreakPoint, bool requireGreater,
- unsigned int nBlockHeight)
+ unsigned int nBlockHeight) const
{
// Counters for a bucket (or range of buckets)
double nConf = 0; // Number of tx's confirmed within the confTarget
@@ -173,7 +280,7 @@ double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal,
return median;
}
-void TxConfirmStats::Write(CAutoFile& fileout)
+void TxConfirmStats::Write(CAutoFile& fileout) const
{
fileout << decay;
fileout << buckets;
@@ -290,9 +397,10 @@ void TxConfirmStats::removeTx(unsigned int entryHeight, unsigned int nBestSeenHe
// of no harm to try to remove them again.
bool CBlockPolicyEstimator::removeTx(uint256 hash)
{
+ LOCK(cs_feeEstimator);
std::map<uint256, TxStatsInfo>::iterator pos = mapMemPoolTxs.find(hash);
if (pos != mapMemPoolTxs.end()) {
- feeStats.removeTx(pos->second.blockHeight, nBestSeenHeight, pos->second.bucketIndex);
+ feeStats->removeTx(pos->second.blockHeight, nBestSeenHeight, pos->second.bucketIndex);
mapMemPoolTxs.erase(hash);
return true;
} else {
@@ -310,11 +418,17 @@ CBlockPolicyEstimator::CBlockPolicyEstimator()
vfeelist.push_back(bucketBoundary);
}
vfeelist.push_back(INF_FEERATE);
- feeStats.Initialize(vfeelist, MAX_BLOCK_CONFIRMS, DEFAULT_DECAY);
+ feeStats = new TxConfirmStats(vfeelist, MAX_BLOCK_CONFIRMS, DEFAULT_DECAY);
+}
+
+CBlockPolicyEstimator::~CBlockPolicyEstimator()
+{
+ delete feeStats;
}
void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, bool validFeeEstimate)
{
+ LOCK(cs_feeEstimator);
unsigned int txHeight = entry.GetHeight();
uint256 hash = entry.GetTx().GetHash();
if (mapMemPoolTxs.count(hash)) {
@@ -343,7 +457,7 @@ void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, boo
CFeeRate feeRate(entry.GetFee(), entry.GetTxSize());
mapMemPoolTxs[hash].blockHeight = txHeight;
- mapMemPoolTxs[hash].bucketIndex = feeStats.NewTx(txHeight, (double)feeRate.GetFeePerK());
+ mapMemPoolTxs[hash].bucketIndex = feeStats->NewTx(txHeight, (double)feeRate.GetFeePerK());
}
bool CBlockPolicyEstimator::processBlockTx(unsigned int nBlockHeight, const CTxMemPoolEntry* entry)
@@ -367,13 +481,14 @@ bool CBlockPolicyEstimator::processBlockTx(unsigned int nBlockHeight, const CTxM
// Feerates are stored and reported as BTC-per-kb:
CFeeRate feeRate(entry->GetFee(), entry->GetTxSize());
- feeStats.Record(blocksToConfirm, (double)feeRate.GetFeePerK());
+ feeStats->Record(blocksToConfirm, (double)feeRate.GetFeePerK());
return true;
}
void CBlockPolicyEstimator::processBlock(unsigned int nBlockHeight,
std::vector<const CTxMemPoolEntry*>& entries)
{
+ LOCK(cs_feeEstimator);
if (nBlockHeight <= nBestSeenHeight) {
// Ignore side chains and re-orgs; assuming they are random
// they don't affect the estimate.
@@ -389,7 +504,7 @@ void CBlockPolicyEstimator::processBlock(unsigned int nBlockHeight,
nBestSeenHeight = nBlockHeight;
// Clear the current block state and update unconfirmed circular buffer
- feeStats.ClearCurrent(nBlockHeight);
+ feeStats->ClearCurrent(nBlockHeight);
unsigned int countedTxs = 0;
// Repopulate the current block states
@@ -399,7 +514,7 @@ void CBlockPolicyEstimator::processBlock(unsigned int nBlockHeight,
}
// Update all exponential averages with the current block state
- feeStats.UpdateMovingAverages();
+ feeStats->UpdateMovingAverages();
LogPrint(BCLog::ESTIMATEFEE, "Blockpolicy after updating estimates for %u of %u txs in block, since last block %u of %u tracked, new mempool map size %u\n",
countedTxs, entries.size(), trackedTxs, trackedTxs + untrackedTxs, mapMemPoolTxs.size());
@@ -408,14 +523,15 @@ void CBlockPolicyEstimator::processBlock(unsigned int nBlockHeight,
untrackedTxs = 0;
}
-CFeeRate CBlockPolicyEstimator::estimateFee(int confTarget)
+CFeeRate CBlockPolicyEstimator::estimateFee(int confTarget) const
{
+ LOCK(cs_feeEstimator);
// Return failure if trying to analyze a target we're not tracking
// It's not possible to get reasonable estimates for confTarget of 1
- if (confTarget <= 1 || (unsigned int)confTarget > feeStats.GetMaxConfirms())
+ if (confTarget <= 1 || (unsigned int)confTarget > feeStats->GetMaxConfirms())
return CFeeRate(0);
- double median = feeStats.EstimateMedianVal(confTarget, SUFFICIENT_FEETXS, MIN_SUCCESS_PCT, true, nBestSeenHeight);
+ double median = feeStats->EstimateMedianVal(confTarget, SUFFICIENT_FEETXS, MIN_SUCCESS_PCT, true, nBestSeenHeight);
if (median < 0)
return CFeeRate(0);
@@ -423,22 +539,28 @@ CFeeRate CBlockPolicyEstimator::estimateFee(int confTarget)
return CFeeRate(median);
}
-CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, int *answerFoundAtTarget, const CTxMemPool& pool)
+CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, int *answerFoundAtTarget, const CTxMemPool& pool) const
{
if (answerFoundAtTarget)
*answerFoundAtTarget = confTarget;
- // Return failure if trying to analyze a target we're not tracking
- if (confTarget <= 0 || (unsigned int)confTarget > feeStats.GetMaxConfirms())
- return CFeeRate(0);
-
- // It's not possible to get reasonable estimates for confTarget of 1
- if (confTarget == 1)
- confTarget = 2;
double median = -1;
- while (median < 0 && (unsigned int)confTarget <= feeStats.GetMaxConfirms()) {
- median = feeStats.EstimateMedianVal(confTarget++, SUFFICIENT_FEETXS, MIN_SUCCESS_PCT, true, nBestSeenHeight);
- }
+
+ {
+ LOCK(cs_feeEstimator);
+
+ // Return failure if trying to analyze a target we're not tracking
+ if (confTarget <= 0 || (unsigned int)confTarget > feeStats->GetMaxConfirms())
+ return CFeeRate(0);
+
+ // It's not possible to get reasonable estimates for confTarget of 1
+ if (confTarget == 1)
+ confTarget = 2;
+
+ while (median < 0 && (unsigned int)confTarget <= feeStats->GetMaxConfirms()) {
+ median = feeStats->EstimateMedianVal(confTarget++, SUFFICIENT_FEETXS, MIN_SUCCESS_PCT, true, nBestSeenHeight);
+ }
+ } // Must unlock cs_feeEstimator before taking mempool locks
if (answerFoundAtTarget)
*answerFoundAtTarget = confTarget - 1;
@@ -454,19 +576,40 @@ CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, int *answerFoun
return CFeeRate(median);
}
-void CBlockPolicyEstimator::Write(CAutoFile& fileout)
+bool CBlockPolicyEstimator::Write(CAutoFile& fileout) const
{
- fileout << nBestSeenHeight;
- feeStats.Write(fileout);
+ try {
+ LOCK(cs_feeEstimator);
+ fileout << 139900; // version required to read: 0.13.99 or later
+ fileout << CLIENT_VERSION; // version that wrote the file
+ fileout << nBestSeenHeight;
+ feeStats->Write(fileout);
+ }
+ catch (const std::exception&) {
+ LogPrintf("CBlockPolicyEstimator::Write(): unable to write policy estimator data (non-fatal)\n");
+ return false;
+ }
+ return true;
}
-void CBlockPolicyEstimator::Read(CAutoFile& filein, int nFileVersion)
+bool CBlockPolicyEstimator::Read(CAutoFile& filein)
{
- int nFileBestSeenHeight;
- filein >> nFileBestSeenHeight;
- feeStats.Read(filein);
- nBestSeenHeight = nFileBestSeenHeight;
- // if nVersionThatWrote < 139900 then another TxConfirmStats (for priority) follows but can be ignored.
+ try {
+ LOCK(cs_feeEstimator);
+ int nVersionRequired, nVersionThatWrote, nFileBestSeenHeight;
+ filein >> nVersionRequired >> nVersionThatWrote;
+ if (nVersionRequired > CLIENT_VERSION)
+ return error("CBlockPolicyEstimator::Read(): up-version (%d) fee estimate file", nVersionRequired);
+ filein >> nFileBestSeenHeight;
+ feeStats->Read(filein);
+ nBestSeenHeight = nFileBestSeenHeight;
+ // if nVersionThatWrote < 139900 then another TxConfirmStats (for priority) follows but can be ignored.
+ }
+ catch (const std::exception&) {
+ LogPrintf("CBlockPolicyEstimator::Read(): unable to read policy estimator data (non-fatal)\n");
+ return false;
+ }
+ return true;
}
FeeFilterRounder::FeeFilterRounder(const CFeeRate& minIncrementalFee)
diff --git a/src/policy/fees.h b/src/policy/fees.h
index dd01c90c45..34f07c7270 100644
--- a/src/policy/fees.h
+++ b/src/policy/fees.h
@@ -8,6 +8,7 @@
#include "amount.h"
#include "uint256.h"
#include "random.h"
+#include "sync.h"
#include <map>
#include <string>
@@ -17,6 +18,7 @@ class CAutoFile;
class CFeeRate;
class CTxMemPoolEntry;
class CTxMemPool;
+class TxConfirmStats;
/** \class CBlockPolicyEstimator
* The BlockPolicyEstimator is used for estimating the feerate needed
@@ -59,113 +61,6 @@ class CTxMemPool;
* they've been outstanding.
*/
-/**
- * We will instantiate an instance of this class to track transactions that were
- * included in a block. We will lump transactions into a bucket according to their
- * approximate feerate and then track how long it took for those txs to be included in a block
- *
- * The tracking of unconfirmed (mempool) transactions is completely independent of the
- * historical tracking of transactions that have been confirmed in a block.
- */
-class TxConfirmStats
-{
-private:
- //Define the buckets we will group transactions into
- std::vector<double> buckets; // The upper-bound of the range for the bucket (inclusive)
- std::map<double, unsigned int> bucketMap; // Map of bucket upper-bound to index into all vectors by bucket
-
- // For each bucket X:
- // Count the total # of txs in each bucket
- // Track the historical moving average of this total over blocks
- std::vector<double> txCtAvg;
- // and calculate the total for the current block to update the moving average
- std::vector<int> curBlockTxCt;
-
- // Count the total # of txs confirmed within Y blocks in each bucket
- // Track the historical moving average of theses totals over blocks
- std::vector<std::vector<double> > confAvg; // confAvg[Y][X]
- // and calculate the totals for the current block to update the moving averages
- std::vector<std::vector<int> > curBlockConf; // curBlockConf[Y][X]
-
- // Sum the total feerate of all tx's in each bucket
- // Track the historical moving average of this total over blocks
- std::vector<double> avg;
- // and calculate the total for the current block to update the moving average
- std::vector<double> curBlockVal;
-
- // Combine the conf counts with tx counts to calculate the confirmation % for each Y,X
- // Combine the total value with the tx counts to calculate the avg feerate per bucket
-
- double decay;
-
- // Mempool counts of outstanding transactions
- // For each bucket X, track the number of transactions in the mempool
- // that are unconfirmed for each possible confirmation value Y
- std::vector<std::vector<int> > unconfTxs; //unconfTxs[Y][X]
- // transactions still unconfirmed after MAX_CONFIRMS for each bucket
- std::vector<int> oldUnconfTxs;
-
-public:
- /**
- * Initialize the data structures. This is called by BlockPolicyEstimator's
- * constructor with default values.
- * @param defaultBuckets contains the upper limits for the bucket boundaries
- * @param maxConfirms max number of confirms to track
- * @param decay how much to decay the historical moving average per block
- */
- void Initialize(std::vector<double>& defaultBuckets, unsigned int maxConfirms, double decay);
-
- /** Clear the state of the curBlock variables to start counting for the new block */
- void ClearCurrent(unsigned int nBlockHeight);
-
- /**
- * Record a new transaction data point in the current block stats
- * @param blocksToConfirm the number of blocks it took this transaction to confirm
- * @param val the feerate of the transaction
- * @warning blocksToConfirm is 1-based and has to be >= 1
- */
- void Record(int blocksToConfirm, double val);
-
- /** Record a new transaction entering the mempool*/
- unsigned int NewTx(unsigned int nBlockHeight, double val);
-
- /** Remove a transaction from mempool tracking stats*/
- void removeTx(unsigned int entryHeight, unsigned int nBestSeenHeight,
- unsigned int bucketIndex);
-
- /** Update our estimates by decaying our historical moving average and updating
- with the data gathered from the current block */
- void UpdateMovingAverages();
-
- /**
- * Calculate a feerate estimate. Find the lowest value bucket (or range of buckets
- * to make sure we have enough data points) whose transactions still have sufficient likelihood
- * of being confirmed within the target number of confirmations
- * @param confTarget target number of confirmations
- * @param sufficientTxVal required average number of transactions per block in a bucket range
- * @param minSuccess the success probability we require
- * @param requireGreater return the lowest feerate such that all higher values pass minSuccess OR
- * return the highest feerate such that all lower values fail minSuccess
- * @param nBlockHeight the current block height
- */
- double EstimateMedianVal(int confTarget, double sufficientTxVal,
- double minSuccess, bool requireGreater, unsigned int nBlockHeight);
-
- /** Return the max number of confirms we're tracking */
- unsigned int GetMaxConfirms() { return confAvg.size(); }
-
- /** Write state of estimation data to a file*/
- void Write(CAutoFile& fileout);
-
- /**
- * Read saved state of estimation data from a file and replace all internal data structures and
- * variables with this state.
- */
- void Read(CAutoFile& filein);
-};
-
-
-
/** Track confirm delays up to 25 blocks, can't estimate beyond that */
static const unsigned int MAX_BLOCK_CONFIRMS = 25;
@@ -204,14 +99,12 @@ class CBlockPolicyEstimator
public:
/** Create new BlockPolicyEstimator and initialize stats tracking classes with default values */
CBlockPolicyEstimator();
+ ~CBlockPolicyEstimator();
/** Process all the transactions that have been included in a block */
void processBlock(unsigned int nBlockHeight,
std::vector<const CTxMemPoolEntry*>& entries);
- /** Process a transaction confirmed in a block*/
- bool processBlockTx(unsigned int nBlockHeight, const CTxMemPoolEntry* entry);
-
/** Process a transaction accepted to the mempool*/
void processTransaction(const CTxMemPoolEntry& entry, bool validFeeEstimate);
@@ -219,19 +112,19 @@ public:
bool removeTx(uint256 hash);
/** Return a feerate estimate */
- CFeeRate estimateFee(int confTarget);
+ CFeeRate estimateFee(int confTarget) const;
/** Estimate feerate needed to get be included in a block within
* confTarget blocks. If no answer can be given at confTarget, return an
* estimate at the lowest target where one can be given.
*/
- CFeeRate estimateSmartFee(int confTarget, int *answerFoundAtTarget, const CTxMemPool& pool);
+ CFeeRate estimateSmartFee(int confTarget, int *answerFoundAtTarget, const CTxMemPool& pool) const;
/** Write estimation data to a file */
- void Write(CAutoFile& fileout);
+ bool Write(CAutoFile& fileout) const;
/** Read estimation data from a file */
- void Read(CAutoFile& filein, int nFileVersion);
+ bool Read(CAutoFile& filein);
private:
CFeeRate minTrackedFee; //!< Passed to constructor to avoid dependency on main
@@ -247,10 +140,16 @@ private:
std::map<uint256, TxStatsInfo> mapMemPoolTxs;
/** Classes to track historical data on transaction confirmations */
- TxConfirmStats feeStats;
+ TxConfirmStats* feeStats;
unsigned int trackedTxs;
unsigned int untrackedTxs;
+
+ mutable CCriticalSection cs_feeEstimator;
+
+ /** Process a transaction confirmed in a block*/
+ bool processBlockTx(unsigned int nBlockHeight, const CTxMemPoolEntry* entry);
+
};
class FeeFilterRounder
diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp
index ec398f6627..6f8b6c2953 100644
--- a/src/policy/policy.cpp
+++ b/src/policy/policy.cpp
@@ -8,6 +8,7 @@
#include "policy/policy.h"
#include "validation.h"
+#include "coins.h"
#include "tinyformat.h"
#include "util.h"
#include "utilstrencodings.h"
diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h
index d413e8b087..5059030309 100644
--- a/src/primitives/transaction.h
+++ b/src/primitives/transaction.h
@@ -22,8 +22,8 @@ public:
uint256 hash;
uint32_t n;
- COutPoint() { SetNull(); }
- COutPoint(uint256 hashIn, uint32_t nIn) { hash = hashIn; n = nIn; }
+ COutPoint(): n((uint32_t) -1) { }
+ COutPoint(const uint256& hashIn, uint32_t nIn): hash(hashIn), n(nIn) { }
ADD_SERIALIZE_METHODS;
diff --git a/src/qt/bantablemodel.cpp b/src/qt/bantablemodel.cpp
index 4b34e73eb7..f8a99506c1 100644
--- a/src/qt/bantablemodel.cpp
+++ b/src/qt/bantablemodel.cpp
@@ -181,7 +181,5 @@ void BanTableModel::sort(int column, Qt::SortOrder order)
bool BanTableModel::shouldShow()
{
- if (priv->size() > 0)
- return true;
- return false;
+ return priv->size() > 0;
}
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 05a3bd71fd..23ec3ab434 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -10,6 +10,7 @@
#include "chainparams.h"
#include "clientmodel.h"
+#include "fs.h"
#include "guiconstants.h"
#include "guiutil.h"
#include "intro.h"
@@ -38,7 +39,6 @@
#include <stdint.h>
-#include <boost/filesystem/operations.hpp>
#include <boost/thread.hpp>
#include <QApplication>
@@ -608,7 +608,7 @@ int main(int argc, char *argv[])
/// 6. Determine availability of data directory and parse bitcoin.conf
/// - Do not call GetDataDir(true) before this step finishes
- if (!boost::filesystem::is_directory(GetDataDir(false)))
+ if (!fs::is_directory(GetDataDir(false)))
{
QMessageBox::critical(0, QObject::tr(PACKAGE_NAME),
QObject::tr("Error: Specified data directory \"%1\" does not exist.").arg(QString::fromStdString(GetArg("-datadir", ""))));
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index be79a67990..5c26baef9e 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -478,6 +478,7 @@ void BitcoinGUI::setClientModel(ClientModel *_clientModel)
connect(_clientModel, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int)));
connect(_clientModel, SIGNAL(networkActiveChanged(bool)), this, SLOT(setNetworkActive(bool)));
+ modalOverlay->setKnownBestHeight(_clientModel->getHeaderTipHeight(), QDateTime::fromTime_t(_clientModel->getHeaderTipTime()));
setNumBlocks(_clientModel->getNumBlocks(), _clientModel->getLastBlockDate(), _clientModel->getVerificationProgress(NULL), false);
connect(_clientModel, SIGNAL(numBlocksChanged(int,QDateTime,double,bool)), this, SLOT(setNumBlocks(int,QDateTime,double,bool)));
@@ -505,8 +506,6 @@ void BitcoinGUI::setClientModel(ClientModel *_clientModel)
// initialize the disable state of the tray icon with the current value in the model.
setTrayIconVisible(optionsModel->getHideTrayIcon());
}
-
- modalOverlay->setKnownBestHeight(clientModel->getHeaderTipHeight(), QDateTime::fromTime_t(clientModel->getHeaderTipTime()));
} else {
// Disable possibility to show main window via action
toggleHideAction->setEnabled(false);
diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp
index bb10e49422..de00eacdb9 100644
--- a/src/qt/clientmodel.cpp
+++ b/src/qt/clientmodel.cpp
@@ -9,6 +9,7 @@
#include "guiutil.h"
#include "peertablemodel.h"
+#include "chain.h"
#include "chainparams.h"
#include "checkpoints.h"
#include "clientversion.h"
@@ -36,6 +37,8 @@ ClientModel::ClientModel(OptionsModel *_optionsModel, QObject *parent) :
banTableModel(0),
pollTimer(0)
{
+ cachedBestHeaderHeight = -1;
+ cachedBestHeaderTime = -1;
peerTableModel = new PeerTableModel(this);
banTableModel = new BanTableModel(this);
pollTimer = new QTimer(this);
@@ -74,18 +77,28 @@ int ClientModel::getNumBlocks() const
int ClientModel::getHeaderTipHeight() const
{
- LOCK(cs_main);
- if (!pindexBestHeader)
- return 0;
- return pindexBestHeader->nHeight;
+ if (cachedBestHeaderHeight == -1) {
+ // make sure we initially populate the cache via a cs_main lock
+ // otherwise we need to wait for a tip update
+ LOCK(cs_main);
+ if (pindexBestHeader) {
+ cachedBestHeaderHeight = pindexBestHeader->nHeight;
+ cachedBestHeaderTime = pindexBestHeader->GetBlockTime();
+ }
+ }
+ return cachedBestHeaderHeight;
}
int64_t ClientModel::getHeaderTipTime() const
{
- LOCK(cs_main);
- if (!pindexBestHeader)
- return 0;
- return pindexBestHeader->GetBlockTime();
+ if (cachedBestHeaderTime == -1) {
+ LOCK(cs_main);
+ if (pindexBestHeader) {
+ cachedBestHeaderHeight = pindexBestHeader->nHeight;
+ cachedBestHeaderTime = pindexBestHeader->GetBlockTime();
+ }
+ }
+ return cachedBestHeaderTime;
}
quint64 ClientModel::getTotalBytesRecv() const
@@ -283,6 +296,11 @@ static void BlockTipChanged(ClientModel *clientmodel, bool initialSync, const CB
int64_t& nLastUpdateNotification = fHeader ? nLastHeaderTipUpdateNotification : nLastBlockTipUpdateNotification;
+ if (fHeader) {
+ // cache best headers time and height to reduce future cs_main locks
+ clientmodel->cachedBestHeaderHeight = pIndex->nHeight;
+ clientmodel->cachedBestHeaderTime = pIndex->GetBlockTime();
+ }
// if we are in-sync, update the UI regardless of last update time
if (!initialSync || now - nLastUpdateNotification > MODEL_UPDATE_DELAY) {
//pass a async signal to the UI thread
diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h
index 2c10e633b8..4c92e2144e 100644
--- a/src/qt/clientmodel.h
+++ b/src/qt/clientmodel.h
@@ -8,6 +8,8 @@
#include <QObject>
#include <QDateTime>
+#include <atomic>
+
class AddressTableModel;
class BanTableModel;
class OptionsModel;
@@ -81,6 +83,10 @@ public:
QString formatClientStartupTime() const;
QString dataDir() const;
+ // caches for the best header
+ mutable std::atomic<int> cachedBestHeaderHeight;
+ mutable std::atomic<int64_t> cachedBestHeaderTime;
+
private:
OptionsModel *optionsModel;
PeerTableModel *peerTableModel;
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index 1d19c65753..38ad6e9aab 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -15,6 +15,7 @@
#include "wallet/coincontrol.h"
#include "init.h"
+#include "policy/fees.h"
#include "policy/policy.h"
#include "validation.h" // For mempool
#include "wallet/wallet.h"
@@ -512,7 +513,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
nBytes -= 34;
// Fee
- nPayFee = CWallet::GetMinimumFee(nBytes, nTxConfirmTarget, mempool);
+ nPayFee = CWallet::GetMinimumFee(nBytes, nTxConfirmTarget, ::mempool, ::feeEstimator);
if (nPayFee > 0 && coinControl->nMinimumTotalFee > nPayFee)
nPayFee = coinControl->nMinimumTotalFee;
@@ -592,7 +593,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
if (payTxFee.GetFeePerK() > 0)
dFeeVary = (double)std::max(CWallet::GetRequiredFee(1000), payTxFee.GetFeePerK()) / 1000;
else {
- dFeeVary = (double)std::max(CWallet::GetRequiredFee(1000), mempool.estimateSmartFee(nTxConfirmTarget).GetFeePerK()) / 1000;
+ dFeeVary = (double)std::max(CWallet::GetRequiredFee(1000), ::feeEstimator.estimateSmartFee(nTxConfirmTarget, NULL, ::mempool).GetFeePerK()) / 1000;
}
QString toolTip4 = tr("Can vary +/- %1 satoshi(s) per input.").arg(dFeeVary);
diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui
index 0b29201872..0f1b3f4a73 100644
--- a/src/qt/forms/optionsdialog.ui
+++ b/src/qt/forms/optionsdialog.ui
@@ -692,17 +692,34 @@
<item>
<layout class="QHBoxLayout" name="horizontalLayout_Buttons">
<item>
- <widget class="QPushButton" name="resetButton">
- <property name="toolTip">
- <string>Reset all client options to default.</string>
- </property>
- <property name="text">
- <string>&amp;Reset Options</string>
- </property>
- <property name="autoDefault">
- <bool>false</bool>
- </property>
- </widget>
+ <layout class="QVBoxLayout" name="verticalLayout_Buttons">
+ <item>
+ <widget class="QPushButton" name="openBitcoinConfButton">
+ <property name="toolTip">
+ <string>Open the %1 configuration file from the working directory.</string>
+ </property>
+ <property name="text">
+ <string>Open Configuration File</string>
+ </property>
+ <property name="autoDefault">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="resetButton">
+ <property name="toolTip">
+ <string>Reset all client options to default.</string>
+ </property>
+ <property name="text">
+ <string>&amp;Reset Options</string>
+ </property>
+ <property name="autoDefault">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
</item>
<item>
<spacer name="horizontalSpacer_1">
@@ -756,27 +773,48 @@
</spacer>
</item>
<item>
- <widget class="QPushButton" name="okButton">
- <property name="text">
- <string>&amp;OK</string>
- </property>
- <property name="autoDefault">
- <bool>false</bool>
- </property>
- <property name="default">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="cancelButton">
- <property name="text">
- <string>&amp;Cancel</string>
- </property>
- <property name="autoDefault">
- <bool>false</bool>
- </property>
- </widget>
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QPushButton" name="okButton">
+ <property name="text">
+ <string>&amp;OK</string>
+ </property>
+ <property name="autoDefault">
+ <bool>false</bool>
+ </property>
+ <property name="default">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="cancelButton">
+ <property name="text">
+ <string>&amp;Cancel</string>
+ </property>
+ <property name="autoDefault">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
</item>
</layout>
</item>
diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui
index cc183908d4..52256ca5c4 100644
--- a/src/qt/forms/sendcoinsdialog.ui
+++ b/src/qt/forms/sendcoinsdialog.ui
@@ -1235,7 +1235,7 @@
<bool>false</bool>
</property>
<property name="default">
- <bool>true</bool>
+ <bool>false</bool>
</property>
</widget>
</item>
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index bb5b2d4347..3f3f9b9ccb 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -9,6 +9,7 @@
#include "qvalidatedlineedit.h"
#include "walletmodel.h"
+#include "fs.h"
#include "primitives/transaction.h"
#include "init.h"
#include "policy/policy.h"
@@ -35,9 +36,6 @@
#include "shlwapi.h"
#endif
-#include <boost/filesystem.hpp>
-#include <boost/filesystem/fstream.hpp>
-#include <boost/filesystem/detail/utf8_codecvt_facet.hpp>
#include <boost/scoped_array.hpp>
#include <QAbstractItemView>
@@ -65,7 +63,7 @@
#include <QFontDatabase>
#endif
-static boost::filesystem::detail::utf8_codecvt_facet utf8;
+static fs::detail::utf8_codecvt_facet utf8;
#if defined(Q_OS_MAC)
extern double NSAppKitVersionNumber;
@@ -410,13 +408,29 @@ bool isObscured(QWidget *w)
void openDebugLogfile()
{
- boost::filesystem::path pathDebug = GetDataDir() / "debug.log";
+ fs::path pathDebug = GetDataDir() / "debug.log";
/* Open debug.log with the associated application */
- if (boost::filesystem::exists(pathDebug))
+ if (fs::exists(pathDebug))
QDesktopServices::openUrl(QUrl::fromLocalFile(boostPathToQString(pathDebug)));
}
+bool openBitcoinConf()
+{
+ boost::filesystem::path pathConfig = GetConfigFile(BITCOIN_CONF_FILENAME);
+
+ /* Create the file */
+ boost::filesystem::ofstream configFile(pathConfig, std::ios_base::app);
+
+ if (!configFile.good())
+ return false;
+
+ configFile.close();
+
+ /* Open bitcoin.conf with the associated application */
+ return QDesktopServices::openUrl(QUrl::fromLocalFile(boostPathToQString(pathConfig)));
+}
+
void SubstituteFonts(const QString& language)
{
#if defined(Q_OS_MAC)
@@ -597,7 +611,7 @@ TableViewLastColumnResizingFixer::TableViewLastColumnResizingFixer(QTableView* t
}
#ifdef WIN32
-boost::filesystem::path static StartupShortcutPath()
+fs::path static StartupShortcutPath()
{
std::string chain = ChainNameFromCommandLine();
if (chain == CBaseChainParams::MAIN)
@@ -610,13 +624,13 @@ boost::filesystem::path static StartupShortcutPath()
bool GetStartOnSystemStartup()
{
// check for Bitcoin*.lnk
- return boost::filesystem::exists(StartupShortcutPath());
+ return fs::exists(StartupShortcutPath());
}
bool SetStartOnSystemStartup(bool fAutoStart)
{
// If the shortcut exists already, remove it for updating
- boost::filesystem::remove(StartupShortcutPath());
+ fs::remove(StartupShortcutPath());
if (fAutoStart)
{
@@ -686,10 +700,8 @@ bool SetStartOnSystemStartup(bool fAutoStart)
// Follow the Desktop Application Autostart Spec:
// http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html
-boost::filesystem::path static GetAutostartDir()
+fs::path static GetAutostartDir()
{
- namespace fs = boost::filesystem;
-
char* pszConfigHome = getenv("XDG_CONFIG_HOME");
if (pszConfigHome) return fs::path(pszConfigHome) / "autostart";
char* pszHome = getenv("HOME");
@@ -697,7 +709,7 @@ boost::filesystem::path static GetAutostartDir()
return fs::path();
}
-boost::filesystem::path static GetAutostartFilePath()
+fs::path static GetAutostartFilePath()
{
std::string chain = ChainNameFromCommandLine();
if (chain == CBaseChainParams::MAIN)
@@ -707,7 +719,7 @@ boost::filesystem::path static GetAutostartFilePath()
bool GetStartOnSystemStartup()
{
- boost::filesystem::ifstream optionFile(GetAutostartFilePath());
+ fs::ifstream optionFile(GetAutostartFilePath());
if (!optionFile.good())
return false;
// Scan through file for "Hidden=true":
@@ -727,7 +739,7 @@ bool GetStartOnSystemStartup()
bool SetStartOnSystemStartup(bool fAutoStart)
{
if (!fAutoStart)
- boost::filesystem::remove(GetAutostartFilePath());
+ fs::remove(GetAutostartFilePath());
else
{
char pszExePath[MAX_PATH+1];
@@ -735,9 +747,9 @@ bool SetStartOnSystemStartup(bool fAutoStart)
if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1)
return false;
- boost::filesystem::create_directories(GetAutostartDir());
+ fs::create_directories(GetAutostartDir());
- boost::filesystem::ofstream optionFile(GetAutostartFilePath(), std::ios_base::out|std::ios_base::trunc);
+ fs::ofstream optionFile(GetAutostartFilePath(), std::ios_base::out|std::ios_base::trunc);
if (!optionFile.good())
return false;
std::string chain = ChainNameFromCommandLine();
@@ -843,14 +855,17 @@ void restoreWindowGeometry(const QString& strSetting, const QSize& defaultSize,
QPoint pos = settings.value(strSetting + "Pos").toPoint();
QSize size = settings.value(strSetting + "Size", defaultSize).toSize();
- if (!pos.x() && !pos.y()) {
- QRect screen = QApplication::desktop()->screenGeometry();
- pos.setX((screen.width() - size.width()) / 2);
- pos.setY((screen.height() - size.height()) / 2);
- }
-
parent->resize(size);
parent->move(pos);
+
+ if ((!pos.x() && !pos.y()) || (QApplication::desktop()->screenNumber(parent) == -1))
+ {
+ QRect screen = QApplication::desktop()->screenGeometry();
+ QPoint defaultPos((screen.width() - defaultSize.width()) / 2,
+ (screen.height() - defaultSize.height()) / 2);
+ parent->resize(defaultSize);
+ parent->move(defaultPos);
+ }
}
void setClipboard(const QString& str)
@@ -859,12 +874,12 @@ void setClipboard(const QString& str)
QApplication::clipboard()->setText(str, QClipboard::Selection);
}
-boost::filesystem::path qstringToBoostPath(const QString &path)
+fs::path qstringToBoostPath(const QString &path)
{
- return boost::filesystem::path(path.toStdString(), utf8);
+ return fs::path(path.toStdString(), utf8);
}
-QString boostPathToQString(const boost::filesystem::path &path)
+QString boostPathToQString(const fs::path &path)
{
return QString::fromStdString(path.string(utf8));
}
diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h
index 913aa5e24b..d6aa8c4ea6 100644
--- a/src/qt/guiutil.h
+++ b/src/qt/guiutil.h
@@ -6,6 +6,7 @@
#define BITCOIN_QT_GUIUTIL_H
#include "amount.h"
+#include "fs.h"
#include <QEvent>
#include <QHeaderView>
@@ -16,8 +17,6 @@
#include <QTableView>
#include <QLabel>
-#include <boost/filesystem.hpp>
-
class QValidatedLineEdit;
class SendCoinsRecipient;
@@ -114,6 +113,9 @@ namespace GUIUtil
// Open debug.log
void openDebugLogfile();
+ // Open the config file
+ bool openBitcoinConf();
+
// Replace invalid default fonts with known good ones
void SubstituteFonts(const QString& language);
@@ -183,10 +185,10 @@ namespace GUIUtil
void restoreWindowGeometry(const QString& strSetting, const QSize &defaultSizeIn, QWidget *parent);
/* Convert QString to OS specific boost path through UTF-8 */
- boost::filesystem::path qstringToBoostPath(const QString &path);
+ fs::path qstringToBoostPath(const QString &path);
/* Convert OS specific boost path to QString through UTF-8 */
- QString boostPathToQString(const boost::filesystem::path &path);
+ QString boostPathToQString(const fs::path &path);
/* Convert seconds into a QString with days, hours, mins, secs */
QString formatDurationStr(int secs);
diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp
index 4939648ff0..2460a59109 100644
--- a/src/qt/intro.cpp
+++ b/src/qt/intro.cpp
@@ -6,6 +6,7 @@
#include "config/bitcoin-config.h"
#endif
+#include "fs.h"
#include "intro.h"
#include "ui_intro.h"
@@ -13,8 +14,6 @@
#include "util.h"
-#include <boost/filesystem.hpp>
-
#include <QFileDialog>
#include <QSettings>
#include <QMessageBox>
@@ -70,7 +69,6 @@ FreespaceChecker::FreespaceChecker(Intro *_intro)
void FreespaceChecker::check()
{
- namespace fs = boost::filesystem;
QString dataDirStr = intro->getPathToCheck();
fs::path dataDir = GUIUtil::qstringToBoostPath(dataDirStr);
uint64_t freeBytesAvailable = 0;
@@ -190,7 +188,6 @@ QString Intro::getDefaultDataDirectory()
bool Intro::pickDataDirectory()
{
- namespace fs = boost::filesystem;
QSettings settings;
/* If data directory provided on command line, no need to look at settings
or show a picking dialog */
diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp
index 7ff00b1e9e..efb25aaf18 100644
--- a/src/qt/optionsdialog.cpp
+++ b/src/qt/optionsdialog.cpp
@@ -232,6 +232,18 @@ void OptionsDialog::on_resetButton_clicked()
}
}
+void OptionsDialog::on_openBitcoinConfButton_clicked()
+{
+ /* explain the purpose of the config file */
+ QMessageBox::information(this, tr("Configuration options"),
+ tr("The configuration file is used to specify advanced user options which override GUI settings. "
+ "Additionally, any command-line options will override this configuration file."));
+
+ /* show an error if there was some problem opening the file */
+ if (!GUIUtil::openBitcoinConf())
+ QMessageBox::critical(this, tr("Error"), tr("The configuration file could not be opened."));
+}
+
void OptionsDialog::on_okButton_clicked()
{
mapper->submit();
diff --git a/src/qt/optionsdialog.h b/src/qt/optionsdialog.h
index d98c1dc193..f9f5823c05 100644
--- a/src/qt/optionsdialog.h
+++ b/src/qt/optionsdialog.h
@@ -47,6 +47,7 @@ private Q_SLOTS:
/* set OK button state (enabled / disabled) */
void setOkButtonState(bool fState);
void on_resetButton_clicked();
+ void on_openBitcoinConfButton_clicked();
void on_okButton_clicked();
void on_cancelButton_clicked();
diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp
index 974a71ddca..fff072fd4c 100644
--- a/src/qt/peertablemodel.cpp
+++ b/src/qt/peertablemodel.cpp
@@ -166,7 +166,7 @@ QVariant PeerTableModel::data(const QModelIndex &index, int role) const
switch(index.column())
{
case NetNodeId:
- return rec->nodeStats.nodeid;
+ return (qint64)rec->nodeStats.nodeid;
case Address:
return QString::fromStdString(rec->nodeStats.addrName);
case Subversion:
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index 5167232d6a..7f2f83d9f7 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -829,7 +829,7 @@ void RPCConsole::on_lineEdit_returnPressed()
cmdBeforeBrowsing = QString();
- message(CMD_REQUEST, cmd);
+ message(CMD_REQUEST, QString::fromStdString(strFilteredCmd));
Q_EMIT cmdRequest(cmd);
cmd = QString::fromStdString(strFilteredCmd);
@@ -1118,7 +1118,7 @@ void RPCConsole::disconnectSelectedNode()
for(int i = 0; i < nodes.count(); i++)
{
// Get currently selected peer address
- NodeId id = nodes.at(i).data().toInt();
+ NodeId id = nodes.at(i).data().toLongLong();
// Find the node, disconnect it and clear the selected node
if(g_connman->DisconnectNode(id))
clearSelectedNode();
@@ -1135,7 +1135,7 @@ void RPCConsole::banSelectedNode(int bantime)
for(int i = 0; i < nodes.count(); i++)
{
// Get currently selected peer address
- NodeId id = nodes.at(i).data().toInt();
+ NodeId id = nodes.at(i).data().toLongLong();
// Get currently selected peer address
int detailNodeRow = clientModel->getPeerTableModel()->getRowByNodeId(id);
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index ed7eab03f3..098cda6d32 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -21,6 +21,7 @@
#include "validation.h" // mempool and minRelayTxFee
#include "ui_interface.h"
#include "txmempool.h"
+#include "policy/fees.h"
#include "wallet/wallet.h"
#include <QFontMetrics>
@@ -114,7 +115,6 @@ SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *p
ui->groupCustomFee->button((int)std::max(0, std::min(1, settings.value("nCustomFeeRadio").toInt())))->setChecked(true);
ui->customFee->setValue(settings.value("nTransactionFee").toLongLong());
ui->checkBoxMinimumFee->setChecked(settings.value("fPayOnlyMinFee").toBool());
- ui->optInRBF->setCheckState(model->getDefaultWalletRbf() ? Qt::Checked : Qt::Unchecked);
minimizeFeeSection(settings.value("fFeeSectionMinimized").toBool());
}
@@ -175,6 +175,9 @@ void SendCoinsDialog::setModel(WalletModel *_model)
updateSmartFeeLabel();
updateGlobalFeeVariables();
+ // set default rbf checkbox state
+ ui->optInRBF->setCheckState(model->getDefaultWalletRbf() ? Qt::Checked : Qt::Unchecked);
+
// set the smartfee-sliders default value (wallets default conf.target or last stored value)
QSettings settings;
if (settings.value("nSmartFeeSliderPosition").toInt() == 0)
@@ -660,7 +663,7 @@ void SendCoinsDialog::updateSmartFeeLabel()
int nBlocksToConfirm = ui->sliderSmartFee->maximum() - ui->sliderSmartFee->value() + 2;
int estimateFoundAtBlocks = nBlocksToConfirm;
- CFeeRate feeRate = mempool.estimateSmartFee(nBlocksToConfirm, &estimateFoundAtBlocks);
+ CFeeRate feeRate = ::feeEstimator.estimateSmartFee(nBlocksToConfirm, &estimateFoundAtBlocks, ::mempool);
if (feeRate <= CFeeRate(0)) // not enough data => minfee
{
ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(),
diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp
index a7b82117d8..dada689731 100644
--- a/src/qt/test/rpcnestedtests.cpp
+++ b/src/qt/test/rpcnestedtests.cpp
@@ -6,6 +6,7 @@
#include "chainparams.h"
#include "consensus/validation.h"
+#include "fs.h"
#include "validation.h"
#include "rpc/register.h"
#include "rpc/server.h"
@@ -17,8 +18,6 @@
#include <QDir>
#include <QtGlobal>
-#include <boost/filesystem.hpp>
-
static UniValue rpcNestedTest_rpc(const JSONRPCRequest& request)
{
if (request.fHelp) {
@@ -156,5 +155,5 @@ void RPCNestedTests::rpcNestedTests()
delete pblocktree;
pblocktree = nullptr;
- boost::filesystem::remove_all(boost::filesystem::path(path));
+ fs::remove_all(fs::path(path));
}
diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp
index d8bcfedb7c..cae18f41a5 100644
--- a/src/qt/test/test_main.cpp
+++ b/src/qt/test/test_main.cpp
@@ -31,6 +31,9 @@ Q_IMPORT_PLUGIN(qjpcodecs)
Q_IMPORT_PLUGIN(qtwcodecs)
Q_IMPORT_PLUGIN(qkrcodecs)
#else
+#if defined(QT_QPA_PLATFORM_MINIMAL)
+Q_IMPORT_PLUGIN(QMinimalIntegrationPlugin);
+#endif
#if defined(QT_QPA_PLATFORM_XCB)
Q_IMPORT_PLUGIN(QXcbIntegrationPlugin);
#elif defined(QT_QPA_PLATFORM_WINDOWS)
@@ -53,6 +56,11 @@ int main(int argc, char *argv[])
bool fInvalid = false;
+ // Prefer the "minimal" platform for the test instead of the normal default
+ // platform ("xcb", "windows", or "cocoa") so tests can't unintentially
+ // interfere with any background GUIs and don't require extra resources.
+ setenv("QT_QPA_PLATFORM", "minimal", 0);
+
// Don't remove this, it's needed to access
// QApplication:: and QCoreApplication:: in the tests
QApplication app(argc, argv);
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index b7c1e4c4d5..a0dce3d997 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -68,13 +68,26 @@ QModelIndex FindTx(const QAbstractItemModel& model, const uint256& txid)
}
//! Simple qt wallet tests.
+//
+// Test widgets can be debugged interactively calling show() on them and
+// manually running the event loop, e.g.:
+//
+// sendCoinsDialog.show();
+// QEventLoop().exec();
+//
+// This also requires overriding the default minimal Qt platform:
+//
+// src/qt/test/test_bitcoin-qt -platform xcb # Linux
+// src/qt/test/test_bitcoin-qt -platform windows # Windows
+// src/qt/test/test_bitcoin-qt -platform cocoa # macOS
void WalletTests::walletTests()
{
// Set up wallet and chain with 101 blocks (1 mature block for spending).
TestChain100Setup test;
test.CreateAndProcessBlock({}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey()));
bitdb.MakeMock();
- CWallet wallet("wallet_test.dat");
+ std::unique_ptr<CWalletDBWrapper> dbw(new CWalletDBWrapper(&bitdb, "wallet_test.dat"));
+ CWallet wallet(std::move(dbw));
bool firstRun;
wallet.LoadWallet(firstRun);
{
diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp
index a9d9b6887e..4bb260aa58 100644
--- a/src/qt/transactionrecord.cpp
+++ b/src/qt/transactionrecord.cpp
@@ -18,14 +18,8 @@
*/
bool TransactionRecord::showTransaction(const CWalletTx &wtx)
{
- if (wtx.IsCoinBase())
- {
- // Ensures we show generated coins / mined transactions at depth 1
- if (!wtx.IsInMainChain())
- {
- return false;
- }
- }
+ // There are currently no cases where we hide transactions, but
+ // we may want to use this in the future for things like RBF.
return true;
}
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index ebcac53c25..a2a9271904 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -13,6 +13,7 @@
#include "transactiontablemodel.h"
#include "base58.h"
+#include "chain.h"
#include "keystore.h"
#include "validation.h"
#include "net.h" // for g_connman
diff --git a/src/random.cpp b/src/random.cpp
index 6bcd0a70ba..6187f16290 100644
--- a/src/random.cpp
+++ b/src/random.cpp
@@ -240,22 +240,16 @@ uint256 GetRandHash()
return hash;
}
-FastRandomContext::FastRandomContext(bool fDeterministic)
+void FastRandomContext::RandomSeed()
{
- // The seed values have some unlikely fixed points which we avoid.
- if (fDeterministic) {
- Rz = Rw = 11;
- } else {
- uint32_t tmp;
- do {
- GetRandBytes((unsigned char*)&tmp, 4);
- } while (tmp == 0 || tmp == 0x9068ffffU);
- Rz = tmp;
- do {
- GetRandBytes((unsigned char*)&tmp, 4);
- } while (tmp == 0 || tmp == 0x464fffffU);
- Rw = tmp;
- }
+ uint256 seed = GetRandHash();
+ rng.SetKey(seed.begin(), 32);
+ requires_seed = false;
+}
+
+FastRandomContext::FastRandomContext(const uint256& seed) : requires_seed(false), bytebuf_size(0), bitbuf_size(0)
+{
+ rng.SetKey(seed.begin(), 32);
}
bool Random_SanityCheck()
@@ -288,3 +282,12 @@ bool Random_SanityCheck()
} while (num_overwritten < NUM_OS_RANDOM_BYTES && tries < MAX_TRIES);
return (num_overwritten == NUM_OS_RANDOM_BYTES); /* If this failed, bailed out after too many tries */
}
+
+FastRandomContext::FastRandomContext(bool fDeterministic) : requires_seed(!fDeterministic), bytebuf_size(0), bitbuf_size(0)
+{
+ if (!fDeterministic) {
+ return;
+ }
+ uint256 seed;
+ rng.SetKey(seed.begin(), 32);
+}
diff --git a/src/random.h b/src/random.h
index 0464bdce14..9551e1c461 100644
--- a/src/random.h
+++ b/src/random.h
@@ -6,6 +6,8 @@
#ifndef BITCOIN_RANDOM_H
#define BITCOIN_RANDOM_H
+#include "crypto/chacha20.h"
+#include "crypto/common.h"
#include "uint256.h"
#include <stdint.h>
@@ -33,17 +35,79 @@ void GetStrongRandBytes(unsigned char* buf, int num);
* This class is not thread-safe.
*/
class FastRandomContext {
+private:
+ bool requires_seed;
+ ChaCha20 rng;
+
+ unsigned char bytebuf[64];
+ int bytebuf_size;
+
+ uint64_t bitbuf;
+ int bitbuf_size;
+
+ void RandomSeed();
+
+ void FillByteBuffer()
+ {
+ if (requires_seed) {
+ RandomSeed();
+ }
+ rng.Output(bytebuf, sizeof(bytebuf));
+ bytebuf_size = sizeof(bytebuf);
+ }
+
+ void FillBitBuffer()
+ {
+ bitbuf = rand64();
+ bitbuf_size = 64;
+ }
+
public:
- explicit FastRandomContext(bool fDeterministic=false);
+ explicit FastRandomContext(bool fDeterministic = false);
+
+ /** Initialize with explicit seed (only for testing) */
+ explicit FastRandomContext(const uint256& seed);
+
+ /** Generate a random 64-bit integer. */
+ uint64_t rand64()
+ {
+ if (bytebuf_size < 8) FillByteBuffer();
+ uint64_t ret = ReadLE64(bytebuf + 64 - bytebuf_size);
+ bytebuf_size -= 8;
+ return ret;
+ }
- uint32_t rand32() {
- Rz = 36969 * (Rz & 65535) + (Rz >> 16);
- Rw = 18000 * (Rw & 65535) + (Rw >> 16);
- return (Rw << 16) + Rz;
+ /** Generate a random (bits)-bit integer. */
+ uint64_t randbits(int bits) {
+ if (bits == 0) {
+ return 0;
+ } else if (bits > 32) {
+ return rand64() >> (64 - bits);
+ } else {
+ if (bitbuf_size < bits) FillBitBuffer();
+ uint64_t ret = bitbuf & (~(uint64_t)0 >> (64 - bits));
+ bitbuf >>= bits;
+ bitbuf_size -= bits;
+ return ret;
+ }
}
- uint32_t Rz;
- uint32_t Rw;
+ /** Generate a random integer in the range [0..range). */
+ uint64_t randrange(uint64_t range)
+ {
+ --range;
+ int bits = CountBits(range);
+ while (true) {
+ uint64_t ret = randbits(bits);
+ if (ret <= range) return ret;
+ }
+ }
+
+ /** Generate a random 32-bit integer. */
+ uint32_t rand32() { return randbits(32); }
+
+ /** Generate a random boolean. */
+ bool randbool() { return randbits(1); }
};
/* Number of random bytes returned by GetOSRand.
diff --git a/src/rest.cpp b/src/rest.cpp
index 9dcaf269d6..7537ed4502 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -5,6 +5,7 @@
#include "chain.h"
#include "chainparams.h"
+#include "core_io.h"
#include "primitives/block.h"
#include "primitives/transaction.h"
#include "validation.h"
@@ -56,10 +57,6 @@ struct CCoin {
}
};
-/* Defined in rawtransaction.cpp */
-void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry);
-void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex);
-
static bool RESTERR(HTTPRequest* req, enum HTTPStatusCode status, std::string message)
{
req->WriteHeader("Content-Type", "text/plain");
@@ -383,7 +380,7 @@ static bool rest_tx(HTTPRequest* req, const std::string& strURIPart)
case RF_JSON: {
UniValue objTx(UniValue::VOBJ);
- TxToJSON(*tx, hashBlock, objTx);
+ TxToUniv(*tx, hashBlock, objTx);
std::string strJSON = objTx.write() + "\n";
req->WriteHeader("Content-Type", "application/json");
req->WriteReply(HTTP_OK, strJSON);
@@ -577,7 +574,7 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart)
// include the script in a json output
UniValue o(UniValue::VOBJ);
- ScriptPubKeyToJSON(coin.out.scriptPubKey, o, true);
+ ScriptPubKeyToUniv(coin.out.scriptPubKey, o, true);
utxo.push_back(Pair("scriptPubKey", o));
utxos.push_back(utxo);
}
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 01066d0eb2..e2e244a5f1 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -12,6 +12,7 @@
#include "coins.h"
#include "consensus/validation.h"
#include "validation.h"
+#include "core_io.h"
#include "policy/policy.h"
#include "primitives/transaction.h"
#include "rpc/server.h"
@@ -42,7 +43,6 @@ static std::condition_variable cond_blockchange;
static CUpdatedBlock latestblock;
extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry);
-void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex);
double GetDifficulty(const CBlockIndex* blockindex)
{
@@ -123,7 +123,7 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx
if(txDetails)
{
UniValue objTx(UniValue::VOBJ);
- TxToJSON(*tx, uint256(), objTx);
+ TxToUniv(*tx, uint256(), objTx);
txs.push_back(objTx);
}
else
@@ -975,7 +975,7 @@ UniValue gettxout(const JSONRPCRequest& request)
ret.push_back(Pair("confirmations", pindex->nHeight - coins.nHeight + 1));
ret.push_back(Pair("value", ValueFromAmount(coins.vout[n].nValue)));
UniValue o(UniValue::VOBJ);
- ScriptPubKeyToJSON(coins.vout[n].scriptPubKey, o, true);
+ ScriptPubKeyToUniv(coins.vout[n].scriptPubKey, o, true);
ret.push_back(Pair("scriptPubKey", o));
ret.push_back(Pair("version", coins.nVersion));
ret.push_back(Pair("coinbase", coins.fCoinBase));
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index 35bc5d6a82..941bdd9379 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -113,6 +113,9 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "getmempoolancestors", 1, "verbose" },
{ "getmempooldescendants", 1, "verbose" },
{ "bumpfee", 1, "options" },
+ { "logging", 0, "include" },
+ { "logging", 1, "exclude" },
+ { "disconnectnode", 1, "nodeid" },
// Echo with conversion (For testing only)
{ "echojson", 0, "arg0" },
{ "echojson", 1, "arg1" },
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 7e5f0d608e..4ce52a6c7f 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -15,6 +15,7 @@
#include "validation.h"
#include "miner.h"
#include "net.h"
+#include "policy/fees.h"
#include "pow.h"
#include "rpc/blockchain.h"
#include "rpc/server.h"
@@ -27,7 +28,6 @@
#include <stdint.h>
#include <boost/assign/list_of.hpp>
-#include <boost/shared_ptr.hpp>
#include <univalue.h>
@@ -95,7 +95,7 @@ UniValue getnetworkhashps(const JSONRPCRequest& request)
return GetNetworkHashPS(request.params.size() > 0 ? request.params[0].get_int() : 120, request.params.size() > 1 ? request.params[1].get_int() : -1);
}
-UniValue generateBlocks(boost::shared_ptr<CReserveScript> coinbaseScript, int nGenerate, uint64_t nMaxTries, bool keepScript)
+UniValue generateBlocks(std::shared_ptr<CReserveScript> coinbaseScript, int nGenerate, uint64_t nMaxTries, bool keepScript)
{
static const int nInnerLoopCount = 0x10000;
int nHeightStart = 0;
@@ -167,7 +167,7 @@ UniValue generate(const JSONRPCRequest& request)
nMaxTries = request.params[1].get_int();
}
- boost::shared_ptr<CReserveScript> coinbaseScript;
+ std::shared_ptr<CReserveScript> coinbaseScript;
GetMainSignals().ScriptForMining(coinbaseScript);
// If the keypool is exhausted, no script is returned at all. Catch this.
@@ -208,7 +208,7 @@ UniValue generatetoaddress(const JSONRPCRequest& request)
if (!address.IsValid())
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Error: Invalid address");
- boost::shared_ptr<CReserveScript> coinbaseScript(new CReserveScript());
+ std::shared_ptr<CReserveScript> coinbaseScript = std::make_shared<CReserveScript>();
coinbaseScript->reserveScript = GetScriptForDestination(address.Get());
return generateBlocks(coinbaseScript, nGenerate, nMaxTries, false);
@@ -710,7 +710,7 @@ public:
submitblock_StateCatcher(const uint256 &hashIn) : hash(hashIn), found(false), state() {}
protected:
- virtual void BlockChecked(const CBlock& block, const CValidationState& stateIn) {
+ void BlockChecked(const CBlock& block, const CValidationState& stateIn) override {
if (block.GetHash() != hash)
return;
found = true;
@@ -819,7 +819,7 @@ UniValue estimatefee(const JSONRPCRequest& request)
if (nBlocks < 1)
nBlocks = 1;
- CFeeRate feeRate = mempool.estimateFee(nBlocks);
+ CFeeRate feeRate = ::feeEstimator.estimateFee(nBlocks);
if (feeRate == CFeeRate(0))
return -1.0;
@@ -857,7 +857,7 @@ UniValue estimatesmartfee(const JSONRPCRequest& request)
UniValue result(UniValue::VOBJ);
int answerFound;
- CFeeRate feeRate = mempool.estimateSmartFee(nBlocks, &answerFound);
+ CFeeRate feeRate = ::feeEstimator.estimateSmartFee(nBlocks, &answerFound, ::mempool);
result.push_back(Pair("feerate", feeRate == CFeeRate(0) ? -1.0 : ValueFromAmount(feeRate.GetFeePerK())));
result.push_back(Pair("blocks", answerFound));
return result;
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index 24c5eeffe9..1f973a0c18 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -4,9 +4,11 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "base58.h"
+#include "chain.h"
#include "clientversion.h"
#include "init.h"
#include "validation.h"
+#include "httpserver.h"
#include "net.h"
#include "netbase.h"
#include "rpc/blockchain.h"
@@ -555,6 +557,73 @@ UniValue getmemoryinfo(const JSONRPCRequest& request)
}
}
+uint32_t getCategoryMask(UniValue cats) {
+ cats = cats.get_array();
+ uint32_t mask = 0;
+ for (unsigned int i = 0; i < cats.size(); ++i) {
+ uint32_t flag = 0;
+ std::string cat = cats[i].get_str();
+ if (!GetLogCategory(&flag, &cat)) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "unknown logging category " + cat);
+ }
+ mask |= flag;
+ }
+ return mask;
+}
+
+UniValue logging(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() > 2) {
+ throw std::runtime_error(
+ "logging [include,...] <exclude>\n"
+ "Gets and sets the logging configuration.\n"
+ "When called without an argument, returns the list of categories that are currently being debug logged.\n"
+ "When called with arguments, adds or removes categories from debug logging.\n"
+ "The valid logging categories are: " + ListLogCategories() + "\n"
+ "libevent logging is configured on startup and cannot be modified by this RPC during runtime."
+ "Arguments:\n"
+ "1. \"include\" (array of strings) add debug logging for these categories.\n"
+ "2. \"exclude\" (array of strings) remove debug logging for these categories.\n"
+ "\nResult: <categories> (string): a list of the logging categories that are active.\n"
+ "\nExamples:\n"
+ + HelpExampleCli("logging", "\"[\\\"all\\\"]\" \"[\\\"http\\\"]\"")
+ + HelpExampleRpc("logging", "[\"all\"], \"[libevent]\"")
+ );
+ }
+
+ uint32_t originalLogCategories = logCategories;
+ if (request.params.size() > 0 && request.params[0].isArray()) {
+ logCategories |= getCategoryMask(request.params[0]);
+ }
+
+ if (request.params.size() > 1 && request.params[1].isArray()) {
+ logCategories &= ~getCategoryMask(request.params[1]);
+ }
+
+ // Update libevent logging if BCLog::LIBEVENT has changed.
+ // If the library version doesn't allow it, UpdateHTTPServerLogging() returns false,
+ // in which case we should clear the BCLog::LIBEVENT flag.
+ // Throw an error if the user has explicitly asked to change only the libevent
+ // flag and it failed.
+ uint32_t changedLogCategories = originalLogCategories ^ logCategories;
+ if (changedLogCategories & BCLog::LIBEVENT) {
+ if (!UpdateHTTPServerLogging(logCategories & BCLog::LIBEVENT)) {
+ logCategories &= ~BCLog::LIBEVENT;
+ if (changedLogCategories == BCLog::LIBEVENT) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "libevent logging cannot be updated when using libevent before v2.1.1.");
+ }
+ }
+ }
+
+ UniValue result(UniValue::VOBJ);
+ std::vector<CLogCategoryActive> vLogCatActive = ListActiveLogCategories();
+ for (const auto& logCatActive : vLogCatActive) {
+ result.pushKV(logCatActive.category, logCatActive.active);
+ }
+
+ return result;
+}
+
UniValue echo(const JSONRPCRequest& request)
{
if (request.fHelp)
@@ -581,7 +650,8 @@ static const CRPCCommand commands[] =
/* Not shown in help */
{ "hidden", "setmocktime", &setmocktime, true, {"timestamp"}},
{ "hidden", "echo", &echo, true, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}},
- { "hidden", "echojson", &echo, true, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}},
+ { "hidden", "echojson", &echo, true, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}},
+ { "hidden", "logging", &logging, true, {"include", "exclude"}},
};
void RegisterMiscRPCCommands(CRPCTable &t)
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index 44c6e6d308..cde5ae723b 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -234,23 +234,43 @@ UniValue addnode(const JSONRPCRequest& request)
UniValue disconnectnode(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 1)
+ if (request.fHelp || request.params.size() == 0 || request.params.size() >= 3)
throw std::runtime_error(
- "disconnectnode \"node\" \n"
- "\nImmediately disconnects from the specified node.\n"
+ "disconnectnode \"[address]\" [nodeid]\n"
+ "\nImmediately disconnects from the specified peer node.\n"
+ "\nStrictly one out of 'address' and 'nodeid' can be provided to identify the node.\n"
+ "\nTo disconnect by nodeid, either set 'address' to the empty string, or call using the named 'nodeid' argument only.\n"
"\nArguments:\n"
- "1. \"node\" (string, required) The node (see getpeerinfo for nodes)\n"
+ "1. \"address\" (string, optional) The IP address/port of the node\n"
+ "2. \"nodeid\" (number, optional) The node ID (see getpeerinfo for node IDs)\n"
"\nExamples:\n"
+ HelpExampleCli("disconnectnode", "\"192.168.0.6:8333\"")
+ + HelpExampleCli("disconnectnode", "\"\" 1")
+ HelpExampleRpc("disconnectnode", "\"192.168.0.6:8333\"")
+ + HelpExampleRpc("disconnectnode", "\"\", 1")
);
if(!g_connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
- bool ret = g_connman->DisconnectNode(request.params[0].get_str());
- if (!ret)
+ bool success;
+ const UniValue &address_arg = request.params[0];
+ const UniValue &id_arg = request.params.size() < 2 ? NullUniValue : request.params[1];
+
+ if (!address_arg.isNull() && id_arg.isNull()) {
+ /* handle disconnect-by-address */
+ success = g_connman->DisconnectNode(address_arg.get_str());
+ } else if (!id_arg.isNull() && (address_arg.isNull() || (address_arg.isStr() && address_arg.get_str().empty()))) {
+ /* handle disconnect-by-id */
+ NodeId nodeid = (NodeId) id_arg.get_int64();
+ success = g_connman->DisconnectNode(nodeid);
+ } else {
+ throw JSONRPCError(RPC_INVALID_PARAMS, "Only one of address and nodeid should be provided.");
+ }
+
+ if (!success) {
throw JSONRPCError(RPC_CLIENT_NODE_NOT_CONNECTED, "Node not found in connected nodes");
+ }
return NullUniValue;
}
@@ -607,7 +627,7 @@ static const CRPCCommand commands[] =
{ "network", "ping", &ping, true, {} },
{ "network", "getpeerinfo", &getpeerinfo, true, {} },
{ "network", "addnode", &addnode, true, {"node","command"} },
- { "network", "disconnectnode", &disconnectnode, true, {"node"} },
+ { "network", "disconnectnode", &disconnectnode, true, {"address", "nodeid"} },
{ "network", "getaddednodeinfo", &getaddednodeinfo, true, {"node"} },
{ "network", "getnettotals", &getnettotals, true, {} },
{ "network", "getnetworkinfo", &getnetworkinfo, true, {} },
diff --git a/src/rpc/protocol.cpp b/src/rpc/protocol.cpp
index 2be1edb5a6..823a5775f6 100644
--- a/src/rpc/protocol.cpp
+++ b/src/rpc/protocol.cpp
@@ -66,9 +66,9 @@ static const std::string COOKIEAUTH_USER = "__cookie__";
/** Default name for auth cookie file */
static const std::string COOKIEAUTH_FILE = ".cookie";
-boost::filesystem::path GetAuthCookieFile()
+fs::path GetAuthCookieFile()
{
- boost::filesystem::path path(GetArg("-rpccookiefile", COOKIEAUTH_FILE));
+ fs::path path(GetArg("-rpccookiefile", COOKIEAUTH_FILE));
if (!path.is_complete()) path = GetDataDir() / path;
return path;
}
@@ -84,7 +84,7 @@ bool GenerateAuthCookie(std::string *cookie_out)
* these are set to 077 in init.cpp unless overridden with -sysperms.
*/
std::ofstream file;
- boost::filesystem::path filepath = GetAuthCookieFile();
+ fs::path filepath = GetAuthCookieFile();
file.open(filepath.string().c_str());
if (!file.is_open()) {
LogPrintf("Unable to open cookie authentication file %s for writing\n", filepath.string());
@@ -103,7 +103,7 @@ bool GetAuthCookie(std::string *cookie_out)
{
std::ifstream file;
std::string cookie;
- boost::filesystem::path filepath = GetAuthCookieFile();
+ fs::path filepath = GetAuthCookieFile();
file.open(filepath.string().c_str());
if (!file.is_open())
return false;
@@ -118,8 +118,8 @@ bool GetAuthCookie(std::string *cookie_out)
void DeleteAuthCookie()
{
try {
- boost::filesystem::remove(GetAuthCookieFile());
- } catch (const boost::filesystem::filesystem_error& e) {
+ fs::remove(GetAuthCookieFile());
+ } catch (const fs::filesystem_error& e) {
LogPrintf("%s: Unable to remove random auth cookie file: %s\n", __func__, e.what());
}
}
diff --git a/src/rpc/protocol.h b/src/rpc/protocol.h
index 85bc4db101..70f7092cfe 100644
--- a/src/rpc/protocol.h
+++ b/src/rpc/protocol.h
@@ -6,11 +6,12 @@
#ifndef BITCOIN_RPCPROTOCOL_H
#define BITCOIN_RPCPROTOCOL_H
+#include "fs.h"
+
#include <list>
#include <map>
#include <stdint.h>
#include <string>
-#include <boost/filesystem.hpp>
#include <univalue.h>
@@ -89,7 +90,7 @@ std::string JSONRPCReply(const UniValue& result, const UniValue& error, const Un
UniValue JSONRPCError(int code, const std::string& message);
/** Get name of RPC authentication cookie file */
-boost::filesystem::path GetAuthCookieFile();
+fs::path GetAuthCookieFile();
/** Generate a new RPC authentication cookie and write it to disk */
bool GenerateAuthCookie(std::string *cookie_out);
/** Read the RPC authentication cookie from disk */
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 717e9d75f3..3947fb3f7d 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -34,77 +34,15 @@
#include <univalue.h>
-void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex)
-{
- txnouttype type;
- std::vector<CTxDestination> addresses;
- int nRequired;
-
- out.push_back(Pair("asm", ScriptToAsmStr(scriptPubKey)));
- if (fIncludeHex)
- out.push_back(Pair("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end())));
-
- if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired)) {
- out.push_back(Pair("type", GetTxnOutputType(type)));
- return;
- }
-
- out.push_back(Pair("reqSigs", nRequired));
- out.push_back(Pair("type", GetTxnOutputType(type)));
-
- UniValue a(UniValue::VARR);
- BOOST_FOREACH(const CTxDestination& addr, addresses)
- a.push_back(CBitcoinAddress(addr).ToString());
- out.push_back(Pair("addresses", a));
-}
void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
{
- entry.push_back(Pair("txid", tx.GetHash().GetHex()));
- entry.push_back(Pair("hash", tx.GetWitnessHash().GetHex()));
- entry.push_back(Pair("size", (int)::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION)));
- entry.push_back(Pair("vsize", (int)::GetVirtualTransactionSize(tx)));
- entry.push_back(Pair("version", tx.nVersion));
- entry.push_back(Pair("locktime", (int64_t)tx.nLockTime));
-
- UniValue vin(UniValue::VARR);
- for (unsigned int i = 0; i < tx.vin.size(); i++) {
- const CTxIn& txin = tx.vin[i];
- UniValue in(UniValue::VOBJ);
- if (tx.IsCoinBase())
- in.push_back(Pair("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
- else {
- in.push_back(Pair("txid", txin.prevout.hash.GetHex()));
- in.push_back(Pair("vout", (int64_t)txin.prevout.n));
- UniValue o(UniValue::VOBJ);
- o.push_back(Pair("asm", ScriptToAsmStr(txin.scriptSig, true)));
- o.push_back(Pair("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
- in.push_back(Pair("scriptSig", o));
- }
- if (tx.HasWitness()) {
- UniValue txinwitness(UniValue::VARR);
- for (unsigned int j = 0; j < tx.vin[i].scriptWitness.stack.size(); j++) {
- std::vector<unsigned char> item = tx.vin[i].scriptWitness.stack[j];
- txinwitness.push_back(HexStr(item.begin(), item.end()));
- }
- in.push_back(Pair("txinwitness", txinwitness));
- }
- in.push_back(Pair("sequence", (int64_t)txin.nSequence));
- vin.push_back(in);
- }
- entry.push_back(Pair("vin", vin));
- UniValue vout(UniValue::VARR);
- for (unsigned int i = 0; i < tx.vout.size(); i++) {
- const CTxOut& txout = tx.vout[i];
- UniValue out(UniValue::VOBJ);
- out.push_back(Pair("value", ValueFromAmount(txout.nValue)));
- out.push_back(Pair("n", (int64_t)i));
- UniValue o(UniValue::VOBJ);
- ScriptPubKeyToJSON(txout.scriptPubKey, o, true);
- out.push_back(Pair("scriptPubKey", o));
- vout.push_back(out);
- }
- entry.push_back(Pair("vout", vout));
+ // Call into TxToUniv() in bitcoin-common to decode the transaction hex.
+ //
+ // Blockchain contextual information (confirmations and blocktime) is not
+ // available to code in bitcoin-common, so we query them here and push the
+ // data into the returned UniValue.
+ TxToUniv(tx, uint256(), entry);
if (!hashBlock.IsNull()) {
entry.push_back(Pair("blockhash", hashBlock.GetHex()));
@@ -525,7 +463,7 @@ UniValue decoderawtransaction(const JSONRPCRequest& request)
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
UniValue result(UniValue::VOBJ);
- TxToJSON(CTransaction(std::move(mtx)), uint256(), result);
+ TxToUniv(CTransaction(std::move(mtx)), uint256(), result);
return result;
}
@@ -565,7 +503,7 @@ UniValue decodescript(const JSONRPCRequest& request)
} else {
// Empty scripts are valid
}
- ScriptPubKeyToJSON(script, r, false);
+ ScriptPubKeyToUniv(script, r, false);
UniValue type;
type = find_value(r, "type");
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
index 141062b3c0..5c493428d8 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -6,6 +6,7 @@
#include "rpc/server.h"
#include "base58.h"
+#include "fs.h"
#include "init.h"
#include "random.h"
#include "sync.h"
@@ -16,7 +17,6 @@
#include <univalue.h>
#include <boost/bind.hpp>
-#include <boost/filesystem.hpp>
#include <boost/foreach.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/signals2/signal.hpp>
@@ -369,9 +369,7 @@ void JSONRPCRequest::parse(const UniValue& valRequest)
if (!valMethod.isStr())
throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string");
strMethod = valMethod.get_str();
- if (strMethod != "getblocktemplate") {
- LogPrint(BCLog::RPC, "ThreadRPCServer method=%s\n", SanitizeString(strMethod));
- }
+ LogPrint(BCLog::RPC, "ThreadRPCServer method=%s\n", SanitizeString(strMethod));
// Parse params
UniValue valParams = find_value(request, "params");
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index 8ecf0bbdac..f4e5313a78 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -247,10 +247,10 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
{
static const CScriptNum bnZero(0);
static const CScriptNum bnOne(1);
- static const CScriptNum bnFalse(0);
- static const CScriptNum bnTrue(1);
+ // static const CScriptNum bnFalse(0);
+ // static const CScriptNum bnTrue(1);
static const valtype vchFalse(0);
- static const valtype vchZero(0);
+ // static const valtype vchZero(0);
static const valtype vchTrue(1, 1);
CScript::const_iterator pc = script.begin();
diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp
index 6f47b725fb..7bb8d9941b 100644
--- a/src/script/sigcache.cpp
+++ b/src/script/sigcache.cpp
@@ -15,28 +15,6 @@
#include <boost/thread.hpp>
namespace {
-
-/**
- * We're hashing a nonce into the entries themselves, so we don't need extra
- * blinding in the set hash computation.
- *
- * This may exhibit platform endian dependent behavior but because these are
- * nonced hashes (random) and this state is only ever used locally it is safe.
- * All that matters is local consistency.
- */
-class SignatureCacheHasher
-{
-public:
- template <uint8_t hash_select>
- uint32_t operator()(const uint256& key) const
- {
- static_assert(hash_select <8, "SignatureCacheHasher only has 8 hashes available.");
- uint32_t u;
- std::memcpy(&u, key.begin()+4*hash_select, 4);
- return u;
- }
-};
-
/**
* Valid signature cache, to avoid doing expensive ECDSA signature checking
* twice for every transaction (once when accepted into memory pool, and
diff --git a/src/script/sigcache.h b/src/script/sigcache.h
index 60690583de..55cec4cc8d 100644
--- a/src/script/sigcache.h
+++ b/src/script/sigcache.h
@@ -19,6 +19,27 @@ static const int64_t MAX_MAX_SIG_CACHE_SIZE = 16384;
class CPubKey;
+/**
+ * We're hashing a nonce into the entries themselves, so we don't need extra
+ * blinding in the set hash computation.
+ *
+ * This may exhibit platform endian dependent behavior but because these are
+ * nonced hashes (random) and this state is only ever used locally it is safe.
+ * All that matters is local consistency.
+ */
+class SignatureCacheHasher
+{
+public:
+ template <uint8_t hash_select>
+ uint32_t operator()(const uint256& key) const
+ {
+ static_assert(hash_select <8, "SignatureCacheHasher only has 8 hashes available.");
+ uint32_t u;
+ std::memcpy(&u, key.begin()+4*hash_select, 4);
+ return u;
+ }
+};
+
class CachingTransactionSignatureChecker : public TransactionSignatureChecker
{
private:
diff --git a/src/serialize.h b/src/serialize.h
index e4d72d2348..e82ddf2c5a 100644
--- a/src/serialize.h
+++ b/src/serialize.h
@@ -336,11 +336,18 @@ I ReadVarInt(Stream& is)
I n = 0;
while(true) {
unsigned char chData = ser_readdata8(is);
+ if (n > (std::numeric_limits<I>::max() >> 7)) {
+ throw std::ios_base::failure("ReadVarInt(): size too large");
+ }
n = (n << 7) | (chData & 0x7F);
- if (chData & 0x80)
+ if (chData & 0x80) {
+ if (n == std::numeric_limits<I>::max()) {
+ throw std::ios_base::failure("ReadVarInt(): size too large");
+ }
n++;
- else
+ } else {
return n;
+ }
}
}
diff --git a/src/streams.h b/src/streams.h
index 1387b9cf54..8dc5a19ead 100644
--- a/src/streams.h
+++ b/src/streams.h
@@ -248,7 +248,8 @@ public:
void insert(iterator it, std::vector<char>::const_iterator first, std::vector<char>::const_iterator last)
{
- assert(last - first >= 0);
+ if (last == first) return;
+ assert(last - first > 0);
if (it == vch.begin() + nReadPos && (unsigned int)(last - first) <= nReadPos)
{
// special case for inserting at the front when there's room
@@ -261,7 +262,8 @@ public:
void insert(iterator it, const char* first, const char* last)
{
- assert(last - first >= 0);
+ if (last == first) return;
+ assert(last - first > 0);
if (it == vch.begin() + nReadPos && (unsigned int)(last - first) <= nReadPos)
{
// special case for inserting at the front when there's room
@@ -339,6 +341,8 @@ public:
void read(char* pch, size_t nSize)
{
+ if (nSize == 0) return;
+
// Read from the beginning of the buffer
unsigned int nReadPosNext = nReadPos + nSize;
if (nReadPosNext >= vch.size())
diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp
index 5d1c5b78d1..39fa381dd0 100644
--- a/src/test/addrman_tests.cpp
+++ b/src/test/addrman_tests.cpp
@@ -203,10 +203,11 @@ BOOST_AUTO_TEST_CASE(addrman_select)
BOOST_CHECK(addrman.size() == 7);
// Test 12: Select pulls from new and tried regardless of port number.
- BOOST_CHECK(addrman.Select().ToString() == "250.4.6.6:8333");
- BOOST_CHECK(addrman.Select().ToString() == "250.3.2.2:9999");
- BOOST_CHECK(addrman.Select().ToString() == "250.3.3.3:9999");
- BOOST_CHECK(addrman.Select().ToString() == "250.4.4.4:8333");
+ std::set<uint16_t> ports;
+ for (int i = 0; i < 20; ++i) {
+ ports.insert(addrman.Select().GetPort());
+ }
+ BOOST_CHECK_EQUAL(ports.size(), 3);
}
BOOST_AUTO_TEST_CASE(addrman_new_collisions)
diff --git a/src/test/amount_tests.cpp b/src/test/amount_tests.cpp
index fd6f88b366..c95def5e87 100644
--- a/src/test/amount_tests.cpp
+++ b/src/test/amount_tests.cpp
@@ -9,9 +9,16 @@
BOOST_FIXTURE_TEST_SUITE(amount_tests, BasicTestingSetup)
+BOOST_AUTO_TEST_CASE(MoneyRangeTest)
+{
+ BOOST_CHECK_EQUAL(MoneyRange(CAmount(-1)), false);
+ BOOST_CHECK_EQUAL(MoneyRange(MAX_MONEY + CAmount(1)), false);
+ BOOST_CHECK_EQUAL(MoneyRange(CAmount(1)), true);
+}
+
BOOST_AUTO_TEST_CASE(GetFeeTest)
{
- CFeeRate feeRate;
+ CFeeRate feeRate, altFeeRate;
feeRate = CFeeRate(0);
// Must always return 0
@@ -53,6 +60,11 @@ BOOST_AUTO_TEST_CASE(GetFeeTest)
BOOST_CHECK_EQUAL(feeRate.GetFee(8), -1); // Special case: returns -1 instead of 0
BOOST_CHECK_EQUAL(feeRate.GetFee(9), -1);
+ // check alternate constructor
+ feeRate = CFeeRate(1000);
+ altFeeRate = CFeeRate(feeRate);
+ BOOST_CHECK_EQUAL(feeRate.GetFee(100), altFeeRate.GetFee(100));
+
// Check full constructor
// default value
BOOST_CHECK(CFeeRate(CAmount(-1), 1000) == CFeeRate(-1));
@@ -68,4 +80,28 @@ BOOST_AUTO_TEST_CASE(GetFeeTest)
CFeeRate(MAX_MONEY, std::numeric_limits<size_t>::max() >> 1).GetFeePerK();
}
+BOOST_AUTO_TEST_CASE(BinaryOperatorTest)
+{
+ CFeeRate a, b;
+ a = CFeeRate(1);
+ b = CFeeRate(2);
+ BOOST_CHECK(a < b);
+ BOOST_CHECK(b > a);
+ BOOST_CHECK(a == a);
+ BOOST_CHECK(a <= b);
+ BOOST_CHECK(a <= a);
+ BOOST_CHECK(b >= a);
+ BOOST_CHECK(b >= b);
+ // a should be 0.00000002 BTC/kB now
+ a += a;
+ BOOST_CHECK(a == b);
+}
+
+BOOST_AUTO_TEST_CASE(ToStringTest)
+{
+ CFeeRate feeRate;
+ feeRate = CFeeRate(1);
+ BOOST_CHECK_EQUAL(feeRate.ToString(), "0.00000001 BTC/kB");
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp
index 4d17417179..72e562808a 100644
--- a/src/test/crypto_tests.cpp
+++ b/src/test/crypto_tests.cpp
@@ -3,12 +3,14 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "crypto/aes.h"
+#include "crypto/chacha20.h"
#include "crypto/ripemd160.h"
#include "crypto/sha1.h"
#include "crypto/sha256.h"
#include "crypto/sha512.h"
#include "crypto/hmac_sha256.h"
#include "crypto/hmac_sha512.h"
+#include "random.h"
#include "utilstrencodings.h"
#include "test/test_bitcoin.h"
#include "test/test_random.h"
@@ -187,6 +189,19 @@ void TestAES256CBC(const std::string &hexkey, const std::string &hexiv, bool pad
}
}
+void TestChaCha20(const std::string &hexkey, uint64_t nonce, uint64_t seek, const std::string& hexout)
+{
+ std::vector<unsigned char> key = ParseHex(hexkey);
+ ChaCha20 rng(key.data(), key.size());
+ rng.SetIV(nonce);
+ rng.Seek(seek);
+ std::vector<unsigned char> out = ParseHex(hexout);
+ std::vector<unsigned char> outres;
+ outres.resize(out.size());
+ rng.Output(outres.data(), outres.size());
+ BOOST_CHECK(out == outres);
+}
+
std::string LongTestString(void) {
std::string ret;
for (int i=0; i<200000; i++) {
@@ -439,4 +454,57 @@ BOOST_AUTO_TEST_CASE(aes_cbc_testvectors) {
"b2eb05e2c39be9fcda6c19078c6a9d1b3f461796d6b0d6b2e0c2a72b4d80e644");
}
+
+BOOST_AUTO_TEST_CASE(chacha20_testvector)
+{
+ // Test vector from RFC 7539
+ TestChaCha20("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0x4a000000UL, 1,
+ "224f51f3401bd9e12fde276fb8631ded8c131f823d2c06e27e4fcaec9ef3cf788a3b0aa372600a92b57974cded2b9334794cb"
+ "a40c63e34cdea212c4cf07d41b769a6749f3f630f4122cafe28ec4dc47e26d4346d70b98c73f3e9c53ac40c5945398b6eda1a"
+ "832c89c167eacd901d7e2bf363");
+
+ // Test vectors from https://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04#section-7
+ TestChaCha20("0000000000000000000000000000000000000000000000000000000000000000", 0, 0,
+ "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7724e03fb8d84a376a43b"
+ "8f41518a11cc387b669b2ee6586");
+ TestChaCha20("0000000000000000000000000000000000000000000000000000000000000001", 0, 0,
+ "4540f05a9f1fb296d7736e7b208e3c96eb4fe1834688d2604f450952ed432d41bbe2a0b6ea7566d2a5d1e7e20d42af2c53d79"
+ "2b1c43fea817e9ad275ae546963");
+ TestChaCha20("0000000000000000000000000000000000000000000000000000000000000000", 0x0100000000000000ULL, 0,
+ "de9cba7bf3d69ef5e786dc63973f653a0b49e015adbff7134fcb7df137821031e85a050278a7084527214f73efc7fa5b52770"
+ "62eb7a0433e445f41e3");
+ TestChaCha20("0000000000000000000000000000000000000000000000000000000000000000", 1, 0,
+ "ef3fdfd6c61578fbf5cf35bd3dd33b8009631634d21e42ac33960bd138e50d32111e4caf237ee53ca8ad6426194a88545ddc4"
+ "97a0b466e7d6bbdb0041b2f586b");
+ TestChaCha20("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0x0706050403020100ULL, 0,
+ "f798a189f195e66982105ffb640bb7757f579da31602fc93ec01ac56f85ac3c134a4547b733b46413042c9440049176905d3b"
+ "e59ea1c53f15916155c2be8241a38008b9a26bc35941e2444177c8ade6689de95264986d95889fb60e84629c9bd9a5acb1cc1"
+ "18be563eb9b3a4a472f82e09a7e778492b562ef7130e88dfe031c79db9d4f7c7a899151b9a475032b63fc385245fe054e3dd5"
+ "a97a5f576fe064025d3ce042c566ab2c507b138db853e3d6959660996546cc9c4a6eafdc777c040d70eaf46f76dad3979e5c5"
+ "360c3317166a1c894c94a371876a94df7628fe4eaaf2ccb27d5aaae0ad7ad0f9d4b6ad3b54098746d4524d38407a6deb3ab78"
+ "fab78c9");
+}
+
+BOOST_AUTO_TEST_CASE(countbits_tests)
+{
+ FastRandomContext ctx;
+ for (int i = 0; i <= 64; ++i) {
+ if (i == 0) {
+ // Check handling of zero.
+ BOOST_CHECK_EQUAL(CountBits(0), 0);
+ } else if (i < 10) {
+ for (uint64_t j = 1 << (i - 1); (j >> i) == 0; ++j) {
+ // Exhaustively test up to 10 bits
+ BOOST_CHECK_EQUAL(CountBits(j), i);
+ }
+ } else {
+ for (int k = 0; k < 1000; k++) {
+ // Randomly test 1000 samples of each length above 10 bits.
+ uint64_t j = ((uint64_t)1) << (i - 1) | ctx.randbits(i - 1);
+ BOOST_CHECK_EQUAL(CountBits(j), i);
+ }
+ }
+ }
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/cuckoocache_tests.cpp b/src/test/cuckoocache_tests.cpp
index ccd7155627..8cae4e66e8 100644
--- a/src/test/cuckoocache_tests.cpp
+++ b/src/test/cuckoocache_tests.cpp
@@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <boost/test/unit_test.hpp>
#include "cuckoocache.h"
+#include "script/sigcache.h"
#include "test/test_bitcoin.h"
#include "random.h"
#include <thread>
@@ -36,20 +37,6 @@ void insecure_GetRandHash(uint256& t)
*(ptr++) = insecure_rand.rand32();
}
-/** Definition copied from /src/script/sigcache.cpp
- */
-class uint256Hasher
-{
-public:
- template <uint8_t hash_select>
- uint32_t operator()(const uint256& key) const
- {
- static_assert(hash_select <8, "SignatureCacheHasher only has 8 hashes available.");
- uint32_t u;
- std::memcpy(&u, key.begin() + 4 * hash_select, 4);
- return u;
- }
-};
/* Test that no values not inserted into the cache are read out of it.
@@ -59,7 +46,7 @@ public:
BOOST_AUTO_TEST_CASE(test_cuckoocache_no_fakes)
{
insecure_rand = FastRandomContext(true);
- CuckooCache::cache<uint256, uint256Hasher> cc{};
+ CuckooCache::cache<uint256, SignatureCacheHasher> cc{};
size_t megabytes = 4;
cc.setup_bytes(megabytes << 20);
uint256 v;
@@ -138,7 +125,7 @@ BOOST_AUTO_TEST_CASE(cuckoocache_hit_rate_ok)
double HitRateThresh = 0.98;
size_t megabytes = 4;
for (double load = 0.1; load < 2; load *= 2) {
- double hits = test_cache<CuckooCache::cache<uint256, uint256Hasher>>(megabytes, load);
+ double hits = test_cache<CuckooCache::cache<uint256, SignatureCacheHasher>>(megabytes, load);
BOOST_CHECK(normalize_hit_rate(hits, load) > HitRateThresh);
}
}
@@ -206,7 +193,7 @@ void test_cache_erase(size_t megabytes)
BOOST_AUTO_TEST_CASE(cuckoocache_erase_ok)
{
size_t megabytes = 4;
- test_cache_erase<CuckooCache::cache<uint256, uint256Hasher>>(megabytes);
+ test_cache_erase<CuckooCache::cache<uint256, SignatureCacheHasher>>(megabytes);
}
template <typename Cache>
@@ -293,7 +280,7 @@ void test_cache_erase_parallel(size_t megabytes)
BOOST_AUTO_TEST_CASE(cuckoocache_erase_parallel_ok)
{
size_t megabytes = 4;
- test_cache_erase_parallel<CuckooCache::cache<uint256, uint256Hasher>>(megabytes);
+ test_cache_erase_parallel<CuckooCache::cache<uint256, SignatureCacheHasher>>(megabytes);
}
@@ -389,7 +376,7 @@ void test_cache_generations()
}
BOOST_AUTO_TEST_CASE(cuckoocache_generations)
{
- test_cache_generations<CuckooCache::cache<uint256, uint256Hasher>>();
+ test_cache_generations<CuckooCache::cache<uint256, SignatureCacheHasher>>();
}
BOOST_AUTO_TEST_SUITE_END();
diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp
index 9d55beb8ea..c9d9849ada 100644
--- a/src/test/dbwrapper_tests.cpp
+++ b/src/test/dbwrapper_tests.cpp
@@ -28,7 +28,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper)
// Perform tests both obfuscated and non-obfuscated.
for (int i = 0; i < 2; i++) {
bool obfuscate = (bool)i;
- boost::filesystem::path ph = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
+ fs::path ph = fs::temp_directory_path() / fs::unique_path();
CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate);
char key = 'k';
uint256 in = GetRandHash();
@@ -49,7 +49,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper_batch)
// Perform tests both obfuscated and non-obfuscated.
for (int i = 0; i < 2; i++) {
bool obfuscate = (bool)i;
- boost::filesystem::path ph = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
+ fs::path ph = fs::temp_directory_path() / fs::unique_path();
CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate);
char key = 'i';
@@ -86,7 +86,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper_iterator)
// Perform tests both obfuscated and non-obfuscated.
for (int i = 0; i < 2; i++) {
bool obfuscate = (bool)i;
- boost::filesystem::path ph = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
+ fs::path ph = fs::temp_directory_path() / fs::unique_path();
CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate);
// The two keys are intentionally chosen for ordering
@@ -125,8 +125,8 @@ BOOST_AUTO_TEST_CASE(dbwrapper_iterator)
// Test that we do not obfuscation if there is existing data.
BOOST_AUTO_TEST_CASE(existing_data_no_obfuscate)
{
- // We're going to share this boost::filesystem::path between two wrappers
- boost::filesystem::path ph = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
+ // We're going to share this fs::path between two wrappers
+ fs::path ph = fs::temp_directory_path() / fs::unique_path();
create_directories(ph);
// Set up a non-obfuscated wrapper to write some initial data.
@@ -167,8 +167,8 @@ BOOST_AUTO_TEST_CASE(existing_data_no_obfuscate)
// Ensure that we start obfuscating during a reindex.
BOOST_AUTO_TEST_CASE(existing_data_reindex)
{
- // We're going to share this boost::filesystem::path between two wrappers
- boost::filesystem::path ph = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
+ // We're going to share this fs::path between two wrappers
+ fs::path ph = fs::temp_directory_path() / fs::unique_path();
create_directories(ph);
// Set up a non-obfuscated wrapper to write some initial data.
@@ -204,7 +204,7 @@ BOOST_AUTO_TEST_CASE(existing_data_reindex)
BOOST_AUTO_TEST_CASE(iterator_ordering)
{
- boost::filesystem::path ph = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
+ fs::path ph = fs::temp_directory_path() / fs::unique_path();
CDBWrapper dbw(ph, (1 << 20), true, false, false);
for (int x=0x00; x<256; ++x) {
uint8_t key = x;
@@ -275,7 +275,7 @@ BOOST_AUTO_TEST_CASE(iterator_string_ordering)
{
char buf[10];
- boost::filesystem::path ph = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
+ fs::path ph = fs::temp_directory_path() / fs::unique_path();
CDBWrapper dbw(ph, (1 << 20), true, false, false);
for (int x=0x00; x<10; ++x) {
for (int y = 0; y < 10; y++) {
diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp
index b9ed4952bb..0c7f3e5e23 100644
--- a/src/test/net_tests.cpp
+++ b/src/test/net_tests.cpp
@@ -11,6 +11,7 @@
#include "net.h"
#include "netbase.h"
#include "chainparams.h"
+#include "util.h"
class CAddrManSerializationMock : public CAddrMan
{
@@ -72,6 +73,18 @@ CDataStream AddrmanToStream(CAddrManSerializationMock& _addrman)
BOOST_FIXTURE_TEST_SUITE(net_tests, BasicTestingSetup)
+BOOST_AUTO_TEST_CASE(cnode_listen_port)
+{
+ // test default
+ unsigned short port = GetListenPort();
+ BOOST_CHECK(port == Params().GetDefaultPort());
+ // test set port
+ unsigned short altPort = 12345;
+ SoftSetArg("-port", std::to_string(altPort));
+ port = GetListenPort();
+ BOOST_CHECK(port == altPort);
+}
+
BOOST_AUTO_TEST_CASE(caddrdb_read)
{
CAddrManUncorrupted addrmanUncorrupted;
diff --git a/src/test/policyestimator_tests.cpp b/src/test/policyestimator_tests.cpp
index bc2f49ef3f..ed6782ea34 100644
--- a/src/test/policyestimator_tests.cpp
+++ b/src/test/policyestimator_tests.cpp
@@ -16,7 +16,8 @@ BOOST_FIXTURE_TEST_SUITE(policyestimator_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
{
- CTxMemPool mpool;
+ CBlockPolicyEstimator feeEst;
+ CTxMemPool mpool(&feeEst);
TestMemPoolEntryHelper entry;
CAmount basefee(2000);
CAmount deltaFee(100);
@@ -78,16 +79,16 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
// At this point we should need to combine 5 buckets to get enough data points
// So estimateFee(1,2,3) should fail and estimateFee(4) should return somewhere around
// 8*baserate. estimateFee(4) %'s are 100,100,100,100,90 = average 98%
- BOOST_CHECK(mpool.estimateFee(1) == CFeeRate(0));
- BOOST_CHECK(mpool.estimateFee(2) == CFeeRate(0));
- BOOST_CHECK(mpool.estimateFee(3) == CFeeRate(0));
- BOOST_CHECK(mpool.estimateFee(4).GetFeePerK() < 8*baseRate.GetFeePerK() + deltaFee);
- BOOST_CHECK(mpool.estimateFee(4).GetFeePerK() > 8*baseRate.GetFeePerK() - deltaFee);
+ BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));
+ BOOST_CHECK(feeEst.estimateFee(2) == CFeeRate(0));
+ BOOST_CHECK(feeEst.estimateFee(3) == CFeeRate(0));
+ BOOST_CHECK(feeEst.estimateFee(4).GetFeePerK() < 8*baseRate.GetFeePerK() + deltaFee);
+ BOOST_CHECK(feeEst.estimateFee(4).GetFeePerK() > 8*baseRate.GetFeePerK() - deltaFee);
int answerFound;
- BOOST_CHECK(mpool.estimateSmartFee(1, &answerFound) == mpool.estimateFee(4) && answerFound == 4);
- BOOST_CHECK(mpool.estimateSmartFee(3, &answerFound) == mpool.estimateFee(4) && answerFound == 4);
- BOOST_CHECK(mpool.estimateSmartFee(4, &answerFound) == mpool.estimateFee(4) && answerFound == 4);
- BOOST_CHECK(mpool.estimateSmartFee(8, &answerFound) == mpool.estimateFee(8) && answerFound == 8);
+ BOOST_CHECK(feeEst.estimateSmartFee(1, &answerFound, mpool) == feeEst.estimateFee(4) && answerFound == 4);
+ BOOST_CHECK(feeEst.estimateSmartFee(3, &answerFound, mpool) == feeEst.estimateFee(4) && answerFound == 4);
+ BOOST_CHECK(feeEst.estimateSmartFee(4, &answerFound, mpool) == feeEst.estimateFee(4) && answerFound == 4);
+ BOOST_CHECK(feeEst.estimateSmartFee(8, &answerFound, mpool) == feeEst.estimateFee(8) && answerFound == 8);
}
}
@@ -99,7 +100,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
// Second highest feerate has 100% chance of being included by 2 blocks,
// so estimateFee(2) should return 9*baseRate etc...
for (int i = 1; i < 10;i++) {
- origFeeEst.push_back(mpool.estimateFee(i).GetFeePerK());
+ origFeeEst.push_back(feeEst.estimateFee(i).GetFeePerK());
if (i > 2) { // Fee estimates should be monotonically decreasing
BOOST_CHECK(origFeeEst[i-1] <= origFeeEst[i-2]);
}
@@ -118,10 +119,10 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
while (blocknum < 250)
mpool.removeForBlock(block, ++blocknum);
- BOOST_CHECK(mpool.estimateFee(1) == CFeeRate(0));
+ BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));
for (int i = 2; i < 10;i++) {
- BOOST_CHECK(mpool.estimateFee(i).GetFeePerK() < origFeeEst[i-1] + deltaFee);
- BOOST_CHECK(mpool.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);
+ BOOST_CHECK(feeEst.estimateFee(i).GetFeePerK() < origFeeEst[i-1] + deltaFee);
+ BOOST_CHECK(feeEst.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);
}
@@ -141,8 +142,8 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
int answerFound;
for (int i = 1; i < 10;i++) {
- BOOST_CHECK(mpool.estimateFee(i) == CFeeRate(0) || mpool.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);
- BOOST_CHECK(mpool.estimateSmartFee(i, &answerFound).GetFeePerK() > origFeeEst[answerFound-1] - deltaFee);
+ BOOST_CHECK(feeEst.estimateFee(i) == CFeeRate(0) || feeEst.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);
+ BOOST_CHECK(feeEst.estimateSmartFee(i, &answerFound, mpool).GetFeePerK() > origFeeEst[answerFound-1] - deltaFee);
}
// Mine all those transactions
@@ -157,9 +158,9 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
}
mpool.removeForBlock(block, 265);
block.clear();
- BOOST_CHECK(mpool.estimateFee(1) == CFeeRate(0));
+ BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));
for (int i = 2; i < 10;i++) {
- BOOST_CHECK(mpool.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);
+ BOOST_CHECK(feeEst.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);
}
// Mine 200 more blocks where everything is mined every block
@@ -179,9 +180,9 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
mpool.removeForBlock(block, ++blocknum);
block.clear();
}
- BOOST_CHECK(mpool.estimateFee(1) == CFeeRate(0));
+ BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));
for (int i = 2; i < 10; i++) {
- BOOST_CHECK(mpool.estimateFee(i).GetFeePerK() < origFeeEst[i-1] - deltaFee);
+ BOOST_CHECK(feeEst.estimateFee(i).GetFeePerK() < origFeeEst[i-1] - deltaFee);
}
// Test that if the mempool is limited, estimateSmartFee won't return a value below the mempool min fee
@@ -190,8 +191,8 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
mpool.TrimToSize(1);
BOOST_CHECK(mpool.GetMinFee(1).GetFeePerK() > feeV[5]);
for (int i = 1; i < 10; i++) {
- BOOST_CHECK(mpool.estimateSmartFee(i).GetFeePerK() >= mpool.estimateFee(i).GetFeePerK());
- BOOST_CHECK(mpool.estimateSmartFee(i).GetFeePerK() >= mpool.GetMinFee(1).GetFeePerK());
+ BOOST_CHECK(feeEst.estimateSmartFee(i, NULL, mpool).GetFeePerK() >= feeEst.estimateFee(i).GetFeePerK());
+ BOOST_CHECK(feeEst.estimateSmartFee(i, NULL, mpool).GetFeePerK() >= mpool.GetMinFee(1).GetFeePerK());
}
}
diff --git a/src/test/prevector_tests.cpp b/src/test/prevector_tests.cpp
index bd8a7819a4..cfed5e347e 100644
--- a/src/test/prevector_tests.cpp
+++ b/src/test/prevector_tests.cpp
@@ -28,6 +28,7 @@ class prevector_tester {
typedef typename pretype::size_type Size;
bool passed = true;
FastRandomContext rand_cache;
+ uint256 rand_seed;
template <typename A, typename B>
@@ -183,13 +184,12 @@ public:
}
~prevector_tester() {
- BOOST_CHECK_MESSAGE(passed, "insecure_rand_Rz: "
- << rand_cache.Rz
- << ", insecure_rand_Rw: "
- << rand_cache.Rw);
+ BOOST_CHECK_MESSAGE(passed, "insecure_rand: " + rand_seed.ToString());
}
+
prevector_tester() {
seed_insecure_rand();
+ rand_seed = insecure_rand_seed;
rand_cache = insecure_rand_ctx;
}
};
diff --git a/src/test/random_tests.cpp b/src/test/random_tests.cpp
index d2c46c0daa..8596734226 100644
--- a/src/test/random_tests.cpp
+++ b/src/test/random_tests.cpp
@@ -15,5 +15,39 @@ BOOST_AUTO_TEST_CASE(osrandom_tests)
BOOST_CHECK(Random_SanityCheck());
}
-BOOST_AUTO_TEST_SUITE_END()
+BOOST_AUTO_TEST_CASE(fastrandom_tests)
+{
+ // Check that deterministic FastRandomContexts are deterministic
+ FastRandomContext ctx1(true);
+ FastRandomContext ctx2(true);
+
+ BOOST_CHECK_EQUAL(ctx1.rand32(), ctx2.rand32());
+ BOOST_CHECK_EQUAL(ctx1.rand32(), ctx2.rand32());
+ BOOST_CHECK_EQUAL(ctx1.rand64(), ctx2.rand64());
+ BOOST_CHECK_EQUAL(ctx1.randbits(3), ctx2.randbits(3));
+ BOOST_CHECK_EQUAL(ctx1.randbits(7), ctx2.randbits(7));
+ BOOST_CHECK_EQUAL(ctx1.rand32(), ctx2.rand32());
+ BOOST_CHECK_EQUAL(ctx1.randbits(3), ctx2.randbits(3));
+
+ // Check that a nondeterministic ones are not
+ FastRandomContext ctx3;
+ FastRandomContext ctx4;
+ BOOST_CHECK(ctx3.rand64() != ctx4.rand64()); // extremely unlikely to be equal
+}
+BOOST_AUTO_TEST_CASE(fastrandom_randbits)
+{
+ FastRandomContext ctx1;
+ FastRandomContext ctx2;
+ for (int bits = 0; bits < 63; ++bits) {
+ for (int j = 0; j < 1000; ++j) {
+ uint64_t rangebits = ctx1.randbits(bits);
+ BOOST_CHECK_EQUAL(rangebits >> bits, 0);
+ uint64_t range = ((uint64_t)1) << bits | rangebits;
+ uint64_t rand = ctx2.randrange(range);
+ BOOST_CHECK(rand < range);
+ }
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp
index abaec45cd7..cb625bda11 100644
--- a/src/test/test_bitcoin.cpp
+++ b/src/test/test_bitcoin.cpp
@@ -7,6 +7,7 @@
#include "chainparams.h"
#include "consensus/consensus.h"
#include "consensus/validation.h"
+#include "fs.h"
#include "key.h"
#include "validation.h"
#include "miner.h"
@@ -24,10 +25,10 @@
#include <memory>
-#include <boost/filesystem.hpp>
#include <boost/thread.hpp>
-FastRandomContext insecure_rand_ctx(true);
+uint256 insecure_rand_seed = GetRandHash();
+FastRandomContext insecure_rand_ctx(insecure_rand_seed);
extern bool fPrintToConsole;
extern void noui_connect();
@@ -59,7 +60,7 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
RegisterAllCoreRPCCommands(tableRPC);
ClearDatadirCache();
pathTemp = GetTempPath() / strprintf("test_bitcoin_%lu_%i", (unsigned long)GetTime(), (int)(GetRand(100000)));
- boost::filesystem::create_directories(pathTemp);
+ fs::create_directories(pathTemp);
ForceSetArg("-datadir", pathTemp.string());
mempool.setSanityCheck(1.0);
pblocktree = new CBlockTreeDB(1 << 20, true);
@@ -91,7 +92,7 @@ TestingSetup::~TestingSetup()
delete pcoinsTip;
delete pcoinsdbview;
delete pblocktree;
- boost::filesystem::remove_all(pathTemp);
+ fs::remove_all(pathTemp);
}
TestChain100Setup::TestChain100Setup() : TestingSetup(CBaseChainParams::REGTEST)
diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h
index a593f136eb..60a86d8c48 100644
--- a/src/test/test_bitcoin.h
+++ b/src/test/test_bitcoin.h
@@ -6,12 +6,12 @@
#define BITCOIN_TEST_TEST_BITCOIN_H
#include "chainparamsbase.h"
+#include "fs.h"
#include "key.h"
#include "pubkey.h"
#include "txdb.h"
#include "txmempool.h"
-#include <boost/filesystem.hpp>
#include <boost/thread.hpp>
/** Basic testing setup.
@@ -30,7 +30,7 @@ struct BasicTestingSetup {
class CConnman;
struct TestingSetup: public BasicTestingSetup {
CCoinsViewDB *pcoinsdbview;
- boost::filesystem::path pathTemp;
+ fs::path pathTemp;
boost::thread_group threadGroup;
CConnman* connman;
diff --git a/src/test/test_random.h b/src/test/test_random.h
index 4a1637ac72..318c44df4d 100644
--- a/src/test/test_random.h
+++ b/src/test/test_random.h
@@ -8,11 +8,17 @@
#include "random.h"
+extern uint256 insecure_rand_seed;
extern FastRandomContext insecure_rand_ctx;
static inline void seed_insecure_rand(bool fDeterministic = false)
{
- insecure_rand_ctx = FastRandomContext(fDeterministic);
+ if (fDeterministic) {
+ insecure_rand_seed = uint256();
+ } else {
+ insecure_rand_seed = GetRandHash();
+ }
+ insecure_rand_ctx = FastRandomContext(insecure_rand_seed);
}
static inline uint32_t insecure_rand(void)
diff --git a/src/test/testutil.cpp b/src/test/testutil.cpp
index e6d8622979..591d0bf302 100644
--- a/src/test/testutil.cpp
+++ b/src/test/testutil.cpp
@@ -8,8 +8,8 @@
#include <shlobj.h>
#endif
-#include <boost/filesystem.hpp>
+#include "fs.h"
-boost::filesystem::path GetTempPath() {
- return boost::filesystem::temp_directory_path();
+fs::path GetTempPath() {
+ return fs::temp_directory_path();
}
diff --git a/src/test/testutil.h b/src/test/testutil.h
index 5875dc50e6..cbe784d640 100644
--- a/src/test/testutil.h
+++ b/src/test/testutil.h
@@ -8,8 +8,8 @@
#ifndef BITCOIN_TEST_TESTUTIL_H
#define BITCOIN_TEST_TESTUTIL_H
-#include <boost/filesystem/path.hpp>
+#include "fs.h"
-boost::filesystem::path GetTempPath();
+fs::path GetTempPath();
#endif // BITCOIN_TEST_TESTUTIL_H
diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp
index 4c88ebe1d5..c1bd95b00f 100644
--- a/src/torcontrol.cpp
+++ b/src/torcontrol.cpp
@@ -314,9 +314,9 @@ static std::map<std::string,std::string> ParseTorReplyMapping(const std::string
* @param maxsize Puts a maximum size limit on the file that is read. If the file is larger than this, truncated data
* (with len > maxsize) will be returned.
*/
-static std::pair<bool,std::string> ReadBinaryFile(const std::string &filename, size_t maxsize=std::numeric_limits<size_t>::max())
+static std::pair<bool,std::string> ReadBinaryFile(const fs::path &filename, size_t maxsize=std::numeric_limits<size_t>::max())
{
- FILE *f = fopen(filename.c_str(), "rb");
+ FILE *f = fsbridge::fopen(filename, "rb");
if (f == NULL)
return std::make_pair(false,"");
std::string retval;
@@ -334,9 +334,9 @@ static std::pair<bool,std::string> ReadBinaryFile(const std::string &filename, s
/** Write contents of std::string to a file.
* @return true on success.
*/
-static bool WriteBinaryFile(const std::string &filename, const std::string &data)
+static bool WriteBinaryFile(const fs::path &filename, const std::string &data)
{
- FILE *f = fopen(filename.c_str(), "wb");
+ FILE *f = fsbridge::fopen(filename, "wb");
if (f == NULL)
return false;
if (fwrite(data.data(), 1, data.size(), f) != data.size()) {
@@ -359,7 +359,7 @@ public:
~TorController();
/** Get name fo file to store private key in */
- std::string GetPrivateKeyFile();
+ fs::path GetPrivateKeyFile();
/** Reconnect, after getting disconnected */
void Reconnect();
@@ -411,7 +411,7 @@ TorController::TorController(struct event_base* _base, const std::string& _targe
// Read service private key if cached
std::pair<bool,std::string> pkf = ReadBinaryFile(GetPrivateKeyFile());
if (pkf.first) {
- LogPrint(BCLog::TOR, "tor: Reading cached private key from %s\n", GetPrivateKeyFile());
+ LogPrint(BCLog::TOR, "tor: Reading cached private key from %s\n", GetPrivateKeyFile().string());
private_key = pkf.second;
}
}
@@ -442,9 +442,9 @@ void TorController::add_onion_cb(TorControlConnection& _conn, const TorControlRe
service = LookupNumeric(std::string(service_id+".onion").c_str(), GetListenPort());
LogPrintf("tor: Got service ID %s, advertising service %s\n", service_id, service.ToString());
if (WriteBinaryFile(GetPrivateKeyFile(), private_key)) {
- LogPrint(BCLog::TOR, "tor: Cached service private key to %s\n", GetPrivateKeyFile());
+ LogPrint(BCLog::TOR, "tor: Cached service private key to %s\n", GetPrivateKeyFile().string());
} else {
- LogPrintf("tor: Error writing service private key to %s\n", GetPrivateKeyFile());
+ LogPrintf("tor: Error writing service private key to %s\n", GetPrivateKeyFile().string());
}
AddLocal(service, LOCAL_MANUAL);
// ... onion requested - keep connection open
@@ -651,9 +651,9 @@ void TorController::Reconnect()
}
}
-std::string TorController::GetPrivateKeyFile()
+fs::path TorController::GetPrivateKeyFile()
{
- return (GetDataDir() / "onion_private_key").string();
+ return GetDataDir() / "onion_private_key";
}
void TorController::reconnect_cb(evutil_socket_t fd, short what, void *arg)
diff --git a/src/txdb.h b/src/txdb.h
index 7f5cf2b583..d9214ba618 100644
--- a/src/txdb.h
+++ b/src/txdb.h
@@ -21,8 +21,14 @@ class CBlockIndex;
class CCoinsViewDBCursor;
class uint256;
+//! Compensate for extra memory peak (x1.5-x1.9) at flush time.
+static constexpr int DB_PEAK_USAGE_FACTOR = 2;
+//! No need to periodic flush if at least this much space still available.
+static constexpr int MAX_BLOCK_COINSDB_USAGE = 200 * DB_PEAK_USAGE_FACTOR;
+//! Always periodic flush if less than this much space still available.
+static constexpr int MIN_BLOCK_COINSDB_USAGE = 50 * DB_PEAK_USAGE_FACTOR;
//! -dbcache default (MiB)
-static const int64_t nDefaultDbCache = 300;
+static const int64_t nDefaultDbCache = 450;
//! max. -dbcache (MiB)
static const int64_t nMaxDbCache = sizeof(void*) > 4 ? 16384 : 1024;
//! min. -dbcache (MiB)
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 36a046ed2a..ac842da6bf 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -5,7 +5,6 @@
#include "txmempool.h"
-#include "clientversion.h"
#include "consensus/consensus.h"
#include "consensus/validation.h"
#include "validation.h"
@@ -16,7 +15,6 @@
#include "util.h"
#include "utilmoneystr.h"
#include "utiltime.h"
-#include "version.h"
CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef& _tx, const CAmount& _nFee,
int64_t _nTime, unsigned int _entryHeight,
@@ -333,8 +331,8 @@ void CTxMemPoolEntry::UpdateAncestorState(int64_t modifySize, CAmount modifyFee,
assert(int(nSigOpCostWithAncestors) >= 0);
}
-CTxMemPool::CTxMemPool() :
- nTransactionsUpdated(0)
+CTxMemPool::CTxMemPool(CBlockPolicyEstimator* estimator) :
+ nTransactionsUpdated(0), minerPolicyEstimator(estimator)
{
_clear(); //lock free clear
@@ -342,13 +340,6 @@ CTxMemPool::CTxMemPool() :
// accepting transactions becomes O(N^2) where N is the number
// of transactions in the pool
nCheckFrequency = 0;
-
- minerPolicyEstimator = new CBlockPolicyEstimator();
-}
-
-CTxMemPool::~CTxMemPool()
-{
- delete minerPolicyEstimator;
}
void CTxMemPool::pruneSpent(const uint256 &hashTx, CCoins &coins)
@@ -427,7 +418,7 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry,
nTransactionsUpdated++;
totalTxSize += entry.GetTxSize();
- minerPolicyEstimator->processTransaction(entry, validFeeEstimate);
+ if (minerPolicyEstimator) {minerPolicyEstimator->processTransaction(entry, validFeeEstimate);}
vTxHashes.emplace_back(tx.GetWitnessHash(), newit);
newit->vTxHashesIdx = vTxHashes.size() - 1;
@@ -457,7 +448,7 @@ void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
mapLinks.erase(it);
mapTx.erase(it);
nTransactionsUpdated++;
- minerPolicyEstimator->removeTx(hash);
+ if (minerPolicyEstimator) {minerPolicyEstimator->removeTx(hash);}
}
// Calculates descendants of entry that are not already in setDescendants, and adds to
@@ -591,7 +582,7 @@ void CTxMemPool::removeForBlock(const std::vector<CTransactionRef>& vtx, unsigne
entries.push_back(&*i);
}
// Before the txs in the new block have been removed from the mempool, update policy estimates
- minerPolicyEstimator->processBlock(nBlockHeight, entries);
+ if (minerPolicyEstimator) {minerPolicyEstimator->processBlock(nBlockHeight, entries);}
for (const auto& tx : vtx)
{
txiter it = mapTx.find(tx->GetHash());
@@ -850,51 +841,6 @@ TxMempoolInfo CTxMemPool::info(const uint256& hash) const
return GetInfo(i);
}
-CFeeRate CTxMemPool::estimateFee(int nBlocks) const
-{
- LOCK(cs);
- return minerPolicyEstimator->estimateFee(nBlocks);
-}
-CFeeRate CTxMemPool::estimateSmartFee(int nBlocks, int *answerFoundAtBlocks) const
-{
- LOCK(cs);
- return minerPolicyEstimator->estimateSmartFee(nBlocks, answerFoundAtBlocks, *this);
-}
-
-bool
-CTxMemPool::WriteFeeEstimates(CAutoFile& fileout) const
-{
- try {
- LOCK(cs);
- fileout << 139900; // version required to read: 0.13.99 or later
- fileout << CLIENT_VERSION; // version that wrote the file
- minerPolicyEstimator->Write(fileout);
- }
- catch (const std::exception&) {
- LogPrintf("CTxMemPool::WriteFeeEstimates(): unable to write policy estimator data (non-fatal)\n");
- return false;
- }
- return true;
-}
-
-bool
-CTxMemPool::ReadFeeEstimates(CAutoFile& filein)
-{
- try {
- int nVersionRequired, nVersionThatWrote;
- filein >> nVersionRequired >> nVersionThatWrote;
- if (nVersionRequired > CLIENT_VERSION)
- return error("CTxMemPool::ReadFeeEstimates(): up-version (%d) fee estimate file", nVersionRequired);
- LOCK(cs);
- minerPolicyEstimator->Read(filein, nVersionThatWrote);
- }
- catch (const std::exception&) {
- LogPrintf("CTxMemPool::ReadFeeEstimates(): unable to read policy estimator data (non-fatal)\n");
- return false;
- }
- return true;
-}
-
void CTxMemPool::PrioritiseTransaction(const uint256& hash, const CAmount& nFeeDelta)
{
{
@@ -912,6 +858,13 @@ void CTxMemPool::PrioritiseTransaction(const uint256& hash, const CAmount& nFeeD
BOOST_FOREACH(txiter ancestorIt, setAncestors) {
mapTx.modify(ancestorIt, update_descendant_state(0, nFeeDelta, 0));
}
+ // Now update all descendants' modified fees with ancestors
+ setEntries setDescendants;
+ CalculateDescendants(it, setDescendants);
+ setDescendants.erase(it);
+ BOOST_FOREACH(txiter descendantIt, setDescendants) {
+ mapTx.modify(descendantIt, update_ancestor_state(0, nFeeDelta, 0, 0));
+ }
}
}
LogPrintf("PrioritiseTransaction: %s feerate += %s\n", hash.ToString(), FormatMoney(nFeeDelta));
diff --git a/src/txmempool.h b/src/txmempool.h
index 4222789510..92c4d9f9d4 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -496,8 +496,7 @@ public:
/** Create a new CTxMemPool.
*/
- CTxMemPool();
- ~CTxMemPool();
+ CTxMemPool(CBlockPolicyEstimator* estimator = nullptr);
/**
* If sanity-checking is turned on, check makes sure the pool is
@@ -618,19 +617,6 @@ public:
TxMempoolInfo info(const uint256& hash) const;
std::vector<TxMempoolInfo> infoAll() const;
- /** Estimate fee rate needed to get into the next nBlocks
- * If no answer can be given at nBlocks, return an estimate
- * at the lowest number of blocks where one can be given
- */
- CFeeRate estimateSmartFee(int nBlocks, int *answerFoundAtBlocks = NULL) const;
-
- /** Estimate fee rate needed to get into the next nBlocks */
- CFeeRate estimateFee(int nBlocks) const;
-
- /** Write/Read estimates to disk */
- bool WriteFeeEstimates(CAutoFile& fileout) const;
- bool ReadFeeEstimates(CAutoFile& filein);
-
size_t DynamicMemoryUsage() const;
boost::signals2::signal<void (CTransactionRef)> NotifyEntryAdded;
diff --git a/src/util.cpp b/src/util.cpp
index db45ad1626..cf10ee4aa5 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -10,6 +10,7 @@
#include "util.h"
#include "chainparamsbase.h"
+#include "fs.h"
#include "random.h"
#include "serialize.h"
#include "sync.h"
@@ -79,8 +80,6 @@
#include <boost/algorithm/string/case_conv.hpp> // for to_lower()
#include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/string/predicate.hpp> // for startswith() and endswith()
-#include <boost/filesystem.hpp>
-#include <boost/filesystem/fstream.hpp>
#include <boost/foreach.hpp>
#include <boost/program_options/detail/config_file.hpp>
#include <boost/program_options/parsers.hpp>
@@ -89,19 +88,6 @@
#include <openssl/rand.h>
#include <openssl/conf.h>
-// Work around clang compilation problem in Boost 1.46:
-// /usr/include/boost/program_options/detail/config_file.hpp:163:17: error: call to function 'to_internal' that is neither visible in the template definition nor found by argument-dependent lookup
-// See also: http://stackoverflow.com/questions/10020179/compilation-fail-in-boost-librairies-program-options
-// http://clang.debian.net/status.php?version=3.0&key=CANNOT_FIND_FUNCTION
-namespace boost {
-
- namespace program_options {
- std::string to_internal(const std::string&);
- }
-
-} // namespace boost
-
-
const char * const BITCOIN_CONF_FILENAME = "bitcoin.conf";
const char * const BITCOIN_PID_FILENAME = "bitcoind.pid";
@@ -119,7 +105,7 @@ bool fLogIPs = DEFAULT_LOGIPS;
std::atomic<bool> fReopenDebugLog(false);
CTranslationInterface translationInterface;
-/** Log categories bitfield. Leveldb/libevent need special handling if their flags are changed at runtime. */
+/** Log categories bitfield. */
std::atomic<uint32_t> logCategories(0);
/** Init OpenSSL library multithreading support */
@@ -215,8 +201,8 @@ void OpenDebugLog()
assert(fileout == NULL);
assert(vMsgsBeforeOpenLog);
- boost::filesystem::path pathDebug = GetDataDir() / "debug.log";
- fileout = fopen(pathDebug.string().c_str(), "a");
+ fs::path pathDebug = GetDataDir() / "debug.log";
+ fileout = fsbridge::fopen(pathDebug, "a");
if (fileout) {
setbuf(fileout, NULL); // unbuffered
// dump buffered messages from before we opened the log
@@ -296,6 +282,21 @@ std::string ListLogCategories()
return ret;
}
+std::vector<CLogCategoryActive> ListActiveLogCategories()
+{
+ std::vector<CLogCategoryActive> ret;
+ for (unsigned int i = 0; i < ARRAYLEN(LogCategories); i++) {
+ // Omit the special cases.
+ if (LogCategories[i].flag != BCLog::NONE && LogCategories[i].flag != BCLog::ALL) {
+ CLogCategoryActive catActive;
+ catActive.category = LogCategories[i].category;
+ catActive.active = LogAcceptCategory(LogCategories[i].flag);
+ ret.push_back(catActive);
+ }
+ }
+ return ret;
+}
+
/**
* fStartedNewLine is a state variable held by the calling context that will
* suppress printing of the timestamp when multiple calls are made that don't
@@ -354,8 +355,8 @@ int LogPrintStr(const std::string &str)
// reopen the log file, if requested
if (fReopenDebugLog) {
fReopenDebugLog = false;
- boost::filesystem::path pathDebug = GetDataDir() / "debug.log";
- if (freopen(pathDebug.string().c_str(),"a",fileout) != NULL)
+ fs::path pathDebug = GetDataDir() / "debug.log";
+ if (fsbridge::freopen(pathDebug,"a",fileout) != NULL)
setbuf(fileout, NULL); // unbuffered
}
@@ -512,9 +513,8 @@ void PrintExceptionContinue(const std::exception* pex, const char* pszThread)
fprintf(stderr, "\n\n************************\n%s\n", message.c_str());
}
-boost::filesystem::path GetDefaultDataDir()
+fs::path GetDefaultDataDir()
{
- namespace fs = boost::filesystem;
// Windows < Vista: C:\Documents and Settings\Username\Application Data\Bitcoin
// Windows >= Vista: C:\Users\Username\AppData\Roaming\Bitcoin
// Mac: ~/Library/Application Support/Bitcoin
@@ -539,13 +539,12 @@ boost::filesystem::path GetDefaultDataDir()
#endif
}
-static boost::filesystem::path pathCached;
-static boost::filesystem::path pathCachedNetSpecific;
+static fs::path pathCached;
+static fs::path pathCachedNetSpecific;
static CCriticalSection csPathCached;
-const boost::filesystem::path &GetDataDir(bool fNetSpecific)
+const fs::path &GetDataDir(bool fNetSpecific)
{
- namespace fs = boost::filesystem;
LOCK(csPathCached);
@@ -577,13 +576,13 @@ void ClearDatadirCache()
{
LOCK(csPathCached);
- pathCached = boost::filesystem::path();
- pathCachedNetSpecific = boost::filesystem::path();
+ pathCached = fs::path();
+ pathCachedNetSpecific = fs::path();
}
-boost::filesystem::path GetConfigFile(const std::string& confPath)
+fs::path GetConfigFile(const std::string& confPath)
{
- boost::filesystem::path pathConfigFile(confPath);
+ fs::path pathConfigFile(confPath);
if (!pathConfigFile.is_complete())
pathConfigFile = GetDataDir(false) / pathConfigFile;
@@ -592,7 +591,7 @@ boost::filesystem::path GetConfigFile(const std::string& confPath)
void ReadConfigFile(const std::string& confPath)
{
- boost::filesystem::ifstream streamConfig(GetConfigFile(confPath));
+ fs::ifstream streamConfig(GetConfigFile(confPath));
if (!streamConfig.good())
return; // No bitcoin.conf file is OK
@@ -617,16 +616,16 @@ void ReadConfigFile(const std::string& confPath)
}
#ifndef WIN32
-boost::filesystem::path GetPidFile()
+fs::path GetPidFile()
{
- boost::filesystem::path pathPidFile(GetArg("-pid", BITCOIN_PID_FILENAME));
+ fs::path pathPidFile(GetArg("-pid", BITCOIN_PID_FILENAME));
if (!pathPidFile.is_complete()) pathPidFile = GetDataDir() / pathPidFile;
return pathPidFile;
}
-void CreatePidFile(const boost::filesystem::path &path, pid_t pid)
+void CreatePidFile(const fs::path &path, pid_t pid)
{
- FILE* file = fopen(path.string().c_str(), "w");
+ FILE* file = fsbridge::fopen(path, "w");
if (file)
{
fprintf(file, "%d\n", pid);
@@ -635,7 +634,7 @@ void CreatePidFile(const boost::filesystem::path &path, pid_t pid)
}
#endif
-bool RenameOver(boost::filesystem::path src, boost::filesystem::path dest)
+bool RenameOver(fs::path src, fs::path dest)
{
#ifdef WIN32
return MoveFileExA(src.string().c_str(), dest.string().c_str(),
@@ -651,13 +650,13 @@ bool RenameOver(boost::filesystem::path src, boost::filesystem::path dest)
* Specifically handles case where path p exists, but it wasn't possible for the user to
* write to the parent directory.
*/
-bool TryCreateDirectory(const boost::filesystem::path& p)
+bool TryCreateDirectory(const fs::path& p)
{
try
{
- return boost::filesystem::create_directory(p);
- } catch (const boost::filesystem::filesystem_error&) {
- if (!boost::filesystem::exists(p) || !boost::filesystem::is_directory(p))
+ return fs::create_directory(p);
+ } catch (const fs::filesystem_error&) {
+ if (!fs::exists(p) || !fs::is_directory(p))
throw;
}
@@ -764,11 +763,11 @@ void ShrinkDebugFile()
// Amount of debug.log to save at end when shrinking (must fit in memory)
constexpr size_t RECENT_DEBUG_HISTORY_SIZE = 10 * 1000000;
// Scroll debug.log if it's getting too big
- boost::filesystem::path pathLog = GetDataDir() / "debug.log";
- FILE* file = fopen(pathLog.string().c_str(), "r");
+ fs::path pathLog = GetDataDir() / "debug.log";
+ FILE* file = fsbridge::fopen(pathLog, "r");
// If debug.log file is more than 10% bigger the RECENT_DEBUG_HISTORY_SIZE
// trim it down by saving only the last RECENT_DEBUG_HISTORY_SIZE bytes
- if (file && boost::filesystem::file_size(pathLog) > 11 * (RECENT_DEBUG_HISTORY_SIZE / 10))
+ if (file && fs::file_size(pathLog) > 11 * (RECENT_DEBUG_HISTORY_SIZE / 10))
{
// Restart the file with some of the end
std::vector<char> vch(RECENT_DEBUG_HISTORY_SIZE, 0);
@@ -776,7 +775,7 @@ void ShrinkDebugFile()
int nBytes = fread(vch.data(), 1, vch.size(), file);
fclose(file);
- file = fopen(pathLog.string().c_str(), "w");
+ file = fsbridge::fopen(pathLog, "w");
if (file)
{
fwrite(vch.data(), 1, nBytes, file);
@@ -788,10 +787,8 @@ void ShrinkDebugFile()
}
#ifdef WIN32
-boost::filesystem::path GetSpecialFolderPath(int nFolder, bool fCreate)
+fs::path GetSpecialFolderPath(int nFolder, bool fCreate)
{
- namespace fs = boost::filesystem;
-
char pszPath[MAX_PATH] = "";
if(SHGetSpecialFolderPathA(NULL, pszPath, nFolder, fCreate))
@@ -851,9 +848,9 @@ void SetupEnvironment()
// The path locale is lazy initialized and to avoid deinitialization errors
// in multithreading environments, it is set explicitly by the main thread.
// A dummy locale is used to extract the internal default locale, used by
- // boost::filesystem::path, which is then used to explicitly imbue the path.
- std::locale loc = boost::filesystem::path::imbue(std::locale::classic());
- boost::filesystem::path::imbue(loc);
+ // fs::path, which is then used to explicitly imbue the path.
+ std::locale loc = fs::path::imbue(std::locale::classic());
+ fs::path::imbue(loc);
}
bool SetupNetworking()
diff --git a/src/util.h b/src/util.h
index 09481bc01c..ed28070a3f 100644
--- a/src/util.h
+++ b/src/util.h
@@ -15,6 +15,7 @@
#endif
#include "compat.h"
+#include "fs.h"
#include "tinyformat.h"
#include "utiltime.h"
@@ -25,7 +26,6 @@
#include <string>
#include <vector>
-#include <boost/filesystem/path.hpp>
#include <boost/signals2/signal.hpp>
#include <boost/thread/exceptions.hpp>
@@ -69,6 +69,12 @@ inline std::string _(const char* psz)
void SetupEnvironment();
bool SetupNetworking();
+struct CLogCategoryActive
+{
+ std::string category;
+ bool active;
+};
+
namespace BCLog {
enum LogFlags : uint32_t {
NONE = 0,
@@ -102,9 +108,12 @@ static inline bool LogAcceptCategory(uint32_t category)
return (logCategories.load(std::memory_order_relaxed) & category) != 0;
}
-/** Returns a string with the supported log categories */
+/** Returns a string with the log categories. */
std::string ListLogCategories();
+/** Returns a vector of the active log categories. */
+std::vector<CLogCategoryActive> ListActiveLogCategories();
+
/** Return true if str parses as a log category and set the flags in f */
bool GetLogCategory(uint32_t *f, const std::string *str);
@@ -144,19 +153,19 @@ void FileCommit(FILE *file);
bool TruncateFile(FILE *file, unsigned int length);
int RaiseFileDescriptorLimit(int nMinFD);
void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length);
-bool RenameOver(boost::filesystem::path src, boost::filesystem::path dest);
-bool TryCreateDirectory(const boost::filesystem::path& p);
-boost::filesystem::path GetDefaultDataDir();
-const boost::filesystem::path &GetDataDir(bool fNetSpecific = true);
+bool RenameOver(fs::path src, fs::path dest);
+bool TryCreateDirectory(const fs::path& p);
+fs::path GetDefaultDataDir();
+const fs::path &GetDataDir(bool fNetSpecific = true);
void ClearDatadirCache();
-boost::filesystem::path GetConfigFile(const std::string& confPath);
+fs::path GetConfigFile(const std::string& confPath);
#ifndef WIN32
-boost::filesystem::path GetPidFile();
-void CreatePidFile(const boost::filesystem::path &path, pid_t pid);
+fs::path GetPidFile();
+void CreatePidFile(const fs::path &path, pid_t pid);
#endif
void ReadConfigFile(const std::string& confPath);
#ifdef WIN32
-boost::filesystem::path GetSpecialFolderPath(int nFolder, bool fCreate = true);
+fs::path GetSpecialFolderPath(int nFolder, bool fCreate = true);
#endif
void OpenDebugLog();
void ShrinkDebugFile();
diff --git a/src/utiltime.cpp b/src/utiltime.cpp
index a9936a645a..510f540b1d 100644
--- a/src/utiltime.cpp
+++ b/src/utiltime.cpp
@@ -9,14 +9,17 @@
#include "utiltime.h"
+#include <atomic>
+
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/thread.hpp>
-static int64_t nMockTime = 0; //!< For unit testing
+static std::atomic<int64_t> nMockTime(0); //!< For unit testing
int64_t GetTime()
{
- if (nMockTime) return nMockTime;
+ int64_t mocktime = nMockTime.load(std::memory_order_relaxed);
+ if (mocktime) return mocktime;
time_t now = time(NULL);
assert(now > 0);
@@ -25,7 +28,7 @@ int64_t GetTime()
void SetMockTime(int64_t nMockTimeIn)
{
- nMockTime = nMockTimeIn;
+ nMockTime.store(nMockTimeIn, std::memory_order_relaxed);
}
int64_t GetTimeMillis()
@@ -52,7 +55,8 @@ int64_t GetSystemTimeInSeconds()
/** Return a time useful for the debug log */
int64_t GetLogTimeMicros()
{
- if (nMockTime) return nMockTime*1000000;
+ int64_t mocktime = nMockTime.load(std::memory_order_relaxed);
+ if (mocktime) return mocktime*1000000;
return GetTimeMicros();
}
diff --git a/src/validation.cpp b/src/validation.cpp
index 7eae5fd800..f189b741cd 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -6,12 +6,14 @@
#include "validation.h"
#include "arith_uint256.h"
+#include "chain.h"
#include "chainparams.h"
#include "checkpoints.h"
#include "checkqueue.h"
#include "consensus/consensus.h"
#include "consensus/merkle.h"
#include "consensus/validation.h"
+#include "fs.h"
#include "hash.h"
#include "init.h"
#include "policy/fees.h"
@@ -41,8 +43,6 @@
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/join.hpp>
-#include <boost/filesystem.hpp>
-#include <boost/filesystem/fstream.hpp>
#include <boost/math/distributions/poisson.hpp>
#include <boost/thread.hpp>
@@ -81,7 +81,8 @@ uint256 hashAssumeValid;
CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE);
CAmount maxTxFee = DEFAULT_TRANSACTION_MAXFEE;
-CTxMemPool mempool;
+CBlockPolicyEstimator feeEstimator;
+CTxMemPool mempool(&feeEstimator);
static void CheckBlockIndex(const Consensus::Params& consensusParams);
@@ -155,39 +156,6 @@ namespace {
std::set<int> setDirtyFileInfo;
} // anon namespace
-/* Use this class to start tracking transactions that are removed from the
- * mempool and pass all those transactions through SyncTransaction when the
- * object goes out of scope. This is currently only used to call SyncTransaction
- * on conflicts removed from the mempool during block connection. Applied in
- * ActivateBestChain around ActivateBestStep which in turn calls:
- * ConnectTip->removeForBlock->removeConflicts
- */
-class MemPoolConflictRemovalTracker
-{
-private:
- std::vector<CTransactionRef> conflictedTxs;
- CTxMemPool &pool;
-
-public:
- MemPoolConflictRemovalTracker(CTxMemPool &_pool) : pool(_pool) {
- pool.NotifyEntryRemoved.connect(boost::bind(&MemPoolConflictRemovalTracker::NotifyEntryRemoved, this, _1, _2));
- }
-
- void NotifyEntryRemoved(CTransactionRef txRemoved, MemPoolRemovalReason reason) {
- if (reason == MemPoolRemovalReason::CONFLICT) {
- conflictedTxs.push_back(txRemoved);
- }
- }
-
- ~MemPoolConflictRemovalTracker() {
- pool.NotifyEntryRemoved.disconnect(boost::bind(&MemPoolConflictRemovalTracker::NotifyEntryRemoved, this, _1, _2));
- for (const auto& tx : conflictedTxs) {
- GetMainSignals().SyncTransaction(*tx, NULL, CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK);
- }
- conflictedTxs.clear();
- }
-};
-
CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator)
{
// Find the first block the caller has in the main chain
@@ -983,7 +951,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
}
}
- GetMainSignals().SyncTransaction(tx, NULL, CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK);
+ GetMainSignals().TransactionAddedToMempool(ptx);
return true;
}
@@ -1331,10 +1299,7 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight)
bool CScriptCheck::operator()() {
const CScript &scriptSig = ptxTo->vin[nIn].scriptSig;
const CScriptWitness *witness = &ptxTo->vin[nIn].scriptWitness;
- if (!VerifyScript(scriptSig, scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, amount, cacheStore, *txdata), &error)) {
- return false;
- }
- return true;
+ return VerifyScript(scriptSig, scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, amount, cacheStore, *txdata), &error);
}
int GetSpendHeight(const CCoinsViewCache& inputs)
@@ -1558,7 +1523,11 @@ bool ApplyTxInUndo(const CTxInUndo& undo, CCoinsViewCache& view, const COutPoint
return fClean;
}
-bool DisconnectBlock(const CBlock& block, CValidationState& state, const CBlockIndex* pindex, CCoinsViewCache& view, bool* pfClean)
+/** Undo the effects of this block (with given index) on the UTXO set represented by coins.
+ * In case pfClean is provided, operation will try to be tolerant about errors, and *pfClean
+ * will be true if no problems were found. Otherwise, the return value will be false in case
+ * of problems. Note that in any case, coins may be modified. */
+static bool DisconnectBlock(const CBlock& block, CValidationState& state, const CBlockIndex* pindex, CCoinsViewCache& view, bool* pfClean = NULL)
{
assert(pindex->GetBlockHash() == view.GetBestBlock());
@@ -1711,8 +1680,11 @@ static int64_t nTimeIndex = 0;
static int64_t nTimeCallbacks = 0;
static int64_t nTimeTotal = 0;
-bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex,
- CCoinsViewCache& view, const CChainParams& chainparams, bool fJustCheck)
+/** Apply the effects of this block (with given index) on the UTXO set represented by coins.
+ * Validity checks that depend on the UTXO set are also done; ConnectBlock()
+ * can fail if those validity checks fail (among other reasons). */
+static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex,
+ CCoinsViewCache& view, const CChainParams& chainparams, bool fJustCheck = false)
{
AssertLockHeld(cs_main);
assert(pindex);
@@ -1915,7 +1887,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
REJECT_INVALID, "bad-cb-amount");
if (!control.Wait())
- return state.DoS(100, false);
+ return state.DoS(100, error("%s: CheckQueue failed", __func__), REJECT_INVALID, "block-validation-failed");
int64_t nTime4 = GetTimeMicros(); nTimeVerify += nTime4 - nTime2;
LogPrint(BCLog::BENCH, " - Verify %u txins: %.2fms (%.3fms/txin) [%.2fs]\n", nInputs - 1, 0.001 * (nTime4 - nTime2), nInputs <= 1 ? 0 : 0.001 * (nTime4 - nTime2) / (nInputs-1), nTimeVerify * 0.000001);
@@ -1951,12 +1923,6 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
int64_t nTime5 = GetTimeMicros(); nTimeIndex += nTime5 - nTime4;
LogPrint(BCLog::BENCH, " - Index writing: %.2fms [%.2fs]\n", 0.001 * (nTime5 - nTime4), nTimeIndex * 0.000001);
- // Watch for changes to the previous coinbase transaction.
- static uint256 hashPrevBestCoinBase;
- GetMainSignals().UpdatedTransaction(hashPrevBestCoinBase);
- hashPrevBestCoinBase = block.vtx[0]->GetHash();
-
-
int64_t nTime6 = GetTimeMicros(); nTimeCallbacks += nTime6 - nTime5;
LogPrint(BCLog::BENCH, " - Callbacks: %.2fms [%.2fs]\n", 0.001 * (nTime6 - nTime5), nTimeCallbacks * 0.000001);
@@ -2006,10 +1972,11 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode, int n
nLastSetChain = nNow;
}
int64_t nMempoolSizeMax = GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
- int64_t cacheSize = pcoinsTip->DynamicMemoryUsage() * 2; // Compensate for extra memory peak (x1.5-x1.9) at flush time.
+ int64_t cacheSize = pcoinsTip->DynamicMemoryUsage() * DB_PEAK_USAGE_FACTOR;
int64_t nTotalSpace = nCoinCacheUsage + std::max<int64_t>(nMempoolSizeMax - nMempoolUsage, 0);
- // The cache is large and we're within 10% and 100 MiB of the limit, but we have time now (not in the middle of a block processing).
- bool fCacheLarge = mode == FLUSH_STATE_PERIODIC && cacheSize > std::max((9 * nTotalSpace) / 10, nTotalSpace - 100 * 1024 * 1024);
+ // The cache is large and we're within 10% and 200 MiB or 50% and 50MiB of the limit, but we have time now (not in the middle of a block processing).
+ bool fCacheLarge = mode == FLUSH_STATE_PERIODIC && cacheSize > std::min(std::max(nTotalSpace / 2, nTotalSpace - MIN_BLOCK_COINSDB_USAGE * 1024 * 1024),
+ std::max((9 * nTotalSpace) / 10, nTotalSpace - MAX_BLOCK_COINSDB_USAGE * 1024 * 1024));
// The cache is over the limit, we have to write now.
bool fCacheCritical = mode == FLUSH_STATE_IF_NEEDED && cacheSize > nTotalSpace;
// It's been a while since we wrote the block index to disk. Do this frequently, so we don't need to redownload after a crash.
@@ -2153,7 +2120,8 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara
CBlockIndex *pindexDelete = chainActive.Tip();
assert(pindexDelete);
// Read block from disk.
- CBlock block;
+ std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
+ CBlock& block = *pblock;
if (!ReadBlockFromDisk(block, pindexDelete, chainparams.GetConsensus()))
return AbortNode(state, "Failed to read block");
// Apply the block atomically to the chain state.
@@ -2195,9 +2163,7 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara
UpdateTip(pindexDelete->pprev, chainparams);
// Let wallets know transactions went from 1-confirmed to
// 0-confirmed or conflicted:
- for (const auto& tx : block.vtx) {
- GetMainSignals().SyncTransaction(*tx, pindexDelete->pprev, CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK);
- }
+ GetMainSignals().BlockDisconnected(pblock);
return true;
}
@@ -2207,36 +2173,92 @@ static int64_t nTimeFlush = 0;
static int64_t nTimeChainState = 0;
static int64_t nTimePostConnect = 0;
+struct PerBlockConnectTrace {
+ CBlockIndex* pindex = NULL;
+ std::shared_ptr<const CBlock> pblock;
+ std::shared_ptr<std::vector<CTransactionRef>> conflictedTxs;
+ PerBlockConnectTrace() : conflictedTxs(std::make_shared<std::vector<CTransactionRef>>()) {}
+};
/**
* Used to track blocks whose transactions were applied to the UTXO state as a
* part of a single ActivateBestChainStep call.
+ *
+ * This class also tracks transactions that are removed from the mempool as
+ * conflicts (per block) and can be used to pass all those transactions
+ * through SyncTransaction.
+ *
+ * This class assumes (and asserts) that the conflicted transactions for a given
+ * block are added via mempool callbacks prior to the BlockConnected() associated
+ * with those transactions. If any transactions are marked conflicted, it is
+ * assumed that an associated block will always be added.
+ *
+ * This class is single-use, once you call GetBlocksConnected() you have to throw
+ * it away and make a new one.
*/
-struct ConnectTrace {
- std::vector<std::pair<CBlockIndex*, std::shared_ptr<const CBlock> > > blocksConnected;
+class ConnectTrace {
+private:
+ std::vector<PerBlockConnectTrace> blocksConnected;
+ CTxMemPool &pool;
+
+public:
+ ConnectTrace(CTxMemPool &_pool) : blocksConnected(1), pool(_pool) {
+ pool.NotifyEntryRemoved.connect(boost::bind(&ConnectTrace::NotifyEntryRemoved, this, _1, _2));
+ }
+
+ ~ConnectTrace() {
+ pool.NotifyEntryRemoved.disconnect(boost::bind(&ConnectTrace::NotifyEntryRemoved, this, _1, _2));
+ }
+
+ void BlockConnected(CBlockIndex* pindex, std::shared_ptr<const CBlock> pblock) {
+ assert(!blocksConnected.back().pindex);
+ assert(pindex);
+ assert(pblock);
+ blocksConnected.back().pindex = pindex;
+ blocksConnected.back().pblock = std::move(pblock);
+ blocksConnected.emplace_back();
+ }
+
+ std::vector<PerBlockConnectTrace>& GetBlocksConnected() {
+ // We always keep one extra block at the end of our list because
+ // blocks are added after all the conflicted transactions have
+ // been filled in. Thus, the last entry should always be an empty
+ // one waiting for the transactions from the next block. We pop
+ // the last entry here to make sure the list we return is sane.
+ assert(!blocksConnected.back().pindex);
+ assert(blocksConnected.back().conflictedTxs->empty());
+ blocksConnected.pop_back();
+ return blocksConnected;
+ }
+
+ void NotifyEntryRemoved(CTransactionRef txRemoved, MemPoolRemovalReason reason) {
+ assert(!blocksConnected.back().pindex);
+ if (reason == MemPoolRemovalReason::CONFLICT) {
+ blocksConnected.back().conflictedTxs->emplace_back(std::move(txRemoved));
+ }
+ }
};
/**
* Connect a new block to chainActive. pblock is either NULL or a pointer to a CBlock
* corresponding to pindexNew, to bypass loading it again from disk.
*
- * The block is always added to connectTrace (either after loading from disk or by copying
- * pblock) - if that is not intended, care must be taken to remove the last entry in
- * blocksConnected in case of failure.
+ * The block is added to connectTrace if connection succeeds.
*/
bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace)
{
assert(pindexNew->pprev == chainActive.Tip());
// Read block from disk.
int64_t nTime1 = GetTimeMicros();
+ std::shared_ptr<const CBlock> pthisBlock;
if (!pblock) {
std::shared_ptr<CBlock> pblockNew = std::make_shared<CBlock>();
- connectTrace.blocksConnected.emplace_back(pindexNew, pblockNew);
if (!ReadBlockFromDisk(*pblockNew, pindexNew, chainparams.GetConsensus()))
return AbortNode(state, "Failed to read block");
+ pthisBlock = pblockNew;
} else {
- connectTrace.blocksConnected.emplace_back(pindexNew, pblock);
+ pthisBlock = pblock;
}
- const CBlock& blockConnecting = *connectTrace.blocksConnected.back().second;
+ const CBlock& blockConnecting = *pthisBlock;
// Apply the block atomically to the chain state.
int64_t nTime2 = GetTimeMicros(); nTimeReadFromDisk += nTime2 - nTime1;
int64_t nTime3;
@@ -2270,6 +2292,8 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams,
int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1;
LogPrint(BCLog::BENCH, " - Connect postprocess: %.2fms [%.2fs]\n", (nTime6 - nTime5) * 0.001, nTimePostConnect * 0.000001);
LogPrint(BCLog::BENCH, "- Connect block: %.2fms [%.2fs]\n", (nTime6 - nTime1) * 0.001, nTimeTotal * 0.000001);
+
+ connectTrace.BlockConnected(pindexNew, std::move(pthisBlock));
return true;
}
@@ -2388,8 +2412,6 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c
state = CValidationState();
fInvalidFound = true;
fContinue = false;
- // If we didn't actually connect the block, don't notify listeners about it
- connectTrace.blocksConnected.pop_back();
break;
} else {
// A system error occurred (disk space, database error, ...).
@@ -2461,18 +2483,11 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams,
break;
const CBlockIndex *pindexFork;
- ConnectTrace connectTrace;
bool fInitialDownload;
{
LOCK(cs_main);
- { // TODO: Temporarily ensure that mempool removals are notified before
- // connected transactions. This shouldn't matter, but the abandoned
- // state of transactions in our wallet is currently cleared when we
- // receive another notification and there is a race condition where
- // notification of a connected conflict might cause an outside process
- // to abandon a transaction and then have it inadvertently cleared by
- // the notification that the conflicted transaction was evicted.
- MemPoolConflictRemovalTracker mrt(mempool);
+ ConnectTrace connectTrace(mempool); // Destructed before cs_main is unlocked
+
CBlockIndex *pindexOldTip = chainActive.Tip();
if (pindexMostWork == NULL) {
pindexMostWork = FindMostWorkChain();
@@ -2495,16 +2510,9 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams,
pindexFork = chainActive.FindFork(pindexOldTip);
fInitialDownload = IsInitialBlockDownload();
- // throw all transactions though the signal-interface
-
- } // MemPoolConflictRemovalTracker destroyed and conflict evictions are notified
-
- // Transactions in the connected block are notified
- for (const auto& pair : connectTrace.blocksConnected) {
- assert(pair.second);
- const CBlock& block = *(pair.second);
- for (unsigned int i = 0; i < block.vtx.size(); i++)
- GetMainSignals().SyncTransaction(*block.vtx[i], pair.first, i);
+ for (const PerBlockConnectTrace& trace : connectTrace.GetBlocksConnected()) {
+ assert(trace.pblock && trace.pindex);
+ GetMainSignals().BlockConnected(trace.pblock, trace.pindex, *trace.conflictedTxs);
}
}
// When we reach this point, we switched to a new tip (stored in pindexNewTip).
@@ -2526,6 +2534,9 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams,
return false;
}
+ int nStopAtHeight = GetArg("-stopatheight", DEFAULT_STOPATHEIGHT);
+ if (nStopAtHeight && pindexNewTip && pindexNewTip->nHeight >= nStopAtHeight) StartShutdown();
+
return true;
}
@@ -2668,7 +2679,7 @@ CBlockIndex* AddToBlockIndex(const CBlockHeader& block)
}
/** Mark a block as having its data received and checked (up to BLOCK_VALID_TRANSACTIONS). */
-bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBlockIndex *pindexNew, const CDiskBlockPos& pos)
+static bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBlockIndex *pindexNew, const CDiskBlockPos& pos, const Consensus::Params& consensusParams)
{
pindexNew->nTx = block.vtx.size();
pindexNew->nChainTx = 0;
@@ -2676,7 +2687,7 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl
pindexNew->nDataPos = pos.nPos;
pindexNew->nUndoPos = 0;
pindexNew->nStatus |= BLOCK_HAVE_DATA;
- if (IsWitnessEnabled(pindexNew->pprev, Params().GetConsensus())) {
+ if (IsWitnessEnabled(pindexNew->pprev, consensusParams)) {
pindexNew->nStatus |= BLOCK_OPT_WITNESS;
}
pindexNew->RaiseValidity(BLOCK_VALID_TRANSACTIONS);
@@ -2882,10 +2893,12 @@ static bool CheckIndexAgainstCheckpoint(const CBlockIndex* pindexPrev, CValidati
return true;
int nHeight = pindexPrev->nHeight+1;
- // Don't accept any forks from the main chain prior to last checkpoint
+ // Don't accept any forks from the main chain prior to last checkpoint.
+ // GetLastCheckpoint finds the last checkpoint in MapCheckpoints that's in our
+ // MapBlockIndex.
CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(chainparams.Checkpoints());
if (pcheckpoint && nHeight < pcheckpoint->nHeight)
- return state.DoS(100, error("%s: forked chain older than last checkpoint (height %d)", __func__, nHeight));
+ return state.DoS(100, error("%s: forked chain older than last checkpoint (height %d)", __func__, nHeight), REJECT_CHECKPOINT, "bad-fork-prior-to-checkpoint");
return true;
}
@@ -3086,7 +3099,7 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state
CBlockIndex* pindexPrev = NULL;
BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock);
if (mi == mapBlockIndex.end())
- return state.DoS(10, error("%s: prev block not found", __func__), 0, "bad-prevblk");
+ return state.DoS(10, error("%s: prev block not found", __func__), 0, "prev-blk-not-found");
pindexPrev = (*mi).second;
if (pindexPrev->nStatus & BLOCK_FAILED_MASK)
return state.DoS(100, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk");
@@ -3196,7 +3209,7 @@ static bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidation
if (dbp == NULL)
if (!WriteBlockToDisk(block, blockPos, chainparams.MessageStart()))
AbortNode(state, "Failed to write block");
- if (!ReceivedBlockTransactions(block, state, pindex, blockPos))
+ if (!ReceivedBlockTransactions(block, state, pindex, blockPos, chainparams.GetConsensus()))
return error("AcceptBlock(): ReceivedBlockTransactions failed");
} catch (const std::runtime_error& e) {
return AbortNode(state, std::string("System error: ") + e.what());
@@ -3317,8 +3330,8 @@ void UnlinkPrunedFiles(const std::set<int>& setFilesToPrune)
{
for (std::set<int>::iterator it = setFilesToPrune.begin(); it != setFilesToPrune.end(); ++it) {
CDiskBlockPos pos(*it, 0);
- boost::filesystem::remove(GetBlockPosFilename(pos, "blk"));
- boost::filesystem::remove(GetBlockPosFilename(pos, "rev"));
+ fs::remove(GetBlockPosFilename(pos, "blk"));
+ fs::remove(GetBlockPosFilename(pos, "rev"));
LogPrintf("Prune: %s deleted blk/rev (%05u)\n", __func__, *it);
}
}
@@ -3402,7 +3415,7 @@ void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight
bool CheckDiskSpace(uint64_t nAdditionalBytes)
{
- uint64_t nFreeBytesAvailable = boost::filesystem::space(GetDataDir()).available;
+ uint64_t nFreeBytesAvailable = fs::space(GetDataDir()).available;
// Check for nMinDiskSpace bytes (currently 50MB)
if (nFreeBytesAvailable < nMinDiskSpace + nAdditionalBytes)
@@ -3415,11 +3428,11 @@ FILE* OpenDiskFile(const CDiskBlockPos &pos, const char *prefix, bool fReadOnly)
{
if (pos.IsNull())
return NULL;
- boost::filesystem::path path = GetBlockPosFilename(pos, prefix);
- boost::filesystem::create_directories(path.parent_path());
- FILE* file = fopen(path.string().c_str(), "rb+");
+ fs::path path = GetBlockPosFilename(pos, prefix);
+ fs::create_directories(path.parent_path());
+ FILE* file = fsbridge::fopen(path, "rb+");
if (!file && !fReadOnly)
- file = fopen(path.string().c_str(), "wb+");
+ file = fsbridge::fopen(path, "wb+");
if (!file) {
LogPrintf("Unable to open file %s\n", path.string());
return NULL;
@@ -3442,7 +3455,7 @@ FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly) {
return OpenDiskFile(pos, "rev", fReadOnly);
}
-boost::filesystem::path GetBlockPosFilename(const CDiskBlockPos &pos, const char *prefix)
+fs::path GetBlockPosFilename(const CDiskBlockPos &pos, const char *prefix)
{
return GetDataDir() / "blocks" / strprintf("%s%05u.dat", prefix, pos.nFile);
}
@@ -3827,7 +3840,7 @@ bool InitBlockIndex(const CChainParams& chainparams)
if (!WriteBlockToDisk(block, blockPos, chainparams.MessageStart()))
return error("LoadBlockIndex(): writing genesis block to disk failed");
CBlockIndex *pindex = AddToBlockIndex(block);
- if (!ReceivedBlockTransactions(block, state, pindex, blockPos))
+ if (!ReceivedBlockTransactions(block, state, pindex, blockPos, chainparams.GetConsensus()))
return error("LoadBlockIndex(): genesis block not accepted");
// Force a chainstate write so that when we VerifyDB in a moment, it doesn't check stale data
return FlushStateToDisk(state, FLUSH_STATE_ALWAYS);
@@ -4167,7 +4180,7 @@ static const uint64_t MEMPOOL_DUMP_VERSION = 1;
bool LoadMempool(void)
{
int64_t nExpiryTimeout = GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60;
- FILE* filestr = fopen((GetDataDir() / "mempool.dat").string().c_str(), "rb");
+ FILE* filestr = fsbridge::fopen(GetDataDir() / "mempool.dat", "rb");
CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
if (file.IsNull()) {
LogPrintf("Failed to open mempool file from disk. Continuing anyway.\n");
@@ -4247,7 +4260,7 @@ void DumpMempool(void)
int64_t mid = GetTimeMicros();
try {
- FILE* filestr = fopen((GetDataDir() / "mempool.dat.new").string().c_str(), "wb");
+ FILE* filestr = fsbridge::fopen(GetDataDir() / "mempool.dat.new", "wb");
if (!filestr) {
return;
}
diff --git a/src/validation.h b/src/validation.h
index 5f8e9a689c..24ebf238df 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -11,8 +11,8 @@
#endif
#include "amount.h"
-#include "chain.h"
#include "coins.h"
+#include "fs.h"
#include "protocol.h" // For CMessageHeader::MessageStartChars
#include "script/script_error.h"
#include "sync.h"
@@ -30,7 +30,6 @@
#include <atomic>
#include <boost/unordered_map.hpp>
-#include <boost/filesystem/path.hpp>
class CBlockIndex;
class CBlockTreeDB;
@@ -39,6 +38,7 @@ class CChainParams;
class CInv;
class CConnman;
class CScriptCheck;
+class CBlockPolicyEstimator;
class CTxMemPool;
class CValidationInterface;
class CValidationState;
@@ -145,6 +145,9 @@ static const int MAX_UNCONNECTING_HEADERS = 10;
static const bool DEFAULT_PEERBLOOMFILTERS = true;
+/** Default for -stopatheight */
+static const int DEFAULT_STOPATHEIGHT = 0;
+
struct BlockHasher
{
size_t operator()(const uint256& hash) const { return hash.GetCheapHash(); }
@@ -152,6 +155,7 @@ struct BlockHasher
extern CScript COINBASE_FLAGS;
extern CCriticalSection cs_main;
+extern CBlockPolicyEstimator feeEstimator;
extern CTxMemPool mempool;
typedef boost::unordered_map<uint256, CBlockIndex*, BlockHasher> BlockMap;
extern BlockMap mapBlockIndex;
@@ -250,7 +254,7 @@ FILE* OpenBlockFile(const CDiskBlockPos &pos, bool fReadOnly = false);
/** Open an undo file (rev?????.dat) */
FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly = false);
/** Translation to a filesystem path */
-boost::filesystem::path GetBlockPosFilename(const CDiskBlockPos &pos, const char *prefix);
+fs::path GetBlockPosFilename(const CDiskBlockPos &pos, const char *prefix);
/** Import blocks from an external file */
bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskBlockPos *dbp = NULL);
/** Initialize a new block tree database + block data on disk */
@@ -483,18 +487,6 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P
bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev, int64_t nAdjustedTime);
bool ContextualCheckBlock(const CBlock& block, CValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev);
-/** Apply the effects of this block (with given index) on the UTXO set represented by coins.
- * Validity checks that depend on the UTXO set are also done; ConnectBlock()
- * can fail if those validity checks fail (among other reasons). */
-bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins,
- const CChainParams& chainparams, bool fJustCheck = false);
-
-/** Undo the effects of this block (with given index) on the UTXO set represented by coins.
- * In case pfClean is provided, operation will try to be tolerant about errors, and *pfClean
- * will be true if no problems were found. Otherwise, the return value will be false in case
- * of problems. Note that in any case, coins may be modified. */
-bool DisconnectBlock(const CBlock& block, CValidationState& state, const CBlockIndex* pindex, CCoinsViewCache& coins, bool* pfClean = NULL);
-
/** Check a block is completely valid from start to finish (only works on top of our current best block, with cs_main held) */
bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, const CBlock& block, CBlockIndex* pindexPrev, bool fCheckPOW = true, bool fCheckMerkleRoot = true);
diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp
index d4121a28bc..46d7c9b329 100644
--- a/src/validationinterface.cpp
+++ b/src/validationinterface.cpp
@@ -14,39 +14,39 @@ CMainSignals& GetMainSignals()
void RegisterValidationInterface(CValidationInterface* pwalletIn) {
g_signals.UpdatedBlockTip.connect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1, _2, _3));
- g_signals.SyncTransaction.connect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2, _3));
- g_signals.UpdatedTransaction.connect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1));
+ g_signals.TransactionAddedToMempool.connect(boost::bind(&CValidationInterface::TransactionAddedToMempool, pwalletIn, _1));
+ g_signals.BlockConnected.connect(boost::bind(&CValidationInterface::BlockConnected, pwalletIn, _1, _2, _3));
+ g_signals.BlockDisconnected.connect(boost::bind(&CValidationInterface::BlockDisconnected, pwalletIn, _1));
g_signals.SetBestChain.connect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1));
g_signals.Inventory.connect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1));
g_signals.Broadcast.connect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1, _2));
g_signals.BlockChecked.connect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2));
g_signals.ScriptForMining.connect(boost::bind(&CValidationInterface::GetScriptForMining, pwalletIn, _1));
- g_signals.BlockFound.connect(boost::bind(&CValidationInterface::ResetRequestCount, pwalletIn, _1));
g_signals.NewPoWValidBlock.connect(boost::bind(&CValidationInterface::NewPoWValidBlock, pwalletIn, _1, _2));
}
void UnregisterValidationInterface(CValidationInterface* pwalletIn) {
- g_signals.BlockFound.disconnect(boost::bind(&CValidationInterface::ResetRequestCount, pwalletIn, _1));
g_signals.ScriptForMining.disconnect(boost::bind(&CValidationInterface::GetScriptForMining, pwalletIn, _1));
g_signals.BlockChecked.disconnect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2));
g_signals.Broadcast.disconnect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1, _2));
g_signals.Inventory.disconnect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1));
g_signals.SetBestChain.disconnect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1));
- g_signals.UpdatedTransaction.disconnect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1));
- g_signals.SyncTransaction.disconnect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2, _3));
+ g_signals.TransactionAddedToMempool.disconnect(boost::bind(&CValidationInterface::TransactionAddedToMempool, pwalletIn, _1));
+ g_signals.BlockConnected.disconnect(boost::bind(&CValidationInterface::BlockConnected, pwalletIn, _1, _2, _3));
+ g_signals.BlockDisconnected.disconnect(boost::bind(&CValidationInterface::BlockDisconnected, pwalletIn, _1));
g_signals.UpdatedBlockTip.disconnect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1, _2, _3));
g_signals.NewPoWValidBlock.disconnect(boost::bind(&CValidationInterface::NewPoWValidBlock, pwalletIn, _1, _2));
}
void UnregisterAllValidationInterfaces() {
- g_signals.BlockFound.disconnect_all_slots();
g_signals.ScriptForMining.disconnect_all_slots();
g_signals.BlockChecked.disconnect_all_slots();
g_signals.Broadcast.disconnect_all_slots();
g_signals.Inventory.disconnect_all_slots();
g_signals.SetBestChain.disconnect_all_slots();
- g_signals.UpdatedTransaction.disconnect_all_slots();
- g_signals.SyncTransaction.disconnect_all_slots();
+ g_signals.TransactionAddedToMempool.disconnect_all_slots();
+ g_signals.BlockConnected.disconnect_all_slots();
+ g_signals.BlockDisconnected.disconnect_all_slots();
g_signals.UpdatedBlockTip.disconnect_all_slots();
g_signals.NewPoWValidBlock.disconnect_all_slots();
}
diff --git a/src/validationinterface.h b/src/validationinterface.h
index 7f13a29d23..460aecf243 100644
--- a/src/validationinterface.h
+++ b/src/validationinterface.h
@@ -7,16 +7,16 @@
#define BITCOIN_VALIDATIONINTERFACE_H
#include <boost/signals2/signal.hpp>
-#include <boost/shared_ptr.hpp>
#include <memory>
+#include "primitives/transaction.h" // CTransaction(Ref)
+
class CBlock;
class CBlockIndex;
struct CBlockLocator;
class CBlockIndex;
class CConnman;
class CReserveScript;
-class CTransaction;
class CValidationInterface;
class CValidationState;
class uint256;
@@ -33,14 +33,14 @@ void UnregisterAllValidationInterfaces();
class CValidationInterface {
protected:
virtual void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {}
- virtual void SyncTransaction(const CTransaction &tx, const CBlockIndex *pindex, int posInBlock) {}
+ virtual void TransactionAddedToMempool(const CTransactionRef &ptxn) {}
+ virtual void BlockConnected(const std::shared_ptr<const CBlock> &block, const CBlockIndex *pindex, const std::vector<CTransactionRef> &txnConflicted) {}
+ virtual void BlockDisconnected(const std::shared_ptr<const CBlock> &block) {}
virtual void SetBestChain(const CBlockLocator &locator) {}
- virtual void UpdatedTransaction(const uint256 &hash) {}
virtual void Inventory(const uint256 &hash) {}
virtual void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) {}
virtual void BlockChecked(const CBlock&, const CValidationState&) {}
- virtual void GetScriptForMining(boost::shared_ptr<CReserveScript>&) {};
- virtual void ResetRequestCount(const uint256 &hash) {};
+ virtual void GetScriptForMining(std::shared_ptr<CReserveScript>&) {};
virtual void NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& block) {};
friend void ::RegisterValidationInterface(CValidationInterface*);
friend void ::UnregisterValidationInterface(CValidationInterface*);
@@ -50,19 +50,15 @@ protected:
struct CMainSignals {
/** Notifies listeners of updated block chain tip */
boost::signals2::signal<void (const CBlockIndex *, const CBlockIndex *, bool fInitialDownload)> UpdatedBlockTip;
- /** A posInBlock value for SyncTransaction calls for transactions not
- * included in connected blocks such as transactions removed from mempool,
- * accepted to mempool or appearing in disconnected blocks.*/
- static const int SYNC_TRANSACTION_NOT_IN_BLOCK = -1;
- /** Notifies listeners of updated transaction data (transaction, and
- * optionally the block it is found in). Called with block data when
- * transaction is included in a connected block, and without block data when
- * transaction was accepted to mempool, removed from mempool (only when
- * removal was due to conflict from connected block), or appeared in a
- * disconnected block.*/
- boost::signals2::signal<void (const CTransaction &, const CBlockIndex *pindex, int posInBlock)> SyncTransaction;
- /** Notifies listeners of an updated transaction without new data (for now: a coinbase potentially becoming visible). */
- boost::signals2::signal<void (const uint256 &)> UpdatedTransaction;
+ /** Notifies listeners of a transaction having been added to mempool. */
+ boost::signals2::signal<void (const CTransactionRef &)> TransactionAddedToMempool;
+ /**
+ * Notifies listeners of a block being connected.
+ * Provides a vector of transactions evicted from the mempool as a result.
+ */
+ boost::signals2::signal<void (const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex, const std::vector<CTransactionRef> &)> BlockConnected;
+ /** Notifies listeners of a block being disconnected */
+ boost::signals2::signal<void (const std::shared_ptr<const CBlock> &)> BlockDisconnected;
/** Notifies listeners of a new active block chain. */
boost::signals2::signal<void (const CBlockLocator &)> SetBestChain;
/** Notifies listeners about an inventory item being seen on the network. */
@@ -77,9 +73,7 @@ struct CMainSignals {
*/
boost::signals2::signal<void (const CBlock&, const CValidationState&)> BlockChecked;
/** Notifies listeners that a key for mining is required (coinbase) */
- boost::signals2::signal<void (boost::shared_ptr<CReserveScript>&)> ScriptForMining;
- /** Notifies listeners that a block has been successfully mined */
- boost::signals2::signal<void (const uint256 &)> BlockFound;
+ boost::signals2::signal<void (std::shared_ptr<CReserveScript>&)> ScriptForMining;
/**
* Notifies listeners that a block which builds directly on our current tip
* has been received and connected to the headers tree, though not validated yet */
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index d3333bf1ab..25f6bdd9d9 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -6,6 +6,7 @@
#include "db.h"
#include "addrman.h"
+#include "fs.h"
#include "hash.h"
#include "protocol.h"
#include "util.h"
@@ -17,7 +18,6 @@
#include <sys/stat.h>
#endif
-#include <boost/filesystem.hpp>
#include <boost/foreach.hpp>
#include <boost/thread.hpp>
#include <boost/version.hpp>
@@ -66,7 +66,7 @@ void CDBEnv::Close()
EnvShutdown();
}
-bool CDBEnv::Open(const boost::filesystem::path& pathIn)
+bool CDBEnv::Open(const fs::path& pathIn)
{
if (fDbEnvInit)
return true;
@@ -74,9 +74,9 @@ bool CDBEnv::Open(const boost::filesystem::path& pathIn)
boost::this_thread::interruption_point();
strPath = pathIn.string();
- boost::filesystem::path pathLogDir = pathIn / "database";
+ fs::path pathLogDir = pathIn / "database";
TryCreateDirectory(pathLogDir);
- boost::filesystem::path pathErrorFile = pathIn / "db.log";
+ fs::path pathErrorFile = pathIn / "db.log";
LogPrintf("CDBEnv::Open: LogDir=%s ErrorFile=%s\n", pathLogDir.string(), pathErrorFile.string());
unsigned int nEnvFlags = 0;
@@ -89,7 +89,7 @@ bool CDBEnv::Open(const boost::filesystem::path& pathIn)
dbenv->set_lg_max(1048576);
dbenv->set_lk_max_locks(40000);
dbenv->set_lk_max_objects(40000);
- dbenv->set_errfile(fopen(pathErrorFile.string().c_str(), "a")); /// debug
+ dbenv->set_errfile(fsbridge::fopen(pathErrorFile, "a")); /// debug
dbenv->set_flags(DB_AUTO_COMMIT, 1);
dbenv->set_flags(DB_TXN_WRITE_NOSYNC, 1);
dbenv->log_set_config(DB_LOG_AUTO_REMOVE, 1);
@@ -227,13 +227,13 @@ bool CDB::Recover(const std::string& filename, void *callbackDataIn, bool (*reco
return fSuccess;
}
-bool CDB::VerifyEnvironment(const std::string& walletFile, const boost::filesystem::path& dataDir, std::string& errorStr)
+bool CDB::VerifyEnvironment(const std::string& walletFile, const fs::path& dataDir, std::string& errorStr)
{
LogPrintf("Using BerkeleyDB version %s\n", DbEnv::version(0, 0, 0));
LogPrintf("Using wallet %s\n", walletFile);
// Wallet file must be a plain filename without a directory
- if (walletFile != boost::filesystem::basename(walletFile) + boost::filesystem::extension(walletFile))
+ if (walletFile != fs::basename(walletFile) + fs::extension(walletFile))
{
errorStr = strprintf(_("Wallet %s resides outside data directory %s"), walletFile, dataDir.string());
return false;
@@ -242,12 +242,12 @@ bool CDB::VerifyEnvironment(const std::string& walletFile, const boost::filesyst
if (!bitdb.Open(dataDir))
{
// try moving the database env out of the way
- boost::filesystem::path pathDatabase = dataDir / "database";
- boost::filesystem::path pathDatabaseBak = dataDir / strprintf("database.%d.bak", GetTime());
+ fs::path pathDatabase = dataDir / "database";
+ fs::path pathDatabaseBak = dataDir / strprintf("database.%d.bak", GetTime());
try {
- boost::filesystem::rename(pathDatabase, pathDatabaseBak);
+ fs::rename(pathDatabase, pathDatabaseBak);
LogPrintf("Moved old %s to %s. Retrying.\n", pathDatabase.string(), pathDatabaseBak.string());
- } catch (const boost::filesystem::filesystem_error&) {
+ } catch (const fs::filesystem_error&) {
// failure is ok (well, not really, but it's not worse than what we started with)
}
@@ -261,9 +261,9 @@ bool CDB::VerifyEnvironment(const std::string& walletFile, const boost::filesyst
return true;
}
-bool CDB::VerifyDatabaseFile(const std::string& walletFile, const boost::filesystem::path& dataDir, std::string& warningStr, std::string& errorStr, bool (*recoverFunc)(const std::string& strFile))
+bool CDB::VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr, bool (*recoverFunc)(const std::string& strFile))
{
- if (boost::filesystem::exists(dataDir / walletFile))
+ if (fs::exists(dataDir / walletFile))
{
CDBEnv::VerifyResult r = bitdb.Verify(walletFile, recoverFunc);
if (r == CDBEnv::RECOVER_OK)
@@ -359,13 +359,16 @@ void CDBEnv::CheckpointLSN(const std::string& strFile)
}
-CDB::CDB(const std::string& strFilename, const char* pszMode, bool fFlushOnCloseIn) : pdb(NULL), activeTxn(NULL)
+CDB::CDB(CWalletDBWrapper& dbw, const char* pszMode, bool fFlushOnCloseIn) : pdb(NULL), activeTxn(NULL)
{
int ret;
fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w'));
fFlushOnClose = fFlushOnCloseIn;
- if (strFilename.empty())
+ env = dbw.env;
+ if (dbw.IsDummy()) {
return;
+ }
+ const std::string &strFilename = dbw.strFile;
bool fCreate = strchr(pszMode, 'c') != NULL;
unsigned int nFlags = DB_THREAD;
@@ -373,17 +376,17 @@ CDB::CDB(const std::string& strFilename, const char* pszMode, bool fFlushOnClose
nFlags |= DB_CREATE;
{
- LOCK(bitdb.cs_db);
- if (!bitdb.Open(GetDataDir()))
+ LOCK(env->cs_db);
+ if (!env->Open(GetDataDir()))
throw std::runtime_error("CDB: Failed to open database environment.");
strFile = strFilename;
- ++bitdb.mapFileUseCount[strFile];
- pdb = bitdb.mapDb[strFile];
+ ++env->mapFileUseCount[strFile];
+ pdb = env->mapDb[strFile];
if (pdb == NULL) {
- pdb = new Db(bitdb.dbenv, 0);
+ pdb = new Db(env->dbenv, 0);
- bool fMockDb = bitdb.IsMock();
+ bool fMockDb = env->IsMock();
if (fMockDb) {
DbMpoolFile* mpf = pdb->get_mpf();
ret = mpf->set_flags(DB_MPOOL_NOFILE, 1);
@@ -401,7 +404,7 @@ CDB::CDB(const std::string& strFilename, const char* pszMode, bool fFlushOnClose
if (ret != 0) {
delete pdb;
pdb = NULL;
- --bitdb.mapFileUseCount[strFile];
+ --env->mapFileUseCount[strFile];
strFile = "";
throw std::runtime_error(strprintf("CDB: Error %d, can't open database %s", ret, strFilename));
}
@@ -413,7 +416,7 @@ CDB::CDB(const std::string& strFilename, const char* pszMode, bool fFlushOnClose
fReadOnly = fTmp;
}
- bitdb.mapDb[strFile] = pdb;
+ env->mapDb[strFile] = pdb;
}
}
}
@@ -428,7 +431,7 @@ void CDB::Flush()
if (fReadOnly)
nMinutes = 1;
- bitdb.dbenv->txn_checkpoint(nMinutes ? GetArg("-dblogsize", DEFAULT_WALLET_DBLOGSIZE) * 1024 : 0, nMinutes, 0);
+ env->dbenv->txn_checkpoint(nMinutes ? GetArg("-dblogsize", DEFAULT_WALLET_DBLOGSIZE) * 1024 : 0, nMinutes, 0);
}
void CDB::Close()
@@ -444,8 +447,8 @@ void CDB::Close()
Flush();
{
- LOCK(bitdb.cs_db);
- --bitdb.mapFileUseCount[strFile];
+ LOCK(env->cs_db);
+ --env->mapFileUseCount[strFile];
}
}
@@ -463,32 +466,28 @@ void CDBEnv::CloseDb(const std::string& strFile)
}
}
-bool CDBEnv::RemoveDb(const std::string& strFile)
-{
- this->CloseDb(strFile);
-
- LOCK(cs_db);
- int rc = dbenv->dbremove(NULL, strFile.c_str(), NULL, DB_AUTO_COMMIT);
- return (rc == 0);
-}
-
-bool CDB::Rewrite(const std::string& strFile, const char* pszSkip)
+bool CDB::Rewrite(CWalletDBWrapper& dbw, const char* pszSkip)
{
+ if (dbw.IsDummy()) {
+ return true;
+ }
+ CDBEnv *env = dbw.env;
+ const std::string& strFile = dbw.strFile;
while (true) {
{
- LOCK(bitdb.cs_db);
- if (!bitdb.mapFileUseCount.count(strFile) || bitdb.mapFileUseCount[strFile] == 0) {
+ LOCK(env->cs_db);
+ if (!env->mapFileUseCount.count(strFile) || env->mapFileUseCount[strFile] == 0) {
// Flush log data to the dat file
- bitdb.CloseDb(strFile);
- bitdb.CheckpointLSN(strFile);
- bitdb.mapFileUseCount.erase(strFile);
+ env->CloseDb(strFile);
+ env->CheckpointLSN(strFile);
+ env->mapFileUseCount.erase(strFile);
bool fSuccess = true;
LogPrintf("CDB::Rewrite: Rewriting %s...\n", strFile);
std::string strFileRes = strFile + ".rewrite";
{ // surround usage of db with extra {}
- CDB db(strFile.c_str(), "r");
- Db* pdbCopy = new Db(bitdb.dbenv, 0);
+ CDB db(dbw, "r");
+ Db* pdbCopy = new Db(env->dbenv, 0);
int ret = pdbCopy->open(NULL, // Txn pointer
strFileRes.c_str(), // Filename
@@ -531,17 +530,17 @@ bool CDB::Rewrite(const std::string& strFile, const char* pszSkip)
}
if (fSuccess) {
db.Close();
- bitdb.CloseDb(strFile);
+ env->CloseDb(strFile);
if (pdbCopy->close(0))
fSuccess = false;
delete pdbCopy;
}
}
if (fSuccess) {
- Db dbA(bitdb.dbenv, 0);
+ Db dbA(env->dbenv, 0);
if (dbA.remove(strFile.c_str(), NULL, 0))
fSuccess = false;
- Db dbB(bitdb.dbenv, 0);
+ Db dbB(env->dbenv, 0);
if (dbB.rename(strFileRes.c_str(), NULL, strFile.c_str(), 0))
fSuccess = false;
}
@@ -590,22 +589,27 @@ void CDBEnv::Flush(bool fShutdown)
dbenv->log_archive(&listp, DB_ARCH_REMOVE);
Close();
if (!fMockDb)
- boost::filesystem::remove_all(boost::filesystem::path(strPath) / "database");
+ fs::remove_all(fs::path(strPath) / "database");
}
}
}
}
-bool CDB::PeriodicFlush(std::string strFile)
+bool CDB::PeriodicFlush(CWalletDBWrapper& dbw)
{
+ if (dbw.IsDummy()) {
+ return true;
+ }
bool ret = false;
+ CDBEnv *env = dbw.env;
+ const std::string& strFile = dbw.strFile;
TRY_LOCK(bitdb.cs_db,lockDb);
if (lockDb)
{
// Don't do this if any databases are in use
int nRefCount = 0;
- std::map<std::string, int>::iterator mit = bitdb.mapFileUseCount.begin();
- while (mit != bitdb.mapFileUseCount.end())
+ std::map<std::string, int>::iterator mit = env->mapFileUseCount.begin();
+ while (mit != env->mapFileUseCount.end())
{
nRefCount += (*mit).second;
mit++;
@@ -614,17 +618,17 @@ bool CDB::PeriodicFlush(std::string strFile)
if (nRefCount == 0)
{
boost::this_thread::interruption_point();
- std::map<std::string, int>::iterator mi = bitdb.mapFileUseCount.find(strFile);
- if (mi != bitdb.mapFileUseCount.end())
+ std::map<std::string, int>::iterator mi = env->mapFileUseCount.find(strFile);
+ if (mi != env->mapFileUseCount.end())
{
LogPrint(BCLog::DB, "Flushing %s\n", strFile);
int64_t nStart = GetTimeMillis();
// Flush wallet file so it's self contained
- bitdb.CloseDb(strFile);
- bitdb.CheckpointLSN(strFile);
+ env->CloseDb(strFile);
+ env->CheckpointLSN(strFile);
- bitdb.mapFileUseCount.erase(mi++);
+ env->mapFileUseCount.erase(mi++);
LogPrint(BCLog::DB, "Flushed %s %dms\n", strFile, GetTimeMillis() - nStart);
ret = true;
}
@@ -633,3 +637,52 @@ bool CDB::PeriodicFlush(std::string strFile)
return ret;
}
+
+bool CWalletDBWrapper::Rewrite(const char* pszSkip)
+{
+ return CDB::Rewrite(*this, pszSkip);
+}
+
+bool CWalletDBWrapper::Backup(const std::string& strDest)
+{
+ if (IsDummy()) {
+ return false;
+ }
+ while (true)
+ {
+ {
+ LOCK(env->cs_db);
+ if (!env->mapFileUseCount.count(strFile) || env->mapFileUseCount[strFile] == 0)
+ {
+ // Flush log data to the dat file
+ env->CloseDb(strFile);
+ env->CheckpointLSN(strFile);
+ env->mapFileUseCount.erase(strFile);
+
+ // Copy wallet file
+ fs::path pathSrc = GetDataDir() / strFile;
+ fs::path pathDest(strDest);
+ if (fs::is_directory(pathDest))
+ pathDest /= strFile;
+
+ try {
+ fs::copy_file(pathSrc, pathDest, fs::copy_option::overwrite_if_exists);
+ LogPrintf("copied %s to %s\n", strFile, pathDest.string());
+ return true;
+ } catch (const fs::filesystem_error& e) {
+ LogPrintf("error copying %s to %s - %s\n", strFile, pathDest.string(), e.what());
+ return false;
+ }
+ }
+ }
+ MilliSleep(100);
+ }
+ return false;
+}
+
+void CWalletDBWrapper::Flush(bool shutdown)
+{
+ if (!IsDummy()) {
+ env->Flush(shutdown);
+ }
+}
diff --git a/src/wallet/db.h b/src/wallet/db.h
index 19c54e314c..1a46448cc7 100644
--- a/src/wallet/db.h
+++ b/src/wallet/db.h
@@ -7,6 +7,7 @@
#define BITCOIN_WALLET_DB_H
#include "clientversion.h"
+#include "fs.h"
#include "serialize.h"
#include "streams.h"
#include "sync.h"
@@ -16,8 +17,6 @@
#include <string>
#include <vector>
-#include <boost/filesystem/path.hpp>
-
#include <db_cxx.h>
static const unsigned int DEFAULT_WALLET_DBLOGSIZE = 100;
@@ -28,7 +27,7 @@ class CDBEnv
private:
bool fDbEnvInit;
bool fMockDb;
- // Don't change into boost::filesystem::path, as that can result in
+ // Don't change into fs::path, as that can result in
// shutdown problems/crashes caused by a static initialized internal pointer.
std::string strPath;
@@ -67,13 +66,12 @@ public:
typedef std::pair<std::vector<unsigned char>, std::vector<unsigned char> > KeyValPair;
bool Salvage(const std::string& strFile, bool fAggressive, std::vector<KeyValPair>& vResult);
- bool Open(const boost::filesystem::path& path);
+ bool Open(const fs::path& path);
void Close();
void Flush(bool fShutdown);
void CheckpointLSN(const std::string& strFile);
void CloseDb(const std::string& strFile);
- bool RemoveDb(const std::string& strFile);
DbTxn* TxnBegin(int flags = DB_TXN_WRITE_NOSYNC)
{
@@ -87,6 +85,52 @@ public:
extern CDBEnv bitdb;
+/** An instance of this class represents one database.
+ * For BerkeleyDB this is just a (env, strFile) tuple.
+ **/
+class CWalletDBWrapper
+{
+ friend class CDB;
+public:
+ /** Create dummy DB handle */
+ CWalletDBWrapper(): env(nullptr)
+ {
+ }
+
+ /** Create DB handle to real database */
+ CWalletDBWrapper(CDBEnv *env_in, const std::string &strFile_in):
+ env(env_in), strFile(strFile_in)
+ {
+ }
+
+ /** Rewrite the entire database on disk, with the exception of key pszSkip if non-zero
+ */
+ bool Rewrite(const char* pszSkip=nullptr);
+
+ /** Back up the entire database to a file.
+ */
+ bool Backup(const std::string& strDest);
+
+ /** Get a name for this database, for debugging etc.
+ */
+ std::string GetName() const { return strFile; }
+
+ /** Make sure all changes are flushed to disk.
+ */
+ void Flush(bool shutdown);
+
+private:
+ /** BerkeleyDB specific */
+ CDBEnv *env;
+ std::string strFile;
+
+ /** Return whether this database handle is a dummy for testing.
+ * Only to be used at a low level, application should ideally not care
+ * about this.
+ */
+ bool IsDummy() { return env == nullptr; }
+};
+
/** RAII class that provides access to a Berkeley database */
class CDB
@@ -97,28 +141,29 @@ protected:
DbTxn* activeTxn;
bool fReadOnly;
bool fFlushOnClose;
+ CDBEnv *env;
- explicit CDB(const std::string& strFilename, const char* pszMode = "r+", bool fFlushOnCloseIn=true);
+public:
+ explicit CDB(CWalletDBWrapper& dbw, const char* pszMode = "r+", bool fFlushOnCloseIn=true);
~CDB() { Close(); }
-public:
void Flush();
void Close();
static bool Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue));
/* flush the wallet passively (TRY_LOCK)
ideal to be called periodically */
- static bool PeriodicFlush(std::string strFile);
+ static bool PeriodicFlush(CWalletDBWrapper& dbw);
/* verifies the database environment */
- static bool VerifyEnvironment(const std::string& walletFile, const boost::filesystem::path& dataDir, std::string& errorStr);
+ static bool VerifyEnvironment(const std::string& walletFile, const fs::path& dataDir, std::string& errorStr);
/* verifies the database file */
- static bool VerifyDatabaseFile(const std::string& walletFile, const boost::filesystem::path& dataDir, std::string& warningStr, std::string& errorStr, bool (*recoverFunc)(const std::string& strFile));
+ static bool VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr, bool (*recoverFunc)(const std::string& strFile));
private:
CDB(const CDB&);
void operator=(const CDB&);
-protected:
+public:
template <typename K, typename T>
bool Read(const K& key, T& value)
{
@@ -157,7 +202,7 @@ protected:
bool Write(const K& key, const T& value, bool fOverwrite = true)
{
if (!pdb)
- return false;
+ return true;
if (fReadOnly)
assert(!"Write called on database in read-only mode");
@@ -311,7 +356,7 @@ public:
return Write(std::string("version"), nVersion);
}
- bool static Rewrite(const std::string& strFile, const char* pszSkip = NULL);
+ bool static Rewrite(CWalletDBWrapper& dbw, const char* pszSkip = NULL);
};
#endif // BITCOIN_WALLET_DB_H
diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp
new file mode 100644
index 0000000000..f5b63c1ecd
--- /dev/null
+++ b/src/wallet/feebumper.cpp
@@ -0,0 +1,284 @@
+// Copyright (c) 2017 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include "consensus/validation.h"
+#include "wallet/feebumper.h"
+#include "wallet/wallet.h"
+#include "policy/fees.h"
+#include "policy/policy.h"
+#include "policy/rbf.h"
+#include "validation.h" //for mempool access
+#include "txmempool.h"
+#include "utilmoneystr.h"
+#include "util.h"
+#include "net.h"
+
+// Calculate the size of the transaction assuming all signatures are max size
+// Use DummySignatureCreator, which inserts 72 byte signatures everywhere.
+// TODO: re-use this in CWallet::CreateTransaction (right now
+// CreateTransaction uses the constructed dummy-signed tx to do a priority
+// calculation, but we should be able to refactor after priority is removed).
+// NOTE: this requires that all inputs must be in mapWallet (eg the tx should
+// be IsAllFromMe).
+int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *pWallet)
+{
+ CMutableTransaction txNew(tx);
+ std::vector<CInputCoin> vCoins;
+ // Look up the inputs. We should have already checked that this transaction
+ // IsAllFromMe(ISMINE_SPENDABLE), so every input should already be in our
+ // wallet, with a valid index into the vout array.
+ for (auto& input : tx.vin) {
+ const auto mi = pWallet->mapWallet.find(input.prevout.hash);
+ assert(mi != pWallet->mapWallet.end() && input.prevout.n < mi->second.tx->vout.size());
+ vCoins.emplace_back(CInputCoin(&(mi->second), input.prevout.n));
+ }
+ if (!pWallet->DummySignTx(txNew, vCoins)) {
+ // This should never happen, because IsAllFromMe(ISMINE_SPENDABLE)
+ // implies that we can sign for every input.
+ return -1;
+ }
+ return GetVirtualTransactionSize(txNew);
+}
+
+CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, int newConfirmTarget, bool specifiedConfirmTarget, CAmount totalFee, bool newTxReplaceable)
+ :
+ txid(std::move(txidIn)),
+ nOldFee(0),
+ nNewFee(0)
+{
+ vErrors.clear();
+ bumpedTxid.SetNull();
+ AssertLockHeld(pWallet->cs_wallet);
+ if (!pWallet->mapWallet.count(txid)) {
+ vErrors.push_back("Invalid or non-wallet transaction id");
+ currentResult = BumpFeeResult::INVALID_ADDRESS_OR_KEY;
+ return;
+ }
+ auto it = pWallet->mapWallet.find(txid);
+ const CWalletTx& wtx = it->second;
+
+ if (pWallet->HasWalletSpend(txid)) {
+ vErrors.push_back("Transaction has descendants in the wallet");
+ currentResult = BumpFeeResult::INVALID_PARAMETER;
+ return;
+ }
+
+ {
+ LOCK(mempool.cs);
+ auto it_mp = mempool.mapTx.find(txid);
+ if (it_mp != mempool.mapTx.end() && it_mp->GetCountWithDescendants() > 1) {
+ vErrors.push_back("Transaction has descendants in the mempool");
+ currentResult = BumpFeeResult::INVALID_PARAMETER;
+ return;
+ }
+ }
+
+ if (wtx.GetDepthInMainChain() != 0) {
+ vErrors.push_back("Transaction has been mined, or is conflicted with a mined transaction");
+ currentResult = BumpFeeResult::WALLET_ERROR;
+ return;
+ }
+
+ if (!SignalsOptInRBF(wtx)) {
+ vErrors.push_back("Transaction is not BIP 125 replaceable");
+ currentResult = BumpFeeResult::WALLET_ERROR;
+ return;
+ }
+
+ if (wtx.mapValue.count("replaced_by_txid")) {
+ vErrors.push_back(strprintf("Cannot bump transaction %s which was already bumped by transaction %s", txid.ToString(), wtx.mapValue.at("replaced_by_txid")));
+ currentResult = BumpFeeResult::WALLET_ERROR;
+ return;
+ }
+
+ // 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)) {
+ vErrors.push_back("Transaction contains inputs that don't belong to this wallet");
+ currentResult = BumpFeeResult::WALLET_ERROR;
+ return;
+ }
+
+ // figure out which output was change
+ // if there was no change output or multiple change outputs, fail
+ int nOutput = -1;
+ for (size_t i = 0; i < wtx.tx->vout.size(); ++i) {
+ if (pWallet->IsChange(wtx.tx->vout[i])) {
+ if (nOutput != -1) {
+ vErrors.push_back("Transaction has multiple change outputs");
+ currentResult = BumpFeeResult::WALLET_ERROR;
+ return;
+ }
+ nOutput = i;
+ }
+ }
+ if (nOutput == -1) {
+ vErrors.push_back("Transaction does not have a change output");
+ currentResult = BumpFeeResult::WALLET_ERROR;
+ return;
+ }
+
+ // Calculate the expected size of the new transaction.
+ int64_t txSize = GetVirtualTransactionSize(*(wtx.tx));
+ const int64_t maxNewTxSize = CalculateMaximumSignedTxSize(*wtx.tx, pWallet);
+ if (maxNewTxSize < 0) {
+ vErrors.push_back("Transaction contains inputs that cannot be signed");
+ currentResult = BumpFeeResult::INVALID_ADDRESS_OR_KEY;
+ return;
+ }
+
+ // calculate the old fee and fee-rate
+ nOldFee = wtx.GetDebit(ISMINE_SPENDABLE) - wtx.tx->GetValueOut();
+ CFeeRate nOldFeeRate(nOldFee, txSize);
+ CFeeRate nNewFeeRate;
+ // The wallet uses a conservative WALLET_INCREMENTAL_RELAY_FEE value to
+ // future proof against changes to network wide policy for incremental relay
+ // fee that our node may not be aware of.
+ CFeeRate walletIncrementalRelayFee = CFeeRate(WALLET_INCREMENTAL_RELAY_FEE);
+ if (::incrementalRelayFee > walletIncrementalRelayFee) {
+ walletIncrementalRelayFee = ::incrementalRelayFee;
+ }
+
+ if (totalFee > 0) {
+ CAmount minTotalFee = nOldFeeRate.GetFee(maxNewTxSize) + ::incrementalRelayFee.GetFee(maxNewTxSize);
+ if (totalFee < minTotalFee) {
+ vErrors.push_back(strprintf("Insufficient totalFee, must be at least %s (oldFee %s + incrementalFee %s)",
+ FormatMoney(minTotalFee), FormatMoney(nOldFeeRate.GetFee(maxNewTxSize)), FormatMoney(::incrementalRelayFee.GetFee(maxNewTxSize))));
+ currentResult = BumpFeeResult::INVALID_PARAMETER;
+ return;
+ }
+ CAmount requiredFee = CWallet::GetRequiredFee(maxNewTxSize);
+ if (totalFee < requiredFee) {
+ vErrors.push_back(strprintf("Insufficient totalFee (cannot be less than required fee %s)",
+ FormatMoney(requiredFee)));
+ currentResult = BumpFeeResult::INVALID_PARAMETER;
+ return;
+ }
+ nNewFee = totalFee;
+ nNewFeeRate = CFeeRate(totalFee, maxNewTxSize);
+ } else {
+ // if user specified a confirm target then don't consider any global payTxFee
+ if (specifiedConfirmTarget) {
+ nNewFee = CWallet::GetMinimumFee(maxNewTxSize, newConfirmTarget, mempool, ::feeEstimator, true);
+ }
+ // otherwise use the regular wallet logic to select payTxFee or default confirm target
+ else {
+ nNewFee = CWallet::GetMinimumFee(maxNewTxSize, newConfirmTarget, mempool, ::feeEstimator);
+ }
+
+ nNewFeeRate = CFeeRate(nNewFee, maxNewTxSize);
+
+ // New fee rate must be at least old rate + minimum incremental relay rate
+ // walletIncrementalRelayFee.GetFeePerK() should be exact, because it's initialized
+ // in that unit (fee per kb).
+ // However, nOldFeeRate is a calculated value from the tx fee/size, so
+ // add 1 satoshi to the result, because it may have been rounded down.
+ if (nNewFeeRate.GetFeePerK() < nOldFeeRate.GetFeePerK() + 1 + walletIncrementalRelayFee.GetFeePerK()) {
+ nNewFeeRate = CFeeRate(nOldFeeRate.GetFeePerK() + 1 + walletIncrementalRelayFee.GetFeePerK());
+ nNewFee = nNewFeeRate.GetFee(maxNewTxSize);
+ }
+ }
+
+ // Check that in all cases the new fee doesn't violate maxTxFee
+ if (nNewFee > maxTxFee) {
+ vErrors.push_back(strprintf("Specified or calculated fee %s is too high (cannot be higher than maxTxFee %s)",
+ FormatMoney(nNewFee), FormatMoney(maxTxFee)));
+ currentResult = BumpFeeResult::WALLET_ERROR;
+ return;
+ }
+
+ // check that fee rate is higher than mempool's minimum fee
+ // (no point in bumping fee if we know that the new tx won't be accepted to the mempool)
+ // This may occur if the user set TotalFee or paytxfee too low, if fallbackfee is too low, or, perhaps,
+ // in a rare situation where the mempool minimum fee increased significantly since the fee estimation just a
+ // moment earlier. In this case, we report an error to the user, who may use totalFee to make an adjustment.
+ CFeeRate minMempoolFeeRate = mempool.GetMinFee(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())));
+ currentResult = BumpFeeResult::WALLET_ERROR;
+ return;
+ }
+
+ // Now modify the output to increase the fee.
+ // If the output is not large enough to pay the fee, fail.
+ CAmount nDelta = nNewFee - nOldFee;
+ assert(nDelta > 0);
+ mtx = *wtx.tx;
+ CTxOut* poutput = &(mtx.vout[nOutput]);
+ if (poutput->nValue < nDelta) {
+ vErrors.push_back("Change output is too small to bump the fee");
+ currentResult = BumpFeeResult::WALLET_ERROR;
+ return;
+ }
+
+ // If the output would become dust, discard it (converting the dust to fee)
+ poutput->nValue -= nDelta;
+ if (poutput->nValue <= poutput->GetDustThreshold(::dustRelayFee)) {
+ LogPrint(BCLog::RPC, "Bumping fee and discarding dust output\n");
+ nNewFee += poutput->nValue;
+ mtx.vout.erase(mtx.vout.begin() + nOutput);
+ }
+
+ // Mark new tx not replaceable, if requested.
+ if (!newTxReplaceable) {
+ for (auto& input : mtx.vin) {
+ if (input.nSequence < 0xfffffffe) input.nSequence = 0xfffffffe;
+ }
+ }
+
+ currentResult = BumpFeeResult::OK;
+}
+
+bool CFeeBumper::signTransaction(CWallet *pWallet)
+{
+ return pWallet->SignTransaction(mtx);
+}
+
+bool CFeeBumper::commit(CWallet *pWallet)
+{
+ AssertLockHeld(pWallet->cs_wallet);
+ if (!vErrors.empty() || currentResult != BumpFeeResult::OK) {
+ return false;
+ }
+ if (txid.IsNull() || !pWallet->mapWallet.count(txid)) {
+ vErrors.push_back("Invalid or non-wallet transaction id");
+ currentResult = BumpFeeResult::MISC_ERROR;
+ return false;
+ }
+ CWalletTx& oldWtx = pWallet->mapWallet[txid];
+
+ CWalletTx wtxBumped(pWallet, MakeTransactionRef(std::move(mtx)));
+ // commit/broadcast the tx
+ CReserveKey reservekey(pWallet);
+ wtxBumped.mapValue = oldWtx.mapValue;
+ wtxBumped.mapValue["replaces_txid"] = oldWtx.GetHash().ToString();
+ wtxBumped.vOrderForm = oldWtx.vOrderForm;
+ wtxBumped.strFromAccount = oldWtx.strFromAccount;
+ wtxBumped.fTimeReceivedIsTxTime = true;
+ wtxBumped.fFromMe = true;
+ 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()));
+ return false;
+ }
+
+ bumpedTxid = wtxBumped.GetHash();
+ 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)));
+ }
+
+ // mark the original tx as bumped
+ if (!pWallet->MarkReplaced(oldWtx.GetHash(), wtxBumped.GetHash())) {
+ // TODO: see if JSON-RPC has a standard way of returning a response
+ // 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.");
+ }
+ return true;
+}
+
diff --git a/src/wallet/feebumper.h b/src/wallet/feebumper.h
new file mode 100644
index 0000000000..681f55e4e5
--- /dev/null
+++ b/src/wallet/feebumper.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2017 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_WALLET_FEEBUMPER_H
+#define BITCOIN_WALLET_FEEBUMPER_H
+
+#include <primitives/transaction.h>
+
+class CWallet;
+class uint256;
+
+enum class BumpFeeResult
+{
+ OK,
+ INVALID_ADDRESS_OR_KEY,
+ INVALID_REQUEST,
+ INVALID_PARAMETER,
+ WALLET_ERROR,
+ MISC_ERROR,
+};
+
+class CFeeBumper
+{
+public:
+ CFeeBumper(const CWallet *pWalletIn, const uint256 txidIn, int newConfirmTarget, bool specifiedConfirmTarget, CAmount totalFee, bool newTxReplaceable);
+ BumpFeeResult getResult() const { return currentResult; }
+ const std::vector<std::string>& getErrors() const { return vErrors; }
+ CAmount getOldFee() const { return nOldFee; }
+ CAmount getNewFee() const { return nNewFee; }
+ uint256 getBumpedTxId() const { return bumpedTxid; }
+
+ /* signs the new transaction,
+ * returns false if the tx couldn't be found or if it was
+ * impossible to create the signature(s)
+ */
+ bool signTransaction(CWallet *pWallet);
+
+ /* commits the fee bump,
+ * returns true, in case of CWallet::CommitTransaction was successful
+ * but, eventually sets vErrors if the tx could not be added to the mempool (will try later)
+ * or if the old transaction could not be marked as replaced
+ */
+ bool commit(CWallet *pWalletNonConst);
+
+private:
+ const uint256 txid;
+ uint256 bumpedTxid;
+ CMutableTransaction mtx;
+ std::vector<std::string> vErrors;
+ BumpFeeResult currentResult;
+ CAmount nOldFee;
+ CAmount nNewFee;
+};
+
+#endif // BITCOIN_WALLET_FEEBUMPER_H
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index 7ff9e7ae58..82708dab26 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -95,6 +95,8 @@ UniValue importprivkey(const JSONRPCRequest& request)
+ HelpExampleCli("importprivkey", "\"mykey\"") +
"\nImport using a label and without rescan\n"
+ HelpExampleCli("importprivkey", "\"mykey\" \"testing\" false") +
+ "\nImport using default blank label and without rescan\n"
+ + HelpExampleCli("importprivkey", "\"mykey\" \"\" false") +
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("importprivkey", "\"mykey\", \"testing\", false")
);
@@ -154,6 +156,31 @@ UniValue importprivkey(const JSONRPCRequest& request)
return NullUniValue;
}
+UniValue abortrescan(const JSONRPCRequest& request)
+{
+ CWallet* const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
+ return NullUniValue;
+ }
+
+ if (request.fHelp || request.params.size() > 0)
+ throw std::runtime_error(
+ "abortrescan\n"
+ "\nStops current wallet rescan triggered e.g. by an importprivkey call.\n"
+ "\nExamples:\n"
+ "\nImport a private key\n"
+ + HelpExampleCli("importprivkey", "\"mykey\"") +
+ "\nAbort the running wallet rescan\n"
+ + HelpExampleCli("abortrescan", "") +
+ "\nAs a JSON-RPC call\n"
+ + HelpExampleRpc("abortrescan", "")
+ );
+
+ if (!pwallet->IsScanning() || pwallet->IsAbortingRescan()) return false;
+ pwallet->AbortRescan();
+ return true;
+}
+
void ImportAddress(CWallet*, const CBitcoinAddress& address, const std::string& strLabel);
void ImportScript(CWallet* const pwallet, const CScript& script, const std::string& strLabel, bool isRedeemScript)
{
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index ccb744c6b2..d1e7485d04 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -9,8 +9,10 @@
#include "consensus/validation.h"
#include "core_io.h"
#include "init.h"
+#include "wallet/coincontrol.h"
#include "validation.h"
#include "net.h"
+#include "policy/fees.h"
#include "policy/policy.h"
#include "policy/rbf.h"
#include "rpc/server.h"
@@ -18,8 +20,9 @@
#include "timedata.h"
#include "util.h"
#include "utilmoneystr.h"
-#include "wallet.h"
-#include "walletdb.h"
+#include "wallet/feebumper.h"
+#include "wallet/wallet.h"
+#include "wallet/walletdb.h"
#include <stdint.h>
@@ -726,6 +729,8 @@ UniValue getbalance(const JSONRPCRequest& request)
if (request.params.size() == 0)
return ValueFromAmount(pwallet->GetBalance());
+ const std::string* account = request.params[0].get_str() != "*" ? &request.params[0].get_str() : nullptr;
+
int nMinDepth = 1;
if (request.params.size() > 1)
nMinDepth = request.params[1].get_int();
@@ -734,41 +739,7 @@ UniValue getbalance(const JSONRPCRequest& request)
if(request.params[2].get_bool())
filter = filter | ISMINE_WATCH_ONLY;
- if (request.params[0].get_str() == "*") {
- // Calculate total balance in a very different way from GetBalance().
- // The biggest difference is that GetBalance() sums up all unspent
- // TxOuts paying to the wallet, while this sums up both spent and
- // unspent TxOuts paying to the wallet, and then subtracts the values of
- // TxIns spending from the wallet. This also has fewer restrictions on
- // which unconfirmed transactions are considered trusted.
- CAmount nBalance = 0;
- for (const std::pair<uint256, CWalletTx>& pairWtx : pwallet->mapWallet) {
- const CWalletTx& wtx = pairWtx.second;
- if (!CheckFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 || wtx.GetDepthInMainChain() < 0)
- continue;
-
- CAmount allFee;
- std::string strSentAccount;
- std::list<COutputEntry> listReceived;
- std::list<COutputEntry> listSent;
- wtx.GetAmounts(listReceived, listSent, allFee, strSentAccount, filter);
- if (wtx.GetDepthInMainChain() >= nMinDepth)
- {
- BOOST_FOREACH(const COutputEntry& r, listReceived)
- nBalance += r.amount;
- }
- BOOST_FOREACH(const COutputEntry& s, listSent)
- nBalance -= s.amount;
- nBalance -= allFee;
- }
- return ValueFromAmount(nBalance);
- }
-
- std::string strAccount = AccountFromValue(request.params[0]);
-
- CAmount nBalance = pwallet->GetAccountBalance(strAccount, nMinDepth, filter);
-
- return ValueFromAmount(nBalance);
+ return ValueFromAmount(pwallet->GetLegacyBalance(filter, nMinDepth, account));
}
UniValue getunconfirmedbalance(const JSONRPCRequest &request)
@@ -898,7 +869,7 @@ UniValue sendfrom(const JSONRPCRequest& request)
EnsureWalletIsUnlocked(pwallet);
// Check funds
- CAmount nBalance = pwallet->GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE);
+ CAmount nBalance = pwallet->GetLegacyBalance(ISMINE_SPENDABLE, nMinDepth, &strAccount);
if (nAmount > nBalance)
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
@@ -1007,7 +978,7 @@ UniValue sendmany(const JSONRPCRequest& request)
EnsureWalletIsUnlocked(pwallet);
// Check funds
- CAmount nBalance = pwallet->GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE);
+ CAmount nBalance = pwallet->GetLegacyBalance(ISMINE_SPENDABLE, nMinDepth, &strAccount);
if (totalAmount > nBalance)
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
@@ -2075,7 +2046,7 @@ UniValue walletpassphrase(const JSONRPCRequest& request)
int64_t nSleepTime = request.params[1].get_int64();
pwallet->nRelockTime = GetTime() + nSleepTime;
- RPCRunLater(strprintf("lockwallet(%s)", pwallet->strWalletFile), boost::bind(LockWallet, pwallet), nSleepTime);
+ RPCRunLater(strprintf("lockwallet(%s)", pwallet->GetName()), boost::bind(LockWallet, pwallet), nSleepTime);
return NullUniValue;
}
@@ -2778,33 +2749,6 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
return result;
}
-// Calculate the size of the transaction assuming all signatures are max size
-// Use DummySignatureCreator, which inserts 72 byte signatures everywhere.
-// TODO: re-use this in CWallet::CreateTransaction (right now
-// CreateTransaction uses the constructed dummy-signed tx to do a priority
-// calculation, but we should be able to refactor after priority is removed).
-// NOTE: this requires that all inputs must be in mapWallet (eg the tx should
-// be IsAllFromMe).
-int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, CWallet &wallet)
-{
- CMutableTransaction txNew(tx);
- std::vector<std::pair<CWalletTx*, unsigned int>> vCoins;
- // Look up the inputs. We should have already checked that this transaction
- // IsAllFromMe(ISMINE_SPENDABLE), so every input should already be in our
- // wallet, with a valid index into the vout array.
- for (auto& input : tx.vin) {
- const auto mi = wallet.mapWallet.find(input.prevout.hash);
- assert(mi != wallet.mapWallet.end() && input.prevout.n < mi->second.tx->vout.size());
- vCoins.emplace_back(std::make_pair(&(mi->second), input.prevout.n));
- }
- if (!wallet.DummySignTx(txNew, vCoins)) {
- // This should never happen, because IsAllFromMe(ISMINE_SPENDABLE)
- // implies that we can sign for every input.
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction contains inputs that cannot be signed");
- }
- return GetVirtualTransactionSize(txNew);
-}
-
UniValue bumpfee(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
@@ -2859,63 +2803,6 @@ UniValue bumpfee(const JSONRPCRequest& request)
uint256 hash;
hash.SetHex(request.params[0].get_str());
- // retrieve the original tx from the wallet
- LOCK2(cs_main, pwallet->cs_wallet);
- EnsureWalletIsUnlocked(pwallet);
- if (!pwallet->mapWallet.count(hash)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id");
- }
- CWalletTx& wtx = pwallet->mapWallet[hash];
-
- if (pwallet->HasWalletSpend(hash)) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Transaction has descendants in the wallet");
- }
-
- {
- LOCK(mempool.cs);
- auto it = mempool.mapTx.find(hash);
- if (it != mempool.mapTx.end() && it->GetCountWithDescendants() > 1) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Transaction has descendants in the mempool");
- }
- }
-
- if (wtx.GetDepthInMainChain() != 0) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Transaction has been mined, or is conflicted with a mined transaction");
- }
-
- if (!SignalsOptInRBF(wtx)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Transaction is not BIP 125 replaceable");
- }
-
- if (wtx.mapValue.count("replaced_by_txid")) {
- throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Cannot bump transaction %s which was already bumped by transaction %s", hash.ToString(), wtx.mapValue.at("replaced_by_txid")));
- }
-
- // 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)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Transaction contains inputs that don't belong to this wallet");
- }
-
- // figure out which output was change
- // if there was no change output or multiple change outputs, fail
- int nOutput = -1;
- for (size_t i = 0; i < wtx.tx->vout.size(); ++i) {
- if (pwallet->IsChange(wtx.tx->vout[i])) {
- if (nOutput != -1) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Transaction has multiple change outputs");
- }
- nOutput = i;
- }
- }
- if (nOutput == -1) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Transaction does not have a change output");
- }
-
- // Calculate the expected size of the new transaction.
- int64_t txSize = GetVirtualTransactionSize(*(wtx.tx));
- const int64_t maxNewTxSize = CalculateMaximumSignedTxSize(*wtx.tx, *pwallet);
-
// optional parameters
bool specifiedConfirmTarget = false;
int newConfirmTarget = nTxConfirmTarget;
@@ -2941,11 +2828,8 @@ UniValue bumpfee(const JSONRPCRequest& request)
}
} else if (options.exists("totalFee")) {
totalFee = options["totalFee"].get_int64();
- CAmount requiredFee = CWallet::GetRequiredFee(maxNewTxSize);
- if (totalFee < requiredFee ) {
- throw JSONRPCError(RPC_INVALID_PARAMETER,
- strprintf("Insufficient totalFee (cannot be less than required fee %s)",
- FormatMoney(requiredFee)));
+ if (totalFee <= 0) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid totalFee %s (must be greater than 0)", FormatMoney(totalFee)));
}
}
@@ -2954,148 +2838,53 @@ UniValue bumpfee(const JSONRPCRequest& request)
}
}
- // calculate the old fee and fee-rate
- CAmount nOldFee = wtx.GetDebit(ISMINE_SPENDABLE) - wtx.tx->GetValueOut();
- CFeeRate nOldFeeRate(nOldFee, txSize);
- CAmount nNewFee;
- CFeeRate nNewFeeRate;
- // The wallet uses a conservative WALLET_INCREMENTAL_RELAY_FEE value to
- // future proof against changes to network wide policy for incremental relay
- // fee that our node may not be aware of.
- CFeeRate walletIncrementalRelayFee = CFeeRate(WALLET_INCREMENTAL_RELAY_FEE);
- if (::incrementalRelayFee > walletIncrementalRelayFee) {
- walletIncrementalRelayFee = ::incrementalRelayFee;
- }
-
- if (totalFee > 0) {
- CAmount minTotalFee = nOldFeeRate.GetFee(maxNewTxSize) + ::incrementalRelayFee.GetFee(maxNewTxSize);
- if (totalFee < minTotalFee) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Insufficient totalFee, must be at least %s (oldFee %s + incrementalFee %s)",
- FormatMoney(minTotalFee), FormatMoney(nOldFeeRate.GetFee(maxNewTxSize)), FormatMoney(::incrementalRelayFee.GetFee(maxNewTxSize))));
- }
- nNewFee = totalFee;
- nNewFeeRate = CFeeRate(totalFee, maxNewTxSize);
- } else {
- // if user specified a confirm target then don't consider any global payTxFee
- if (specifiedConfirmTarget) {
- nNewFee = CWallet::GetMinimumFee(maxNewTxSize, newConfirmTarget, mempool, CAmount(0));
- }
- // otherwise use the regular wallet logic to select payTxFee or default confirm target
- else {
- nNewFee = CWallet::GetMinimumFee(maxNewTxSize, newConfirmTarget, mempool);
- }
-
- nNewFeeRate = CFeeRate(nNewFee, maxNewTxSize);
-
- // New fee rate must be at least old rate + minimum incremental relay rate
- // walletIncrementalRelayFee.GetFeePerK() should be exact, because it's initialized
- // in that unit (fee per kb).
- // However, nOldFeeRate is a calculated value from the tx fee/size, so
- // add 1 satoshi to the result, because it may have been rounded down.
- if (nNewFeeRate.GetFeePerK() < nOldFeeRate.GetFeePerK() + 1 + walletIncrementalRelayFee.GetFeePerK()) {
- nNewFeeRate = CFeeRate(nOldFeeRate.GetFeePerK() + 1 + walletIncrementalRelayFee.GetFeePerK());
- nNewFee = nNewFeeRate.GetFee(maxNewTxSize);
- }
- }
-
- // Check that in all cases the new fee doesn't violate maxTxFee
- if (nNewFee > maxTxFee) {
- throw JSONRPCError(RPC_WALLET_ERROR,
- strprintf("Specified or calculated fee %s is too high (cannot be higher than maxTxFee %s)",
- FormatMoney(nNewFee), FormatMoney(maxTxFee)));
- }
-
- // check that fee rate is higher than mempool's minimum fee
- // (no point in bumping fee if we know that the new tx won't be accepted to the mempool)
- // This may occur if the user set TotalFee or paytxfee too low, if fallbackfee is too low, or, perhaps,
- // in a rare situation where the mempool minimum fee increased significantly since the fee estimation just a
- // moment earlier. In this case, we report an error to the user, who may use totalFee to make an adjustment.
- CFeeRate minMempoolFeeRate = mempool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
- if (nNewFeeRate.GetFeePerK() < minMempoolFeeRate.GetFeePerK()) {
- throw JSONRPCError(RPC_WALLET_ERROR, 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())));
- }
-
- // Now modify the output to increase the fee.
- // If the output is not large enough to pay the fee, fail.
- CAmount nDelta = nNewFee - nOldFee;
- assert(nDelta > 0);
- CMutableTransaction tx(*(wtx.tx));
- CTxOut* poutput = &(tx.vout[nOutput]);
- if (poutput->nValue < nDelta) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Change output is too small to bump the fee");
- }
-
- // If the output would become dust, discard it (converting the dust to fee)
- poutput->nValue -= nDelta;
- if (poutput->nValue <= poutput->GetDustThreshold(::dustRelayFee)) {
- LogPrint(BCLog::RPC, "Bumping fee and discarding dust output\n");
- nNewFee += poutput->nValue;
- tx.vout.erase(tx.vout.begin() + nOutput);
- }
-
- // Mark new tx not replaceable, if requested.
- if (!replaceable) {
- for (auto& input : tx.vin) {
- if (input.nSequence < 0xfffffffe) input.nSequence = 0xfffffffe;
- }
- }
+ LOCK2(cs_main, pwallet->cs_wallet);
+ EnsureWalletIsUnlocked(pwallet);
- // sign the new tx
- CTransaction txNewConst(tx);
- int nIn = 0;
- for (auto& input : tx.vin) {
- std::map<uint256, CWalletTx>::const_iterator mi = pwallet->mapWallet.find(input.prevout.hash);
- assert(mi != pwallet->mapWallet.end() && input.prevout.n < mi->second.tx->vout.size());
- const CScript& scriptPubKey = mi->second.tx->vout[input.prevout.n].scriptPubKey;
- const CAmount& amount = mi->second.tx->vout[input.prevout.n].nValue;
- SignatureData sigdata;
- if (!ProduceSignature(TransactionSignatureCreator(pwallet, &txNewConst, nIn, amount, SIGHASH_ALL), scriptPubKey, sigdata)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Can't sign transaction.");
+ CFeeBumper feeBump(pwallet, hash, newConfirmTarget, specifiedConfirmTarget, totalFee, replaceable);
+ BumpFeeResult res = feeBump.getResult();
+ if (res != BumpFeeResult::OK)
+ {
+ switch(res) {
+ case BumpFeeResult::INVALID_ADDRESS_OR_KEY:
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, feeBump.getErrors()[0]);
+ break;
+ case BumpFeeResult::INVALID_REQUEST:
+ throw JSONRPCError(RPC_INVALID_REQUEST, feeBump.getErrors()[0]);
+ break;
+ case BumpFeeResult::INVALID_PARAMETER:
+ throw JSONRPCError(RPC_INVALID_PARAMETER, feeBump.getErrors()[0]);
+ break;
+ case BumpFeeResult::WALLET_ERROR:
+ throw JSONRPCError(RPC_WALLET_ERROR, feeBump.getErrors()[0]);
+ break;
+ default:
+ throw JSONRPCError(RPC_MISC_ERROR, feeBump.getErrors()[0]);
+ break;
}
- UpdateTransaction(tx, nIn, sigdata);
- nIn++;
}
- // commit/broadcast the tx
- CReserveKey reservekey(pwallet);
- CWalletTx wtxBumped(pwallet, MakeTransactionRef(std::move(tx)));
- wtxBumped.mapValue = wtx.mapValue;
- wtxBumped.mapValue["replaces_txid"] = hash.ToString();
- wtxBumped.vOrderForm = wtx.vOrderForm;
- wtxBumped.strFromAccount = wtx.strFromAccount;
- wtxBumped.fTimeReceivedIsTxTime = true;
- wtxBumped.fFromMe = true;
- CValidationState state;
- if (!pwallet->CommitTransaction(wtxBumped, reservekey, g_connman.get(), state)) {
- // NOTE: CommitTransaction never returns false, so this should never happen.
- throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason()));
+ // sign bumped transaction
+ if (!feeBump.signTransaction(pwallet)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Can't sign transaction.");
}
-
- UniValue vErrors(UniValue::VARR);
- 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)));
+ // commit the bumped transaction
+ if(!feeBump.commit(pwallet)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, feeBump.getErrors()[0]);
}
-
- // mark the original tx as bumped
- if (!pwallet->MarkReplaced(wtx.GetHash(), wtxBumped.GetHash())) {
- // TODO: see if JSON-RPC has a standard way of returning a response
- // 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.");
- }
-
UniValue result(UniValue::VOBJ);
- result.push_back(Pair("txid", wtxBumped.GetHash().GetHex()));
- result.push_back(Pair("origfee", ValueFromAmount(nOldFee)));
- result.push_back(Pair("fee", ValueFromAmount(nNewFee)));
- result.push_back(Pair("errors", vErrors));
+ result.push_back(Pair("txid", feeBump.getBumpedTxId().GetHex()));
+ result.push_back(Pair("origfee", ValueFromAmount(feeBump.getOldFee())));
+ result.push_back(Pair("fee", ValueFromAmount(feeBump.getNewFee())));
+ UniValue errors(UniValue::VARR);
+ for (const std::string& err: feeBump.getErrors())
+ errors.push_back(err);
+ result.push_back(errors);
return result;
}
+extern UniValue abortrescan(const JSONRPCRequest& request); // in rpcdump.cpp
extern UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp
extern UniValue importprivkey(const JSONRPCRequest& request);
extern UniValue importaddress(const JSONRPCRequest& request);
@@ -3112,6 +2901,7 @@ static const CRPCCommand commands[] =
{ "rawtransactions", "fundrawtransaction", &fundrawtransaction, false, {"hexstring","options"} },
{ "hidden", "resendwallettransactions", &resendwallettransactions, true, {} },
{ "wallet", "abandontransaction", &abandontransaction, false, {"txid"} },
+ { "wallet", "abortrescan", &abortrescan, false, {} },
{ "wallet", "addmultisigaddress", &addmultisigaddress, true, {"nrequired","keys","account"} },
{ "wallet", "addwitnessaddress", &addwitnessaddress, true, {"address"} },
{ "wallet", "backupwallet", &backupwallet, true, {"destination"} },
diff --git a/src/wallet/test/wallet_test_fixture.cpp b/src/wallet/test/wallet_test_fixture.cpp
index a76db37617..1989bf8d9b 100644
--- a/src/wallet/test/wallet_test_fixture.cpp
+++ b/src/wallet/test/wallet_test_fixture.cpp
@@ -14,7 +14,8 @@ WalletTestingSetup::WalletTestingSetup(const std::string& chainName):
bitdb.MakeMock();
bool fFirstRun;
- pwalletMain = new CWallet("wallet_test.dat");
+ std::unique_ptr<CWalletDBWrapper> dbw(new CWalletDBWrapper(&bitdb, "wallet_test.dat"));
+ pwalletMain = new CWallet(std::move(dbw));
pwalletMain->LoadWallet(fFirstRun);
RegisterValidationInterface(pwalletMain);
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 67e5e90224..8eeba72a06 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -29,7 +29,7 @@ extern UniValue importmulti(const JSONRPCRequest& request);
std::vector<std::unique_ptr<CWalletTx>> wtxn;
-typedef std::set<std::pair<const CWalletTx*,unsigned int> > CoinSet;
+typedef std::set<CInputCoin> CoinSet;
BOOST_FIXTURE_TEST_SUITE(wallet_tests, WalletTestingSetup)
@@ -424,6 +424,17 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
BOOST_CHECK_EQUAL(response.write(), strprintf("[{\"success\":false,\"error\":{\"code\":-1,\"message\":\"Failed to rescan before time %d, transactions may be missing.\"}},{\"success\":true}]", newTip->GetBlockTimeMax()));
::pwalletMain = backup;
}
+
+ // Verify ScanForWalletTransactions does not return null when the scan is
+ // elided due to the nTimeFirstKey optimization.
+ {
+ CWallet wallet;
+ {
+ LOCK(wallet.cs_wallet);
+ wallet.UpdateTimeFirstKey(newTip->GetBlockTime() + 7200 + 1);
+ }
+ BOOST_CHECK_EQUAL(newTip, wallet.ScanForWalletTransactions(newTip));
+ }
}
// Check that GetImmatureCredit() returns a newly calculated value instead of
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 68d4bc35ee..a8f818a494 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -11,11 +11,14 @@
#include "wallet/coincontrol.h"
#include "consensus/consensus.h"
#include "consensus/validation.h"
+#include "fs.h"
#include "key.h"
#include "keystore.h"
#include "validation.h"
#include "net.h"
+#include "policy/fees.h"
#include "policy/policy.h"
+#include "policy/rbf.h"
#include "primitives/block.h"
#include "primitives/transaction.h"
#include "script/script.h"
@@ -30,7 +33,6 @@
#include <assert.h>
#include <boost/algorithm/string/replace.hpp>
-#include <boost/filesystem.hpp>
#include <boost/thread.hpp>
CWallet* pwalletMain = NULL;
@@ -64,10 +66,10 @@ const uint256 CMerkleTx::ABANDON_HASH(uint256S("00000000000000000000000000000000
struct CompareValueOnly
{
- bool operator()(const std::pair<CAmount, std::pair<const CWalletTx*, unsigned int> >& t1,
- const std::pair<CAmount, std::pair<const CWalletTx*, unsigned int> >& t2) const
+ bool operator()(const CInputCoin& t1,
+ const CInputCoin& t2) const
{
- return t1.first < t2.first;
+ return t1.txout.nValue < t2.txout.nValue;
}
};
@@ -160,7 +162,7 @@ void CWallet::DeriveNewChildKey(CKeyMetadata& metadata, CKey& secret, bool inter
secret = childKey.key;
metadata.hdMasterKeyID = hdChain.masterKeyID;
// update the chain model in the database
- if (!CWalletDB(strWalletFile).WriteHDChain(hdChain))
+ if (!CWalletDB(*dbw).WriteHDChain(hdChain))
throw std::runtime_error(std::string(__func__) + ": Writing HD chain model failed");
}
@@ -179,10 +181,8 @@ bool CWallet::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey)
if (HaveWatchOnly(script))
RemoveWatchOnly(script);
- if (!fFileBacked)
- return true;
if (!IsCrypted()) {
- return CWalletDB(strWalletFile).WriteKey(pubkey,
+ return CWalletDB(*dbw).WriteKey(pubkey,
secret.GetPrivKey(),
mapKeyMetadata[pubkey.GetID()]);
}
@@ -194,8 +194,6 @@ bool CWallet::AddCryptedKey(const CPubKey &vchPubKey,
{
if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret))
return false;
- if (!fFileBacked)
- return true;
{
LOCK(cs_wallet);
if (pwalletdbEncryption)
@@ -203,7 +201,7 @@ bool CWallet::AddCryptedKey(const CPubKey &vchPubKey,
vchCryptedSecret,
mapKeyMetadata[vchPubKey.GetID()]);
else
- return CWalletDB(strWalletFile).WriteCryptedKey(vchPubKey,
+ return CWalletDB(*dbw).WriteCryptedKey(vchPubKey,
vchCryptedSecret,
mapKeyMetadata[vchPubKey.GetID()]);
}
@@ -239,9 +237,7 @@ bool CWallet::AddCScript(const CScript& redeemScript)
{
if (!CCryptoKeyStore::AddCScript(redeemScript))
return false;
- if (!fFileBacked)
- return true;
- return CWalletDB(strWalletFile).WriteCScript(Hash160(redeemScript), redeemScript);
+ return CWalletDB(*dbw).WriteCScript(Hash160(redeemScript), redeemScript);
}
bool CWallet::LoadCScript(const CScript& redeemScript)
@@ -267,9 +263,7 @@ bool CWallet::AddWatchOnly(const CScript& dest)
const CKeyMetadata& meta = mapKeyMetadata[CScriptID(dest)];
UpdateTimeFirstKey(meta.nCreateTime);
NotifyWatchonlyChanged(true);
- if (!fFileBacked)
- return true;
- return CWalletDB(strWalletFile).WriteWatchOnly(dest, meta);
+ return CWalletDB(*dbw).WriteWatchOnly(dest, meta);
}
bool CWallet::AddWatchOnly(const CScript& dest, int64_t nCreateTime)
@@ -285,9 +279,8 @@ bool CWallet::RemoveWatchOnly(const CScript &dest)
return false;
if (!HaveWatchOnly())
NotifyWatchonlyChanged(false);
- if (fFileBacked)
- if (!CWalletDB(strWalletFile).EraseWatchOnly(dest))
- return false;
+ if (!CWalletDB(*dbw).EraseWatchOnly(dest))
+ return false;
return true;
}
@@ -352,7 +345,7 @@ bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase,
return false;
if (!crypter.Encrypt(_vMasterKey, pMasterKey.second.vchCryptedKey))
return false;
- CWalletDB(strWalletFile).WriteMasterKey(pMasterKey.first, pMasterKey.second);
+ CWalletDB(*dbw).WriteMasterKey(pMasterKey.first, pMasterKey.second);
if (fWasLocked)
Lock();
return true;
@@ -365,7 +358,7 @@ bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase,
void CWallet::SetBestChain(const CBlockLocator& loc)
{
- CWalletDB walletdb(strWalletFile);
+ CWalletDB walletdb(*dbw);
walletdb.WriteBestBlock(loc);
}
@@ -384,9 +377,8 @@ bool CWallet::SetMinVersion(enum WalletFeature nVersion, CWalletDB* pwalletdbIn,
if (nVersion > nWalletMaxVersion)
nWalletMaxVersion = nVersion;
- if (fFileBacked)
{
- CWalletDB* pwalletdb = pwalletdbIn ? pwalletdbIn : new CWalletDB(strWalletFile);
+ CWalletDB* pwalletdb = pwalletdbIn ? pwalletdbIn : new CWalletDB(*dbw);
if (nWalletVersion > 40000)
pwalletdb->WriteMinVersion(nWalletVersion);
if (!pwalletdbIn)
@@ -440,7 +432,7 @@ bool CWallet::HasWalletSpend(const uint256& txid) const
void CWallet::Flush(bool shutdown)
{
- bitdb.Flush(shutdown);
+ dbw->Flush(shutdown);
}
bool CWallet::Verify()
@@ -593,24 +585,19 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
{
LOCK(cs_wallet);
mapMasterKeys[++nMasterKeyMaxID] = kMasterKey;
- if (fFileBacked)
- {
- assert(!pwalletdbEncryption);
- pwalletdbEncryption = new CWalletDB(strWalletFile);
- if (!pwalletdbEncryption->TxnBegin()) {
- delete pwalletdbEncryption;
- pwalletdbEncryption = NULL;
- return false;
- }
- pwalletdbEncryption->WriteMasterKey(nMasterKeyMaxID, kMasterKey);
+ assert(!pwalletdbEncryption);
+ pwalletdbEncryption = new CWalletDB(*dbw);
+ if (!pwalletdbEncryption->TxnBegin()) {
+ delete pwalletdbEncryption;
+ pwalletdbEncryption = NULL;
+ return false;
}
+ pwalletdbEncryption->WriteMasterKey(nMasterKeyMaxID, kMasterKey);
if (!EncryptKeys(_vMasterKey))
{
- if (fFileBacked) {
- pwalletdbEncryption->TxnAbort();
- delete pwalletdbEncryption;
- }
+ pwalletdbEncryption->TxnAbort();
+ delete pwalletdbEncryption;
// We now probably have half of our keys encrypted in memory, and half not...
// die and let the user reload the unencrypted wallet.
assert(false);
@@ -619,19 +606,16 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
// Encryption was introduced in version 0.4.0
SetMinVersion(FEATURE_WALLETCRYPT, pwalletdbEncryption, true);
- if (fFileBacked)
- {
- if (!pwalletdbEncryption->TxnCommit()) {
- delete pwalletdbEncryption;
- // We now have keys encrypted in memory, but not on disk...
- // die to avoid confusion and let the user reload the unencrypted wallet.
- assert(false);
- }
-
+ if (!pwalletdbEncryption->TxnCommit()) {
delete pwalletdbEncryption;
- pwalletdbEncryption = NULL;
+ // We now have keys encrypted in memory, but not on disk...
+ // die to avoid confusion and let the user reload the unencrypted wallet.
+ assert(false);
}
+ delete pwalletdbEncryption;
+ pwalletdbEncryption = NULL;
+
Lock();
Unlock(strWalletPassphrase);
@@ -650,7 +634,7 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
// Need to completely rewrite the wallet file; if we don't, bdb might keep
// bits of the unencrypted private key in slack space in the database file.
- CDB::Rewrite(strWalletFile);
+ dbw->Rewrite();
}
NotifyStatusChanged(this);
@@ -661,7 +645,7 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
DBErrors CWallet::ReorderTransactions()
{
LOCK(cs_wallet);
- CWalletDB walletdb(strWalletFile);
+ CWalletDB walletdb(*dbw);
// Old wallets didn't have any defined order for transactions
// Probably a bad idea to change the output of this
@@ -742,14 +726,14 @@ int64_t CWallet::IncOrderPosNext(CWalletDB *pwalletdb)
if (pwalletdb) {
pwalletdb->WriteOrderPosNext(nOrderPosNext);
} else {
- CWalletDB(strWalletFile).WriteOrderPosNext(nOrderPosNext);
+ CWalletDB(*dbw).WriteOrderPosNext(nOrderPosNext);
}
return nRet;
}
bool CWallet::AccountMove(std::string strFrom, std::string strTo, CAmount nAmount, std::string strComment)
{
- CWalletDB walletdb(strWalletFile);
+ CWalletDB walletdb(*dbw);
if (!walletdb.TxnBegin())
return false;
@@ -783,7 +767,7 @@ bool CWallet::AccountMove(std::string strFrom, std::string strTo, CAmount nAmoun
bool CWallet::GetAccountPubkey(CPubKey &pubKey, std::string strAccount, bool bForceNew)
{
- CWalletDB walletdb(strWalletFile);
+ CWalletDB walletdb(*dbw);
CAccount account;
walletdb.ReadAccount(strAccount, account);
@@ -844,7 +828,7 @@ bool CWallet::MarkReplaced(const uint256& originalHash, const uint256& newHash)
wtx.mapValue["replaced_by_txid"] = newHash.ToString();
- CWalletDB walletdb(strWalletFile, "r+");
+ CWalletDB walletdb(*dbw, "r+");
bool success = true;
if (!walletdb.WriteTx(wtx)) {
@@ -861,7 +845,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
{
LOCK(cs_wallet);
- CWalletDB walletdb(strWalletFile, "r+", fFlushOnClose);
+ CWalletDB walletdb(*dbw, "r+", fFlushOnClose);
uint256 hash = wtxIn.GetHash();
@@ -956,9 +940,9 @@ bool CWallet::LoadToWallet(const CWalletTx& wtxIn)
/**
* Add a transaction to the wallet, or update it. pIndex and posInBlock should
* be set when the transaction was known to be included in a block. When
- * posInBlock = SYNC_TRANSACTION_NOT_IN_BLOCK (-1) , then wallet state is not
- * updated in AddToWallet, but notifications happen and cached balances are
- * marked dirty.
+ * pIndex == NULL, then wallet state is not updated in AddToWallet, but
+ * notifications happen and cached balances are marked dirty.
+ *
* If fUpdate is true, existing transactions will be updated.
* TODO: One exception to this is that the abandoned state is cleared under the
* assumption that any further notification of a transaction that was considered
@@ -966,12 +950,13 @@ bool CWallet::LoadToWallet(const CWalletTx& wtxIn)
* Abandoned state should probably be more carefully tracked via different
* posInBlock signals or by checking mempool presence when necessary.
*/
-bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate)
+bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate)
{
+ const CTransaction& tx = *ptx;
{
AssertLockHeld(cs_wallet);
- if (posInBlock != -1) {
+ if (pIndex != NULL) {
BOOST_FOREACH(const CTxIn& txin, tx.vin) {
std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range = mapTxSpends.equal_range(txin.prevout);
while (range.first != range.second) {
@@ -988,10 +973,10 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlockIndex
if (fExisted && !fUpdate) return false;
if (fExisted || IsMine(tx) || IsFromMe(tx))
{
- CWalletTx wtx(this, MakeTransactionRef(tx));
+ CWalletTx wtx(this, ptx);
// Get merkle branch if transaction was found in a block
- if (posInBlock != -1)
+ if (pIndex != NULL)
wtx.SetMerkleBranch(pIndex, posInBlock);
return AddToWallet(wtx, false);
@@ -1004,7 +989,7 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
{
LOCK2(cs_main, cs_wallet);
- CWalletDB walletdb(strWalletFile, "r+");
+ CWalletDB walletdb(*dbw, "r+");
std::set<uint256> todo;
std::set<uint256> done;
@@ -1076,7 +1061,7 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx)
return;
// Do not flush the wallet here for performance reasons
- CWalletDB walletdb(strWalletFile, "r+", false);
+ CWalletDB walletdb(*dbw, "r+", false);
std::set<uint256> todo;
std::set<uint256> done;
@@ -1116,11 +1101,10 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx)
}
}
-void CWallet::SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, int posInBlock)
-{
- LOCK2(cs_main, cs_wallet);
+void CWallet::SyncTransaction(const CTransactionRef& ptx, const CBlockIndex *pindex, int posInBlock) {
+ const CTransaction& tx = *ptx;
- if (!AddToWalletIfInvolvingMe(tx, pindex, posInBlock, true))
+ if (!AddToWalletIfInvolvingMe(ptx, pindex, posInBlock, true))
return; // Not one of ours
// If a transaction changes 'conflicted' state, that changes the balance
@@ -1133,6 +1117,38 @@ void CWallet::SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex,
}
}
+void CWallet::TransactionAddedToMempool(const CTransactionRef& ptx) {
+ LOCK2(cs_main, cs_wallet);
+ SyncTransaction(ptx);
+}
+
+void CWallet::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex *pindex, const std::vector<CTransactionRef>& vtxConflicted) {
+ LOCK2(cs_main, cs_wallet);
+ // TODO: Temporarily ensure that mempool removals are notified before
+ // connected transactions. This shouldn't matter, but the abandoned
+ // state of transactions in our wallet is currently cleared when we
+ // receive another notification and there is a race condition where
+ // notification of a connected conflict might cause an outside process
+ // to abandon a transaction and then have it inadvertently cleared by
+ // the notification that the conflicted transaction was evicted.
+
+ for (const CTransactionRef& ptx : vtxConflicted) {
+ SyncTransaction(ptx);
+ }
+ for (size_t i = 0; i < pblock->vtx.size(); i++) {
+ SyncTransaction(pblock->vtx[i], pindex, i);
+ }
+}
+
+void CWallet::BlockDisconnected(const std::shared_ptr<const CBlock>& pblock) {
+ LOCK2(cs_main, cs_wallet);
+
+ for (const CTransactionRef& ptx : pblock->vtx) {
+ SyncTransaction(ptx);
+ }
+}
+
+
isminetype CWallet::IsMine(const CTxIn &txin) const
{
@@ -1328,7 +1344,7 @@ bool CWallet::SetHDMasterKey(const CPubKey& pubkey, CHDChain *possibleOldChain)
bool CWallet::SetHDChain(const CHDChain& chain, bool memonly)
{
LOCK(cs_wallet);
- if (!memonly && !CWalletDB(strWalletFile).WriteHDChain(chain))
+ if (!memonly && !CWalletDB(*dbw).WriteHDChain(chain))
throw std::runtime_error(std::string(__func__) + ": writing chain failed");
hdChain = chain;
@@ -1441,59 +1457,27 @@ void CWalletTx::GetAmounts(std::list<COutputEntry>& listReceived,
}
-void CWalletTx::GetAccountAmounts(const std::string& strAccount, CAmount& nReceived,
- CAmount& nSent, CAmount& nFee, const isminefilter& filter) const
-{
- nReceived = nSent = nFee = 0;
-
- CAmount allFee;
- std::string strSentAccount;
- std::list<COutputEntry> listReceived;
- std::list<COutputEntry> listSent;
- GetAmounts(listReceived, listSent, allFee, strSentAccount, filter);
-
- if (strAccount == strSentAccount)
- {
- BOOST_FOREACH(const COutputEntry& s, listSent)
- nSent += s.amount;
- nFee = allFee;
- }
- {
- LOCK(pwallet->cs_wallet);
- BOOST_FOREACH(const COutputEntry& r, listReceived)
- {
- if (pwallet->mapAddressBook.count(r.destination))
- {
- std::map<CTxDestination, CAddressBookData>::const_iterator mi = pwallet->mapAddressBook.find(r.destination);
- if (mi != pwallet->mapAddressBook.end() && (*mi).second.name == strAccount)
- nReceived += r.amount;
- }
- else if (strAccount.empty())
- {
- nReceived += r.amount;
- }
- }
- }
-}
-
/**
* Scan the block chain (starting in pindexStart) for transactions
* from or to us. If fUpdate is true, found transactions that already
* exist in the wallet will be updated.
*
* Returns pointer to the first block in the last contiguous range that was
- * successfully scanned.
- *
+ * successfully scanned or elided (elided if pIndexStart points at a block
+ * before CWallet::nTimeFirstKey). Returns null if there is no such range, or
+ * the range doesn't include chainActive.Tip().
*/
CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate)
{
- CBlockIndex* ret = nullptr;
int64_t nNow = GetTime();
const CChainParams& chainParams = Params();
CBlockIndex* pindex = pindexStart;
+ CBlockIndex* ret = pindexStart;
{
LOCK2(cs_main, cs_wallet);
+ fAbortRescan = false;
+ fScanningWallet = true;
// no need to read and scan block, if block was created before
// our wallet birthday (as adjusted for block time variability)
@@ -1503,15 +1487,19 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool f
ShowProgress(_("Rescanning..."), 0); // show rescan progress in GUI as dialog or on splashscreen, if -rescan on startup
double dProgressStart = GuessVerificationProgress(chainParams.TxData(), pindex);
double dProgressTip = GuessVerificationProgress(chainParams.TxData(), chainActive.Tip());
- while (pindex)
+ while (pindex && !fAbortRescan)
{
if (pindex->nHeight % 100 == 0 && dProgressTip - dProgressStart > 0.0)
ShowProgress(_("Rescanning..."), std::max(1, std::min(99, (int)((GuessVerificationProgress(chainParams.TxData(), pindex) - dProgressStart) / (dProgressTip - dProgressStart) * 100))));
+ if (GetTime() >= nNow + 60) {
+ nNow = GetTime();
+ LogPrintf("Still rescanning. At block %d. Progress=%f\n", pindex->nHeight, GuessVerificationProgress(chainParams.TxData(), pindex));
+ }
CBlock block;
if (ReadBlockFromDisk(block, pindex, Params().GetConsensus())) {
for (size_t posInBlock = 0; posInBlock < block.vtx.size(); ++posInBlock) {
- AddToWalletIfInvolvingMe(*block.vtx[posInBlock], pindex, posInBlock, fUpdate);
+ AddToWalletIfInvolvingMe(block.vtx[posInBlock], pindex, posInBlock, fUpdate);
}
if (!ret) {
ret = pindex;
@@ -1520,12 +1508,13 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool f
ret = nullptr;
}
pindex = chainActive.Next(pindex);
- if (GetTime() >= nNow + 60) {
- nNow = GetTime();
- LogPrintf("Still rescanning. At block %d. Progress=%f\n", pindex->nHeight, GuessVerificationProgress(chainParams.TxData(), pindex));
- }
+ }
+ if (pindex && fAbortRescan) {
+ LogPrintf("Rescan aborted at block %d. Progress=%f\n", pindex->nHeight, GuessVerificationProgress(chainParams.TxData(), pindex));
}
ShowProgress(_("Rescanning..."), 100); // hide progress dialog in GUI
+
+ fScanningWallet = false;
}
return ret;
}
@@ -1760,10 +1749,7 @@ CAmount CWalletTx::GetChange() const
bool CWalletTx::InMempool() const
{
LOCK(mempool.cs);
- if (mempool.exists(GetHash())) {
- return true;
- }
- return false;
+ return mempool.exists(GetHash());
}
bool CWalletTx::IsTrusted() const
@@ -1954,6 +1940,49 @@ CAmount CWallet::GetImmatureWatchOnlyBalance() const
return nTotal;
}
+// Calculate total balance in a different way from GetBalance. The biggest
+// difference is that GetBalance sums up all unspent TxOuts paying to the
+// wallet, while this sums up both spent and unspent TxOuts paying to the
+// wallet, and then subtracts the values of TxIns spending from the wallet. This
+// also has fewer restrictions on which unconfirmed transactions are considered
+// trusted.
+CAmount CWallet::GetLegacyBalance(const isminefilter& filter, int minDepth, const std::string* account) const
+{
+ LOCK2(cs_main, cs_wallet);
+
+ CAmount balance = 0;
+ for (const auto& entry : mapWallet) {
+ const CWalletTx& wtx = entry.second;
+ const int depth = wtx.GetDepthInMainChain();
+ if (depth < 0 || !CheckFinalTx(*wtx.tx) || wtx.GetBlocksToMaturity() > 0) {
+ continue;
+ }
+
+ // Loop through tx outputs and add incoming payments. For outgoing txs,
+ // treat change outputs specially, as part of the amount debited.
+ CAmount debit = wtx.GetDebit(filter);
+ const bool outgoing = debit > 0;
+ for (const CTxOut& out : wtx.tx->vout) {
+ if (outgoing && IsChange(out)) {
+ debit -= out.nValue;
+ } else if (IsMine(out) & filter && depth >= minDepth && (!account || *account == GetAccountName(out.scriptPubKey))) {
+ balance += out.nValue;
+ }
+ }
+
+ // For outgoing txs, subtract amount debited.
+ if (outgoing && (!account || *account == wtx.strFromAccount)) {
+ balance -= debit;
+ }
+ }
+
+ if (account) {
+ balance += CWalletDB(*dbw).GetAccountCreditDebit(*account);
+ }
+
+ return balance;
+}
+
void CWallet::AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe, const CCoinControl *coinControl, bool fIncludeZeroValue) const
{
vCoins.clear();
@@ -2031,7 +2060,7 @@ void CWallet::AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe, const
}
}
-static void ApproximateBestSubset(const std::vector<std::pair<CAmount, std::pair<const CWalletTx*,unsigned int> > >& vValue, const CAmount& nTotalLower, const CAmount& nTargetValue,
+static void ApproximateBestSubset(const std::vector<CInputCoin>& vValue, const CAmount& nTotalLower, const CAmount& nTargetValue,
std::vector<char>& vfBest, CAmount& nBest, int iterations = 1000)
{
std::vector<char> vfIncluded;
@@ -2056,9 +2085,9 @@ static void ApproximateBestSubset(const std::vector<std::pair<CAmount, std::pair
//that the rng is fast. We do not use a constant random sequence,
//because there may be some privacy improvement by making
//the selection random.
- if (nPass == 0 ? insecure_rand.rand32()&1 : !vfIncluded[i])
+ if (nPass == 0 ? insecure_rand.randbool() : !vfIncluded[i])
{
- nTotal += vValue[i].first;
+ nTotal += vValue[i].txout.nValue;
vfIncluded[i] = true;
if (nTotal >= nTargetValue)
{
@@ -2068,7 +2097,7 @@ static void ApproximateBestSubset(const std::vector<std::pair<CAmount, std::pair
nBest = nTotal;
vfBest = vfIncluded;
}
- nTotal -= vValue[i].first;
+ nTotal -= vValue[i].txout.nValue;
vfIncluded[i] = false;
}
}
@@ -2078,16 +2107,14 @@ static void ApproximateBestSubset(const std::vector<std::pair<CAmount, std::pair
}
bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const int nConfMine, const int nConfTheirs, const uint64_t nMaxAncestors, std::vector<COutput> vCoins,
- std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet) const
+ std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet) const
{
setCoinsRet.clear();
nValueRet = 0;
// List of values less than target
- std::pair<CAmount, std::pair<const CWalletTx*,unsigned int> > coinLowestLarger;
- coinLowestLarger.first = std::numeric_limits<CAmount>::max();
- coinLowestLarger.second.first = NULL;
- std::vector<std::pair<CAmount, std::pair<const CWalletTx*,unsigned int> > > vValue;
+ boost::optional<CInputCoin> coinLowestLarger;
+ std::vector<CInputCoin> vValue;
CAmount nTotalLower = 0;
random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt);
@@ -2106,22 +2133,21 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const int nConfMin
continue;
int i = output.i;
- CAmount n = pcoin->tx->vout[i].nValue;
- std::pair<CAmount,std::pair<const CWalletTx*,unsigned int> > coin = std::make_pair(n,std::make_pair(pcoin, i));
+ CInputCoin coin = CInputCoin(pcoin, i);
- if (n == nTargetValue)
+ if (coin.txout.nValue == nTargetValue)
{
- setCoinsRet.insert(coin.second);
- nValueRet += coin.first;
+ setCoinsRet.insert(coin);
+ nValueRet += coin.txout.nValue;
return true;
}
- else if (n < nTargetValue + MIN_CHANGE)
+ else if (coin.txout.nValue < nTargetValue + MIN_CHANGE)
{
vValue.push_back(coin);
- nTotalLower += n;
+ nTotalLower += coin.txout.nValue;
}
- else if (n < coinLowestLarger.first)
+ else if (!coinLowestLarger || coin.txout.nValue < coinLowestLarger->txout.nValue)
{
coinLowestLarger = coin;
}
@@ -2131,18 +2157,18 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const int nConfMin
{
for (unsigned int i = 0; i < vValue.size(); ++i)
{
- setCoinsRet.insert(vValue[i].second);
- nValueRet += vValue[i].first;
+ setCoinsRet.insert(vValue[i]);
+ nValueRet += vValue[i].txout.nValue;
}
return true;
}
if (nTotalLower < nTargetValue)
{
- if (coinLowestLarger.second.first == NULL)
+ if (!coinLowestLarger)
return false;
- setCoinsRet.insert(coinLowestLarger.second);
- nValueRet += coinLowestLarger.first;
+ setCoinsRet.insert(coinLowestLarger.get());
+ nValueRet += coinLowestLarger->txout.nValue;
return true;
}
@@ -2158,25 +2184,25 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const int nConfMin
// If we have a bigger coin and (either the stochastic approximation didn't find a good solution,
// or the next bigger coin is closer), return the bigger coin
- if (coinLowestLarger.second.first &&
- ((nBest != nTargetValue && nBest < nTargetValue + MIN_CHANGE) || coinLowestLarger.first <= nBest))
+ if (coinLowestLarger &&
+ ((nBest != nTargetValue && nBest < nTargetValue + MIN_CHANGE) || coinLowestLarger->txout.nValue <= nBest))
{
- setCoinsRet.insert(coinLowestLarger.second);
- nValueRet += coinLowestLarger.first;
+ setCoinsRet.insert(coinLowestLarger.get());
+ nValueRet += coinLowestLarger->txout.nValue;
}
else {
for (unsigned int i = 0; i < vValue.size(); i++)
if (vfBest[i])
{
- setCoinsRet.insert(vValue[i].second);
- nValueRet += vValue[i].first;
+ setCoinsRet.insert(vValue[i]);
+ nValueRet += vValue[i].txout.nValue;
}
if (LogAcceptCategory(BCLog::SELECTCOINS)) {
LogPrint(BCLog::SELECTCOINS, "SelectCoins() best subset: ");
for (unsigned int i = 0; i < vValue.size(); i++) {
if (vfBest[i]) {
- LogPrint(BCLog::SELECTCOINS, "%s ", FormatMoney(vValue[i].first));
+ LogPrint(BCLog::SELECTCOINS, "%s ", FormatMoney(vValue[i].txout.nValue));
}
}
LogPrint(BCLog::SELECTCOINS, "total %s\n", FormatMoney(nBest));
@@ -2186,7 +2212,7 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const int nConfMin
return true;
}
-bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet, const CCoinControl* coinControl) const
+bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CCoinControl* coinControl) const
{
std::vector<COutput> vCoins(vAvailableCoins);
@@ -2198,13 +2224,13 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
if (!out.fSpendable)
continue;
nValueRet += out.tx->tx->vout[out.i].nValue;
- setCoinsRet.insert(std::make_pair(out.tx, out.i));
+ setCoinsRet.insert(CInputCoin(out.tx, out.i));
}
return (nValueRet >= nTargetValue);
}
// calculate value from preset inputs and store them
- std::set<std::pair<const CWalletTx*, uint32_t> > setPresetCoins;
+ std::set<CInputCoin> setPresetCoins;
CAmount nValueFromPresetInputs = 0;
std::vector<COutPoint> vPresetInputs;
@@ -2220,7 +2246,7 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
if (pcoin->tx->vout.size() <= outpoint.n)
return false;
nValueFromPresetInputs += pcoin->tx->vout[outpoint.n].nValue;
- setPresetCoins.insert(std::make_pair(pcoin, outpoint.n));
+ setPresetCoins.insert(CInputCoin(pcoin, outpoint.n));
} else
return false; // TODO: Allow non-wallet inputs
}
@@ -2228,7 +2254,7 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
// remove preset inputs from vCoins
for (std::vector<COutput>::iterator it = vCoins.begin(); it != vCoins.end() && coinControl && coinControl->HasSelected();)
{
- if (setPresetCoins.count(std::make_pair(it->tx, it->i)))
+ if (setPresetCoins.count(CInputCoin(it->tx, it->i)))
it = vCoins.erase(it);
else
++it;
@@ -2255,6 +2281,28 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
return res;
}
+bool CWallet::SignTransaction(CMutableTransaction &tx)
+{
+ // sign the new tx
+ CTransaction txNewConst(tx);
+ int nIn = 0;
+ for (auto& input : tx.vin) {
+ std::map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(input.prevout.hash);
+ if(mi == mapWallet.end() || input.prevout.n >= mi->second.tx->vout.size()) {
+ return false;
+ }
+ const CScript& scriptPubKey = mi->second.tx->vout[input.prevout.n].scriptPubKey;
+ const CAmount& amount = mi->second.tx->vout[input.prevout.n].nValue;
+ SignatureData sigdata;
+ if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, amount, SIGHASH_ALL), scriptPubKey, sigdata)) {
+ return false;
+ }
+ UpdateTransaction(tx, nIn, sigdata);
+ nIn++;
+ }
+ return true;
+}
+
bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool overrideEstimatedFeeRate, const CFeeRate& specificFeeRate, int& nChangePosInOut, std::string& strFailReason, bool includeWatching, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, bool keepReserveKey, const CTxDestination& destChange)
{
std::vector<CRecipient> vecSend;
@@ -2372,7 +2420,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
assert(txNew.nLockTime < LOCKTIME_THRESHOLD);
{
- std::set<std::pair<const CWalletTx*,unsigned int> > setCoins;
+ std::set<CInputCoin> setCoins;
LOCK2(cs_main, cs_wallet);
{
std::vector<COutput> vAvailableCoins;
@@ -2515,9 +2563,10 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
std::vector<CTxOut>::iterator position = txNew.vout.begin()+nChangePosInOut;
txNew.vout.insert(position, newTxOut);
}
- }
- else
+ } else {
reservekey.ReturnKey();
+ nChangePosInOut = -1;
+ }
// Fill vin
//
@@ -2531,7 +2580,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
// behavior."
bool rbf = coinControl ? coinControl->signalRbf : fWalletRbf;
for (const auto& coin : setCoins)
- txNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second,CScript(),
+ txNew.vin.push_back(CTxIn(coin.outpoint,CScript(),
std::numeric_limits<unsigned int>::max() - (rbf ? 2 : 1)));
// Fill in dummy signatures for fee calculation.
@@ -2555,7 +2604,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
if (coinControl && coinControl->nConfirmTarget > 0)
currentConfirmationTarget = coinControl->nConfirmTarget;
- CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, mempool);
+ CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, ::mempool, ::feeEstimator);
if (coinControl && nFeeNeeded > 0 && coinControl->nMinimumTotalFee > nFeeNeeded) {
nFeeNeeded = coinControl->nMinimumTotalFee;
}
@@ -2614,10 +2663,10 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
int nIn = 0;
for (const auto& coin : setCoins)
{
- const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey;
+ const CScript& scriptPubKey = coin.txout.scriptPubKey;
SignatureData sigdata;
- if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->tx->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata))
+ if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.txout.nValue, SIGHASH_ALL), scriptPubKey, sigdata))
{
strFailReason = _("Signing transaction failed");
return false;
@@ -2701,13 +2750,13 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CCon
}
void CWallet::ListAccountCreditDebit(const std::string& strAccount, std::list<CAccountingEntry>& entries) {
- CWalletDB walletdb(strWalletFile);
+ CWalletDB walletdb(*dbw);
return walletdb.ListAccountCreditDebit(strAccount, entries);
}
bool CWallet::AddAccountingEntry(const CAccountingEntry& acentry)
{
- CWalletDB walletdb(strWalletFile);
+ CWalletDB walletdb(*dbw);
return AddAccountingEntry(acentry, &walletdb);
}
@@ -2729,19 +2778,14 @@ CAmount CWallet::GetRequiredFee(unsigned int nTxBytes)
return std::max(minTxFee.GetFee(nTxBytes), ::minRelayTxFee.GetFee(nTxBytes));
}
-CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool)
+CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, bool ignoreUserSetFee)
{
// payTxFee is the user-set global for desired feerate
- return GetMinimumFee(nTxBytes, nConfirmTarget, pool, payTxFee.GetFee(nTxBytes));
-}
-
-CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, CAmount targetFee)
-{
- CAmount nFeeNeeded = targetFee;
+ CAmount nFeeNeeded = payTxFee.GetFee(nTxBytes);
// User didn't set: use -txconfirmtarget to estimate...
- if (nFeeNeeded == 0) {
+ if (nFeeNeeded == 0 || ignoreUserSetFee) {
int estimateFoundTarget = nConfirmTarget;
- nFeeNeeded = pool.estimateSmartFee(nConfirmTarget, &estimateFoundTarget).GetFee(nTxBytes);
+ nFeeNeeded = estimator.estimateSmartFee(nConfirmTarget, &estimateFoundTarget, pool).GetFee(nTxBytes);
// ... unless we don't have enough mempool data for estimatefee, then use fallbackFee
if (nFeeNeeded == 0)
nFeeNeeded = fallbackFee.GetFee(nTxBytes);
@@ -2759,13 +2803,11 @@ CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarge
DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
{
- if (!fFileBacked)
- return DB_LOAD_OK;
fFirstRunRet = false;
- DBErrors nLoadWalletRet = CWalletDB(strWalletFile,"cr+").LoadWallet(this);
+ DBErrors nLoadWalletRet = CWalletDB(*dbw,"cr+").LoadWallet(this);
if (nLoadWalletRet == DB_NEED_REWRITE)
{
- if (CDB::Rewrite(strWalletFile, "\x04pool"))
+ if (dbw->Rewrite("\x04pool"))
{
LOCK(cs_wallet);
setKeyPool.clear();
@@ -2786,17 +2828,15 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut)
{
- if (!fFileBacked)
- return DB_LOAD_OK;
AssertLockHeld(cs_wallet); // mapWallet
vchDefaultKey = CPubKey();
- DBErrors nZapSelectTxRet = CWalletDB(strWalletFile,"cr+").ZapSelectTx(vHashIn, vHashOut);
+ DBErrors nZapSelectTxRet = CWalletDB(*dbw,"cr+").ZapSelectTx(vHashIn, vHashOut);
for (uint256 hash : vHashOut)
mapWallet.erase(hash);
if (nZapSelectTxRet == DB_NEED_REWRITE)
{
- if (CDB::Rewrite(strWalletFile, "\x04pool"))
+ if (dbw->Rewrite("\x04pool"))
{
setKeyPool.clear();
// Note: can't top-up keypool here, because wallet is locked.
@@ -2816,13 +2856,11 @@ DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256
DBErrors CWallet::ZapWalletTx(std::vector<CWalletTx>& vWtx)
{
- if (!fFileBacked)
- return DB_LOAD_OK;
vchDefaultKey = CPubKey();
- DBErrors nZapWalletTxRet = CWalletDB(strWalletFile,"cr+").ZapWalletTx(vWtx);
+ DBErrors nZapWalletTxRet = CWalletDB(*dbw,"cr+").ZapWalletTx(vWtx);
if (nZapWalletTxRet == DB_NEED_REWRITE)
{
- if (CDB::Rewrite(strWalletFile, "\x04pool"))
+ if (dbw->Rewrite("\x04pool"))
{
LOCK(cs_wallet);
setKeyPool.clear();
@@ -2852,11 +2890,9 @@ bool CWallet::SetAddressBook(const CTxDestination& address, const std::string& s
}
NotifyAddressBookChanged(this, address, strName, ::IsMine(*this, address) != ISMINE_NO,
strPurpose, (fUpdated ? CT_UPDATED : CT_NEW) );
- if (!fFileBacked)
- return false;
- if (!strPurpose.empty() && !CWalletDB(strWalletFile).WritePurpose(CBitcoinAddress(address).ToString(), strPurpose))
+ if (!strPurpose.empty() && !CWalletDB(*dbw).WritePurpose(CBitcoinAddress(address).ToString(), strPurpose))
return false;
- return CWalletDB(strWalletFile).WriteName(CBitcoinAddress(address).ToString(), strName);
+ return CWalletDB(*dbw).WriteName(CBitcoinAddress(address).ToString(), strName);
}
bool CWallet::DelAddressBook(const CTxDestination& address)
@@ -2864,33 +2900,40 @@ bool CWallet::DelAddressBook(const CTxDestination& address)
{
LOCK(cs_wallet); // mapAddressBook
- if(fFileBacked)
+ // Delete destdata tuples associated with address
+ std::string strAddress = CBitcoinAddress(address).ToString();
+ BOOST_FOREACH(const PAIRTYPE(std::string, std::string) &item, mapAddressBook[address].destdata)
{
- // Delete destdata tuples associated with address
- std::string strAddress = CBitcoinAddress(address).ToString();
- BOOST_FOREACH(const PAIRTYPE(std::string, std::string) &item, mapAddressBook[address].destdata)
- {
- CWalletDB(strWalletFile).EraseDestData(strAddress, item.first);
- }
+ CWalletDB(*dbw).EraseDestData(strAddress, item.first);
}
mapAddressBook.erase(address);
}
NotifyAddressBookChanged(this, address, "", ::IsMine(*this, address) != ISMINE_NO, "", CT_DELETED);
- if (!fFileBacked)
- return false;
- CWalletDB(strWalletFile).ErasePurpose(CBitcoinAddress(address).ToString());
- return CWalletDB(strWalletFile).EraseName(CBitcoinAddress(address).ToString());
+ CWalletDB(*dbw).ErasePurpose(CBitcoinAddress(address).ToString());
+ return CWalletDB(*dbw).EraseName(CBitcoinAddress(address).ToString());
}
-bool CWallet::SetDefaultKey(const CPubKey &vchPubKey)
+const std::string& CWallet::GetAccountName(const CScript& scriptPubKey) const
{
- if (fFileBacked)
- {
- if (!CWalletDB(strWalletFile).WriteDefaultKey(vchPubKey))
- return false;
+ CTxDestination address;
+ if (ExtractDestination(scriptPubKey, address) && !scriptPubKey.IsUnspendable()) {
+ auto mi = mapAddressBook.find(address);
+ if (mi != mapAddressBook.end()) {
+ return mi->second.name;
+ }
}
+ // A scriptPubKey that doesn't have an entry in the address book is
+ // associated with the default account ("").
+ const static std::string DEFAULT_ACCOUNT_NAME;
+ return DEFAULT_ACCOUNT_NAME;
+}
+
+bool CWallet::SetDefaultKey(const CPubKey &vchPubKey)
+{
+ if (!CWalletDB(*dbw).WriteDefaultKey(vchPubKey))
+ return false;
vchDefaultKey = vchPubKey;
return true;
}
@@ -2903,7 +2946,7 @@ bool CWallet::NewKeyPool()
{
{
LOCK(cs_wallet);
- CWalletDB walletdb(strWalletFile);
+ CWalletDB walletdb(*dbw);
BOOST_FOREACH(int64_t nIndex, setKeyPool)
walletdb.ErasePool(nIndex);
setKeyPool.clear();
@@ -2924,7 +2967,7 @@ size_t CWallet::KeypoolCountExternalKeys()
if (!IsHDEnabled() || !CanSupportFeature(FEATURE_HD_SPLIT))
return setKeyPool.size();
- CWalletDB walletdb(strWalletFile);
+ CWalletDB walletdb(*dbw);
// count amount of external keys
size_t amountE = 0;
@@ -2967,7 +3010,7 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize)
missingInternal = 0;
}
bool internal = false;
- CWalletDB walletdb(strWalletFile);
+ CWalletDB walletdb(*dbw);
for (int64_t i = missingInternal + missingExternal; i--;)
{
int64_t nEnd = 1;
@@ -2998,7 +3041,7 @@ void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool int
if(setKeyPool.empty())
return;
- CWalletDB walletdb(strWalletFile);
+ CWalletDB walletdb(*dbw);
// try to find a key that matches the internal/external filter
for(const int64_t& id : setKeyPool)
@@ -3024,11 +3067,8 @@ void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool int
void CWallet::KeepKey(int64_t nIndex)
{
// Remove from key pool
- if (fFileBacked)
- {
- CWalletDB walletdb(strWalletFile);
- walletdb.ErasePool(nIndex);
- }
+ CWalletDB walletdb(*dbw);
+ walletdb.ErasePool(nIndex);
LogPrintf("keypool keep %d\n", nIndex);
}
@@ -3070,7 +3110,7 @@ int64_t CWallet::GetOldestKeyPoolTime()
return GetTime();
CKeyPool keypool;
- CWalletDB walletdb(strWalletFile);
+ CWalletDB walletdb(*dbw);
if (IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT))
{
@@ -3236,37 +3276,6 @@ std::set< std::set<CTxDestination> > CWallet::GetAddressGroupings()
return ret;
}
-CAmount CWallet::GetAccountBalance(const std::string& strAccount, int nMinDepth, const isminefilter& filter)
-{
- CWalletDB walletdb(strWalletFile);
- return GetAccountBalance(walletdb, strAccount, nMinDepth, filter);
-}
-
-CAmount CWallet::GetAccountBalance(CWalletDB& walletdb, const std::string& strAccount, int nMinDepth, const isminefilter& filter)
-{
- CAmount nBalance = 0;
-
- // Tally wallet transactions
- for (std::map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
- {
- const CWalletTx& wtx = (*it).second;
- if (!CheckFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 || wtx.GetDepthInMainChain() < 0)
- continue;
-
- CAmount nReceived, nSent, nFee;
- wtx.GetAccountAmounts(strAccount, nReceived, nSent, nFee, filter);
-
- if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth)
- nBalance += nReceived;
- nBalance -= nSent + nFee;
- }
-
- // Tally internal accounting entries
- nBalance += walletdb.GetAccountCreditDebit(strAccount);
-
- return nBalance;
-}
-
std::set<CTxDestination> CWallet::GetAccountAddresses(const std::string& strAccount) const
{
LOCK(cs_wallet);
@@ -3318,7 +3327,7 @@ void CWallet::GetAllReserveKeys(std::set<CKeyID>& setAddress) const
{
setAddress.clear();
- CWalletDB walletdb(strWalletFile);
+ CWalletDB walletdb(*dbw);
LOCK2(cs_main, cs_wallet);
BOOST_FOREACH(const int64_t& id, setKeyPool)
@@ -3334,20 +3343,9 @@ void CWallet::GetAllReserveKeys(std::set<CKeyID>& setAddress) const
}
}
-void CWallet::UpdatedTransaction(const uint256 &hashTx)
+void CWallet::GetScriptForMining(std::shared_ptr<CReserveScript> &script)
{
- {
- LOCK(cs_wallet);
- // Only notify UI if this transaction is in this wallet
- std::map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(hashTx);
- if (mi != mapWallet.end())
- NotifyTransactionChanged(this, hashTx, CT_UPDATED);
- }
-}
-
-void CWallet::GetScriptForMining(boost::shared_ptr<CReserveScript> &script)
-{
- boost::shared_ptr<CReserveKey> rKey(new CReserveKey(this));
+ std::shared_ptr<CReserveKey> rKey = std::make_shared<CReserveKey>(this);
CPubKey pubkey;
if (!rKey->GetReservedKey(pubkey))
return;
@@ -3551,18 +3549,14 @@ bool CWallet::AddDestData(const CTxDestination &dest, const std::string &key, co
return false;
mapAddressBook[dest].destdata.insert(std::make_pair(key, value));
- if (!fFileBacked)
- return true;
- return CWalletDB(strWalletFile).WriteDestData(CBitcoinAddress(dest).ToString(), key, value);
+ return CWalletDB(*dbw).WriteDestData(CBitcoinAddress(dest).ToString(), key, value);
}
bool CWallet::EraseDestData(const CTxDestination &dest, const std::string &key)
{
if (!mapAddressBook[dest].destdata.erase(key))
return false;
- if (!fFileBacked)
- return true;
- return CWalletDB(strWalletFile).EraseDestData(CBitcoinAddress(dest).ToString(), key);
+ return CWalletDB(*dbw).EraseDestData(CBitcoinAddress(dest).ToString(), key);
}
bool CWallet::LoadDestData(const CTxDestination &dest, const std::string &key, const std::string &value)
@@ -3632,7 +3626,8 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
if (GetBoolArg("-zapwallettxes", false)) {
uiInterface.InitMessage(_("Zapping all transactions from wallet..."));
- CWallet *tempWallet = new CWallet(walletFile);
+ std::unique_ptr<CWalletDBWrapper> dbw(new CWalletDBWrapper(&bitdb, walletFile));
+ CWallet *tempWallet = new CWallet(std::move(dbw));
DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx);
if (nZapWalletRet != DB_LOAD_OK) {
InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile));
@@ -3647,7 +3642,8 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
int64_t nStart = GetTimeMillis();
bool fFirstRun = true;
- CWallet *walletInstance = new CWallet(walletFile);
+ std::unique_ptr<CWalletDBWrapper> dbw(new CWalletDBWrapper(&bitdb, walletFile));
+ CWallet *walletInstance = new CWallet(std::move(dbw));
DBErrors nLoadWalletRet = walletInstance->LoadWallet(fFirstRun);
if (nLoadWalletRet != DB_LOAD_OK)
{
@@ -3738,7 +3734,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
CBlockIndex *pindexRescan = chainActive.Genesis();
if (!GetBoolArg("-rescan", false))
{
- CWalletDB walletdb(walletFile);
+ CWalletDB walletdb(*walletInstance->dbw);
CBlockLocator locator;
if (walletdb.ReadBestBlock(locator))
pindexRescan = FindForkInGlobalIndex(chainActive, locator);
@@ -3771,7 +3767,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
// Restore wallet transaction metadata after -zapwallettxes=1
if (GetBoolArg("-zapwallettxes", false) && GetArg("-zapwallettxes", "1") != "2")
{
- CWalletDB walletdb(walletFile);
+ CWalletDB walletdb(*walletInstance->dbw);
BOOST_FOREACH(const CWalletTx& wtxOld, vWtx)
{
@@ -3815,7 +3811,7 @@ bool CWallet::InitLoadWallet()
std::string walletFile = GetArg("-wallet", DEFAULT_WALLET_DAT);
- if (walletFile.find_first_of("/\\") != std::string::npos) {
+ if (boost::filesystem::path(walletFile).filename() != walletFile) {
return InitError(_("-wallet parameter must only specify a filename (not a path)"));
} else if (SanitizeString(walletFile, SAFE_CHARS_FILENAME) != walletFile) {
return InitError(_("Invalid characters in -wallet filename"));
@@ -3931,38 +3927,7 @@ bool CWallet::ParameterInteraction()
bool CWallet::BackupWallet(const std::string& strDest)
{
- if (!fFileBacked)
- return false;
- while (true)
- {
- {
- LOCK(bitdb.cs_db);
- if (!bitdb.mapFileUseCount.count(strWalletFile) || bitdb.mapFileUseCount[strWalletFile] == 0)
- {
- // Flush log data to the dat file
- bitdb.CloseDb(strWalletFile);
- bitdb.CheckpointLSN(strWalletFile);
- bitdb.mapFileUseCount.erase(strWalletFile);
-
- // Copy wallet file
- boost::filesystem::path pathSrc = GetDataDir() / strWalletFile;
- boost::filesystem::path pathDest(strDest);
- if (boost::filesystem::is_directory(pathDest))
- pathDest /= strWalletFile;
-
- try {
- boost::filesystem::copy_file(pathSrc, pathDest, boost::filesystem::copy_option::overwrite_if_exists);
- LogPrintf("copied %s to %s\n", strWalletFile, pathDest.string());
- return true;
- } catch (const boost::filesystem::filesystem_error& e) {
- LogPrintf("error copying %s to %s - %s\n", strWalletFile, pathDest.string(), e.what());
- return false;
- }
- }
- }
- MilliSleep(100);
- }
- return false;
+ return dbw->Backup(strDest);
}
CKeyPool::CKeyPool()
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index ccede60097..8015cc8492 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -28,8 +28,6 @@
#include <utility>
#include <vector>
-#include <boost/shared_ptr.hpp>
-
extern CWallet* pwalletMain;
/**
@@ -75,6 +73,7 @@ class CReserveKey;
class CScript;
class CScheduler;
class CTxMemPool;
+class CBlockPolicyEstimator;
class CWalletTx;
/** (client) version numbers for particular wallet features */
@@ -452,9 +451,6 @@ public:
void GetAmounts(std::list<COutputEntry>& listReceived,
std::list<COutputEntry>& listSent, CAmount& nFee, std::string& strSentAccount, const isminefilter& filter) const;
- void GetAccountAmounts(const std::string& strAccount, CAmount& nReceived,
- CAmount& nSent, CAmount& nFee, const isminefilter& filter) const;
-
bool IsFromMe(const isminefilter& filter) const
{
return (GetDebit(filter) > 0);
@@ -475,7 +471,34 @@ public:
};
+class CInputCoin {
+public:
+ CInputCoin(const CWalletTx* walletTx, unsigned int i)
+ {
+ if (!walletTx)
+ throw std::invalid_argument("walletTx should not be null");
+ if (i >= walletTx->tx->vout.size())
+ throw std::out_of_range("The output index is out of range");
+
+ outpoint = COutPoint(walletTx->GetHash(), i);
+ txout = walletTx->tx->vout[i];
+ }
+
+ COutPoint outpoint;
+ CTxOut txout;
+
+ bool operator<(const CInputCoin& rhs) const {
+ return outpoint < rhs.outpoint;
+ }
+
+ bool operator!=(const CInputCoin& rhs) const {
+ return outpoint != rhs.outpoint;
+ }
+ bool operator==(const CInputCoin& rhs) const {
+ return outpoint == rhs.outpoint;
+ }
+};
class COutput
{
@@ -626,13 +649,15 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
{
private:
static std::atomic<bool> fFlushScheduled;
+ std::atomic<bool> fAbortRescan;
+ std::atomic<bool> fScanningWallet;
/**
* Select a set of coins such that nValueRet >= nTargetValue and at least
* all coins from coinControl are selected; Never select unconfirmed coins
* if they are not ours
*/
- bool SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet, const CCoinControl *coinControl = NULL) const;
+ bool SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CCoinControl *coinControl = NULL) const;
CWalletDB *pwalletdbEncryption;
@@ -661,14 +686,16 @@ private:
void SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator>);
+ /* Used by TransactionAddedToMemorypool/BlockConnected/Disconnected.
+ * Should be called with pindexBlock and posInBlock if this is for a transaction that is included in a block. */
+ void SyncTransaction(const CTransactionRef& tx, const CBlockIndex *pindex = NULL, int posInBlock = 0);
+
/* the HD chain data model (external chain counters) */
CHDChain hdChain;
/* HD derive new child key (on internal or external chain) */
void DeriveNewChildKey(CKeyMetadata& metadata, CKey& secret, bool internal = false);
- bool fFileBacked;
-
std::set<int64_t> setKeyPool;
int64_t nTimeFirstKey;
@@ -684,17 +711,33 @@ private:
*/
bool AddWatchOnly(const CScript& dest) override;
+ std::unique_ptr<CWalletDBWrapper> dbw;
+
public:
/*
* Main wallet lock.
- * This lock protects all the fields added by CWallet
- * except for:
- * fFileBacked (immutable after instantiation)
- * strWalletFile (immutable after instantiation)
+ * This lock protects all the fields added by CWallet.
*/
mutable CCriticalSection cs_wallet;
- const std::string strWalletFile;
+ /** Get database handle used by this wallet. Ideally this function would
+ * not be necessary.
+ */
+ CWalletDBWrapper& GetDBHandle()
+ {
+ return *dbw;
+ }
+
+ /** Get a name for this wallet for logging/debugging purposes.
+ */
+ std::string GetName() const
+ {
+ if (dbw) {
+ return dbw->GetName();
+ } else {
+ return "dummy";
+ }
+ }
void LoadKeyPool(int nIndex, const CKeyPool &keypool)
{
@@ -716,15 +759,16 @@ public:
MasterKeyMap mapMasterKeys;
unsigned int nMasterKeyMaxID;
- CWallet()
+ // Create wallet with dummy database handle
+ CWallet(): dbw(new CWalletDBWrapper())
{
SetNull();
}
- CWallet(const std::string& strWalletFileIn) : strWalletFile(strWalletFileIn)
+ // Create wallet with passed-in database handle
+ CWallet(std::unique_ptr<CWalletDBWrapper> dbw_in) : dbw(std::move(dbw_in))
{
SetNull();
- fFileBacked = true;
}
~CWallet()
@@ -737,7 +781,6 @@ public:
{
nWalletVersion = FEATURE_BASE;
nWalletMaxVersion = FEATURE_BASE;
- fFileBacked = false;
nMasterKeyMaxID = 0;
pwalletdbEncryption = NULL;
nOrderPosNext = 0;
@@ -746,6 +789,8 @@ public:
nTimeFirstKey = 0;
fBroadcastTransactions = false;
nRelockTime = 0;
+ fAbortRescan = false;
+ fScanningWallet = false;
}
std::map<uint256, CWalletTx> mapWallet;
@@ -780,7 +825,7 @@ public:
* completion the coin set and corresponding actual target value is
* assembled
*/
- bool SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, uint64_t nMaxAncestors, std::vector<COutput> vCoins, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet) const;
+ bool SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, uint64_t nMaxAncestors, std::vector<COutput> vCoins, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet) const;
bool IsSpent(const uint256& hash, unsigned int n) const;
@@ -790,6 +835,13 @@ public:
void UnlockAllCoins();
void ListLockedCoins(std::vector<COutPoint>& vOutpts);
+ /*
+ * Rescan abort properties
+ */
+ void AbortRescan() { fAbortRescan = true; }
+ bool IsAbortingRescan() { return fAbortRescan; }
+ bool IsScanning() { return fScanningWallet; }
+
/**
* keystore implementation
* Generate a new key
@@ -849,8 +901,10 @@ public:
void MarkDirty();
bool AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose=true);
bool LoadToWallet(const CWalletTx& wtxIn);
- void SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, int posInBlock) override;
- bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate);
+ void TransactionAddedToMempool(const CTransactionRef& tx) override;
+ void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex *pindex, const std::vector<CTransactionRef>& vtxConflicted) override;
+ void BlockDisconnected(const std::shared_ptr<const CBlock>& pblock) override;
+ bool AddToWalletIfInvolvingMe(const CTransactionRef& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate);
CBlockIndex* ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false);
void ReacceptWalletTransactions();
void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) override;
@@ -861,12 +915,14 @@ public:
CAmount GetWatchOnlyBalance() const;
CAmount GetUnconfirmedWatchOnlyBalance() const;
CAmount GetImmatureWatchOnlyBalance() const;
+ CAmount GetLegacyBalance(const isminefilter& filter, int minDepth, const std::string* account) const;
/**
* Insert additional inputs into the transaction by
* calling CreateTransaction();
*/
bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool overrideEstimatedFeeRate, const CFeeRate& specificFeeRate, int& nChangePosInOut, std::string& strFailReason, bool includeWatching, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, bool keepReserveKey = true, const CTxDestination& destChange = CNoDestination());
+ bool SignTransaction(CMutableTransaction& tx);
/**
* Create a new transaction paying the recipients with a set of coins
@@ -881,7 +937,7 @@ public:
bool AddAccountingEntry(const CAccountingEntry&);
bool AddAccountingEntry(const CAccountingEntry&, CWalletDB *pwalletdb);
template <typename ContainerType>
- bool DummySignTx(CMutableTransaction &txNew, const ContainerType &coins);
+ bool DummySignTx(CMutableTransaction &txNew, const ContainerType &coins) const;
static CFeeRate minTxFee;
static CFeeRate fallbackFee;
@@ -889,12 +945,7 @@ public:
* Estimate the minimum fee considering user set parameters
* and the required fee
*/
- static CAmount GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool);
- /**
- * Estimate the minimum fee considering required fee and targetFee or if 0
- * then fee estimation for nConfirmTarget
- */
- static CAmount GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, CAmount targetFee);
+ static CAmount GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, bool ignoreUserSetFee = false);
/**
* Return the minimum required fee taking into account the
* floating relay fee and user set minimum transaction fee
@@ -914,8 +965,6 @@ public:
std::set< std::set<CTxDestination> > GetAddressGroupings();
std::map<CTxDestination, CAmount> GetAddressBalances();
- CAmount GetAccountBalance(const std::string& strAccount, int nMinDepth, const isminefilter& filter);
- CAmount GetAccountBalance(CWalletDB& walletdb, const std::string& strAccount, int nMinDepth, const isminefilter& filter);
std::set<CTxDestination> GetAccountAddresses(const std::string& strAccount) const;
isminetype IsMine(const CTxIn& txin) const;
@@ -946,7 +995,7 @@ public:
bool DelAddressBook(const CTxDestination& address);
- void UpdatedTransaction(const uint256 &hashTx) override;
+ const std::string& GetAccountName(const CScript& scriptPubKey) const;
void Inventory(const uint256 &hash) override
{
@@ -958,12 +1007,7 @@ public:
}
}
- void GetScriptForMining(boost::shared_ptr<CReserveScript> &script) override;
- void ResetRequestCount(const uint256 &hash) override
- {
- LOCK(cs_wallet);
- mapRequestCount[hash] = 0;
- };
+ void GetScriptForMining(std::shared_ptr<CReserveScript> &script) override;
unsigned int GetKeyPoolSize()
{
@@ -1125,13 +1169,13 @@ public:
// ContainerType is meant to hold pair<CWalletTx *, int>, and be iterable
// so that each entry corresponds to each vIn, in order.
template <typename ContainerType>
-bool CWallet::DummySignTx(CMutableTransaction &txNew, const ContainerType &coins)
+bool CWallet::DummySignTx(CMutableTransaction &txNew, const ContainerType &coins) const
{
// Fill in dummy signatures for fee calculation.
int nIn = 0;
for (const auto& coin : coins)
{
- const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey;
+ const CScript& scriptPubKey = coin.txout.scriptPubKey;
SignatureData sigdata;
if (!ProduceSignature(DummySignatureCreator(this), scriptPubKey, sigdata))
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index 73d79eb452..a90fa6dbbd 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -7,6 +7,7 @@
#include "base58.h"
#include "consensus/validation.h"
+#include "fs.h"
#include "validation.h" // For CheckTransaction
#include "protocol.h"
#include "serialize.h"
@@ -18,7 +19,6 @@
#include <atomic>
#include <boost/version.hpp>
-#include <boost/filesystem.hpp>
#include <boost/foreach.hpp>
#include <boost/thread.hpp>
@@ -33,7 +33,7 @@ static std::atomic<unsigned int> nWalletDBUpdateCounter;
bool CWalletDB::WriteName(const std::string& strAddress, const std::string& strName)
{
nWalletDBUpdateCounter++;
- return Write(make_pair(std::string("name"), strAddress), strName);
+ return batch.Write(std::make_pair(std::string("name"), strAddress), strName);
}
bool CWalletDB::EraseName(const std::string& strAddress)
@@ -41,38 +41,38 @@ bool CWalletDB::EraseName(const std::string& strAddress)
// This should only be used for sending addresses, never for receiving addresses,
// receiving addresses must always have an address book entry if they're not change return.
nWalletDBUpdateCounter++;
- return Erase(make_pair(std::string("name"), strAddress));
+ return batch.Erase(std::make_pair(std::string("name"), strAddress));
}
bool CWalletDB::WritePurpose(const std::string& strAddress, const std::string& strPurpose)
{
nWalletDBUpdateCounter++;
- return Write(make_pair(std::string("purpose"), strAddress), strPurpose);
+ return batch.Write(std::make_pair(std::string("purpose"), strAddress), strPurpose);
}
bool CWalletDB::ErasePurpose(const std::string& strPurpose)
{
nWalletDBUpdateCounter++;
- return Erase(make_pair(std::string("purpose"), strPurpose));
+ return batch.Erase(std::make_pair(std::string("purpose"), strPurpose));
}
bool CWalletDB::WriteTx(const CWalletTx& wtx)
{
nWalletDBUpdateCounter++;
- return Write(std::make_pair(std::string("tx"), wtx.GetHash()), wtx);
+ return batch.Write(std::make_pair(std::string("tx"), wtx.GetHash()), wtx);
}
bool CWalletDB::EraseTx(uint256 hash)
{
nWalletDBUpdateCounter++;
- return Erase(std::make_pair(std::string("tx"), hash));
+ return batch.Erase(std::make_pair(std::string("tx"), hash));
}
bool CWalletDB::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CKeyMetadata& keyMeta)
{
nWalletDBUpdateCounter++;
- if (!Write(std::make_pair(std::string("keymeta"), vchPubKey),
+ if (!batch.Write(std::make_pair(std::string("keymeta"), vchPubKey),
keyMeta, false))
return false;
@@ -82,7 +82,7 @@ bool CWalletDB::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, c
vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end());
vchKey.insert(vchKey.end(), vchPrivKey.begin(), vchPrivKey.end());
- return Write(std::make_pair(std::string("key"), vchPubKey), std::make_pair(vchPrivKey, Hash(vchKey.begin(), vchKey.end())), false);
+ return batch.Write(std::make_pair(std::string("key"), vchPubKey), std::make_pair(vchPrivKey, Hash(vchKey.begin(), vchKey.end())), false);
}
bool CWalletDB::WriteCryptedKey(const CPubKey& vchPubKey,
@@ -92,16 +92,16 @@ bool CWalletDB::WriteCryptedKey(const CPubKey& vchPubKey,
const bool fEraseUnencryptedKey = true;
nWalletDBUpdateCounter++;
- if (!Write(std::make_pair(std::string("keymeta"), vchPubKey),
+ if (!batch.Write(std::make_pair(std::string("keymeta"), vchPubKey),
keyMeta))
return false;
- if (!Write(std::make_pair(std::string("ckey"), vchPubKey), vchCryptedSecret, false))
+ if (!batch.Write(std::make_pair(std::string("ckey"), vchPubKey), vchCryptedSecret, false))
return false;
if (fEraseUnencryptedKey)
{
- Erase(std::make_pair(std::string("key"), vchPubKey));
- Erase(std::make_pair(std::string("wkey"), vchPubKey));
+ batch.Erase(std::make_pair(std::string("key"), vchPubKey));
+ batch.Erase(std::make_pair(std::string("wkey"), vchPubKey));
}
return true;
}
@@ -109,92 +109,92 @@ bool CWalletDB::WriteCryptedKey(const CPubKey& vchPubKey,
bool CWalletDB::WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey)
{
nWalletDBUpdateCounter++;
- return Write(std::make_pair(std::string("mkey"), nID), kMasterKey, true);
+ return batch.Write(std::make_pair(std::string("mkey"), nID), kMasterKey, true);
}
bool CWalletDB::WriteCScript(const uint160& hash, const CScript& redeemScript)
{
nWalletDBUpdateCounter++;
- return Write(std::make_pair(std::string("cscript"), hash), *(const CScriptBase*)(&redeemScript), false);
+ return batch.Write(std::make_pair(std::string("cscript"), hash), *(const CScriptBase*)(&redeemScript), false);
}
bool CWalletDB::WriteWatchOnly(const CScript &dest, const CKeyMetadata& keyMeta)
{
nWalletDBUpdateCounter++;
- if (!Write(std::make_pair(std::string("watchmeta"), *(const CScriptBase*)(&dest)), keyMeta))
+ if (!batch.Write(std::make_pair(std::string("watchmeta"), *(const CScriptBase*)(&dest)), keyMeta))
return false;
- return Write(std::make_pair(std::string("watchs"), *(const CScriptBase*)(&dest)), '1');
+ return batch.Write(std::make_pair(std::string("watchs"), *(const CScriptBase*)(&dest)), '1');
}
bool CWalletDB::EraseWatchOnly(const CScript &dest)
{
nWalletDBUpdateCounter++;
- if (!Erase(std::make_pair(std::string("watchmeta"), *(const CScriptBase*)(&dest))))
+ if (!batch.Erase(std::make_pair(std::string("watchmeta"), *(const CScriptBase*)(&dest))))
return false;
- return Erase(std::make_pair(std::string("watchs"), *(const CScriptBase*)(&dest)));
+ return batch.Erase(std::make_pair(std::string("watchs"), *(const CScriptBase*)(&dest)));
}
bool CWalletDB::WriteBestBlock(const CBlockLocator& locator)
{
nWalletDBUpdateCounter++;
- Write(std::string("bestblock"), CBlockLocator()); // Write empty block locator so versions that require a merkle branch automatically rescan
- return Write(std::string("bestblock_nomerkle"), locator);
+ batch.Write(std::string("bestblock"), CBlockLocator()); // Write empty block locator so versions that require a merkle branch automatically rescan
+ return batch.Write(std::string("bestblock_nomerkle"), locator);
}
bool CWalletDB::ReadBestBlock(CBlockLocator& locator)
{
- if (Read(std::string("bestblock"), locator) && !locator.vHave.empty()) return true;
- return Read(std::string("bestblock_nomerkle"), locator);
+ if (batch.Read(std::string("bestblock"), locator) && !locator.vHave.empty()) return true;
+ return batch.Read(std::string("bestblock_nomerkle"), locator);
}
bool CWalletDB::WriteOrderPosNext(int64_t nOrderPosNext)
{
nWalletDBUpdateCounter++;
- return Write(std::string("orderposnext"), nOrderPosNext);
+ return batch.Write(std::string("orderposnext"), nOrderPosNext);
}
bool CWalletDB::WriteDefaultKey(const CPubKey& vchPubKey)
{
nWalletDBUpdateCounter++;
- return Write(std::string("defaultkey"), vchPubKey);
+ return batch.Write(std::string("defaultkey"), vchPubKey);
}
bool CWalletDB::ReadPool(int64_t nPool, CKeyPool& keypool)
{
- return Read(std::make_pair(std::string("pool"), nPool), keypool);
+ return batch.Read(std::make_pair(std::string("pool"), nPool), keypool);
}
bool CWalletDB::WritePool(int64_t nPool, const CKeyPool& keypool)
{
nWalletDBUpdateCounter++;
- return Write(std::make_pair(std::string("pool"), nPool), keypool);
+ return batch.Write(std::make_pair(std::string("pool"), nPool), keypool);
}
bool CWalletDB::ErasePool(int64_t nPool)
{
nWalletDBUpdateCounter++;
- return Erase(std::make_pair(std::string("pool"), nPool));
+ return batch.Erase(std::make_pair(std::string("pool"), nPool));
}
bool CWalletDB::WriteMinVersion(int nVersion)
{
- return Write(std::string("minversion"), nVersion);
+ return batch.Write(std::string("minversion"), nVersion);
}
bool CWalletDB::ReadAccount(const std::string& strAccount, CAccount& account)
{
account.SetNull();
- return Read(make_pair(std::string("acc"), strAccount), account);
+ return batch.Read(std::make_pair(std::string("acc"), strAccount), account);
}
bool CWalletDB::WriteAccount(const std::string& strAccount, const CAccount& account)
{
- return Write(make_pair(std::string("acc"), strAccount), account);
+ return batch.Write(std::make_pair(std::string("acc"), strAccount), account);
}
bool CWalletDB::WriteAccountingEntry(const uint64_t nAccEntryNum, const CAccountingEntry& acentry)
{
- return Write(std::make_pair(std::string("acentry"), std::make_pair(acentry.strAccount, nAccEntryNum)), acentry);
+ return batch.Write(std::make_pair(std::string("acentry"), std::make_pair(acentry.strAccount, nAccEntryNum)), acentry);
}
bool CWalletDB::WriteAccountingEntry_Backend(const CAccountingEntry& acentry)
@@ -218,7 +218,7 @@ void CWalletDB::ListAccountCreditDebit(const std::string& strAccount, std::list<
{
bool fAllAccounts = (strAccount == "*");
- Dbc* pcursor = GetCursor();
+ Dbc* pcursor = batch.GetCursor();
if (!pcursor)
throw std::runtime_error(std::string(__func__) + ": cannot create DB cursor");
bool setRange = true;
@@ -229,7 +229,7 @@ void CWalletDB::ListAccountCreditDebit(const std::string& strAccount, std::list<
if (setRange)
ssKey << std::make_pair(std::string("acentry"), std::make_pair((fAllAccounts ? std::string("") : strAccount), uint64_t(0)));
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
- int ret = ReadAtCursor(pcursor, ssKey, ssValue, setRange);
+ int ret = batch.ReadAtCursor(pcursor, ssKey, ssValue, setRange);
setRange = false;
if (ret == DB_NOTFOUND)
break;
@@ -560,7 +560,7 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
LOCK(pwallet->cs_wallet);
try {
int nMinVersion = 0;
- if (Read((std::string)"minversion", nMinVersion))
+ if (batch.Read((std::string)"minversion", nMinVersion))
{
if (nMinVersion > CLIENT_VERSION)
return DB_TOO_NEW;
@@ -568,7 +568,7 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
}
// Get cursor
- Dbc* pcursor = GetCursor();
+ Dbc* pcursor = batch.GetCursor();
if (!pcursor)
{
LogPrintf("Error getting wallet database cursor\n");
@@ -580,7 +580,7 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
// Read next record
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
- int ret = ReadAtCursor(pcursor, ssKey, ssValue);
+ int ret = batch.ReadAtCursor(pcursor, ssKey, ssValue);
if (ret == DB_NOTFOUND)
break;
else if (ret != 0)
@@ -664,14 +664,14 @@ DBErrors CWalletDB::FindWalletTx(std::vector<uint256>& vTxHash, std::vector<CWal
try {
int nMinVersion = 0;
- if (Read((std::string)"minversion", nMinVersion))
+ if (batch.Read((std::string)"minversion", nMinVersion))
{
if (nMinVersion > CLIENT_VERSION)
return DB_TOO_NEW;
}
// Get cursor
- Dbc* pcursor = GetCursor();
+ Dbc* pcursor = batch.GetCursor();
if (!pcursor)
{
LogPrintf("Error getting wallet database cursor\n");
@@ -683,7 +683,7 @@ DBErrors CWalletDB::FindWalletTx(std::vector<uint256>& vTxHash, std::vector<CWal
// Read next record
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
- int ret = ReadAtCursor(pcursor, ssKey, ssValue);
+ int ret = batch.ReadAtCursor(pcursor, ssKey, ssValue);
if (ret == DB_NOTFOUND)
break;
else if (ret != 0)
@@ -797,9 +797,9 @@ void MaybeCompactWalletDB()
if (nLastFlushed != CWalletDB::GetUpdateCounter() && GetTime() - nLastWalletUpdate >= 2)
{
- const std::string& strFile = pwalletMain->strWalletFile;
- if (CDB::PeriodicFlush(strFile))
+ if (CDB::PeriodicFlush(pwalletMain->GetDBHandle())) {
nLastFlushed = CWalletDB::GetUpdateCounter();
+ }
}
fOneThread = false;
}
@@ -842,12 +842,12 @@ bool CWalletDB::RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, CDa
return true;
}
-bool CWalletDB::VerifyEnvironment(const std::string& walletFile, const boost::filesystem::path& dataDir, std::string& errorStr)
+bool CWalletDB::VerifyEnvironment(const std::string& walletFile, const fs::path& dataDir, std::string& errorStr)
{
return CDB::VerifyEnvironment(walletFile, dataDir, errorStr);
}
-bool CWalletDB::VerifyDatabaseFile(const std::string& walletFile, const boost::filesystem::path& dataDir, std::string& warningStr, std::string& errorStr)
+bool CWalletDB::VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr)
{
return CDB::VerifyDatabaseFile(walletFile, dataDir, errorStr, warningStr, CWalletDB::Recover);
}
@@ -855,20 +855,20 @@ bool CWalletDB::VerifyDatabaseFile(const std::string& walletFile, const boost::f
bool CWalletDB::WriteDestData(const std::string &address, const std::string &key, const std::string &value)
{
nWalletDBUpdateCounter++;
- return Write(std::make_pair(std::string("destdata"), std::make_pair(address, key)), value);
+ return batch.Write(std::make_pair(std::string("destdata"), std::make_pair(address, key)), value);
}
bool CWalletDB::EraseDestData(const std::string &address, const std::string &key)
{
nWalletDBUpdateCounter++;
- return Erase(std::make_pair(std::string("destdata"), std::make_pair(address, key)));
+ return batch.Erase(std::make_pair(std::string("destdata"), std::make_pair(address, key)));
}
bool CWalletDB::WriteHDChain(const CHDChain& chain)
{
nWalletDBUpdateCounter++;
- return Write(std::string("hdchain"), chain);
+ return batch.Write(std::string("hdchain"), chain);
}
void CWalletDB::IncrementUpdateCounter()
@@ -880,3 +880,28 @@ unsigned int CWalletDB::GetUpdateCounter()
{
return nWalletDBUpdateCounter;
}
+
+bool CWalletDB::TxnBegin()
+{
+ return batch.TxnBegin();
+}
+
+bool CWalletDB::TxnCommit()
+{
+ return batch.TxnCommit();
+}
+
+bool CWalletDB::TxnAbort()
+{
+ return batch.TxnAbort();
+}
+
+bool CWalletDB::ReadVersion(int& nVersion)
+{
+ return batch.ReadVersion(nVersion);
+}
+
+bool CWalletDB::WriteVersion(int nVersion)
+{
+ return batch.WriteVersion(nVersion);
+}
diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h
index 271c1d66c9..cd9fe279c5 100644
--- a/src/wallet/walletdb.h
+++ b/src/wallet/walletdb.h
@@ -17,6 +17,21 @@
#include <utility>
#include <vector>
+/**
+ * Overview of wallet database classes:
+ *
+ * - CDBEnv is an environment in which the database exists (has no analog in dbwrapper.h)
+ * - CWalletDBWrapper represents a wallet database (similar to CDBWrapper in dbwrapper.h)
+ * - CDB is a low-level database transaction (similar to CDBBatch in dbwrapper.h)
+ * - CWalletDB is a modifier object for the wallet, and encapsulates a database
+ * transaction as well as methods to act on the database (no analog in
+ * dbwrapper.h)
+ *
+ * The latter two are named confusingly, in contrast to what the names CDB
+ * and CWalletDB suggest they are transient transaction objects and don't
+ * represent the database itself.
+ */
+
static const bool DEFAULT_FLUSHWALLET = true;
class CAccount;
@@ -118,11 +133,16 @@ public:
}
};
-/** Access to the wallet database */
-class CWalletDB : public CDB
+/** Access to the wallet database.
+ * This should really be named CWalletDBBatch, as it represents a single transaction at the
+ * database. It will be committed when the object goes out of scope.
+ * Optionally (on by default) it will flush to disk as well.
+ */
+class CWalletDB
{
public:
- CWalletDB(const std::string& strFilename, const char* pszMode = "r+", bool _fFlushOnClose = true) : CDB(strFilename, pszMode, _fFlushOnClose)
+ CWalletDB(CWalletDBWrapper& dbw, const char* pszMode = "r+", bool _fFlushOnClose = true) :
+ batch(dbw, pszMode, _fFlushOnClose)
{
}
@@ -185,16 +205,29 @@ public:
/* Function to determine if a certain KV/key-type is a key (cryptographical key) type */
static bool IsKeyType(const std::string& strType);
/* verifies the database environment */
- static bool VerifyEnvironment(const std::string& walletFile, const boost::filesystem::path& dataDir, std::string& errorStr);
+ static bool VerifyEnvironment(const std::string& walletFile, const fs::path& dataDir, std::string& errorStr);
/* verifies the database file */
- static bool VerifyDatabaseFile(const std::string& walletFile, const boost::filesystem::path& dataDir, std::string& warningStr, std::string& errorStr);
+ static bool VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr);
//! write the hdchain model (external chain child index counter)
bool WriteHDChain(const CHDChain& chain);
static void IncrementUpdateCounter();
static unsigned int GetUpdateCounter();
+
+ //! Begin a new transaction
+ bool TxnBegin();
+ //! Commit current transaction
+ bool TxnCommit();
+ //! Abort current transaction
+ bool TxnAbort();
+ //! Read wallet version
+ bool ReadVersion(int& nVersion);
+ //! Write wallet version
+ bool WriteVersion(int nVersion);
private:
+ CDB batch;
+
CWalletDB(const CWalletDB&);
void operator=(const CWalletDB&);
};
diff --git a/src/zmq/zmqnotificationinterface.cpp b/src/zmq/zmqnotificationinterface.cpp
index fac2a3c57a..c063898056 100644
--- a/src/zmq/zmqnotificationinterface.cpp
+++ b/src/zmq/zmqnotificationinterface.cpp
@@ -144,8 +144,12 @@ void CZMQNotificationInterface::UpdatedBlockTip(const CBlockIndex *pindexNew, co
}
}
-void CZMQNotificationInterface::SyncTransaction(const CTransaction& tx, const CBlockIndex* pindex, int posInBlock)
+void CZMQNotificationInterface::TransactionAddedToMempool(const CTransactionRef& ptx)
{
+ // Used by BlockConnected and BlockDisconnected as well, because they're
+ // all the same external callback.
+ const CTransaction& tx = *ptx;
+
for (std::list<CZMQAbstractNotifier*>::iterator i = notifiers.begin(); i!=notifiers.end(); )
{
CZMQAbstractNotifier *notifier = *i;
@@ -160,3 +164,19 @@ void CZMQNotificationInterface::SyncTransaction(const CTransaction& tx, const CB
}
}
}
+
+void CZMQNotificationInterface::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexConnected, const std::vector<CTransactionRef>& vtxConflicted)
+{
+ for (const CTransactionRef& ptx : pblock->vtx) {
+ // Do a normal notify for each transaction added in the block
+ TransactionAddedToMempool(ptx);
+ }
+}
+
+void CZMQNotificationInterface::BlockDisconnected(const std::shared_ptr<const CBlock>& pblock)
+{
+ for (const CTransactionRef& ptx : pblock->vtx) {
+ // Do a normal notify for each transaction removed in block disconnection
+ TransactionAddedToMempool(ptx);
+ }
+}
diff --git a/src/zmq/zmqnotificationinterface.h b/src/zmq/zmqnotificationinterface.h
index beabb78da6..eec6f7bc64 100644
--- a/src/zmq/zmqnotificationinterface.h
+++ b/src/zmq/zmqnotificationinterface.h
@@ -8,6 +8,7 @@
#include "validationinterface.h"
#include <string>
#include <map>
+#include <list>
class CBlockIndex;
class CZMQAbstractNotifier;
@@ -24,8 +25,10 @@ protected:
void Shutdown();
// CValidationInterface
- void SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, int posInBlock);
- void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload);
+ void TransactionAddedToMempool(const CTransactionRef& tx) override;
+ void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexConnected, const std::vector<CTransactionRef>& vtxConflicted) override;
+ void BlockDisconnected(const std::shared_ptr<const CBlock>& pblock) override;
+ void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) override;
private:
CZMQNotificationInterface();
diff --git a/src/zmq/zmqpublishnotifier.cpp b/src/zmq/zmqpublishnotifier.cpp
index b2963e9bde..700c39f66e 100644
--- a/src/zmq/zmqpublishnotifier.cpp
+++ b/src/zmq/zmqpublishnotifier.cpp
@@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include "chain.h"
#include "chainparams.h"
#include "streams.h"
#include "zmqpublishnotifier.h"
diff --git a/test/README.md b/test/README.md
index dec8db960d..b40052b898 100644
--- a/test/README.md
+++ b/test/README.md
@@ -25,6 +25,8 @@ The ZMQ functional test requires a python ZMQ library. To install it:
Running tests locally
=====================
+Build for your system first. Be sure to enable wallet, utils and daemon when you configure. Tests will not run otherwise.
+
Functional tests
----------------
diff --git a/test/functional/README.md b/test/functional/README.md
index 651b01f18a..e6c4849702 100644
--- a/test/functional/README.md
+++ b/test/functional/README.md
@@ -59,7 +59,7 @@ thread.)
* RPC calls are available in p2p tests.
* Can be used to write free-form tests, where specific p2p-protocol behavior
-is tested. Examples: ```p2p-accept-block.py```, ```maxblocksinflight.py```.
+is tested. Examples: ```p2p-accept-block.py```, ```p2p-compactblocks.py```.
## Comptool
diff --git a/test/functional/abandonconflict.py b/test/functional/abandonconflict.py
index 887dbebd4f..0439d168de 100755
--- a/test/functional/abandonconflict.py
+++ b/test/functional/abandonconflict.py
@@ -20,12 +20,7 @@ class AbandonConflictTest(BitcoinTestFramework):
super().__init__()
self.num_nodes = 2
self.setup_clean_chain = False
-
- def setup_network(self):
- self.nodes = []
- self.nodes.append(start_node(0, self.options.tmpdir, ["-minrelaytxfee=0.00001"]))
- self.nodes.append(start_node(1, self.options.tmpdir))
- connect_nodes(self.nodes[0], 1)
+ self.extra_args = [["-minrelaytxfee=0.00001"], []]
def run_test(self):
self.nodes[1].generate(100)
diff --git a/test/functional/bip65-cltv-p2p.py b/test/functional/bip65-cltv-p2p.py
index 63d05e8fc9..bb83042f35 100755
--- a/test/functional/bip65-cltv-p2p.py
+++ b/test/functional/bip65-cltv-p2p.py
@@ -39,12 +39,7 @@ class BIP65Test(ComparisonTestFramework):
def __init__(self):
super().__init__()
self.num_nodes = 1
-
- def setup_network(self):
- # Must set the blockversion for this test
- self.nodes = start_nodes(self.num_nodes, self.options.tmpdir,
- extra_args=[['-whitelist=127.0.0.1', '-blockversion=3']],
- binary=[self.options.testbinary])
+ self.extra_args = [['-whitelist=127.0.0.1', '-blockversion=3']]
def run_test(self):
test = TestManager(self, self.options.tmpdir)
diff --git a/test/functional/bip65-cltv.py b/test/functional/bip65-cltv.py
index 7f13bb9952..ddf932c746 100755
--- a/test/functional/bip65-cltv.py
+++ b/test/functional/bip65-cltv.py
@@ -12,15 +12,12 @@ class BIP65Test(BitcoinTestFramework):
super().__init__()
self.num_nodes = 3
self.setup_clean_chain = False
+ self.extra_args = [[], ["-blockversion=3"], ["-blockversion=4"]]
def setup_network(self):
- self.nodes = []
- self.nodes.append(start_node(0, self.options.tmpdir, []))
- self.nodes.append(start_node(1, self.options.tmpdir, ["-blockversion=3"]))
- self.nodes.append(start_node(2, self.options.tmpdir, ["-blockversion=4"]))
+ self.setup_nodes()
connect_nodes(self.nodes[1], 0)
connect_nodes(self.nodes[2], 0)
- self.is_network_split = False
self.sync_all()
def run_test(self):
diff --git a/test/functional/bip68-112-113-p2p.py b/test/functional/bip68-112-113-p2p.py
index 0867f42585..5a322e8c0e 100755
--- a/test/functional/bip68-112-113-p2p.py
+++ b/test/functional/bip68-112-113-p2p.py
@@ -95,12 +95,7 @@ class BIP68_112_113Test(ComparisonTestFramework):
def __init__(self):
super().__init__()
self.num_nodes = 1
-
- def setup_network(self):
- # Must set the blockversion for this test
- self.nodes = start_nodes(self.num_nodes, self.options.tmpdir,
- extra_args=[['-whitelist=127.0.0.1', '-blockversion=4']],
- binary=[self.options.testbinary])
+ self.extra_args = [['-whitelist=127.0.0.1', '-blockversion=4']]
def run_test(self):
test = TestManager(self, self.options.tmpdir)
diff --git a/test/functional/bip68-sequence.py b/test/functional/bip68-sequence.py
index 89d42a710c..db66b7719c 100755
--- a/test/functional/bip68-sequence.py
+++ b/test/functional/bip68-sequence.py
@@ -21,16 +21,11 @@ class BIP68Test(BitcoinTestFramework):
super().__init__()
self.num_nodes = 2
self.setup_clean_chain = False
+ self.extra_args = [[], ["-acceptnonstdtxn=0"]]
- def setup_network(self):
- self.nodes = []
- self.nodes.append(start_node(0, self.options.tmpdir))
- self.nodes.append(start_node(1, self.options.tmpdir, ["-acceptnonstdtxn=0"]))
- self.is_network_split = False
+ def run_test(self):
self.relayfee = self.nodes[0].getnetworkinfo()["relayfee"]
- connect_nodes(self.nodes[0], 1)
- def run_test(self):
# Generate some coins
self.nodes[0].generate(110)
diff --git a/test/functional/bip9-softforks.py b/test/functional/bip9-softforks.py
index a6383c0679..1b2dff63d2 100755
--- a/test/functional/bip9-softforks.py
+++ b/test/functional/bip9-softforks.py
@@ -31,11 +31,7 @@ class BIP9SoftForksTest(ComparisonTestFramework):
def __init__(self):
super().__init__()
self.num_nodes = 1
-
- def setup_network(self):
- self.nodes = start_nodes(self.num_nodes, self.options.tmpdir,
- extra_args=[['-whitelist=127.0.0.1']],
- binary=[self.options.testbinary])
+ self.extra_args = [['-whitelist=127.0.0.1']]
def run_test(self):
self.test = TestManager(self, self.options.tmpdir)
diff --git a/test/functional/bipdersig-p2p.py b/test/functional/bipdersig-p2p.py
index 22bd39fbe5..31c7ebba90 100755
--- a/test/functional/bipdersig-p2p.py
+++ b/test/functional/bipdersig-p2p.py
@@ -47,12 +47,6 @@ class BIP66Test(ComparisonTestFramework):
super().__init__()
self.num_nodes = 1
- def setup_network(self):
- # Must set the blockversion for this test
- self.nodes = start_nodes(self.num_nodes, self.options.tmpdir,
- extra_args=[['-whitelist=127.0.0.1', '-blockversion=2']],
- binary=[self.options.testbinary])
-
def run_test(self):
test = TestManager(self, self.options.tmpdir)
test.add_all_connections(self.nodes)
diff --git a/test/functional/bipdersig.py b/test/functional/bipdersig.py
index 371cc41bb7..41f88fb664 100755
--- a/test/functional/bipdersig.py
+++ b/test/functional/bipdersig.py
@@ -12,15 +12,12 @@ class BIP66Test(BitcoinTestFramework):
super().__init__()
self.num_nodes = 3
self.setup_clean_chain = False
+ self.extra_args = [[], ["-blockversion=2"], ["-blockversion=3"]]
def setup_network(self):
- self.nodes = []
- self.nodes.append(start_node(0, self.options.tmpdir, []))
- self.nodes.append(start_node(1, self.options.tmpdir, ["-blockversion=2"]))
- self.nodes.append(start_node(2, self.options.tmpdir, ["-blockversion=3"]))
+ self.setup_nodes()
connect_nodes(self.nodes[1], 0)
connect_nodes(self.nodes[2], 0)
- self.is_network_split = False
self.sync_all()
def run_test(self):
diff --git a/test/functional/blockchain.py b/test/functional/blockchain.py
index 596aed50ec..00ae3d7572 100755
--- a/test/functional/blockchain.py
+++ b/test/functional/blockchain.py
@@ -6,6 +6,10 @@
Test the following RPCs:
- gettxoutsetinfo
+ - getdifficulty
+ - getbestblockhash
+ - getblockhash
+ - getblockheader
- verifychain
Tests correspond to code in rpc/blockchain.cpp.
@@ -31,15 +35,10 @@ class BlockchainTest(BitcoinTestFramework):
self.setup_clean_chain = False
self.num_nodes = 2
- def setup_network(self, split=False):
- self.nodes = start_nodes(self.num_nodes, self.options.tmpdir)
- connect_nodes_bi(self.nodes, 0, 1)
- self.is_network_split = False
- self.sync_all()
-
def run_test(self):
self._test_gettxoutsetinfo()
self._test_getblockheader()
+ self._test_getdifficulty()
self.nodes[0].verifychain(4, 0)
def _test_gettxoutsetinfo(self):
@@ -57,7 +56,8 @@ class BlockchainTest(BitcoinTestFramework):
def _test_getblockheader(self):
node = self.nodes[0]
- assert_raises_jsonrpc(-5, "Block not found", node.getblockheader, "nonsense")
+ assert_raises_jsonrpc(-5, "Block not found",
+ node.getblockheader, "nonsense")
besthash = node.getbestblockhash()
secondbesthash = node.getblockhash(199)
@@ -79,5 +79,11 @@ class BlockchainTest(BitcoinTestFramework):
assert isinstance(int(header['versionHex'], 16), int)
assert isinstance(header['difficulty'], Decimal)
+ def _test_getdifficulty(self):
+ difficulty = self.nodes[0].getdifficulty()
+ # 1 hash in 2 should be valid, so difficulty should be 1/2**31
+ # binary => decimal => binary math is why we do this check
+ assert abs(difficulty * 2**31 - 1) < 0.0001
+
if __name__ == '__main__':
BlockchainTest().main()
diff --git a/test/functional/bumpfee.py b/test/functional/bumpfee.py
index c51a75cc26..54fd7740c1 100755
--- a/test/functional/bumpfee.py
+++ b/test/functional/bumpfee.py
@@ -47,7 +47,6 @@ class BumpFeeTest(BitcoinTestFramework):
self.nodes[1].walletpassphrase(WALLET_PASSPHRASE, WALLET_PASSPHRASE_TIMEOUT)
connect_nodes_bi(self.nodes, 0, 1)
- self.is_network_split = False
self.sync_all()
def run_test(self):
diff --git a/test/functional/decodescript.py b/test/functional/decodescript.py
index 5555e96c44..21a9f1223f 100755
--- a/test/functional/decodescript.py
+++ b/test/functional/decodescript.py
@@ -16,10 +16,6 @@ class DecodeScriptTest(BitcoinTestFramework):
self.setup_clean_chain = True
self.num_nodes = 1
- def setup_network(self, split=False):
- self.nodes = start_nodes(self.num_nodes, self.options.tmpdir)
- self.is_network_split = False
-
def decodescript_script_sig(self):
signature = '304502207fa7a6d1e0ee81132a269ad84e68d695483745cde8b541e3bf630749894e342a022100c1f7ab20e13e22fb95281a870f3dcf38d782e53023ee313d741ad0cfbc0c509001'
push_signature = '48' + signature
diff --git a/test/functional/disablewallet.py b/test/functional/disablewallet.py
index 2f729e19bf..3f77a1828d 100755
--- a/test/functional/disablewallet.py
+++ b/test/functional/disablewallet.py
@@ -18,11 +18,7 @@ class DisableWalletTest (BitcoinTestFramework):
super().__init__()
self.setup_clean_chain = True
self.num_nodes = 1
-
- def setup_network(self, split=False):
- self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, [['-disablewallet']])
- self.is_network_split = False
- self.sync_all()
+ self.extra_args = [["-disablewallet"]]
def run_test (self):
x = self.nodes[0].validateaddress('3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy')
diff --git a/test/functional/disconnect_ban.py b/test/functional/disconnect_ban.py
new file mode 100755
index 0000000000..50d79db903
--- /dev/null
+++ b/test/functional/disconnect_ban.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python3
+# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Test node disconnect and ban behavior"""
+
+from test_framework.mininode import wait_until
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (assert_equal,
+ assert_raises_jsonrpc,
+ connect_nodes_bi,
+ start_node,
+ stop_node,
+ )
+
+class DisconnectBanTest(BitcoinTestFramework):
+
+ def __init__(self):
+ super().__init__()
+ self.num_nodes = 2
+ self.setup_clean_chain = False
+
+ def run_test(self):
+ self.log.info("Test setban and listbanned RPCs")
+
+ self.log.info("setban: successfully ban single IP address")
+ assert_equal(len(self.nodes[1].getpeerinfo()), 2) # node1 should have 2 connections to node0 at this point
+ self.nodes[1].setban("127.0.0.1", "add")
+ assert wait_until(lambda: len(self.nodes[1].getpeerinfo()) == 0, timeout=10)
+ assert_equal(len(self.nodes[1].getpeerinfo()), 0) # all nodes must be disconnected at this point
+ assert_equal(len(self.nodes[1].listbanned()), 1)
+
+ self.log.info("clearbanned: successfully clear ban list")
+ self.nodes[1].clearbanned()
+ assert_equal(len(self.nodes[1].listbanned()), 0)
+ self.nodes[1].setban("127.0.0.0/24", "add")
+
+ self.log.info("setban: fail to ban an already banned subnet")
+ assert_equal(len(self.nodes[1].listbanned()), 1)
+ assert_raises_jsonrpc(-23, "IP/Subnet already banned", self.nodes[1].setban, "127.0.0.1", "add")
+
+ self.log.info("setban: fail to ban an invalid subnet")
+ assert_raises_jsonrpc(-30, "Error: Invalid IP/Subnet", self.nodes[1].setban, "127.0.0.1/42", "add")
+ assert_equal(len(self.nodes[1].listbanned()), 1) # still only one banned ip because 127.0.0.1 is within the range of 127.0.0.0/24
+
+ self.log.info("setban remove: fail to unban a non-banned subnet")
+ assert_raises_jsonrpc(-30, "Error: Unban failed", self.nodes[1].setban, "127.0.0.1", "remove")
+ assert_equal(len(self.nodes[1].listbanned()), 1)
+
+ self.log.info("setban remove: successfully unban subnet")
+ self.nodes[1].setban("127.0.0.0/24", "remove")
+ assert_equal(len(self.nodes[1].listbanned()), 0)
+ self.nodes[1].clearbanned()
+ assert_equal(len(self.nodes[1].listbanned()), 0)
+
+ self.log.info("setban: test persistence across node restart")
+ self.nodes[1].setban("127.0.0.0/32", "add")
+ self.nodes[1].setban("127.0.0.0/24", "add")
+ self.nodes[1].setban("192.168.0.1", "add", 1) # ban for 1 seconds
+ self.nodes[1].setban("2001:4d48:ac57:400:cacf:e9ff:fe1d:9c63/19", "add", 1000) # ban for 1000 seconds
+ listBeforeShutdown = self.nodes[1].listbanned()
+ assert_equal("192.168.0.1/32", listBeforeShutdown[2]['address'])
+ assert wait_until(lambda: len(self.nodes[1].listbanned()) == 3, timeout=10)
+
+ stop_node(self.nodes[1], 1)
+
+ self.nodes[1] = start_node(1, self.options.tmpdir)
+ listAfterShutdown = self.nodes[1].listbanned()
+ assert_equal("127.0.0.0/24", listAfterShutdown[0]['address'])
+ assert_equal("127.0.0.0/32", listAfterShutdown[1]['address'])
+ assert_equal("/19" in listAfterShutdown[2]['address'], True)
+
+ # Clear ban lists
+ self.nodes[1].clearbanned()
+ connect_nodes_bi(self.nodes, 0, 1)
+
+ self.log.info("Test disconnectnode RPCs")
+
+ self.log.info("disconnectnode: fail to disconnect when calling with address and nodeid")
+ address1 = self.nodes[0].getpeerinfo()[0]['addr']
+ node1 = self.nodes[0].getpeerinfo()[0]['addr']
+ assert_raises_jsonrpc(-32602, "Only one of address and nodeid should be provided.", self.nodes[0].disconnectnode, address=address1, nodeid=node1)
+
+ self.log.info("disconnectnode: fail to disconnect when calling with junk address")
+ assert_raises_jsonrpc(-29, "Node not found in connected nodes", self.nodes[0].disconnectnode, address="221B Baker Street")
+
+ self.log.info("disconnectnode: successfully disconnect node by address")
+ address1 = self.nodes[0].getpeerinfo()[0]['addr']
+ self.nodes[0].disconnectnode(address=address1)
+ assert wait_until(lambda: len(self.nodes[0].getpeerinfo()) == 1, timeout=10)
+ assert not [node for node in self.nodes[0].getpeerinfo() if node['addr'] == address1]
+
+ self.log.info("disconnectnode: successfully reconnect node")
+ connect_nodes_bi(self.nodes, 0, 1) # reconnect the node
+ assert_equal(len(self.nodes[0].getpeerinfo()), 2)
+ assert [node for node in self.nodes[0].getpeerinfo() if node['addr'] == address1]
+
+ self.log.info("disconnectnode: successfully disconnect node by node id")
+ id1 = self.nodes[0].getpeerinfo()[0]['id']
+ self.nodes[0].disconnectnode(nodeid=id1)
+ assert wait_until(lambda: len(self.nodes[0].getpeerinfo()) == 1, timeout=10)
+ assert not [node for node in self.nodes[0].getpeerinfo() if node['id'] == id1]
+
+if __name__ == '__main__':
+ DisconnectBanTest().main()
diff --git a/test/functional/forknotify.py b/test/functional/forknotify.py
index 7a365438cc..9db61c8350 100755
--- a/test/functional/forknotify.py
+++ b/test/functional/forknotify.py
@@ -16,8 +16,6 @@ class ForkNotifyTest(BitcoinTestFramework):
self.num_nodes = 2
self.setup_clean_chain = False
- alert_filename = None # Set by setup_network
-
def setup_network(self):
self.nodes = []
self.alert_filename = os.path.join(self.options.tmpdir, "alert.txt")
@@ -30,7 +28,6 @@ class ForkNotifyTest(BitcoinTestFramework):
["-blockversion=211"]))
connect_nodes(self.nodes[1], 0)
- self.is_network_split = False
self.sync_all()
def run_test(self):
diff --git a/test/functional/fundrawtransaction.py b/test/functional/fundrawtransaction.py
index b86ea2d877..9ddafeb611 100755
--- a/test/functional/fundrawtransaction.py
+++ b/test/functional/fundrawtransaction.py
@@ -23,15 +23,12 @@ class RawTransactionsTest(BitcoinTestFramework):
self.num_nodes = 4
def setup_network(self, split=False):
- self.nodes = start_nodes(self.num_nodes, self.options.tmpdir)
-
- connect_nodes_bi(self.nodes,0,1)
- connect_nodes_bi(self.nodes,1,2)
- connect_nodes_bi(self.nodes,0,2)
- connect_nodes_bi(self.nodes,0,3)
+ self.setup_nodes()
- self.is_network_split=False
- self.sync_all()
+ connect_nodes_bi(self.nodes, 0, 1)
+ connect_nodes_bi(self.nodes, 1, 2)
+ connect_nodes_bi(self.nodes, 0, 2)
+ connect_nodes_bi(self.nodes, 0, 3)
def run_test(self):
min_relay_tx_fee = self.nodes[0].getnetworkinfo()['relayfee']
@@ -53,6 +50,11 @@ class RawTransactionsTest(BitcoinTestFramework):
self.nodes[0].generate(121)
self.sync_all()
+ # ensure that setting changePosition in fundraw with an exact match is handled properly
+ rawmatch = self.nodes[2].createrawtransaction([], {self.nodes[2].getnewaddress():50})
+ rawmatch = self.nodes[2].fundrawtransaction(rawmatch, {"changePosition":1, "subtractFeeFromOutputs":[0]})
+ assert_equal(rawmatch["changepos"], -1)
+
watchonly_address = self.nodes[0].getnewaddress()
watchonly_pubkey = self.nodes[0].validateaddress(watchonly_address)["pubkey"]
watchonly_amount = Decimal(200)
@@ -462,7 +464,6 @@ class RawTransactionsTest(BitcoinTestFramework):
connect_nodes_bi(self.nodes,1,2)
connect_nodes_bi(self.nodes,0,2)
connect_nodes_bi(self.nodes,0,3)
- self.is_network_split=False
self.sync_all()
# drain the keypool
diff --git a/test/functional/getblocktemplate_proposals.py b/test/functional/getblocktemplate_proposals.py
index 67745f77d1..fca99c7df5 100755
--- a/test/functional/getblocktemplate_proposals.py
+++ b/test/functional/getblocktemplate_proposals.py
@@ -73,10 +73,6 @@ class GetBlockTemplateProposalTest(BitcoinTestFramework):
self.num_nodes = 2
self.setup_clean_chain = False
- def setup_network(self):
- self.nodes = self.setup_nodes()
- connect_nodes_bi(self.nodes, 0, 1)
-
def run_test(self):
node = self.nodes[0]
node.generate(1) # Mine a block to leave initial block download
diff --git a/test/functional/getchaintips.py b/test/functional/getchaintips.py
index 14222334a6..15f96c565f 100755
--- a/test/functional/getchaintips.py
+++ b/test/functional/getchaintips.py
@@ -31,7 +31,7 @@ class GetChainTipsTest (BitcoinTestFramework):
self.split_network ()
self.nodes[0].generate(10)
self.nodes[2].generate(20)
- self.sync_all ()
+ self.sync_all([self.nodes[:2], self.nodes[2:]])
tips = self.nodes[1].getchaintips ()
assert_equal (len (tips), 1)
diff --git a/test/functional/httpbasics.py b/test/functional/httpbasics.py
index 8f35f0ab87..4b32e8d9ca 100755
--- a/test/functional/httpbasics.py
+++ b/test/functional/httpbasics.py
@@ -17,7 +17,7 @@ class HTTPBasicsTest (BitcoinTestFramework):
self.setup_clean_chain = False
def setup_network(self):
- self.nodes = self.setup_nodes()
+ self.setup_nodes()
def run_test(self):
diff --git a/test/functional/import-abort-rescan.py b/test/functional/import-abort-rescan.py
new file mode 100755
index 0000000000..ffe45bbb1d
--- /dev/null
+++ b/test/functional/import-abort-rescan.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python3
+# Copyright (c) 2017 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Test wallet import RPCs.
+
+Test rescan behavior of importprivkey when aborted. The test ensures that:
+1. The abortrescan command indeed stops the rescan process.
+2. Subsequent rescan catches the aborted address UTXO
+"""
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (assert_equal, get_rpc_proxy)
+from decimal import Decimal
+import threading # for bg importprivkey
+import time # for sleep
+
+class ImportAbortRescanTest(BitcoinTestFramework):
+ def __init__(self):
+ super().__init__()
+ self.setup_clean_chain = True
+
+ def run_test(self):
+ # Generate for BTC
+ assert_equal(self.nodes[0].getbalance(), 0)
+ assert_equal(self.nodes[1].getbalance(), 0)
+ self.nodes[0].generate(300)
+ assert_equal(self.nodes[1].getbalance(), 0)
+ # Make blocks with spam to cause rescan delay
+ for i in range(5):
+ for j in range(5):
+ self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 0.1)
+ self.nodes[0].generate(10)
+ addr = self.nodes[0].getnewaddress()
+ privkey = self.nodes[0].dumpprivkey(addr)
+ self.nodes[0].sendtoaddress(addr, 0.123)
+ self.nodes[0].generate(10) # mature tx
+ self.sync_all()
+
+ # Import this address in the background ...
+ node1ref = get_rpc_proxy(self.nodes[1].url, 1, timeout=600)
+ importthread = threading.Thread(target=node1ref.importprivkey, args=[privkey])
+ importthread.start()
+ # ... then abort rescan; try a bunch until abortres becomes true,
+ # because we will start checking before above thread starts processing
+ for i in range(2000):
+ time.sleep(0.001)
+ abortres = self.nodes[1].abortrescan()
+ if abortres: break
+ assert abortres # if false, we failed to abort
+ # import should die soon
+ for i in range(10):
+ time.sleep(0.1)
+ deadres = not importthread.isAlive()
+ if deadres: break
+
+ assert deadres # if false, importthread did not die soon enough
+ assert_equal(self.nodes[1].getbalance(), 0.0)
+
+ # Import a different address and let it run
+ self.nodes[1].importprivkey(self.nodes[0].dumpprivkey(self.nodes[0].getnewaddress()))
+ # Expect original privkey to now also be discovered and added to balance
+ assert_equal(self.nodes[1].getbalance(), Decimal("0.123"))
+
+if __name__ == "__main__":
+ ImportAbortRescanTest().main()
diff --git a/test/functional/importmulti.py b/test/functional/importmulti.py
index e049302632..9e3491c428 100755
--- a/test/functional/importmulti.py
+++ b/test/functional/importmulti.py
@@ -12,9 +12,8 @@ class ImportMultiTest (BitcoinTestFramework):
self.num_nodes = 2
self.setup_clean_chain = True
- def setup_network(self, split=False):
- self.nodes = start_nodes(2, self.options.tmpdir)
- self.is_network_split=False
+ def setup_network(self):
+ self.setup_nodes()
def run_test (self):
self.log.info("Mining blocks...")
diff --git a/test/functional/importprunedfunds.py b/test/functional/importprunedfunds.py
index b4c8ee6c70..94753fe431 100755
--- a/test/functional/importprunedfunds.py
+++ b/test/functional/importprunedfunds.py
@@ -14,12 +14,6 @@ class ImportPrunedFundsTest(BitcoinTestFramework):
self.setup_clean_chain = True
self.num_nodes = 2
- def setup_network(self, split=False):
- self.nodes = start_nodes(self.num_nodes, self.options.tmpdir)
- connect_nodes_bi(self.nodes,0,1)
- self.is_network_split=False
- self.sync_all()
-
def run_test(self):
self.log.info("Mining blocks...")
self.nodes[0].generate(101)
diff --git a/test/functional/invalidateblock.py b/test/functional/invalidateblock.py
index 8c80b64003..c499d57b90 100755
--- a/test/functional/invalidateblock.py
+++ b/test/functional/invalidateblock.py
@@ -8,20 +8,15 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
class InvalidateTest(BitcoinTestFramework):
-
-
+
def __init__(self):
super().__init__()
self.setup_clean_chain = True
self.num_nodes = 3
def setup_network(self):
- self.nodes = []
- self.is_network_split = False
- self.nodes.append(start_node(0, self.options.tmpdir))
- self.nodes.append(start_node(1, self.options.tmpdir))
- self.nodes.append(start_node(2, self.options.tmpdir))
-
+ self.setup_nodes()
+
def run_test(self):
self.log.info("Make sure we repopulate setBlockIndexCandidates after InvalidateBlock:")
self.log.info("Mine 4 blocks on Node 0")
diff --git a/test/functional/keypool.py b/test/functional/keypool.py
index cb9ab688d1..c276e64c7c 100755
--- a/test/functional/keypool.py
+++ b/test/functional/keypool.py
@@ -84,8 +84,5 @@ class KeyPoolTest(BitcoinTestFramework):
self.setup_clean_chain = False
self.num_nodes = 1
- def setup_network(self):
- self.nodes = self.setup_nodes()
-
if __name__ == '__main__':
KeyPoolTest().main()
diff --git a/test/functional/listsinceblock.py b/test/functional/listsinceblock.py
index a75e66c8c4..f3d41e573e 100755
--- a/test/functional/listsinceblock.py
+++ b/test/functional/listsinceblock.py
@@ -43,7 +43,6 @@ class ListSinceBlockTest (BitcoinTestFramework):
This test only checks that [tx0] is present.
'''
- assert_equal(self.is_network_split, False)
self.nodes[2].generate(101)
self.sync_all()
@@ -54,7 +53,6 @@ class ListSinceBlockTest (BitcoinTestFramework):
# Split network into two
self.split_network()
- assert_equal(self.is_network_split, True)
# send to nodes[0] from nodes[2]
senttx = self.nodes[2].sendtoaddress(self.nodes[0].getnewaddress(), 1)
@@ -64,7 +62,7 @@ class ListSinceBlockTest (BitcoinTestFramework):
self.nodes[2].generate(7)
self.log.info('lastblockhash=%s' % (lastblockhash))
- self.sync_all()
+ self.sync_all([self.nodes[:2], self.nodes[2:]])
self.join_network()
diff --git a/test/functional/listtransactions.py b/test/functional/listtransactions.py
index 68d14093ce..cba370d8b0 100755
--- a/test/functional/listtransactions.py
+++ b/test/functional/listtransactions.py
@@ -24,7 +24,7 @@ class ListTransactionsTest(BitcoinTestFramework):
def setup_nodes(self):
#This test requires mocktime
enable_mocktime()
- return start_nodes(self.num_nodes, self.options.tmpdir)
+ self.nodes = start_nodes(self.num_nodes, self.options.tmpdir)
def run_test(self):
# Simple send, 0 to 1:
diff --git a/test/functional/maxblocksinflight.py b/test/functional/maxblocksinflight.py
deleted file mode 100755
index 4ef2a35a44..0000000000
--- a/test/functional/maxblocksinflight.py
+++ /dev/null
@@ -1,93 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (c) 2015-2016 The Bitcoin Core developers
-# Distributed under the MIT software license, see the accompanying
-# file COPYING or http://www.opensource.org/licenses/mit-license.php.
-"""Test nodes responses to having many blocks in flight.
-
-In this test we connect to one node over p2p, send it numerous inv's, and
-compare the resulting number of getdata requests to a max allowed value. We
-test for exceeding 128 blocks in flight, which was the limit an 0.9 client will
-reach. [0.10 clients shouldn't request more than 16 from a single peer.]
-"""
-
-from test_framework.mininode import *
-from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import *
-
-MAX_REQUESTS = 128
-
-class TestManager(NodeConnCB):
- def on_getdata(self, conn, message):
- self.log.debug("got getdata %s" % repr(message))
- # Log the requests
- for inv in message.inv:
- if inv.hash not in self.blockReqCounts:
- self.blockReqCounts[inv.hash] = 0
- self.blockReqCounts[inv.hash] += 1
-
- def on_close(self, conn):
- if not self.disconnectOkay:
- raise EarlyDisconnectError(0)
-
- def add_new_connection(self, connection):
- super().add_connection(connection)
- self.blockReqCounts = {}
- self.disconnectOkay = False
-
- def run(self):
- self.connection.rpc.generate(1) # Leave IBD
-
- numBlocksToGenerate = [8, 16, 128, 1024]
- for count in range(len(numBlocksToGenerate)):
- current_invs = []
- for i in range(numBlocksToGenerate[count]):
- current_invs.append(CInv(2, random.randrange(0, 1 << 256)))
- if len(current_invs) >= 50000:
- self.connection.send_message(msg_inv(current_invs))
- current_invs = []
- if len(current_invs) > 0:
- self.connection.send_message(msg_inv(current_invs))
-
- # Wait and see how many blocks were requested
- time.sleep(2)
-
- total_requests = 0
- with mininode_lock:
- for key in self.blockReqCounts:
- total_requests += self.blockReqCounts[key]
- if self.blockReqCounts[key] > 1:
- raise AssertionError("Error, test failed: block %064x requested more than once" % key)
- if total_requests > MAX_REQUESTS:
- raise AssertionError("Error, too many blocks (%d) requested" % total_requests)
- self.log.info("Round %d: success (total requests: %d)" % (count, total_requests))
-
- self.disconnectOkay = True
- self.connection.disconnect_node()
-
-
-class MaxBlocksInFlightTest(BitcoinTestFramework):
- def add_options(self, parser):
- parser.add_option("--testbinary", dest="testbinary",
- default=os.getenv("BITCOIND", "bitcoind"),
- help="Binary to test max block requests behavior")
-
- def __init__(self):
- super().__init__()
- self.setup_clean_chain = True
- self.num_nodes = 1
-
- def setup_network(self):
- self.nodes = start_nodes(self.num_nodes, self.options.tmpdir,
- extra_args=[['-whitelist=127.0.0.1']],
- binary=[self.options.testbinary])
-
- def run_test(self):
- test = TestManager()
- # pass log handler through to the test manager object
- test.log = self.log
- test.add_new_connection(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test))
- NetworkThread().start() # Start up network handling in another thread
- test.run()
-
-if __name__ == '__main__':
- MaxBlocksInFlightTest().main()
diff --git a/test/functional/maxuploadtarget.py b/test/functional/maxuploadtarget.py
index 9b42bf276c..bff1b53234 100755
--- a/test/functional/maxuploadtarget.py
+++ b/test/functional/maxuploadtarget.py
@@ -10,63 +10,24 @@ if uploadtarget has been reached.
if uploadtarget has been reached.
* Verify that the upload counters are reset after 24 hours.
"""
+from collections import defaultdict
+import time
from test_framework.mininode import *
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
-import time
-# TestNode: bare-bones "peer". Used mostly as a conduit for a test to sending
-# p2p messages to a node, generating the messages in the main testing logic.
class TestNode(NodeConnCB):
def __init__(self):
super().__init__()
- self.connection = None
- self.ping_counter = 1
- self.last_pong = msg_pong()
- self.block_receive_map = {}
-
- def add_connection(self, conn):
- self.connection = conn
- self.peer_disconnected = False
+ self.block_receive_map = defaultdict(int)
def on_inv(self, conn, message):
pass
- # Track the last getdata message we receive (used in the test)
- def on_getdata(self, conn, message):
- self.last_getdata = message
-
def on_block(self, conn, message):
message.block.calc_sha256()
- try:
- self.block_receive_map[message.block.sha256] += 1
- except KeyError as e:
- self.block_receive_map[message.block.sha256] = 1
-
- # Spin until verack message is received from the node.
- # We use this to signal that our test can begin. This
- # is called from the testing thread, so it needs to acquire
- # the global lock.
- def wait_for_verack(self):
- def veracked():
- return self.verack_received
- return wait_until(veracked, timeout=10)
-
- def wait_for_disconnect(self):
- def disconnected():
- return self.peer_disconnected
- return wait_until(disconnected, timeout=10)
-
- # Wrapper for the NodeConn's send_message function
- def send_message(self, message):
- self.connection.send_message(message)
-
- def on_pong(self, conn, message):
- self.last_pong = message
-
- def on_close(self, conn):
- self.peer_disconnected = True
+ self.block_receive_map[message.block.sha256] += 1
class MaxUploadTest(BitcoinTestFramework):
@@ -74,15 +35,11 @@ class MaxUploadTest(BitcoinTestFramework):
super().__init__()
self.setup_clean_chain = True
self.num_nodes = 1
+ self.extra_args = [["-maxuploadtarget=800", "-blockmaxsize=999000"]]
# Cache for utxos, as the listunspent may take a long time later in the test
self.utxo_cache = []
- def setup_network(self):
- # Start a node with maxuploadtarget of 200 MB (/24h)
- self.nodes = []
- self.nodes.append(start_node(0, self.options.tmpdir, ["-maxuploadtarget=800", "-blockmaxsize=999000"]))
-
def run_test(self):
# Before we connect anything, we first set the time on the node
# to be in the past, otherwise things break because the CNode
@@ -192,33 +149,26 @@ class MaxUploadTest(BitcoinTestFramework):
stop_node(self.nodes[0], 0)
self.nodes[0] = start_node(0, self.options.tmpdir, ["-whitelist=127.0.0.1", "-maxuploadtarget=1", "-blockmaxsize=999000"])
- #recreate/reconnect 3 test nodes
- test_nodes = []
- connections = []
-
- 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])
+ #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])
NetworkThread().start() # Start up network handling in another thread
- [x.wait_for_verack() for x in test_nodes]
+ test_nodes[0].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[1].send_message(getdata_request)
- test_nodes[1].sync_with_ping()
- assert_equal(test_nodes[1].block_receive_map[big_new_block], i+1)
+ 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)
getdata_request.inv = [CInv(2, big_old_block)]
- test_nodes[1].send_message(getdata_request)
- test_nodes[1].wait_for_disconnect()
- assert_equal(len(self.nodes[0].getpeerinfo()), 3) #node is still connected because of the whitelist
+ test_nodes[0].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 1 still connected after trying to download old block (whitelisted)")
-
- [c.disconnect_node() for c in connections]
+ self.log.info("Peer still connected after trying to download old block (whitelisted)")
if __name__ == '__main__':
MaxUploadTest().main()
diff --git a/test/functional/mempool_limit.py b/test/functional/mempool_limit.py
index a7ca576aee..2777291dd0 100755
--- a/test/functional/mempool_limit.py
+++ b/test/functional/mempool_limit.py
@@ -9,30 +9,25 @@ from test_framework.util import *
class MempoolLimitTest(BitcoinTestFramework):
- def setup_network(self):
- self.nodes = []
- self.nodes.append(start_node(0, self.options.tmpdir, ["-maxmempool=5", "-spendzeroconfchange=0"]))
- self.is_network_split = False
- self.sync_all()
- self.relayfee = self.nodes[0].getnetworkinfo()['relayfee']
-
def __init__(self):
super().__init__()
self.setup_clean_chain = True
self.num_nodes = 1
-
- self.txouts = gen_return_txouts()
+ self.extra_args = [["-maxmempool=5", "-spendzeroconfchange=0"]]
def run_test(self):
+ txouts = gen_return_txouts()
+ relayfee = self.nodes[0].getnetworkinfo()['relayfee']
+
txids = []
- utxos = create_confirmed_utxos(self.relayfee, self.nodes[0], 91)
+ utxos = create_confirmed_utxos(relayfee, self.nodes[0], 91)
#create a mempool tx that will be evicted
us0 = utxos.pop()
inputs = [{ "txid" : us0["txid"], "vout" : us0["vout"]}]
outputs = {self.nodes[0].getnewaddress() : 0.0001}
tx = self.nodes[0].createrawtransaction(inputs, outputs)
- self.nodes[0].settxfee(self.relayfee) # specifically fund this tx with low fee
+ self.nodes[0].settxfee(relayfee) # specifically fund this tx with low fee
txF = self.nodes[0].fundrawtransaction(tx)
self.nodes[0].settxfee(0) # return to automatic fee selection
txFS = self.nodes[0].signrawtransaction(txF['hex'])
@@ -42,7 +37,7 @@ class MempoolLimitTest(BitcoinTestFramework):
base_fee = relayfee*100
for i in range (3):
txids.append([])
- txids[i] = create_lots_of_big_transactions(self.nodes[0], self.txouts, utxos[30*i:30*i+30], 30, (i+1)*base_fee)
+ txids[i] = create_lots_of_big_transactions(self.nodes[0], txouts, utxos[30*i:30*i+30], 30, (i+1)*base_fee)
# by now, the tx should be evicted, check confirmation state
assert(txid not in self.nodes[0].getrawmempool())
diff --git a/test/functional/mempool_packages.py b/test/functional/mempool_packages.py
index 17e3a9a967..72f04095f4 100755
--- a/test/functional/mempool_packages.py
+++ b/test/functional/mempool_packages.py
@@ -16,14 +16,7 @@ class MempoolPackagesTest(BitcoinTestFramework):
super().__init__()
self.num_nodes = 2
self.setup_clean_chain = False
-
- def setup_network(self):
- self.nodes = []
- self.nodes.append(start_node(0, self.options.tmpdir, ["-maxorphantx=1000"]))
- self.nodes.append(start_node(1, self.options.tmpdir, ["-maxorphantx=1000", "-limitancestorcount=5"]))
- connect_nodes(self.nodes[0], 1)
- self.is_network_split = False
- self.sync_all()
+ self.extra_args = [["-maxorphantx=1000"], ["-maxorphantx=1000", "-limitancestorcount=5"]]
# Build a transaction that spends parent_txid:vout
# Return amount sent
@@ -101,6 +94,18 @@ class MempoolPackagesTest(BitcoinTestFramework):
assert_equal(mempool[x], v_descendants[x])
assert(chain[0] not in v_descendants.keys())
+ # Check that ancestor modified fees includes fee deltas from
+ # prioritisetransaction
+ self.nodes[0].prioritisetransaction(chain[0], 1000)
+ mempool = self.nodes[0].getrawmempool(True)
+ ancestor_fees = 0
+ for x in chain:
+ ancestor_fees += mempool[x]['fee']
+ assert_equal(mempool[x]['ancestorfees'], ancestor_fees * COIN + 1000)
+
+ # Undo the prioritisetransaction for later tests
+ self.nodes[0].prioritisetransaction(chain[0], -1000)
+
# Check that descendant modified fees includes fee deltas from
# prioritisetransaction
self.nodes[0].prioritisetransaction(chain[-1], 1000)
diff --git a/test/functional/mempool_reorg.py b/test/functional/mempool_reorg.py
index 812b54ffcb..937bf4bab5 100755
--- a/test/functional/mempool_reorg.py
+++ b/test/functional/mempool_reorg.py
@@ -17,18 +17,10 @@ class MempoolCoinbaseTest(BitcoinTestFramework):
super().__init__()
self.num_nodes = 2
self.setup_clean_chain = False
+ self.extra_args = [["-checkmempool"]] * 2
alert_filename = None # Set by setup_network
- def setup_network(self):
- args = ["-checkmempool"]
- self.nodes = []
- self.nodes.append(start_node(0, self.options.tmpdir, args))
- self.nodes.append(start_node(1, self.options.tmpdir, args))
- connect_nodes(self.nodes[1], 0)
- self.is_network_split = False
- self.sync_all()
-
def run_test(self):
# Start with a 200 block chain
assert_equal(self.nodes[0].getblockcount(), 200)
diff --git a/test/functional/mempool_resurrect_test.py b/test/functional/mempool_resurrect_test.py
index 727892d1f2..a2f6228df9 100755
--- a/test/functional/mempool_resurrect_test.py
+++ b/test/functional/mempool_resurrect_test.py
@@ -14,13 +14,8 @@ class MempoolCoinbaseTest(BitcoinTestFramework):
super().__init__()
self.num_nodes = 1
self.setup_clean_chain = False
-
- def setup_network(self):
# Just need one node for this test
- args = ["-checkmempool"]
- self.nodes = []
- self.nodes.append(start_node(0, self.options.tmpdir, args))
- self.is_network_split = False
+ self.extra_args = [["-checkmempool"]]
def run_test(self):
node0_address = self.nodes[0].getnewaddress()
diff --git a/test/functional/mempool_spendcoinbase.py b/test/functional/mempool_spendcoinbase.py
index f562a93d86..277ea45ad5 100755
--- a/test/functional/mempool_spendcoinbase.py
+++ b/test/functional/mempool_spendcoinbase.py
@@ -22,13 +22,7 @@ class MempoolSpendCoinbaseTest(BitcoinTestFramework):
super().__init__()
self.num_nodes = 1
self.setup_clean_chain = False
-
- def setup_network(self):
- # Just need one node for this test
- args = ["-checkmempool"]
- self.nodes = []
- self.nodes.append(start_node(0, self.options.tmpdir, args))
- self.is_network_split = False
+ self.extra_args = [["-checkmempool"]]
def run_test(self):
chain_height = self.nodes[0].getblockcount()
diff --git a/test/functional/merkle_blocks.py b/test/functional/merkle_blocks.py
index 5963f2e7b6..06af72ef10 100755
--- a/test/functional/merkle_blocks.py
+++ b/test/functional/merkle_blocks.py
@@ -13,20 +13,15 @@ class MerkleBlockTest(BitcoinTestFramework):
super().__init__()
self.setup_clean_chain = True
self.num_nodes = 4
+ # Nodes 0/1 are "wallet" nodes, Nodes 2/3 are used for testing
+ self.extra_args = [[], [], [], ["-txindex"]]
def setup_network(self):
- self.nodes = []
- # Nodes 0/1 are "wallet" nodes
- self.nodes.append(start_node(0, self.options.tmpdir))
- self.nodes.append(start_node(1, self.options.tmpdir))
- # Nodes 2/3 are used for testing
- self.nodes.append(start_node(2, self.options.tmpdir))
- self.nodes.append(start_node(3, self.options.tmpdir, ["-txindex"]))
+ self.setup_nodes()
connect_nodes(self.nodes[0], 1)
connect_nodes(self.nodes[0], 2)
connect_nodes(self.nodes[0], 3)
- self.is_network_split = False
self.sync_all()
def run_test(self):
diff --git a/test/functional/multi_rpc.py b/test/functional/multi_rpc.py
index a9701c548b..6ff91a960b 100755
--- a/test/functional/multi_rpc.py
+++ b/test/functional/multi_rpc.py
@@ -27,9 +27,6 @@ class HTTPBasicsTest (BitcoinTestFramework):
f.write(rpcauth+"\n")
f.write(rpcauth2+"\n")
- def setup_network(self):
- self.nodes = self.setup_nodes()
-
def run_test(self):
##################################################
diff --git a/test/functional/net.py b/test/functional/net.py
index b4ad820e85..16476f7d34 100755
--- a/test/functional/net.py
+++ b/test/functional/net.py
@@ -12,8 +12,10 @@ import time
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
- start_nodes,
+ assert_raises_jsonrpc,
connect_nodes_bi,
+ p2p_port,
+ start_nodes,
)
@@ -23,15 +25,42 @@ class NetTest(BitcoinTestFramework):
self.setup_clean_chain = True
self.num_nodes = 2
- def setup_network(self):
- self.nodes = start_nodes(self.num_nodes, self.options.tmpdir)
- connect_nodes_bi(self.nodes, 0, 1)
- self.is_network_split = False
- self.sync_all()
-
def run_test(self):
+ self._test_connection_count()
+ self._test_getnettotals()
+ self._test_getnetworkinginfo()
+ self._test_getaddednodeinfo()
+
+ def _test_connection_count(self):
+ # connect_nodes_bi connects each node to the other
+ assert_equal(self.nodes[0].getconnectioncount(), 2)
+
+ def _test_getnettotals(self):
+ # check that getnettotals totalbytesrecv and totalbytessent
+ # are consistent with getpeerinfo
+ peer_info = self.nodes[0].getpeerinfo()
+ assert_equal(len(peer_info), 2)
+ net_totals = self.nodes[0].getnettotals()
+ assert_equal(sum([peer['bytesrecv'] for peer in peer_info]),
+ net_totals['totalbytesrecv'])
+ assert_equal(sum([peer['bytessent'] for peer in peer_info]),
+ net_totals['totalbytessent'])
+ # test getnettotals and getpeerinfo by doing a ping
+ # the bytes sent/received should change
+ # note ping and pong are 32 bytes each
+ self.nodes[0].ping()
+ time.sleep(0.1)
+ peer_info_after_ping = self.nodes[0].getpeerinfo()
+ net_totals_after_ping = self.nodes[0].getnettotals()
+ for before, after in zip(peer_info, peer_info_after_ping):
+ assert_equal(before['bytesrecv_per_msg']['pong'] + 32, after['bytesrecv_per_msg']['pong'])
+ assert_equal(before['bytessent_per_msg']['ping'] + 32, after['bytessent_per_msg']['ping'])
+ assert_equal(net_totals['totalbytesrecv'] + 32*2, net_totals_after_ping['totalbytesrecv'])
+ assert_equal(net_totals['totalbytessent'] + 32*2, net_totals_after_ping['totalbytessent'])
+
+ def _test_getnetworkinginfo(self):
assert_equal(self.nodes[0].getnetworkinfo()['networkactive'], True)
- assert_equal(self.nodes[0].getnetworkinfo()['connections'], 2) # bilateral connection
+ assert_equal(self.nodes[0].getnetworkinfo()['connections'], 2)
self.nodes[0].setnetworkactive(False)
assert_equal(self.nodes[0].getnetworkinfo()['networkactive'], False)
@@ -47,6 +76,19 @@ class NetTest(BitcoinTestFramework):
assert_equal(self.nodes[0].getnetworkinfo()['networkactive'], True)
assert_equal(self.nodes[0].getnetworkinfo()['connections'], 2)
+ def _test_getaddednodeinfo(self):
+ assert_equal(self.nodes[0].getaddednodeinfo(), [])
+ # add a node (node2) to node0
+ ip_port = "127.0.0.1:{}".format(p2p_port(2))
+ self.nodes[0].addnode(ip_port, 'add')
+ # check that the node has indeed been added
+ added_nodes = self.nodes[0].getaddednodeinfo(ip_port)
+ assert_equal(len(added_nodes), 1)
+ assert_equal(added_nodes[0]['addednode'], ip_port)
+ # check that a non-existant node returns an error
+ assert_raises_jsonrpc(-24, "Node has not been added",
+ self.nodes[0].getaddednodeinfo, '1.1.1.1')
+
if __name__ == '__main__':
NetTest().main()
diff --git a/test/functional/nodehandling.py b/test/functional/nodehandling.py
deleted file mode 100755
index a6b10a0d83..0000000000
--- a/test/functional/nodehandling.py
+++ /dev/null
@@ -1,80 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (c) 2014-2016 The Bitcoin Core developers
-# Distributed under the MIT software license, see the accompanying
-# file COPYING or http://www.opensource.org/licenses/mit-license.php.
-"""Test node handling."""
-
-from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import *
-
-import urllib.parse
-
-class NodeHandlingTest (BitcoinTestFramework):
-
- def __init__(self):
- super().__init__()
- self.num_nodes = 4
- self.setup_clean_chain = False
-
- def run_test(self):
- ###########################
- # setban/listbanned tests #
- ###########################
- assert_equal(len(self.nodes[2].getpeerinfo()), 4) #we should have 4 nodes at this point
- self.nodes[2].setban("127.0.0.1", "add")
- time.sleep(3) #wait till the nodes are disconected
- assert_equal(len(self.nodes[2].getpeerinfo()), 0) #all nodes must be disconnected at this point
- assert_equal(len(self.nodes[2].listbanned()), 1)
- self.nodes[2].clearbanned()
- assert_equal(len(self.nodes[2].listbanned()), 0)
- self.nodes[2].setban("127.0.0.0/24", "add")
- assert_equal(len(self.nodes[2].listbanned()), 1)
- # This will throw an exception because 127.0.0.1 is within range 127.0.0.0/24
- assert_raises_jsonrpc(-23, "IP/Subnet already banned", self.nodes[2].setban, "127.0.0.1", "add")
- # This will throw an exception because 127.0.0.1/42 is not a real subnet
- assert_raises_jsonrpc(-30, "Error: Invalid IP/Subnet", self.nodes[2].setban, "127.0.0.1/42", "add")
- assert_equal(len(self.nodes[2].listbanned()), 1) #still only one banned ip because 127.0.0.1 is within the range of 127.0.0.0/24
- # This will throw an exception because 127.0.0.1 was not added above
- assert_raises_jsonrpc(-30, "Error: Unban failed", self.nodes[2].setban, "127.0.0.1", "remove")
- assert_equal(len(self.nodes[2].listbanned()), 1)
- self.nodes[2].setban("127.0.0.0/24", "remove")
- assert_equal(len(self.nodes[2].listbanned()), 0)
- self.nodes[2].clearbanned()
- assert_equal(len(self.nodes[2].listbanned()), 0)
-
- ##test persisted banlist
- self.nodes[2].setban("127.0.0.0/32", "add")
- self.nodes[2].setban("127.0.0.0/24", "add")
- self.nodes[2].setban("192.168.0.1", "add", 1) #ban for 1 seconds
- self.nodes[2].setban("2001:4d48:ac57:400:cacf:e9ff:fe1d:9c63/19", "add", 1000) #ban for 1000 seconds
- listBeforeShutdown = self.nodes[2].listbanned()
- assert_equal("192.168.0.1/32", listBeforeShutdown[2]['address']) #must be here
- time.sleep(2) #make 100% sure we expired 192.168.0.1 node time
-
- #stop node
- stop_node(self.nodes[2], 2)
-
- self.nodes[2] = start_node(2, self.options.tmpdir)
- listAfterShutdown = self.nodes[2].listbanned()
- assert_equal("127.0.0.0/24", listAfterShutdown[0]['address'])
- assert_equal("127.0.0.0/32", listAfterShutdown[1]['address'])
- assert_equal("/19" in listAfterShutdown[2]['address'], True)
-
- ###########################
- # RPC disconnectnode test #
- ###########################
- url = urllib.parse.urlparse(self.nodes[1].url)
- self.nodes[0].disconnectnode(url.hostname+":"+str(p2p_port(1)))
- time.sleep(2) #disconnecting a node needs a little bit of time
- for node in self.nodes[0].getpeerinfo():
- assert(node['addr'] != url.hostname+":"+str(p2p_port(1)))
-
- connect_nodes_bi(self.nodes,0,1) #reconnect the node
- found = False
- for node in self.nodes[0].getpeerinfo():
- if node['addr'] == url.hostname+":"+str(p2p_port(1)):
- found = True
- assert(found)
-
-if __name__ == '__main__':
- NodeHandlingTest ().main ()
diff --git a/test/functional/nulldummy.py b/test/functional/nulldummy.py
index 369c593a90..9717add272 100755
--- a/test/functional/nulldummy.py
+++ b/test/functional/nulldummy.py
@@ -41,11 +41,7 @@ class NULLDUMMYTest(BitcoinTestFramework):
super().__init__()
self.num_nodes = 1
self.setup_clean_chain = True
-
- def setup_network(self):
- # Must set the blockversion for this test
- self.nodes = start_nodes(self.num_nodes, self.options.tmpdir,
- extra_args=[['-whitelist=127.0.0.1', '-walletprematurewitness']])
+ self.extra_args = [['-whitelist=127.0.0.1', '-walletprematurewitness']]
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 c09945baa6..322cb767db 100755
--- a/test/functional/p2p-acceptblock.py
+++ b/test/functional/p2p-acceptblock.py
@@ -54,40 +54,6 @@ from test_framework.util import *
import time
from test_framework.blocktools import create_block, create_coinbase
-# TestNode: bare-bones "peer". Used mostly as a conduit for a test to sending
-# p2p messages to a node, generating the messages in the main testing logic.
-class TestNode(NodeConnCB):
- def __init__(self):
- super().__init__()
- self.connection = None
- self.ping_counter = 1
- self.last_pong = msg_pong()
-
- def add_connection(self, conn):
- self.connection = conn
-
- # Track the last getdata message we receive (used in the test)
- def on_getdata(self, conn, message):
- self.last_getdata = message
-
- # Spin until verack message is received from the node.
- # We use this to signal that our test can begin. This
- # is called from the testing thread, so it needs to acquire
- # the global lock.
- def wait_for_verack(self):
- while True:
- with mininode_lock:
- if self.verack_received:
- return
- time.sleep(0.05)
-
- # Wrapper for the NodeConn's send_message function
- def send_message(self, message):
- self.connection.send_message(message)
-
- def on_pong(self, conn, message):
- self.last_pong = message
-
class AcceptBlockTest(BitcoinTestFramework):
def add_options(self, parser):
parser.add_option("--testbinary", dest="testbinary",
@@ -98,22 +64,18 @@ class AcceptBlockTest(BitcoinTestFramework):
super().__init__()
self.setup_clean_chain = True
self.num_nodes = 2
+ self.extra_args = [[], ["-whitelist=127.0.0.1"]]
def setup_network(self):
# Node0 will be used to test behavior of processing unrequested blocks
# from peers which are not whitelisted, while Node1 will be used for
# the whitelisted case.
- self.nodes = []
- self.nodes.append(start_node(0, self.options.tmpdir,
- binary=self.options.testbinary))
- self.nodes.append(start_node(1, self.options.tmpdir,
- ["-whitelist=127.0.0.1"],
- binary=self.options.testbinary))
+ self.setup_nodes()
def run_test(self):
# Setup the p2p connections and start up the network thread.
- test_node = TestNode() # connects to node0 (not whitelisted)
- white_node = TestNode() # connects to node1 (whitelisted)
+ test_node = NodeConnCB() # connects to node0 (not whitelisted)
+ white_node = NodeConnCB() # connects to node1 (whitelisted)
connections = []
connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_node))
@@ -238,12 +200,12 @@ class AcceptBlockTest(BitcoinTestFramework):
# triggers a getdata on block 2 (it should if block 2 is missing).
with mininode_lock:
# Clear state so we can check the getdata request
- test_node.last_getdata = None
+ test_node.last_message.pop("getdata", None)
test_node.send_message(msg_inv([CInv(2, blocks_h3[0].sha256)]))
test_node.sync_with_ping()
with mininode_lock:
- getdata = test_node.last_getdata
+ getdata = test_node.last_message["getdata"]
# Check that the getdata includes the right block
assert_equal(getdata.inv[0].hash, blocks_h2f[0].sha256)
diff --git a/test/functional/p2p-compactblocks.py b/test/functional/p2p-compactblocks.py
index bf8d113767..9b302120ac 100755
--- a/test/functional/p2p-compactblocks.py
+++ b/test/functional/p2p-compactblocks.py
@@ -19,64 +19,31 @@ class TestNode(NodeConnCB):
def __init__(self):
super().__init__()
self.last_sendcmpct = []
- self.last_headers = None
- self.last_inv = None
- self.last_cmpctblock = None
self.block_announced = False
- self.last_getdata = None
- self.last_getheaders = None
- self.last_getblocktxn = None
- self.last_block = None
- self.last_blocktxn = None
# Store the hashes of blocks we've seen announced.
# This is for synchronizing the p2p message traffic,
# so we can eg wait until a particular block is announced.
- self.set_announced_blockhashes = set()
- self.connected = False
-
- def on_open(self, conn):
- self.connected = True
-
- def on_close(self, conn):
- self.connected = False
+ self.announced_blockhashes = set()
def on_sendcmpct(self, conn, message):
self.last_sendcmpct.append(message)
- def on_block(self, conn, message):
- self.last_block = message
-
def on_cmpctblock(self, conn, message):
- self.last_cmpctblock = message
self.block_announced = True
- self.last_cmpctblock.header_and_shortids.header.calc_sha256()
- self.set_announced_blockhashes.add(self.last_cmpctblock.header_and_shortids.header.sha256)
+ self.last_message["cmpctblock"].header_and_shortids.header.calc_sha256()
+ self.announced_blockhashes.add(self.last_message["cmpctblock"].header_and_shortids.header.sha256)
def on_headers(self, conn, message):
- self.last_headers = message
self.block_announced = True
- for x in self.last_headers.headers:
+ for x in self.last_message["headers"].headers:
x.calc_sha256()
- self.set_announced_blockhashes.add(x.sha256)
+ self.announced_blockhashes.add(x.sha256)
def on_inv(self, conn, message):
- self.last_inv = message
- for x in self.last_inv.inv:
+ for x in self.last_message["inv"].inv:
if x.type == 2:
self.block_announced = True
- self.set_announced_blockhashes.add(x.hash)
-
- def on_getdata(self, conn, message):
- self.last_getdata = message
-
- def on_getheaders(self, conn, message):
- self.last_getheaders = message
-
- def on_getblocktxn(self, conn, message):
- self.last_getblocktxn = message
-
- def on_blocktxn(self, conn, message):
- self.last_blocktxn = message
+ self.announced_blockhashes.add(x.hash)
# Requires caller to hold mininode_lock
def received_block_announcement(self):
@@ -85,9 +52,9 @@ class TestNode(NodeConnCB):
def clear_block_announcement(self):
with mininode_lock:
self.block_announced = False
- self.last_inv = None
- self.last_headers = None
- self.last_cmpctblock = None
+ self.last_message.pop("inv", None)
+ self.last_message.pop("headers", None)
+ self.last_message.pop("cmpctblock", None)
def get_headers(self, locator, hashstop):
msg = msg_getheaders()
@@ -103,15 +70,14 @@ class TestNode(NodeConnCB):
def request_headers_and_sync(self, locator, hashstop=0):
self.clear_block_announcement()
self.get_headers(locator, hashstop)
- assert(wait_until(self.received_block_announcement, timeout=30))
- assert(self.received_block_announcement())
+ assert wait_until(self.received_block_announcement, timeout=30)
self.clear_block_announcement()
# Block until a block announcement for a particular block hash is
# received.
def wait_for_block_announcement(self, block_hash, timeout=30):
def received_hash():
- return (block_hash in self.set_announced_blockhashes)
+ return (block_hash in self.announced_blockhashes)
return wait_until(received_hash, timeout=timeout)
def send_await_disconnect(self, message, timeout=30):
@@ -132,17 +98,9 @@ class CompactBlocksTest(BitcoinTestFramework):
self.setup_clean_chain = True
# Node0 = pre-segwit, node1 = segwit-aware
self.num_nodes = 2
+ self.extra_args = [["-bip9params=segwit:0:0"], ["-txindex"]]
self.utxos = []
- def setup_network(self):
- self.nodes = []
-
- # Start up node0 to be a version 1, pre-segwit node.
- self.nodes = start_nodes(self.num_nodes, self.options.tmpdir,
- [["-bip9params=segwit:0:0"],
- ["-txindex"]])
- connect_nodes(self.nodes[0], 1)
-
def build_block_on_tip(self, node, segwit=False):
height = node.getblockcount()
tip = node.getbestblockhash()
@@ -214,14 +172,14 @@ class CompactBlocksTest(BitcoinTestFramework):
with mininode_lock:
assert predicate(peer), (
"block_hash={!r}, cmpctblock={!r}, inv={!r}".format(
- block_hash, peer.last_cmpctblock, peer.last_inv))
+ block_hash, peer.last_message.get("cmpctblock", None), peer.last_message.get("inv", None)))
# We shouldn't get any block announcements via cmpctblock yet.
- check_announcement_of_new_block(node, test_node, lambda p: p.last_cmpctblock is None)
+ check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" not in p.last_message)
# Try one more time, this time after requesting headers.
test_node.request_headers_and_sync(locator=[tip])
- check_announcement_of_new_block(node, test_node, lambda p: p.last_cmpctblock is None and p.last_inv is not None)
+ check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" not in p.last_message and "inv" in p.last_message)
# Test a few ways of using sendcmpct that should NOT
# result in compact block announcements.
@@ -233,7 +191,7 @@ class CompactBlocksTest(BitcoinTestFramework):
sendcmpct.version = preferred_version+1
sendcmpct.announce = True
test_node.send_and_ping(sendcmpct)
- check_announcement_of_new_block(node, test_node, lambda p: p.last_cmpctblock is None)
+ check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" not in p.last_message)
# Headers sync before next test.
test_node.request_headers_and_sync(locator=[tip])
@@ -242,7 +200,7 @@ class CompactBlocksTest(BitcoinTestFramework):
sendcmpct.version = preferred_version
sendcmpct.announce = False
test_node.send_and_ping(sendcmpct)
- check_announcement_of_new_block(node, test_node, lambda p: p.last_cmpctblock is None)
+ check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" not in p.last_message)
# Headers sync before next test.
test_node.request_headers_and_sync(locator=[tip])
@@ -251,26 +209,26 @@ class CompactBlocksTest(BitcoinTestFramework):
sendcmpct.version = preferred_version
sendcmpct.announce = True
test_node.send_and_ping(sendcmpct)
- check_announcement_of_new_block(node, test_node, lambda p: p.last_cmpctblock is not None)
+ check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" in p.last_message)
# Try one more time (no headers sync should be needed!)
- check_announcement_of_new_block(node, test_node, lambda p: p.last_cmpctblock is not None)
+ check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" in p.last_message)
# Try one more time, after turning on sendheaders
test_node.send_and_ping(msg_sendheaders())
- check_announcement_of_new_block(node, test_node, lambda p: p.last_cmpctblock is not None)
+ check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" in p.last_message)
# Try one more time, after sending a version-1, announce=false message.
sendcmpct.version = preferred_version-1
sendcmpct.announce = False
test_node.send_and_ping(sendcmpct)
- check_announcement_of_new_block(node, test_node, lambda p: p.last_cmpctblock is not None)
+ check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" in p.last_message)
# Now turn off announcements
sendcmpct.version = preferred_version
sendcmpct.announce = False
test_node.send_and_ping(sendcmpct)
- check_announcement_of_new_block(node, test_node, lambda p: p.last_cmpctblock is None and p.last_headers is not None)
+ check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" not in p.last_message and "headers" in p.last_message)
if old_node is not None:
# Verify that a peer using an older protocol version can receive
@@ -280,7 +238,7 @@ class CompactBlocksTest(BitcoinTestFramework):
old_node.send_and_ping(sendcmpct)
# Header sync
old_node.request_headers_and_sync(locator=[tip])
- check_announcement_of_new_block(node, old_node, lambda p: p.last_cmpctblock is not None)
+ check_announcement_of_new_block(node, old_node, lambda p: "cmpctblock" in p.last_message)
# This test actually causes bitcoind to (reasonably!) disconnect us, so do this last.
def test_invalid_cmpctblock_message(self):
@@ -345,9 +303,9 @@ class CompactBlocksTest(BitcoinTestFramework):
# Now fetch and check the compact block
header_and_shortids = None
with mininode_lock:
- assert(test_node.last_cmpctblock is not None)
+ assert("cmpctblock" in test_node.last_message)
# Convert the on-the-wire representation to absolute indexes
- header_and_shortids = HeaderAndShortIDs(test_node.last_cmpctblock.header_and_shortids)
+ header_and_shortids = HeaderAndShortIDs(test_node.last_message["cmpctblock"].header_and_shortids)
self.check_compactblock_construction_from_block(version, header_and_shortids, block_hash, block)
# Now fetch the compact block using a normal non-announce getdata
@@ -362,9 +320,9 @@ class CompactBlocksTest(BitcoinTestFramework):
# Now fetch and check the compact block
header_and_shortids = None
with mininode_lock:
- assert(test_node.last_cmpctblock is not None)
+ assert("cmpctblock" in test_node.last_message)
# Convert the on-the-wire representation to absolute indexes
- header_and_shortids = HeaderAndShortIDs(test_node.last_cmpctblock.header_and_shortids)
+ header_and_shortids = HeaderAndShortIDs(test_node.last_message["cmpctblock"].header_and_shortids)
self.check_compactblock_construction_from_block(version, header_and_shortids, block_hash, block)
def check_compactblock_construction_from_block(self, version, header_and_shortids, block_hash, block):
@@ -424,20 +382,20 @@ class CompactBlocksTest(BitcoinTestFramework):
for announce in ["inv", "header"]:
block = self.build_block_on_tip(node, segwit=segwit)
with mininode_lock:
- test_node.last_getdata = None
+ test_node.last_message.pop("getdata", None)
if announce == "inv":
test_node.send_message(msg_inv([CInv(2, block.sha256)]))
- success = wait_until(lambda: test_node.last_getheaders is not None, timeout=30)
+ success = wait_until(lambda: "getheaders" in test_node.last_message, timeout=30)
assert(success)
test_node.send_header_for_blocks([block])
else:
test_node.send_header_for_blocks([block])
- success = wait_until(lambda: test_node.last_getdata is not None, timeout=30)
+ success = wait_until(lambda: "getdata" in test_node.last_message, timeout=30)
assert(success)
- assert_equal(len(test_node.last_getdata.inv), 1)
- assert_equal(test_node.last_getdata.inv[0].type, 4)
- assert_equal(test_node.last_getdata.inv[0].hash, block.sha256)
+ assert_equal(len(test_node.last_message["getdata"].inv), 1)
+ assert_equal(test_node.last_message["getdata"].inv[0].type, 4)
+ assert_equal(test_node.last_message["getdata"].inv[0].hash, block.sha256)
# Send back a compactblock message that omits the coinbase
comp_block = HeaderAndShortIDs()
@@ -453,8 +411,8 @@ class CompactBlocksTest(BitcoinTestFramework):
assert_equal(int(node.getbestblockhash(), 16), block.hashPrevBlock)
# Expect a getblocktxn message.
with mininode_lock:
- assert(test_node.last_getblocktxn is not None)
- absolute_indexes = test_node.last_getblocktxn.block_txn_request.to_absolute()
+ assert("getblocktxn" in test_node.last_message)
+ absolute_indexes = test_node.last_message["getblocktxn"].block_txn_request.to_absolute()
assert_equal(absolute_indexes, [0]) # should be a coinbase request
# Send the coinbase, and verify that the tip advances.
@@ -493,8 +451,8 @@ class CompactBlocksTest(BitcoinTestFramework):
msg = msg_cmpctblock(compact_block.to_p2p())
peer.send_and_ping(msg)
with mininode_lock:
- assert(peer.last_getblocktxn is not None)
- absolute_indexes = peer.last_getblocktxn.block_txn_request.to_absolute()
+ assert("getblocktxn" in peer.last_message)
+ absolute_indexes = peer.last_message["getblocktxn"].block_txn_request.to_absolute()
assert_equal(absolute_indexes, expected_result)
def test_tip_after_message(node, peer, msg, tip):
@@ -558,14 +516,14 @@ class CompactBlocksTest(BitcoinTestFramework):
# Clear out last request.
with mininode_lock:
- test_node.last_getblocktxn = None
+ test_node.last_message.pop("getblocktxn", None)
# Send compact block
comp_block.initialize_from_block(block, prefill_list=[0], use_witness=with_witness)
test_tip_after_message(node, test_node, msg_cmpctblock(comp_block.to_p2p()), block.sha256)
with mininode_lock:
# Shouldn't have gotten a request for any transaction
- assert(test_node.last_getblocktxn is None)
+ assert("getblocktxn" not in test_node.last_message)
# Incorrectly responding to a getblocktxn shouldn't cause the block to be
# permanently failed.
@@ -591,8 +549,8 @@ class CompactBlocksTest(BitcoinTestFramework):
test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p()))
absolute_indexes = []
with mininode_lock:
- assert(test_node.last_getblocktxn is not None)
- absolute_indexes = test_node.last_getblocktxn.block_txn_request.to_absolute()
+ assert("getblocktxn" in test_node.last_message)
+ absolute_indexes = test_node.last_message["getblocktxn"].block_txn_request.to_absolute()
assert_equal(absolute_indexes, [6, 7, 8, 9, 10])
# Now give an incorrect response.
@@ -613,11 +571,11 @@ class CompactBlocksTest(BitcoinTestFramework):
assert_equal(int(node.getbestblockhash(), 16), block.hashPrevBlock)
# We should receive a getdata request
- success = wait_until(lambda: test_node.last_getdata is not None, timeout=10)
+ success = wait_until(lambda: "getdata" in test_node.last_message, timeout=10)
assert(success)
- assert_equal(len(test_node.last_getdata.inv), 1)
- assert(test_node.last_getdata.inv[0].type == 2 or test_node.last_getdata.inv[0].type == 2|MSG_WITNESS_FLAG)
- assert_equal(test_node.last_getdata.inv[0].hash, block.sha256)
+ assert_equal(len(test_node.last_message["getdata"].inv), 1)
+ assert(test_node.last_message["getdata"].inv[0].type == 2 or test_node.last_message["getdata"].inv[0].type == 2|MSG_WITNESS_FLAG)
+ assert_equal(test_node.last_message["getdata"].inv[0].hash, block.sha256)
# Deliver the block
if version==2:
@@ -641,15 +599,15 @@ class CompactBlocksTest(BitcoinTestFramework):
num_to_request = random.randint(1, len(block.vtx))
msg.block_txn_request.from_absolute(sorted(random.sample(range(len(block.vtx)), num_to_request)))
test_node.send_message(msg)
- success = wait_until(lambda: test_node.last_blocktxn is not None, timeout=10)
+ success = wait_until(lambda: "blocktxn" in test_node.last_message, timeout=10)
assert(success)
[tx.calc_sha256() for tx in block.vtx]
with mininode_lock:
- assert_equal(test_node.last_blocktxn.block_transactions.blockhash, int(block_hash, 16))
+ assert_equal(test_node.last_message["blocktxn"].block_transactions.blockhash, int(block_hash, 16))
all_indices = msg.block_txn_request.to_absolute()
for index in all_indices:
- tx = test_node.last_blocktxn.block_transactions.transactions.pop(0)
+ tx = test_node.last_message["blocktxn"].block_transactions.transactions.pop(0)
tx.calc_sha256()
assert_equal(tx.sha256, block.vtx[index].sha256)
if version == 1:
@@ -658,7 +616,7 @@ class CompactBlocksTest(BitcoinTestFramework):
else:
# Check that the witness matches
assert_equal(tx.calc_sha256(True), block.vtx[index].calc_sha256(True))
- test_node.last_blocktxn = None
+ test_node.last_message.pop("blocktxn", None)
current_height -= 1
# Next request should send a full block response, as we're past the
@@ -666,13 +624,13 @@ class CompactBlocksTest(BitcoinTestFramework):
block_hash = node.getblockhash(current_height)
msg.block_txn_request = BlockTransactionsRequest(int(block_hash, 16), [0])
with mininode_lock:
- test_node.last_block = None
- test_node.last_blocktxn = None
+ test_node.last_message.pop("block", None)
+ test_node.last_message.pop("blocktxn", None)
test_node.send_and_ping(msg)
with mininode_lock:
- test_node.last_block.block.calc_sha256()
- assert_equal(test_node.last_block.block.sha256, int(block_hash, 16))
- assert_equal(test_node.last_blocktxn, None)
+ test_node.last_message["block"].block.calc_sha256()
+ assert_equal(test_node.last_message["block"].block.sha256, int(block_hash, 16))
+ assert "blocktxn" not in test_node.last_message
def test_compactblocks_not_at_tip(self, node, test_node):
# Test that requesting old compactblocks doesn't work.
@@ -685,7 +643,7 @@ class CompactBlocksTest(BitcoinTestFramework):
test_node.clear_block_announcement()
test_node.send_message(msg_getdata([CInv(4, int(new_blocks[0], 16))]))
- success = wait_until(lambda: test_node.last_cmpctblock is not None, timeout=30)
+ success = wait_until(lambda: "cmpctblock" in test_node.last_message, timeout=30)
assert(success)
test_node.clear_block_announcement()
@@ -693,13 +651,13 @@ class CompactBlocksTest(BitcoinTestFramework):
wait_until(test_node.received_block_announcement, timeout=30)
test_node.clear_block_announcement()
with mininode_lock:
- test_node.last_block = None
+ test_node.last_message.pop("block", None)
test_node.send_message(msg_getdata([CInv(4, int(new_blocks[0], 16))]))
- success = wait_until(lambda: test_node.last_block is not None, timeout=30)
+ success = wait_until(lambda: "block" in test_node.last_message, timeout=30)
assert(success)
with mininode_lock:
- test_node.last_block.block.calc_sha256()
- assert_equal(test_node.last_block.block.sha256, int(new_blocks[0], 16))
+ test_node.last_message["block"].block.calc_sha256()
+ assert_equal(test_node.last_message["block"].block.sha256, int(new_blocks[0], 16))
# Generate an old compactblock, and verify that it's not accepted.
cur_height = node.getblockcount()
@@ -726,10 +684,10 @@ class CompactBlocksTest(BitcoinTestFramework):
msg = msg_getblocktxn()
msg.block_txn_request = BlockTransactionsRequest(block.sha256, [0])
with mininode_lock:
- test_node.last_blocktxn = None
+ test_node.last_message.pop("blocktxn", None)
test_node.send_and_ping(msg)
with mininode_lock:
- assert(test_node.last_blocktxn is None)
+ assert "blocktxn" not in test_node.last_message
def activate_segwit(self, node):
node.generate(144*3)
@@ -750,9 +708,9 @@ class CompactBlocksTest(BitcoinTestFramework):
wait_until(lambda: l.received_block_announcement(), timeout=30)
with mininode_lock:
for l in listeners:
- assert(l.last_cmpctblock is not None)
- l.last_cmpctblock.header_and_shortids.header.calc_sha256()
- assert_equal(l.last_cmpctblock.header_and_shortids.header.sha256, block.sha256)
+ assert "cmpctblock" in l.last_message
+ l.last_message["cmpctblock"].header_and_shortids.header.calc_sha256()
+ assert_equal(l.last_message["cmpctblock"].header_and_shortids.header.sha256, block.sha256)
# Test that we don't get disconnected if we relay a compact block with valid header,
# but invalid transactions.
@@ -804,7 +762,7 @@ class CompactBlocksTest(BitcoinTestFramework):
msg = msg_cmpctblock(cmpct_block.to_p2p())
peer.send_and_ping(msg)
with mininode_lock:
- assert(peer.last_getblocktxn is not None)
+ assert "getblocktxn" in peer.last_message
return block, cmpct_block
block, cmpct_block = announce_cmpct_block(node, stalling_peer)
diff --git a/test/functional/p2p-feefilter.py b/test/functional/p2p-feefilter.py
index 12539be950..dbccb633a5 100755
--- a/test/functional/p2p-feefilter.py
+++ b/test/functional/p2p-feefilter.py
@@ -22,8 +22,6 @@ def allInvsMatch(invsExpected, testnode):
time.sleep(1)
return False
-# TestNode: bare-bones "peer". Used to track which invs are received from a node
-# and to send the node feefilter messages.
class TestNode(NodeConnCB):
def __init__(self):
super().__init__()
@@ -38,10 +36,6 @@ class TestNode(NodeConnCB):
with mininode_lock:
self.txinvs = []
- def send_filter(self, feerate):
- self.send_message(msg_feefilter(feerate))
- self.sync_with_ping()
-
class FeeFilterTest(BitcoinTestFramework):
def __init__(self):
@@ -49,14 +43,6 @@ class FeeFilterTest(BitcoinTestFramework):
self.num_nodes = 2
self.setup_clean_chain = False
- def setup_network(self):
- # Node1 will be used to generate txs which should be relayed from Node0
- # to our test node
- self.nodes = []
- self.nodes.append(start_node(0, self.options.tmpdir))
- self.nodes.append(start_node(1, self.options.tmpdir))
- connect_nodes(self.nodes[0], 1)
-
def run_test(self):
node1 = self.nodes[1]
node0 = self.nodes[0]
@@ -78,7 +64,7 @@ class FeeFilterTest(BitcoinTestFramework):
test_node.clear_invs()
# Set a filter of 15 sat/byte
- test_node.send_filter(15000)
+ test_node.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)]
@@ -103,7 +89,7 @@ class FeeFilterTest(BitcoinTestFramework):
test_node.clear_invs()
# Remove fee filter and check that txs are received again
- test_node.send_filter(0)
+ test_node.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()
diff --git a/test/functional/p2p-fullblocktest.py b/test/functional/p2p-fullblocktest.py
index 274dbb8a92..e7fe7372c8 100755
--- a/test/functional/p2p-fullblocktest.py
+++ b/test/functional/p2p-fullblocktest.py
@@ -398,7 +398,7 @@ class FullBlockTest(ComparisonTestFramework):
# Extend the b26 chain to make sure bitcoind isn't accepting b26
b27 = block(27, spend=out[7])
- yield rejected(RejectResult(0, b'bad-prevblk'))
+ yield rejected(False)
# Now try a too-large-coinbase script
tip(15)
@@ -410,7 +410,7 @@ class FullBlockTest(ComparisonTestFramework):
# Extend the b28 chain to make sure bitcoind isn't accepting b28
b29 = block(29, spend=out[7])
- yield rejected(RejectResult(0, b'bad-prevblk'))
+ yield rejected(False)
# b30 has a max-sized coinbase scriptSig.
tip(23)
diff --git a/test/functional/p2p-leaktests.py b/test/functional/p2p-leaktests.py
index 5853ec86f0..33b57ef33d 100755
--- a/test/functional/p2p-leaktests.py
+++ b/test/functional/p2p-leaktests.py
@@ -20,15 +20,8 @@ banscore = 10
class CLazyNode(NodeConnCB):
def __init__(self):
super().__init__()
- self.connection = None
self.unexpected_msg = False
- self.connected = False
-
- def add_connection(self, conn):
- self.connection = conn
-
- def send_message(self, message):
- self.connection.send_message(message)
+ self.ever_connected = False
def bad_message(self, message):
self.unexpected_msg = True
@@ -36,6 +29,7 @@ class CLazyNode(NodeConnCB):
def on_open(self, conn):
self.connected = True
+ self.ever_connected = True
def on_version(self, conn, message): self.bad_message(message)
def on_verack(self, conn, message): self.bad_message(message)
@@ -63,9 +57,6 @@ class CLazyNode(NodeConnCB):
# Node that never sends a version. We'll use this to send a bunch of messages
# anyway, and eventually get disconnected.
class CNodeNoVersionBan(CLazyNode):
- def __init__(self):
- super().__init__()
-
# send a bunch of veracks without sending a message. This should get us disconnected.
# NOTE: implementation-specific check here. Remove if bitcoind ban behavior changes
def on_open(self, conn):
@@ -101,10 +92,7 @@ class P2PLeakTest(BitcoinTestFramework):
def __init__(self):
super().__init__()
self.num_nodes = 1
- def setup_network(self):
- extra_args = [['-banscore='+str(banscore)]
- for i in range(self.num_nodes)]
- self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, extra_args)
+ self.extra_args = [['-banscore='+str(banscore)]]
def run_test(self):
no_version_bannode = CNodeNoVersionBan()
@@ -121,7 +109,9 @@ class P2PLeakTest(BitcoinTestFramework):
NetworkThread().start() # Start up network handling in another thread
- assert(wait_until(lambda: no_version_bannode.connected and no_version_idlenode.connected and no_verack_idlenode.version_received, timeout=10))
+ assert wait_until(lambda: no_version_bannode.ever_connected, timeout=10)
+ assert wait_until(lambda: no_version_idlenode.ever_connected, timeout=10)
+ assert wait_until(lambda: no_verack_idlenode.version_received, timeout=10)
# Mine a block and make sure that it's not sent to the connected nodes
self.nodes[0].generate(1)
@@ -130,7 +120,7 @@ class P2PLeakTest(BitcoinTestFramework):
time.sleep(5)
#This node should have been banned
- assert(no_version_bannode.connection.state == "closed")
+ assert not no_version_bannode.connected
[conn.disconnect_node() for conn in connections]
diff --git a/test/functional/p2p-mempool.py b/test/functional/p2p-mempool.py
index 5064ce74aa..34ef249eea 100755
--- a/test/functional/p2p-mempool.py
+++ b/test/functional/p2p-mempool.py
@@ -12,82 +12,24 @@ from test_framework.mininode import *
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
-class TestNode(NodeConnCB):
- def __init__(self):
- super().__init__()
- self.connection = None
- self.ping_counter = 1
- self.last_pong = msg_pong()
- self.block_receive_map = {}
-
- def add_connection(self, conn):
- self.connection = conn
- self.peer_disconnected = False
-
- def on_inv(self, conn, message):
- pass
-
- # Track the last getdata message we receive (used in the test)
- def on_getdata(self, conn, message):
- self.last_getdata = message
-
- def on_block(self, conn, message):
- message.block.calc_sha256()
- try:
- self.block_receive_map[message.block.sha256] += 1
- except KeyError as e:
- self.block_receive_map[message.block.sha256] = 1
-
- # Spin until verack message is received from the node.
- # We use this to signal that our test can begin. This
- # is called from the testing thread, so it needs to acquire
- # the global lock.
- def wait_for_verack(self):
- def veracked():
- return self.verack_received
- return wait_until(veracked, timeout=10)
-
- def wait_for_disconnect(self):
- def disconnected():
- return self.peer_disconnected
- return wait_until(disconnected, timeout=10)
-
- # Wrapper for the NodeConn's send_message function
- def send_message(self, message):
- self.connection.send_message(message)
-
- def on_pong(self, conn, message):
- self.last_pong = message
-
- def on_close(self, conn):
- self.peer_disconnected = True
-
- def send_mempool(self):
- self.lastInv = []
- self.send_message(msg_mempool())
-
class P2PMempoolTests(BitcoinTestFramework):
def __init__(self):
super().__init__()
self.setup_clean_chain = True
- self.num_nodes = 2
-
- def setup_network(self):
- # Start a node with maxuploadtarget of 200 MB (/24h)
- self.nodes = []
- self.nodes.append(start_node(0, self.options.tmpdir, ["-peerbloomfilters=0"]))
+ self.num_nodes = 1
+ self.extra_args = [["-peerbloomfilters=0"]]
def run_test(self):
#connect a mininode
- aTestNode = TestNode()
+ aTestNode = NodeConnCB()
node = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], aTestNode)
aTestNode.add_connection(node)
NetworkThread().start()
aTestNode.wait_for_verack()
#request mempool
- aTestNode.send_mempool()
+ aTestNode.send_message(msg_mempool())
aTestNode.wait_for_disconnect()
#mininode must be disconnected at this point
diff --git a/test/functional/p2p-segwit.py b/test/functional/p2p-segwit.py
index cd7b788eb4..335777b2ab 100755
--- a/test/functional/p2p-segwit.py
+++ b/test/functional/p2p-segwit.py
@@ -8,7 +8,7 @@ from test_framework.mininode import *
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
from test_framework.script import *
-from test_framework.blocktools import create_block, create_coinbase, add_witness_commitment, WITNESS_COMMITMENT_HEADER
+from test_framework.blocktools import create_block, create_coinbase, add_witness_commitment, get_witness_script, WITNESS_COMMITMENT_HEADER
from test_framework.key import CECKey, CPubKey
import time
import random
@@ -35,79 +35,22 @@ def get_virtual_size(witness_block):
class TestNode(NodeConnCB):
def __init__(self):
super().__init__()
- self.connection = None
- self.ping_counter = 1
- self.last_pong = msg_pong(0)
- self.sleep_time = 0.05
self.getdataset = set()
- self.last_reject = None
-
- def add_connection(self, conn):
- self.connection = conn
-
- # Wrapper for the NodeConn's send_message function
- def send_message(self, message):
- self.connection.send_message(message)
-
- def on_inv(self, conn, message):
- self.last_inv = message
-
- def on_block(self, conn, message):
- self.last_block = message.block
- self.last_block.calc_sha256()
def on_getdata(self, conn, message):
for inv in message.inv:
self.getdataset.add(inv.hash)
- self.last_getdata = message
-
- def on_getheaders(self, conn, message):
- self.last_getheaders = message
-
- def on_pong(self, conn, message):
- self.last_pong = message
-
- def on_reject(self, conn, message):
- self.last_reject = message
-
- # Syncing helpers
- def sync(self, test_function, timeout=60):
- while timeout > 0:
- with mininode_lock:
- if test_function():
- return
- time.sleep(self.sleep_time)
- timeout -= self.sleep_time
- raise AssertionError("Sync failed to complete")
-
- def wait_for_block(self, blockhash, timeout=60):
- test_function = lambda: self.last_block != None and self.last_block.sha256 == blockhash
- self.sync(test_function, timeout)
- return
-
- def wait_for_getdata(self, timeout=60):
- test_function = lambda: self.last_getdata != None
- self.sync(test_function, timeout)
-
- def wait_for_getheaders(self, timeout=60):
- test_function = lambda: self.last_getheaders != None
- self.sync(test_function, timeout)
-
- def wait_for_inv(self, expected_inv, timeout=60):
- test_function = lambda: self.last_inv != expected_inv
- self.sync(test_function, timeout)
def announce_tx_and_wait_for_getdata(self, tx, timeout=60):
with mininode_lock:
- self.last_getdata = None
+ self.last_message.pop("getdata", None)
self.send_message(msg_inv(inv=[CInv(1, tx.sha256)]))
self.wait_for_getdata(timeout)
- return
def announce_block_and_wait_for_getdata(self, block, use_header, timeout=60):
with mininode_lock:
- self.last_getdata = None
- self.last_getheaders = None
+ self.last_message.pop("getdata", None)
+ self.last_message.pop("getheaders", None)
msg = msg_headers()
msg.headers = [ CBlockHeader(block) ]
if use_header:
@@ -117,11 +60,10 @@ class TestNode(NodeConnCB):
self.wait_for_getheaders()
self.send_message(msg)
self.wait_for_getdata()
- return
def announce_block(self, block, use_header):
with mininode_lock:
- self.last_getdata = None
+ self.last_message.pop("getdata", None)
if use_header:
msg = msg_headers()
msg.headers = [ CBlockHeader(block) ]
@@ -131,22 +73,22 @@ class TestNode(NodeConnCB):
def request_block(self, blockhash, inv_type, timeout=60):
with mininode_lock:
- self.last_block = None
+ self.last_message.pop("block", None)
self.send_message(msg_getdata(inv=[CInv(inv_type, blockhash)]))
self.wait_for_block(blockhash, timeout)
- return self.last_block
+ return self.last_message["block"].block
def test_transaction_acceptance(self, tx, with_witness, accepted, reason=None):
tx_message = msg_tx(tx)
if with_witness:
tx_message = msg_witness_tx(tx)
self.send_message(tx_message)
- self.sync_with_ping(60)
+ self.sync_with_ping()
assert_equal(tx.hash in self.connection.rpc.getrawmempool(), accepted)
if (reason != None and not accepted):
# Check the rejection reason as well.
with mininode_lock:
- assert_equal(self.last_reject.reason, reason)
+ assert_equal(self.last_message["reject"].reason, reason)
# Test whether a witness block had the correct effect on the tip
def test_witness_block(self, block, accepted, with_witness=True):
@@ -154,10 +96,9 @@ class TestNode(NodeConnCB):
self.send_message(msg_witness_block(block))
else:
self.send_message(msg_block(block))
- self.sync_with_ping(60)
+ self.sync_with_ping()
assert_equal(self.connection.rpc.getbestblockhash() == block.hash, accepted)
-
# Used to keep track of anyone-can-spend outputs that we can use in the tests
class UTXO(object):
def __init__(self, sha256, n, nValue):
@@ -183,17 +124,13 @@ class SegWitTest(BitcoinTestFramework):
super().__init__()
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", "-bip9params=segwit:0:0"]]
def setup_network(self):
- self.nodes = []
- self.nodes.append(start_node(0, self.options.tmpdir, ["-whitelist=127.0.0.1"]))
- # Start a node for testing IsStandard rules.
- self.nodes.append(start_node(1, self.options.tmpdir, ["-whitelist=127.0.0.1", "-acceptnonstdtxn=0"]))
+ self.setup_nodes()
connect_nodes(self.nodes[0], 1)
-
- # Disable segwit's bip9 parameter to simulate upgrading after activation.
- self.nodes.append(start_node(2, self.options.tmpdir, ["-whitelist=127.0.0.1", "-bip9params=segwit:0:0"]))
connect_nodes(self.nodes[0], 2)
+ self.sync_all()
''' Helpers '''
# Build a block on top of node0's tip.
@@ -228,7 +165,7 @@ class SegWitTest(BitcoinTestFramework):
block = self.build_next_block(nVersion=1)
block.solve()
self.test_node.send_message(msg_block(block))
- self.test_node.sync_with_ping(60) # make sure the block was processed
+ self.test_node.sync_with_ping() # make sure the block was processed
txid = block.vtx[0].sha256
self.nodes[0].generate(99) # let the block mature
@@ -244,7 +181,7 @@ class SegWitTest(BitcoinTestFramework):
assert_equal(msg_tx(tx).serialize(), msg_witness_tx(tx).serialize())
self.test_node.send_message(msg_witness_tx(tx))
- self.test_node.sync_with_ping(60) # make sure the tx was processed
+ self.test_node.sync_with_ping() # make sure the tx was processed
assert(tx.hash in self.nodes[0].getrawmempool())
# Save this transaction for later
self.utxo.append(UTXO(tx.sha256, 0, 49*100000000))
@@ -279,12 +216,12 @@ class SegWitTest(BitcoinTestFramework):
# TODO: fix synchronization so we can test reject reason
# Right now, bitcoind delays sending reject messages for blocks
# until the future, making synchronization here difficult.
- #assert_equal(self.test_node.last_reject.reason, "unexpected-witness")
+ #assert_equal(self.test_node.last_message["reject"].reason, "unexpected-witness")
# But it should not be permanently marked bad...
# Resend without witness information.
self.test_node.send_message(msg_block(block))
- self.test_node.sync_with_ping(60)
+ self.test_node.sync_with_ping()
assert_equal(self.nodes[0].getbestblockhash(), block.hash)
sync_blocks(self.nodes)
@@ -893,7 +830,7 @@ class SegWitTest(BitcoinTestFramework):
# Verify that if a peer doesn't set nServices to include NODE_WITNESS,
# the getdata is just for the non-witness portion.
self.old_node.announce_tx_and_wait_for_getdata(tx)
- assert(self.old_node.last_getdata.inv[0].type == 1)
+ assert(self.old_node.last_message["getdata"].inv[0].type == 1)
# Since we haven't delivered the tx yet, inv'ing the same tx from
# a witness transaction ought not result in a getdata.
@@ -1028,20 +965,20 @@ class SegWitTest(BitcoinTestFramework):
block1.solve()
self.test_node.announce_block_and_wait_for_getdata(block1, use_header=False)
- assert(self.test_node.last_getdata.inv[0].type == blocktype)
+ assert(self.test_node.last_message["getdata"].inv[0].type == blocktype)
self.test_node.test_witness_block(block1, True)
block2 = self.build_next_block(nVersion=4)
block2.solve()
self.test_node.announce_block_and_wait_for_getdata(block2, use_header=True)
- assert(self.test_node.last_getdata.inv[0].type == blocktype)
+ assert(self.test_node.last_message["getdata"].inv[0].type == blocktype)
self.test_node.test_witness_block(block2, True)
block3 = self.build_next_block(nVersion=(VB_TOP_BITS | (1<<15)))
block3.solve()
self.test_node.announce_block_and_wait_for_getdata(block3, use_header=True)
- assert(self.test_node.last_getdata.inv[0].type == blocktype)
+ assert(self.test_node.last_message["getdata"].inv[0].type == blocktype)
self.test_node.test_witness_block(block3, True)
# Check that we can getdata for witness blocks or regular blocks,
@@ -1250,9 +1187,9 @@ class SegWitTest(BitcoinTestFramework):
# Spending a higher version witness output is not allowed by policy,
# even with fRequireStandard=false.
self.test_node.test_transaction_acceptance(tx3, with_witness=True, accepted=False)
- self.test_node.sync_with_ping(60)
+ self.test_node.sync_with_ping()
with mininode_lock:
- assert(b"reserved for soft-fork upgrades" in self.test_node.last_reject.reason)
+ assert(b"reserved for soft-fork upgrades" in self.test_node.last_message["reject"].reason)
# Building a block with the transaction must be valid, however.
block = self.build_next_block()
@@ -1380,7 +1317,7 @@ class SegWitTest(BitcoinTestFramework):
for i in range(NUM_TESTS):
# Ping regularly to keep the connection alive
if (not i % 100):
- self.test_node.sync_with_ping(60)
+ self.test_node.sync_with_ping()
# Choose random number of inputs to use.
num_inputs = random.randint(1, 10)
# Create a slight bias for producing more utxos
@@ -1721,15 +1658,10 @@ class SegWitTest(BitcoinTestFramework):
assert('default_witness_commitment' in gbt_results)
witness_commitment = gbt_results['default_witness_commitment']
- # TODO: this duplicates some code from blocktools.py, would be nice
- # to refactor.
# Check that default_witness_commitment is present.
- block = CBlock()
- witness_root = block.get_merkle_root([ser_uint256(0), ser_uint256(txid)])
- check_commitment = uint256_from_str(hash256(ser_uint256(witness_root)+ser_uint256(0)))
- from test_framework.blocktools import WITNESS_COMMITMENT_HEADER
- output_data = WITNESS_COMMITMENT_HEADER + ser_uint256(check_commitment)
- script = CScript([OP_RETURN, output_data])
+ witness_root = CBlock.get_merkle_root([ser_uint256(0),
+ ser_uint256(txid)])
+ script = get_witness_script(witness_root, 0)
assert_equal(witness_commitment, bytes_to_hex_str(script))
# undo mocktime
diff --git a/test/functional/p2p-timeouts.py b/test/functional/p2p-timeouts.py
index de4edd6800..c3b29c215b 100755
--- a/test/functional/p2p-timeouts.py
+++ b/test/functional/p2p-timeouts.py
@@ -28,20 +28,9 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
class TestNode(NodeConnCB):
- def __init__(self):
- super().__init__()
- self.connected = False
- self.received_version = False
-
- def on_open(self, conn):
- self.connected = True
-
- def on_close(self, conn):
- self.connected = False
-
def on_version(self, conn, message):
# Don't send a verack in response
- self.received_version = True
+ pass
class TimeoutsTest(BitcoinTestFramework):
def __init__(self):
@@ -49,12 +38,6 @@ class TimeoutsTest(BitcoinTestFramework):
self.setup_clean_chain = True
self.num_nodes = 1
- def setup_network(self):
- self.nodes = []
-
- # Start up node0 to be a version 1, pre-segwit node.
- self.nodes = start_nodes(self.num_nodes, self.options.tmpdir)
-
def run_test(self):
# Setup the p2p connections and start up the network thread.
self.no_verack_node = TestNode() # never send verack
@@ -83,7 +66,7 @@ class TimeoutsTest(BitcoinTestFramework):
sleep(30)
- assert(self.no_verack_node.received_version)
+ assert "version" in self.no_verack_node.last_message
assert(self.no_verack_node.connected)
assert(self.no_version_node.connected)
diff --git a/test/functional/p2p-versionbits-warning.py b/test/functional/p2p-versionbits-warning.py
index cdf3b84878..41921fe14e 100755
--- a/test/functional/p2p-versionbits-warning.py
+++ b/test/functional/p2p-versionbits-warning.py
@@ -23,28 +23,10 @@ WARN_UNKNOWN_RULES_MINED = "Unknown block versions being mined! It's possible un
WARN_UNKNOWN_RULES_ACTIVE = "unknown new rules activated (versionbit {})".format(VB_UNKNOWN_BIT)
VB_PATTERN = re.compile("^Warning.*versionbit")
-# TestNode: bare-bones "peer". Used mostly as a conduit for a test to sending
-# p2p messages to a node, generating the messages in the main testing logic.
class TestNode(NodeConnCB):
- def __init__(self):
- super().__init__()
- self.connection = None
- self.ping_counter = 1
- self.last_pong = msg_pong()
-
- def add_connection(self, conn):
- self.connection = conn
-
def on_inv(self, conn, message):
pass
- # Wrapper for the NodeConn's send_message function
- def send_message(self, message):
- self.connection.send_message(message)
-
- def on_pong(self, conn, message):
- self.last_pong = message
-
class VersionBitsWarningTest(BitcoinTestFramework):
def __init__(self):
super().__init__()
@@ -57,7 +39,7 @@ class VersionBitsWarningTest(BitcoinTestFramework):
with open(self.alert_filename, 'w', encoding='utf8') as _:
pass
self.extra_args = [["-alertnotify=echo %s >> \"" + self.alert_filename + "\""]]
- self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, self.extra_args)
+ self.setup_nodes()
# Send numblocks blocks via peer with nVersionToUse set.
def send_blocks_with_version(self, peer, numblocks, nVersionToUse):
diff --git a/test/functional/preciousblock.py b/test/functional/preciousblock.py
index 30b0b5a301..04b41e76ba 100755
--- a/test/functional/preciousblock.py
+++ b/test/functional/preciousblock.py
@@ -41,7 +41,7 @@ class PreciousTest(BitcoinTestFramework):
self.num_nodes = 3
def setup_network(self):
- self.nodes = self.setup_nodes()
+ self.setup_nodes()
def run_test(self):
self.log.info("Ensure submitblock can in principle reorg to a competing chain")
diff --git a/test/functional/prioritise_transaction.py b/test/functional/prioritise_transaction.py
index 0b04ad17ab..a07923edba 100755
--- a/test/functional/prioritise_transaction.py
+++ b/test/functional/prioritise_transaction.py
@@ -14,17 +14,12 @@ class PrioritiseTransactionTest(BitcoinTestFramework):
super().__init__()
self.setup_clean_chain = True
self.num_nodes = 1
+ self.extra_args = [["-printpriority=1"]]
+ def run_test(self):
self.txouts = gen_return_txouts()
-
- def setup_network(self):
- self.nodes = []
- self.is_network_split = False
-
- self.nodes.append(start_node(0, self.options.tmpdir, ["-printpriority=1"]))
self.relayfee = self.nodes[0].getnetworkinfo()['relayfee']
- def run_test(self):
utxo_count = 90
utxos = create_confirmed_utxos(self.relayfee, self.nodes[0], utxo_count)
base_fee = self.relayfee*100 # our transactions are smaller than 100kb
diff --git a/test/functional/proxy_test.py b/test/functional/proxy_test.py
index 748e3e69f6..69384d9d85 100755
--- a/test/functional/proxy_test.py
+++ b/test/functional/proxy_test.py
@@ -90,7 +90,7 @@ class ProxyTest(BitcoinTestFramework):
]
if self.have_ipv6:
args[3] = ['-listen', '-proxy=[%s]:%i' % (self.conf3.addr),'-proxyrandomize=0', '-noonion']
- return start_nodes(self.num_nodes, self.options.tmpdir, extra_args=args)
+ self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, extra_args=args)
def node_test(self, node, proxies, auth, test_onion=True):
rv = []
diff --git a/test/functional/pruning.py b/test/functional/pruning.py
index cc84c8c085..17019c658b 100755
--- a/test/functional/pruning.py
+++ b/test/functional/pruning.py
@@ -32,31 +32,21 @@ class PruneTest(BitcoinTestFramework):
self.setup_clean_chain = True
self.num_nodes = 6
- # Cache for utxos, as the listunspent may take a long time later in the test
- self.utxo_cache_0 = []
- self.utxo_cache_1 = []
-
- def setup_network(self):
- self.nodes = []
- self.is_network_split = False
-
- # Create nodes 0 and 1 to mine
- self.nodes.append(start_node(0, self.options.tmpdir, ["-maxreceivebuffer=20000","-blockmaxsize=999000", "-checkblocks=5"], timewait=900))
- self.nodes.append(start_node(1, self.options.tmpdir, ["-maxreceivebuffer=20000","-blockmaxsize=999000", "-checkblocks=5"], timewait=900))
-
- # Create node 2 to test pruning
- self.nodes.append(start_node(2, self.options.tmpdir, ["-maxreceivebuffer=20000","-prune=550"], timewait=900))
- self.prunedir = self.options.tmpdir+"/node2/regtest/blocks/"
-
+ # Create nodes 0 and 1 to mine.
+ # Create node 2 to test pruning.
# Create nodes 3 and 4 to test manual pruning (they will be re-started with manual pruning later)
- self.nodes.append(start_node(3, self.options.tmpdir, ["-maxreceivebuffer=20000","-blockmaxsize=999000"], timewait=900))
- self.nodes.append(start_node(4, self.options.tmpdir, ["-maxreceivebuffer=20000","-blockmaxsize=999000"], timewait=900))
-
# Create nodes 5 to test wallet in prune mode, but do not connect
- self.nodes.append(start_node(5, self.options.tmpdir, ["-prune=550"]))
+ self.extra_args = [["-maxreceivebuffer=20000", "-blockmaxsize=999000", "-checkblocks=5"],
+ ["-maxreceivebuffer=20000", "-blockmaxsize=999000", "-checkblocks=5"],
+ ["-maxreceivebuffer=20000", "-prune=550"],
+ ["-maxreceivebuffer=20000", "-blockmaxsize=999000"],
+ ["-maxreceivebuffer=20000", "-blockmaxsize=999000"],
+ ["-prune=550"]]
- # Determine default relay fee
- self.relayfee = self.nodes[0].getnetworkinfo()["relayfee"]
+ def setup_network(self):
+ self.setup_nodes()
+
+ self.prunedir = self.options.tmpdir + "/node2/regtest/blocks/"
connect_nodes(self.nodes[0], 1)
connect_nodes(self.nodes[1], 2)
@@ -332,6 +322,14 @@ class PruneTest(BitcoinTestFramework):
def run_test(self):
self.log.info("Warning! This test requires 4GB of disk space and takes over 30 mins (up to 2 hours)")
self.log.info("Mining a big blockchain of 995 blocks")
+
+ # Determine default relay fee
+ self.relayfee = self.nodes[0].getnetworkinfo()["relayfee"]
+
+ # Cache for utxos, as the listunspent may take a long time later in the test
+ self.utxo_cache_0 = []
+ self.utxo_cache_1 = []
+
self.create_big_chain()
# Chain diagram key:
# * blocks on main chain
diff --git a/test/functional/rawtransactions.py b/test/functional/rawtransactions.py
index d4c684136c..35debf9cab 100755
--- a/test/functional/rawtransactions.py
+++ b/test/functional/rawtransactions.py
@@ -24,21 +24,9 @@ class RawTransactionsTest(BitcoinTestFramework):
self.num_nodes = 3
def setup_network(self, split=False):
- self.nodes = start_nodes(self.num_nodes, self.options.tmpdir)
-
- #connect to a local machine for debugging
- #url = "http://bitcoinrpc:DP6DvqZtqXarpeNWyN3LZTFchCCyCUuHwNF7E8pX99x1@%s:%d" % ('127.0.0.1', 18332)
- #proxy = AuthServiceProxy(url)
- #proxy.url = url # store URL on proxy for info
- #self.nodes.append(proxy)
-
- connect_nodes_bi(self.nodes,0,1)
- connect_nodes_bi(self.nodes,1,2)
+ super().setup_network()
connect_nodes_bi(self.nodes,0,2)
- self.is_network_split=False
- self.sync_all()
-
def run_test(self):
#prepare some coins for multiple *rawtransaction commands
diff --git a/test/functional/receivedby.py b/test/functional/receivedby.py
index 248bcdbd68..a1cae301c5 100755
--- a/test/functional/receivedby.py
+++ b/test/functional/receivedby.py
@@ -32,7 +32,7 @@ class ReceivedByTest(BitcoinTestFramework):
def setup_nodes(self):
#This test requires mocktime
enable_mocktime()
- return start_nodes(self.num_nodes, self.options.tmpdir)
+ self.nodes = start_nodes(self.num_nodes, self.options.tmpdir)
def run_test(self):
'''
diff --git a/test/functional/reindex.py b/test/functional/reindex.py
index 0cebb0466f..8b8c5f3e71 100755
--- a/test/functional/reindex.py
+++ b/test/functional/reindex.py
@@ -24,9 +24,6 @@ class ReindexTest(BitcoinTestFramework):
self.setup_clean_chain = True
self.num_nodes = 1
- def setup_network(self):
- self.nodes = start_nodes(self.num_nodes, self.options.tmpdir)
-
def reindex(self, justchainstate=False):
self.nodes[0].generate(3)
blockcount = self.nodes[0].getblockcount()
diff --git a/test/functional/replace-by-fee.py b/test/functional/replace-by-fee.py
index 163c304eba..e940ce535c 100755
--- a/test/functional/replace-by-fee.py
+++ b/test/functional/replace-by-fee.py
@@ -65,17 +65,12 @@ class ReplaceByFeeTest(BitcoinTestFramework):
super().__init__()
self.num_nodes = 1
self.setup_clean_chain = False
-
- def setup_network(self):
- self.nodes = []
- self.nodes.append(start_node(0, self.options.tmpdir, ["-maxorphantx=1000",
- "-whitelist=127.0.0.1",
- "-limitancestorcount=50",
- "-limitancestorsize=101",
- "-limitdescendantcount=200",
- "-limitdescendantsize=101"
- ]))
- self.is_network_split = False
+ self.extra_args= [["-maxorphantx=1000",
+ "-whitelist=127.0.0.1",
+ "-limitancestorcount=50",
+ "-limitancestorsize=101",
+ "-limitdescendantcount=200",
+ "-limitdescendantsize=101"]]
def run_test(self):
make_utxo(self.nodes[0], 1*COIN)
diff --git a/test/functional/rest.py b/test/functional/rest.py
index 776211d301..fbcceba0fa 100755
--- a/test/functional/rest.py
+++ b/test/functional/rest.py
@@ -49,12 +49,8 @@ class RESTTest (BitcoinTestFramework):
self.num_nodes = 3
def setup_network(self, split=False):
- self.nodes = start_nodes(self.num_nodes, self.options.tmpdir)
- connect_nodes_bi(self.nodes,0,1)
- connect_nodes_bi(self.nodes,1,2)
- connect_nodes_bi(self.nodes,0,2)
- self.is_network_split=False
- self.sync_all()
+ super().setup_network()
+ connect_nodes_bi(self.nodes, 0, 2)
def run_test(self):
url = urllib.parse.urlparse(self.nodes[0].url)
diff --git a/test/functional/rpcnamedargs.py b/test/functional/rpcnamedargs.py
index f6175c8ca7..fd81b02ead 100755
--- a/test/functional/rpcnamedargs.py
+++ b/test/functional/rpcnamedargs.py
@@ -22,11 +22,6 @@ class NamedArgumentTest(BitcoinTestFramework):
self.setup_clean_chain = False
self.num_nodes = 1
- def setup_network(self, split=False):
- self.nodes = start_nodes(self.num_nodes, self.options.tmpdir)
- self.is_network_split = False
- self.sync_all()
-
def run_test(self):
node = self.nodes[0]
h = node.help(command='getinfo')
diff --git a/test/functional/segwit.py b/test/functional/segwit.py
index a1fffcb81a..ac95d66466 100755
--- a/test/functional/segwit.py
+++ b/test/functional/segwit.py
@@ -80,16 +80,13 @@ class SegWitTest(BitcoinTestFramework):
super().__init__()
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"]]
def setup_network(self):
- self.nodes = []
- self.nodes.append(start_node(0, self.options.tmpdir, ["-walletprematurewitness", "-rpcserialversion=0"]))
- self.nodes.append(start_node(1, self.options.tmpdir, ["-blockversion=4", "-promiscuousmempoolflags=517", "-prematurewitness", "-walletprematurewitness", "-rpcserialversion=1"]))
- self.nodes.append(start_node(2, self.options.tmpdir, ["-blockversion=536870915", "-promiscuousmempoolflags=517", "-prematurewitness", "-walletprematurewitness"]))
- connect_nodes(self.nodes[1], 0)
- connect_nodes(self.nodes[2], 1)
+ super().setup_network()
connect_nodes(self.nodes[0], 2)
- self.is_network_split = False
self.sync_all()
def success_mine(self, node, txid, sign, redeem_script=""):
diff --git a/test/functional/sendheaders.py b/test/functional/sendheaders.py
index 1a7475ae84..97d64c0d98 100755
--- a/test/functional/sendheaders.py
+++ b/test/functional/sendheaders.py
@@ -81,23 +81,17 @@ from test_framework.blocktools import create_block, create_coinbase
direct_fetch_response_time = 0.05
-class BaseNode(NodeConnCB):
+class TestNode(NodeConnCB):
def __init__(self):
super().__init__()
- self.last_inv = None
- self.last_headers = None
- self.last_block = None
- self.last_getdata = None
self.block_announced = False
- self.last_getheaders = None
- self.disconnected = False
self.last_blockhash_announced = None
def clear_last_announcement(self):
with mininode_lock:
self.block_announced = False
- self.last_inv = None
- self.last_headers = None
+ self.last_message.pop("inv", None)
+ self.last_message.pop("headers", None)
# Request data for a list of block hashes
def get_data(self, block_hashes):
@@ -118,29 +112,17 @@ class BaseNode(NodeConnCB):
self.connection.send_message(msg)
def on_inv(self, conn, message):
- self.last_inv = message
self.block_announced = True
self.last_blockhash_announced = message.inv[-1].hash
def on_headers(self, conn, message):
- self.last_headers = message
if len(message.headers):
self.block_announced = True
message.headers[-1].calc_sha256()
self.last_blockhash_announced = message.headers[-1].sha256
def on_block(self, conn, message):
- self.last_block = message.block
- self.last_block.calc_sha256()
-
- def on_getdata(self, conn, message):
- self.last_getdata = message
-
- def on_getheaders(self, conn, message):
- self.last_getheaders = message
-
- def on_close(self, conn):
- self.disconnected = True
+ self.last_message["block"].calc_sha256()
# Test whether the last announcement we received had the
# right header or the right inv
@@ -155,43 +137,27 @@ class BaseNode(NodeConnCB):
success = True
compare_inv = []
- if self.last_inv != None:
- compare_inv = [x.hash for x in self.last_inv.inv]
+ if "inv" in self.last_message:
+ compare_inv = [x.hash for x in self.last_message["inv"].inv]
if compare_inv != expect_inv:
success = False
hash_headers = []
- if self.last_headers != None:
+ if "headers" in self.last_message:
# treat headers as a list of block hashes
- hash_headers = [ x.sha256 for x in self.last_headers.headers ]
+ hash_headers = [ x.sha256 for x in self.last_message["headers"].headers ]
if hash_headers != expect_headers:
success = False
- self.last_inv = None
- self.last_headers = None
+ self.last_message.pop("inv", None)
+ self.last_message.pop("headers", None)
return success
- # Syncing helpers
- def wait_for_block(self, blockhash, timeout=60):
- test_function = lambda: self.last_block != None and self.last_block.sha256 == blockhash
- assert(wait_until(test_function, timeout=timeout))
- return
-
- def wait_for_getheaders(self, timeout=60):
- test_function = lambda: self.last_getheaders != None
- assert(wait_until(test_function, timeout=timeout))
- return
-
def wait_for_getdata(self, hash_list, timeout=60):
if hash_list == []:
return
- test_function = lambda: self.last_getdata != None and [x.hash for x in self.last_getdata.inv] == hash_list
- assert(wait_until(test_function, timeout=timeout))
- return
-
- def wait_for_disconnect(self, timeout=60):
- test_function = lambda: self.disconnected
+ test_function = lambda: "getdata" in self.last_message and [x.hash for x in self.last_message["getdata"].inv] == hash_list
assert(wait_until(test_function, timeout=timeout))
return
@@ -210,28 +176,12 @@ class BaseNode(NodeConnCB):
getblocks_message.locator.vHave = locator
self.send_message(getblocks_message)
-# InvNode: This peer should only ever receive inv's, because it doesn't ever send a
-# "sendheaders" message.
-class InvNode(BaseNode):
- def __init__(self):
- BaseNode.__init__(self)
-
-# TestNode: This peer is the one we use for most of the testing.
-class TestNode(BaseNode):
- def __init__(self):
- BaseNode.__init__(self)
-
class SendHeadersTest(BitcoinTestFramework):
def __init__(self):
super().__init__()
self.setup_clean_chain = True
self.num_nodes = 2
- def setup_network(self):
- self.nodes = []
- self.nodes = start_nodes(self.num_nodes, self.options.tmpdir)
- connect_nodes(self.nodes[0], 1)
-
# mine count blocks and return the new tip
def mine_blocks(self, count):
# Clear out last block announcement from each p2p listener
@@ -260,7 +210,7 @@ class SendHeadersTest(BitcoinTestFramework):
def run_test(self):
# Setup the p2p connections and start up the network thread.
- inv_node = InvNode()
+ inv_node = TestNode()
test_node = TestNode()
self.p2p_connections = [inv_node, test_node]
@@ -368,8 +318,8 @@ class SendHeadersTest(BitcoinTestFramework):
inv_node.sync_with_ping()
# This block should not be announced to the inv node (since it also
# broadcast it)
- assert_equal(inv_node.last_inv, None)
- assert_equal(inv_node.last_headers, None)
+ assert "inv" not in inv_node.last_message
+ assert "headers" not in inv_node.last_message
tip = self.mine_blocks(1)
assert_equal(inv_node.check_last_announcement(inv=[tip]), True)
assert_equal(test_node.check_last_announcement(headers=[tip]), True)
@@ -459,12 +409,12 @@ class SendHeadersTest(BitcoinTestFramework):
inv_node.send_message(msg_block(blocks[-1]))
inv_node.sync_with_ping() # Make sure blocks are processed
- test_node.last_getdata = None
+ test_node.last_message.pop("getdata", None)
test_node.send_header_for_blocks(blocks)
test_node.sync_with_ping()
# should not have received any getdata messages
with mininode_lock:
- assert_equal(test_node.last_getdata, None)
+ assert "getdata" not in test_node.last_message
# This time, direct fetch should work
blocks = []
@@ -498,11 +448,11 @@ class SendHeadersTest(BitcoinTestFramework):
# Announcing one block on fork should not trigger direct fetch
# (less work than tip)
- test_node.last_getdata = None
+ test_node.last_message.pop("getdata", None)
test_node.send_header_for_blocks(blocks[0:1])
test_node.sync_with_ping()
with mininode_lock:
- assert_equal(test_node.last_getdata, None)
+ assert "getdata" not in test_node.last_message
# Announcing one more block on fork should trigger direct fetch for
# both blocks (same work as tip)
@@ -517,11 +467,11 @@ class SendHeadersTest(BitcoinTestFramework):
test_node.wait_for_getdata([x.sha256 for x in blocks[2:16]], timeout=direct_fetch_response_time)
# Announcing 1 more header should not trigger any response
- test_node.last_getdata = None
+ test_node.last_message.pop("getdata", None)
test_node.send_header_for_blocks(blocks[18:19])
test_node.sync_with_ping()
with mininode_lock:
- assert_equal(test_node.last_getdata, None)
+ assert "getdata" not in test_node.last_message
self.log.info("Part 4: success!")
@@ -532,7 +482,7 @@ class SendHeadersTest(BitcoinTestFramework):
# First we test that receipt of an unconnecting header doesn't prevent
# chain sync.
for i in range(10):
- test_node.last_getdata = None
+ test_node.last_message.pop("getdata", None)
blocks = []
# Create two more blocks.
for j in range(2):
@@ -543,7 +493,7 @@ class SendHeadersTest(BitcoinTestFramework):
height += 1
# Send the header of the second block -> this won't connect.
with mininode_lock:
- test_node.last_getheaders = None
+ test_node.last_message.pop("getheaders", None)
test_node.send_header_for_blocks([blocks[1]])
test_node.wait_for_getheaders(timeout=1)
test_node.send_header_for_blocks(blocks)
@@ -566,7 +516,7 @@ class SendHeadersTest(BitcoinTestFramework):
for i in range(1, MAX_UNCONNECTING_HEADERS):
# Send a header that doesn't connect, check that we get a getheaders.
with mininode_lock:
- test_node.last_getheaders = None
+ test_node.last_message.pop("getheaders", None)
test_node.send_header_for_blocks([blocks[i]])
test_node.wait_for_getheaders(timeout=1)
@@ -581,25 +531,21 @@ class SendHeadersTest(BitcoinTestFramework):
for i in range(5*MAX_UNCONNECTING_HEADERS - 1):
# Send a header that doesn't connect, check that we get a getheaders.
with mininode_lock:
- test_node.last_getheaders = None
+ test_node.last_message.pop("getheaders", None)
test_node.send_header_for_blocks([blocks[i%len(blocks)]])
test_node.wait_for_getheaders(timeout=1)
# Eventually this stops working.
- with mininode_lock:
- self.last_getheaders = None
test_node.send_header_for_blocks([blocks[-1]])
# Should get disconnected
test_node.wait_for_disconnect()
- with mininode_lock:
- self.last_getheaders = True
self.log.info("Part 5: success!")
# Finally, check that the inv node never received a getdata request,
# throughout the test
- assert_equal(inv_node.last_getdata, None)
+ assert "getdata" not in inv_node.last_message
if __name__ == '__main__':
SendHeadersTest().main()
diff --git a/test/functional/signmessages.py b/test/functional/signmessages.py
index 91f5abef5d..bfde8e40ec 100755
--- a/test/functional/signmessages.py
+++ b/test/functional/signmessages.py
@@ -14,10 +14,6 @@ class SignMessagesTest(BitcoinTestFramework):
self.setup_clean_chain = True
self.num_nodes = 1
- def setup_network(self, split=False):
- self.nodes = start_nodes(self.num_nodes, self.options.tmpdir)
- self.is_network_split = False
-
def run_test(self):
message = 'This is just a test message'
diff --git a/test/functional/signrawtransactions.py b/test/functional/signrawtransactions.py
index b24162ab97..fc49c23b9f 100755
--- a/test/functional/signrawtransactions.py
+++ b/test/functional/signrawtransactions.py
@@ -14,10 +14,6 @@ class SignRawTransactionsTest(BitcoinTestFramework):
self.setup_clean_chain = True
self.num_nodes = 1
- def setup_network(self, split=False):
- self.nodes = start_nodes(self.num_nodes, self.options.tmpdir)
- self.is_network_split = False
-
def successful_signing_test(self):
"""Create and sign a valid raw transaction with one input.
diff --git a/test/functional/smartfees.py b/test/functional/smartfees.py
index b7f4b86e7b..19ffe9e851 100755
--- a/test/functional/smartfees.py
+++ b/test/functional/smartfees.py
@@ -203,7 +203,6 @@ class EstimateFeeTest(BitcoinTestFramework):
connect_nodes(self.nodes[0], 2)
connect_nodes(self.nodes[2], 1)
- self.is_network_split = False
self.sync_all()
def transact_and_mine(self, numblocks, mining_node):
diff --git a/test/functional/test_framework/blocktools.py b/test/functional/test_framework/blocktools.py
index 2c9a0857df..5dcf516dc6 100644
--- a/test/functional/test_framework/blocktools.py
+++ b/test/functional/test_framework/blocktools.py
@@ -25,6 +25,13 @@ def create_block(hashprev, coinbase, nTime=None):
# From BIP141
WITNESS_COMMITMENT_HEADER = b"\xaa\x21\xa9\xed"
+
+def get_witness_script(witness_root, witness_nonce):
+ witness_commitment = uint256_from_str(hash256(ser_uint256(witness_root)+ser_uint256(witness_nonce)))
+ output_data = WITNESS_COMMITMENT_HEADER + ser_uint256(witness_commitment)
+ return CScript([OP_RETURN, output_data])
+
+
# According to BIP141, blocks with witness rules active must commit to the
# hash of all in-block transactions including witness.
def add_witness_commitment(block, nonce=0):
@@ -32,14 +39,12 @@ def add_witness_commitment(block, nonce=0):
# transactions, with witnesses.
witness_nonce = nonce
witness_root = block.calc_witness_merkle_root()
- witness_commitment = uint256_from_str(hash256(ser_uint256(witness_root)+ser_uint256(witness_nonce)))
# witness_nonce should go to coinbase witness.
block.vtx[0].wit.vtxinwit = [CTxInWitness()]
block.vtx[0].wit.vtxinwit[0].scriptWitness.stack = [ser_uint256(witness_nonce)]
# witness commitment is the last OP_RETURN output in coinbase
- output_data = WITNESS_COMMITMENT_HEADER + ser_uint256(witness_commitment)
- block.vtx[0].vout.append(CTxOut(0, CScript([OP_RETURN, output_data])))
+ block.vtx[0].vout.append(CTxOut(0, get_witness_script(witness_root, witness_nonce)))
block.vtx[0].rehash()
block.hashMerkleRoot = block.calc_merkle_root()
block.rehash()
diff --git a/test/functional/test_framework/comptool.py b/test/functional/test_framework/comptool.py
index 25c18bda82..9f062865a3 100755
--- a/test/functional/test_framework/comptool.py
+++ b/test/functional/test_framework/comptool.py
@@ -192,9 +192,7 @@ class TestManager(object):
return wait_until(disconnected, timeout=10)
def wait_for_verack(self):
- def veracked():
- return all(node.verack_received for node in self.test_nodes)
- return wait_until(veracked, timeout=10)
+ return all(node.wait_for_verack() for node in self.test_nodes)
def wait_for_pings(self, counter):
def received_pongs():
diff --git a/test/functional/test_framework/mininode.py b/test/functional/test_framework/mininode.py
index ebb846a237..03db0d1092 100755
--- a/test/functional/test_framework/mininode.py
+++ b/test/functional/test_framework/mininode.py
@@ -20,21 +20,22 @@ msg_block, msg_tx, msg_headers, etc.:
ser_*, deser_*: functions that handle serialization/deserialization
"""
-import struct
-import socket
import asyncore
-import time
-import sys
-import random
-from .util import hex_str_to_bytes, bytes_to_hex_str
-from io import BytesIO
from codecs import encode
+from collections import defaultdict
+import copy
import hashlib
-from threading import RLock
-from threading import Thread
+from io import BytesIO
import logging
-import copy
+import random
+import socket
+import struct
+import sys
+import time
+from threading import RLock, Thread
+
from test_framework.siphash import siphash256
+from test_framework.util import hex_str_to_bytes, bytes_to_hex_str
BIP0031_VERSION = 60000
MY_VERSION = 70014 # past bip-31 for ping/pong
@@ -610,7 +611,8 @@ class CBlock(CBlockHeader):
return r
# Calculate the merkle root given a vector of transaction hashes
- def get_merkle_root(self, hashes):
+ @classmethod
+ def get_merkle_root(cls, hashes):
while len(hashes) > 1:
newhashes = []
for i in range(0, len(hashes), 2):
@@ -1465,30 +1467,57 @@ class msg_witness_blocktxn(msg_blocktxn):
r += self.block_transactions.serialize(with_witness=True)
return r
-# This is what a callback should look like for NodeConn
-# Reimplement the on_* functions to provide handling for events
class NodeConnCB(object):
+ """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.
+ """
+
def __init__(self):
- self.verack_received = False
+ # Track whether we have a P2P connection open to the node
+ self.connected = False
+ self.connection = None
+
+ # Track number of messages of each type received and the most recent
+ # message of each type
+ self.message_count = defaultdict(int)
+ self.last_message = {}
+
+ # 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
+
# Remember the services our peer has advertised
self.peer_services = None
- self.connection = None
- self.ping_counter = 1
- self.last_pong = msg_pong()
+
+ # 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)
with mininode_lock:
try:
- getattr(self, 'on_' + message.command.decode('ascii'))(conn, message)
+ command = message.command.decode('ascii')
+ self.message_count[command] += 1
+ self.last_message[command] = message
+ getattr(self, 'on_' + command)(conn, message)
except:
- logger.exception("ERROR delivering %s" % repr(message))
+ print("ERROR delivering %s (%s)" % (repr(message),
+ sys.exc_info()[0]))
def set_deliver_sleep_time(self, value):
with mininode_lock:
@@ -1498,14 +1527,20 @@ class NodeConnCB(object):
with mininode_lock:
return self.deliver_sleep_time
- # Callbacks which can be overridden by subclasses
- #################################################
+ # Callback methods. Can be overridden by subclasses in individual test
+ # cases to provide custom message handling behaviour.
+
+ def on_open(self, conn):
+ self.connected = True
+
+ def on_close(self, conn):
+ self.connected = False
+ 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_close(self, conn): pass
def on_cmpctblock(self, conn, message): pass
def on_feefilter(self, conn, message): pass
def on_getaddr(self, conn, message): pass
@@ -1515,7 +1550,7 @@ class NodeConnCB(object):
def on_getheaders(self, conn, message): pass
def on_headers(self, conn, message): pass
def on_mempool(self, conn): pass
- def on_open(self, conn): pass
+ def on_pong(self, conn, message): pass
def on_reject(self, conn, message): pass
def on_sendcmpct(self, conn, message): pass
def on_sendheaders(self, conn, message): pass
@@ -1533,9 +1568,6 @@ class NodeConnCB(object):
if conn.ver_send > BIP0031_VERSION:
conn.send_message(msg_pong(message.nonce))
- def on_pong(self, conn, message):
- self.last_pong = message
-
def on_verack(self, conn, message):
conn.ver_recv = conn.ver_send
self.verack_received = True
@@ -1548,15 +1580,44 @@ class NodeConnCB(object):
conn.ver_recv = conn.ver_send
conn.nServices = message.nServices
- # Helper functions
- ##################
+ # Connection helper methods
def add_connection(self, conn):
self.connection = conn
- # Wrapper for the NodeConn's send_message function
+ def wait_for_disconnect(self, timeout=60):
+ test_function = lambda: not self.connected
+ assert wait_until(test_function, timeout=timeout)
+
+ # Message receiving helper methods
+
+ def wait_for_block(self, blockhash, timeout=60):
+ test_function = lambda: self.last_message.get("block") and self.last_message["block"].block.rehash() == blockhash
+ assert wait_until(test_function, timeout=timeout)
+
+ def wait_for_getdata(self, timeout=60):
+ test_function = lambda: self.last_message.get("getdata")
+ assert wait_until(test_function, timeout=timeout)
+
+ def wait_for_getheaders(self, timeout=60):
+ test_function = lambda: self.last_message.get("getheaders")
+ assert wait_until(test_function, timeout=timeout)
+
+ def wait_for_inv(self, expected_inv, timeout=60):
+ test_function = lambda: self.last_message.get("inv") and self.last_message["inv"] != expected_inv
+ assert wait_until(test_function, timeout=timeout)
+
+ def wait_for_verack(self, timeout=60):
+ test_function = lambda: self.message_count["verack"]
+ assert wait_until(test_function, timeout=timeout)
+
+ # Message sending helper functions
+
def send_message(self, message):
- self.connection.send_message(message)
+ if self.connection:
+ self.connection.send_message(message)
+ else:
+ logger.error("Cannot send message. No connection to node!")
def send_and_ping(self, message):
self.send_message(message)
@@ -1564,27 +1625,11 @@ class NodeConnCB(object):
# Sync up with the node
def sync_with_ping(self, timeout=60):
- def received_pong():
- return (self.last_pong.nonce == self.ping_counter)
self.send_message(msg_ping(nonce=self.ping_counter))
- success = wait_until(received_pong, timeout=timeout)
- if not success:
- logger.error("sync_with_ping failed!")
- raise AssertionError("sync_with_ping failed!")
+ test_function = lambda: self.last_message.get("pong") and self.last_message["pong"].nonce == self.ping_counter
+ assert wait_until(test_function, timeout=timeout)
self.ping_counter += 1
-
- return success
-
- # Spin until verack message is received from the node.
- # Tests may want to use this as a signal that the test can begin.
- # This can be called from the testing thread, so it needs to acquire the
- # global lock.
- def wait_for_verack(self):
- while True:
- with mininode_lock:
- if self.verack_received:
- return
- time.sleep(0.05)
+ return True
# The actual NodeConn class
# This class provides an interface for a p2p connection to a specified node
@@ -1749,7 +1794,7 @@ class NodeConn(asyncore.dispatcher):
def send_message(self, message, pushbuf=False):
if self.state != "connected" and not pushbuf:
raise IOError('Not connected, no pushbuf')
- logger.debug("Send message to %s:%d: %s" % (self.dstaddr, self.dstport, repr(message)))
+ self._log_message("send", message)
command = message.command
data = message.serialize()
tmsg = self.MAGIC_BYTES[self.network]
@@ -1771,9 +1816,19 @@ class NodeConn(asyncore.dispatcher):
self.messagemap[b'ping'] = msg_ping_prebip31
if self.last_sent + 30 * 60 < time.time():
self.send_message(self.messagemap[b'ping']())
- logger.debug("Received message from %s:%d: %s" % (self.dstaddr, self.dstport, repr(message)))
+ self._log_message("receive", message)
self.cb.deliver(self, message)
+ def _log_message(self, direction, msg):
+ if direction == "send":
+ log_message = "Send message to "
+ elif direction == "receive":
+ log_message = "Received message from "
+ log_message += "%s:%d: %s" % (self.dstaddr, self.dstport, repr(msg)[:500])
+ if len(log_message) > 500:
+ log_message += "... (msg truncated)"
+ logger.debug(log_message)
+
def disconnect_node(self):
self.disconnect = True
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index 473b7c14a9..e912dcbaff 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -17,6 +17,7 @@ from .util import (
initialize_chain,
start_nodes,
connect_nodes_bi,
+ disconnect_nodes,
sync_blocks,
sync_mempools,
stop_nodes,
@@ -56,52 +57,42 @@ class BitcoinTestFramework(object):
stop_node(self.nodes[num_node], num_node)
def setup_nodes(self):
- return start_nodes(self.num_nodes, self.options.tmpdir)
+ extra_args = None
+ if hasattr(self, "extra_args"):
+ extra_args = self.extra_args
+ self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, extra_args)
- def setup_network(self, split = False):
- self.nodes = self.setup_nodes()
+ def setup_network(self):
+ self.setup_nodes()
# Connect the nodes as a "chain". This allows us
# to split the network between nodes 1 and 2 to get
# two halves that can work on competing chains.
-
- # If we joined network halves, connect the nodes from the joint
- # on outward. This ensures that chains are properly reorganised.
- if not split:
- connect_nodes_bi(self.nodes, 1, 2)
- sync_blocks(self.nodes[1:3])
- sync_mempools(self.nodes[1:3])
-
- connect_nodes_bi(self.nodes, 0, 1)
- connect_nodes_bi(self.nodes, 2, 3)
- self.is_network_split = split
+ for i in range(self.num_nodes - 1):
+ connect_nodes_bi(self.nodes, i, i + 1)
self.sync_all()
def split_network(self):
"""
Split the network of four nodes into nodes 0/1 and 2/3.
"""
- assert not self.is_network_split
- stop_nodes(self.nodes)
- self.setup_network(True)
-
- def sync_all(self):
- if self.is_network_split:
- sync_blocks(self.nodes[:2])
- sync_blocks(self.nodes[2:])
- sync_mempools(self.nodes[:2])
- sync_mempools(self.nodes[2:])
- else:
- sync_blocks(self.nodes)
- sync_mempools(self.nodes)
+ disconnect_nodes(self.nodes[1], 2)
+ disconnect_nodes(self.nodes[2], 1)
+ self.sync_all([self.nodes[:2], self.nodes[2:]])
+
+ def sync_all(self, node_groups=None):
+ if not node_groups:
+ node_groups = [self.nodes]
+
+ [sync_blocks(group) for group in node_groups]
+ [sync_mempools(group) for group in node_groups]
def join_network(self):
"""
Join the (previously split) network halves together.
"""
- assert self.is_network_split
- stop_nodes(self.nodes)
- self.setup_network(False)
+ connect_nodes_bi(self.nodes, 1, 2)
+ self.sync_all()
def main(self):
diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py
index 23ac324510..9186c3cbe9 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -315,7 +315,7 @@ def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary=
datadir = os.path.join(dirname, "node"+str(i))
if binary is None:
binary = os.getenv("BITCOIND", "bitcoind")
- args = [ binary, "-datadir="+datadir, "-server", "-keypool=1", "-discover=0", "-rest", "-logtimemicros", "-debug", "-mocktime="+str(get_mocktime()) ]
+ args = [binary, "-datadir=" + datadir, "-server", "-keypool=1", "-discover=0", "-rest", "-logtimemicros", "-debug", "-debugexclude=libevent", "-debugexclude=leveldb", "-mocktime=" + str(get_mocktime()), "-uacomment=testnode%d" % i]
if extra_args is not None: args.extend(extra_args)
bitcoind_processes[i] = subprocess.Popen(args, stderr=stderr)
logger.debug("initialize_chain: bitcoind started, waiting for RPC to come up")
@@ -354,6 +354,8 @@ def start_nodes(num_nodes, dirname, extra_args=None, rpchost=None, timewait=None
"""
if extra_args is None: extra_args = [ None for _ in range(num_nodes) ]
if binary is None: binary = [ None for _ in range(num_nodes) ]
+ assert_equal(len(extra_args), num_nodes)
+ assert_equal(len(binary), num_nodes)
rpcs = []
try:
for i in range(num_nodes):
@@ -385,6 +387,17 @@ def set_node_times(nodes, t):
for node in nodes:
node.setmocktime(t)
+def disconnect_nodes(from_connection, node_num):
+ for peer_id in [peer['id'] for peer in from_connection.getpeerinfo() if "testnode%d" % node_num in peer['subver']]:
+ from_connection.disconnectnode(nodeid=peer_id)
+
+ for _ in range(50):
+ if [peer['id'] for peer in from_connection.getpeerinfo() if "testnode%d" % node_num in peer['subver']] == []:
+ break
+ time.sleep(0.1)
+ else:
+ raise AssertionError("timed out waiting for disconnect")
+
def connect_nodes(from_connection, node_num):
ip_port = "127.0.0.1:"+str(p2p_port(node_num))
from_connection.addnode(ip_port, "onetry")
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index b1a5d2de94..0996b1bc20 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -25,6 +25,27 @@ import tempfile
import re
import logging
+# Formatting. Default colors to empty strings.
+BOLD, BLUE, RED, GREY = ("", ""), ("", ""), ("", ""), ("", "")
+try:
+ # Make sure python thinks it can write unicode to its stdout
+ "\u2713".encode("utf_8").decode(sys.stdout.encoding)
+ TICK = "✓ "
+ CROSS = "✖ "
+ CIRCLE = "○ "
+except UnicodeDecodeError:
+ TICK = "P "
+ CROSS = "x "
+ CIRCLE = "o "
+
+if os.name == 'posix':
+ # primitive formatting on supported
+ # terminal via ANSI escape sequences:
+ BOLD = ('\033[0m', '\033[1m')
+ BLUE = ('\033[0m', '\033[0;34m')
+ RED = ('\033[0m', '\033[0;31m')
+ GREY = ('\033[0m', '\033[1;30m')
+
TEST_EXIT_PASSED = 0
TEST_EXIT_SKIPPED = 77
@@ -56,6 +77,7 @@ BASE_SCRIPTS= [
'rawtransactions.py',
'reindex.py',
# vv Tests less than 30s vv
+ "zmq_test.py",
'mempool_resurrect_test.py',
'txn_doublespend.py --mineblock',
'txn_clone.py',
@@ -67,7 +89,7 @@ BASE_SCRIPTS= [
'multi_rpc.py',
'proxy_test.py',
'signrawtransactions.py',
- 'nodehandling.py',
+ 'disconnect_ban.py',
'decodescript.py',
'blockchain.py',
'disablewallet.py',
@@ -87,13 +109,9 @@ BASE_SCRIPTS= [
'rpcnamedargs.py',
'listsinceblock.py',
'p2p-leaktests.py',
+ 'import-abort-rescan.py',
]
-ZMQ_SCRIPTS = [
- # ZMQ test can only be run if bitcoin was built with zmq-enabled.
- # call test_runner.py with -nozmq to explicitly exclude these tests.
- 'zmq_test.py']
-
EXTENDED_SCRIPTS = [
# These tests are not run by the travis build process.
# Longest test should go first, to favor running tests in parallel
@@ -122,12 +140,12 @@ EXTENDED_SCRIPTS = [
'txn_clone.py --mineblock',
'forknotify.py',
'invalidateblock.py',
- 'maxblocksinflight.py',
'p2p-acceptblock.py',
'replace-by-fee.py',
]
-ALL_SCRIPTS = BASE_SCRIPTS + ZMQ_SCRIPTS + EXTENDED_SCRIPTS
+# Place EXTENDED_SCRIPTS first since it has the 3 longest running tests
+ALL_SCRIPTS = EXTENDED_SCRIPTS + BASE_SCRIPTS
NON_SCRIPTS = [
# These are python files that live in the functional tests directory, but are not test scripts.
@@ -150,8 +168,8 @@ def main():
parser.add_argument('--force', '-f', action='store_true', help='run tests even on platforms where they are disabled by default (e.g. windows).')
parser.add_argument('--help', '-h', '-?', action='store_true', help='print help text and exit')
parser.add_argument('--jobs', '-j', type=int, default=4, help='how many test scripts to run in parallel. Default=4.')
+ parser.add_argument('--keepcache', '-k', action='store_true', help='the default behavior is to flush the cache directory on startup. --keepcache retains the cache from the previous testrun.')
parser.add_argument('--quiet', '-q', action='store_true', help='only print results summary and failure logs')
- parser.add_argument('--nozmq', action='store_true', help='do not run the zmq tests')
args, unknown_args = parser.parse_known_args()
# Create a set to store arguments and create the passon string
@@ -169,7 +187,6 @@ def main():
enable_wallet = config["components"].getboolean("ENABLE_WALLET")
enable_utils = config["components"].getboolean("ENABLE_UTILS")
enable_bitcoind = config["components"].getboolean("ENABLE_BITCOIND")
- enable_zmq = config["components"].getboolean("ENABLE_ZMQ") and not args.nozmq
if config["environment"]["EXEEXT"] == ".exe" and not args.force:
# https://github.com/bitcoin/bitcoin/commit/d52802551752140cf41f0d9a225a43e84404d3e9
@@ -182,15 +199,6 @@ def main():
print("Rerun `configure` with -enable-wallet, -with-utils and -with-daemon and rerun make")
sys.exit(0)
- # python3-zmq may not be installed. Handle this gracefully and with some helpful info
- if enable_zmq:
- try:
- import zmq
- except ImportError:
- print("ERROR: \"import zmq\" failed. Use -nozmq to run without the ZMQ tests."
- "To run zmq tests, see dependency info in /test/README.md.")
- raise
-
# Build list of tests
if tests:
# Individual tests have been specified. Run specified tests that exist
@@ -198,16 +206,13 @@ def main():
test_list = [t for t in ALL_SCRIPTS if
(t in tests or re.sub(".py$", "", t) in tests)]
else:
- # No individual tests have been specified. Run base tests, and
- # optionally ZMQ tests and extended tests.
+ # No individual tests have been specified.
+ # Run all base tests, and optionally run extended tests.
test_list = BASE_SCRIPTS
- if enable_zmq:
- test_list += ZMQ_SCRIPTS
if args.extended:
- test_list += EXTENDED_SCRIPTS
- # TODO: BASE_SCRIPTS and EXTENDED_SCRIPTS are sorted by runtime
- # (for parallel running efficiency). This combined list will is no
- # longer sorted.
+ # place the EXTENDED_SCRIPTS first since the three longest ones
+ # are there and the list is shorter
+ test_list = EXTENDED_SCRIPTS + test_list
# Remove the test cases that the user has explicitly asked to exclude.
if args.exclude:
@@ -228,14 +233,23 @@ def main():
check_script_list(config["environment"]["SRCDIR"])
+ if not args.keepcache:
+ shutil.rmtree("%s/test/cache" % config["environment"]["BUILDDIR"], ignore_errors=True)
+
run_tests(test_list, config["environment"]["SRCDIR"], config["environment"]["BUILDDIR"], config["environment"]["EXEEXT"], args.jobs, args.coverage, passon_args)
def run_tests(test_list, src_dir, build_dir, exeext, jobs=1, enable_coverage=False, args=[]):
- BOLD = ("","")
- if os.name == 'posix':
- # primitive formatting on supported
- # terminal via ANSI escape sequences:
- BOLD = ('\033[0m', '\033[1m')
+ # Warn if bitcoind is already running (unix only)
+ try:
+ if subprocess.check_output(["pidof", "bitcoind"]) is not None:
+ print("%sWARNING!%s There is already a bitcoind process running on this system. Tests may fail unexpectedly due to resource contention!" % (BOLD[1], BOLD[0]))
+ except (OSError, subprocess.SubprocessError):
+ pass
+
+ # Warn if there is a cache directory
+ cache_dir = "%s/test/cache" % build_dir
+ if os.path.isdir(cache_dir):
+ print("%sWARNING!%s There is a cache directory here: %s. If tests fail unexpectedly, try deleting the cache directory." % (BOLD[1], BOLD[0], cache_dir))
#Set env vars
if "BITCOIND" not in os.environ:
@@ -244,7 +258,7 @@ def run_tests(test_list, src_dir, build_dir, exeext, jobs=1, enable_coverage=Fal
tests_dir = src_dir + '/test/functional/'
flags = ["--srcdir={}/src".format(build_dir)] + args
- flags.append("--cachedir=%s/test/cache" % build_dir)
+ flags.append("--cachedir=%s" % cache_dir)
if enable_coverage:
coverage = RPCCoverage()
@@ -258,33 +272,26 @@ def run_tests(test_list, src_dir, build_dir, exeext, jobs=1, enable_coverage=Fal
subprocess.check_output([tests_dir + 'create_cache.py'] + flags)
#Run Tests
- all_passed = True
- time_sum = 0
- time0 = time.time()
-
job_queue = TestHandler(jobs, tests_dir, test_list, flags)
+ time0 = time.time()
+ test_results = []
max_len_name = len(max(test_list, key=len))
- results = "\n" + BOLD[1] + "%s | %s | %s\n\n" % ("TEST".ljust(max_len_name), "STATUS ", "DURATION") + BOLD[0]
+
for _ in range(len(test_list)):
- (name, stdout, stderr, status, duration) = job_queue.get_next()
- all_passed = all_passed and status != "Failed"
- time_sum += duration
-
- if status == "Passed":
- logging.debug("\n%s%s%s passed, Duration: %s s" % (BOLD[1], name, BOLD[0], duration))
- elif status == "Skipped":
- logging.debug("\n%s%s%s skipped" % (BOLD[1], name, BOLD[0]))
+ test_result, stdout, stderr = job_queue.get_next()
+ test_results.append(test_result)
+
+ if test_result.status == "Passed":
+ logging.debug("\n%s%s%s passed, Duration: %s s" % (BOLD[1], test_result.name, BOLD[0], test_result.time))
+ elif test_result.status == "Skipped":
+ logging.debug("\n%s%s%s skipped" % (BOLD[1], test_result.name, BOLD[0]))
else:
- print("\n%s%s%s failed, Duration: %s s\n" % (BOLD[1], name, BOLD[0], duration))
+ print("\n%s%s%s failed, Duration: %s s\n" % (BOLD[1], test_result.name, BOLD[0], test_result.time))
print(BOLD[1] + 'stdout:\n' + BOLD[0] + stdout + '\n')
print(BOLD[1] + 'stderr:\n' + BOLD[0] + stderr + '\n')
- results += "%s | %s | %s s\n" % (name.ljust(max_len_name), status.ljust(7), duration)
-
- results += BOLD[1] + "\n%s | %s | %s s (accumulated)" % ("ALL".ljust(max_len_name), str(all_passed).ljust(7), time_sum) + BOLD[0]
- print(results)
- print("\nRuntime: %s s" % (int(time.time() - time0)))
+ print_results(test_results, max_len_name, (int(time.time() - time0)))
if coverage:
coverage.report_rpc_coverage()
@@ -292,8 +299,28 @@ def run_tests(test_list, src_dir, build_dir, exeext, jobs=1, enable_coverage=Fal
logging.debug("Cleaning up coverage data")
coverage.cleanup()
+ all_passed = all(map(lambda test_result: test_result.was_successful, test_results))
+
sys.exit(not all_passed)
+def print_results(test_results, max_len_name, runtime):
+ results = "\n" + BOLD[1] + "%s | %s | %s\n\n" % ("TEST".ljust(max_len_name), "STATUS ", "DURATION") + BOLD[0]
+
+ test_results.sort(key=lambda result: result.name.lower())
+ all_passed = True
+ time_sum = 0
+
+ for test_result in test_results:
+ all_passed = all_passed and test_result.was_successful
+ time_sum += test_result.time
+ test_result.padding = max_len_name
+ results += str(test_result)
+
+ status = TICK + "Passed" if all_passed else CROSS + "Failed"
+ results += BOLD[1] + "\n%s | %s | %s s (accumulated) \n" % ("ALL".ljust(max_len_name), status.ljust(9), time_sum) + BOLD[0]
+ results += "Runtime: %s s\n" % (runtime)
+ print(results)
+
class TestHandler:
"""
Trigger the testscrips passed in via the list.
@@ -348,9 +375,35 @@ class TestHandler:
status = "Failed"
self.num_running -= 1
self.jobs.remove(j)
- return name, stdout, stderr, status, int(time.time() - time0)
+
+ return TestResult(name, status, int(time.time() - time0)), stdout, stderr
print('.', end='', flush=True)
+class TestResult():
+ def __init__(self, name, status, time):
+ self.name = name
+ self.status = status
+ self.time = time
+ self.padding = 0
+
+ def __repr__(self):
+ if self.status == "Passed":
+ color = BLUE
+ glyph = TICK
+ elif self.status == "Failed":
+ color = RED
+ glyph = CROSS
+ elif self.status == "Skipped":
+ color = GREY
+ glyph = CIRCLE
+
+ return color[1] + "%s | %s%s | %s s\n" % (self.name.ljust(self.padding), glyph, self.status.ljust(7), self.time) + color[0]
+
+ @property
+ def was_successful(self):
+ return self.status != "Failed"
+
+
def check_script_list(src_dir):
"""Check scripts directory.
@@ -360,9 +413,10 @@ def check_script_list(src_dir):
python_files = set([t for t in os.listdir(script_dir) if t[-3:] == ".py"])
missed_tests = list(python_files - set(map(lambda x: x.split()[0], ALL_SCRIPTS + NON_SCRIPTS)))
if len(missed_tests) != 0:
- print("The following scripts are not being run:" + str(missed_tests))
- print("Check the test lists in test_runner.py")
- sys.exit(1)
+ print("%sWARNING!%s The following scripts are not being run: %s. Check the test lists in test_runner.py." % (BOLD[1], BOLD[0], str(missed_tests)))
+ if os.getenv('TRAVIS') == 'true':
+ # On travis this warning is an error to prevent merging incomplete commits into master
+ sys.exit(1)
class RPCCoverage(object):
"""
diff --git a/test/functional/txn_clone.py b/test/functional/txn_clone.py
index 7a3b8d3474..9b81af96cf 100755
--- a/test/functional/txn_clone.py
+++ b/test/functional/txn_clone.py
@@ -20,7 +20,9 @@ class TxnMallTest(BitcoinTestFramework):
def setup_network(self):
# Start with split network:
- return super(TxnMallTest, self).setup_network(True)
+ super(TxnMallTest, self).setup_network()
+ disconnect_nodes(self.nodes[1], 2)
+ disconnect_nodes(self.nodes[2], 1)
def run_test(self):
# All nodes should start with 1,250 BTC:
diff --git a/test/functional/txn_doublespend.py b/test/functional/txn_doublespend.py
index 5b12cf4c29..1bd3b3271c 100755
--- a/test/functional/txn_doublespend.py
+++ b/test/functional/txn_doublespend.py
@@ -20,7 +20,9 @@ class TxnMallTest(BitcoinTestFramework):
def setup_network(self):
# Start with split network:
- return super(TxnMallTest, self).setup_network(True)
+ super().setup_network()
+ disconnect_nodes(self.nodes[1], 2)
+ disconnect_nodes(self.nodes[2], 1)
def run_test(self):
# All nodes should start with 1,250 BTC:
diff --git a/test/functional/wallet-accounts.py b/test/functional/wallet-accounts.py
index ea12d4ec22..8dc1589117 100755
--- a/test/functional/wallet-accounts.py
+++ b/test/functional/wallet-accounts.py
@@ -7,6 +7,7 @@
RPCs tested are:
- getaccountaddress
- getaddressesbyaccount
+ - listaddressgroupings
- setaccount
- sendfrom (with account arguments)
- move (with account arguments)
@@ -24,22 +25,59 @@ class WalletAccountsTest(BitcoinTestFramework):
super().__init__()
self.setup_clean_chain = True
self.num_nodes = 1
- self.node_args = [[]]
+ self.extra_args = [[]]
- def setup_network(self):
- self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, self.node_args)
- self.is_network_split = False
-
- def run_test (self):
+ def run_test(self):
node = self.nodes[0]
# Check that there's no UTXO on any of the nodes
assert_equal(len(node.listunspent()), 0)
-
+
+ # Note each time we call generate, all generated coins go into
+ # the same address, so we call twice to get two addresses w/50 each
+ node.generate(1)
node.generate(101)
-
- assert_equal(node.getbalance(), 50)
-
- accounts = ["a","b","c","d","e"]
+ assert_equal(node.getbalance(), 100)
+
+ # there should be 2 address groups
+ # each with 1 address with a balance of 50 Bitcoins
+ address_groups = node.listaddressgroupings()
+ assert_equal(len(address_groups), 2)
+ # the addresses aren't linked now, but will be after we send to the
+ # common address
+ linked_addresses = set()
+ for address_group in address_groups:
+ assert_equal(len(address_group), 1)
+ assert_equal(len(address_group[0]), 2)
+ assert_equal(address_group[0][1], 50)
+ linked_addresses.add(address_group[0][0])
+
+ # send 50 from each address to a third address not in this wallet
+ # There's some fee that will come back to us when the miner reward
+ # matures.
+ common_address = "msf4WtN1YQKXvNtvdFYt9JBnUD2FB41kjr"
+ txid = node.sendmany(
+ fromaccount="",
+ amounts={common_address: 100},
+ subtractfeefrom=[common_address],
+ minconf=1,
+ )
+ tx_details = node.gettransaction(txid)
+ fee = -tx_details['details'][0]['fee']
+ # there should be 1 address group, with the previously
+ # unlinked addresses now linked (they both have 0 balance)
+ address_groups = node.listaddressgroupings()
+ assert_equal(len(address_groups), 1)
+ assert_equal(len(address_groups[0]), 2)
+ assert_equal(set([a[0] for a in address_groups[0]]), linked_addresses)
+ assert_equal([a[1] for a in address_groups[0]], [0, 0])
+
+ node.generate(1)
+
+ # we want to reset so that the "" account has what's expected.
+ # 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()
for account in accounts:
@@ -56,7 +94,7 @@ class WalletAccountsTest(BitcoinTestFramework):
for i in range(len(accounts)):
from_account = accounts[i]
- to_account = accounts[(i+1)%len(accounts)]
+ to_account = accounts[(i+1) % len(accounts)]
to_address = account_addresses[to_account]
node.sendfrom(from_account, to_address, amount_to_send)
@@ -67,7 +105,7 @@ class WalletAccountsTest(BitcoinTestFramework):
assert(address != account_addresses[account])
assert_equal(node.getreceivedbyaccount(account), 2)
node.move(account, "", node.getbalance(account))
-
+
node.generate(101)
expected_account_balances = {"": 5200}
@@ -97,4 +135,4 @@ class WalletAccountsTest(BitcoinTestFramework):
assert_equal(node.getbalance(account), 50)
if __name__ == '__main__':
- WalletAccountsTest().main ()
+ WalletAccountsTest().main()
diff --git a/test/functional/wallet-hd.py b/test/functional/wallet-hd.py
index f29b875191..bbf53e7dba 100755
--- a/test/functional/wallet-hd.py
+++ b/test/functional/wallet-hd.py
@@ -22,12 +22,7 @@ class WalletHDTest(BitcoinTestFramework):
super().__init__()
self.setup_clean_chain = True
self.num_nodes = 2
- self.node_args = [['-usehd=0'], ['-usehd=1', '-keypool=0']]
-
- def setup_network(self):
- self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, self.node_args)
- self.is_network_split = False
- connect_nodes_bi(self.nodes, 0, 1)
+ self.extra_args = [['-usehd=0'], ['-usehd=1', '-keypool=0']]
def run_test (self):
tmpdir = self.options.tmpdir
@@ -35,7 +30,7 @@ class WalletHDTest(BitcoinTestFramework):
# Make sure can't switch off usehd after wallet creation
self.stop_node(1)
assert_start_raises_init_error(1, self.options.tmpdir, ['-usehd=0'], 'already existing HD wallet')
- self.nodes[1] = start_node(1, self.options.tmpdir, self.node_args[1])
+ self.nodes[1] = start_node(1, self.options.tmpdir, self.extra_args[1])
connect_nodes_bi(self.nodes, 0, 1)
# Make sure we use hd, keep masterkeyid
@@ -82,7 +77,7 @@ class WalletHDTest(BitcoinTestFramework):
self.stop_node(1)
os.remove(self.options.tmpdir + "/node1/regtest/wallet.dat")
shutil.copyfile(tmpdir + "/hd.bak", tmpdir + "/node1/regtest/wallet.dat")
- self.nodes[1] = start_node(1, self.options.tmpdir, self.node_args[1])
+ self.nodes[1] = start_node(1, self.options.tmpdir, self.extra_args[1])
#connect_nodes_bi(self.nodes, 0, 1)
# Assert that derivation is deterministic
@@ -96,7 +91,7 @@ class WalletHDTest(BitcoinTestFramework):
# Needs rescan
self.stop_node(1)
- self.nodes[1] = start_node(1, self.options.tmpdir, self.node_args[1] + ['-rescan'])
+ self.nodes[1] = start_node(1, self.options.tmpdir, self.extra_args[1] + ['-rescan'])
#connect_nodes_bi(self.nodes, 0, 1)
assert_equal(self.nodes[1].getbalance(), num_hd_adds + 1)
diff --git a/test/functional/wallet.py b/test/functional/wallet.py
index 80f74fa108..57f6dfdaa9 100755
--- a/test/functional/wallet.py
+++ b/test/functional/wallet.py
@@ -6,7 +6,7 @@
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
-class WalletTest (BitcoinTestFramework):
+class WalletTest(BitcoinTestFramework):
def check_fee_amount(self, curr_balance, balance_with_fee, fee_per_byte, tx_size):
"""Return curr_balance after asserting the fee was in range"""
@@ -20,15 +20,14 @@ class WalletTest (BitcoinTestFramework):
self.num_nodes = 4
self.extra_args = [['-usehd={:d}'.format(i%2==0)] for i in range(4)]
- def setup_network(self, split=False):
+ def setup_network(self):
self.nodes = start_nodes(3, self.options.tmpdir, self.extra_args[:3])
connect_nodes_bi(self.nodes,0,1)
connect_nodes_bi(self.nodes,1,2)
connect_nodes_bi(self.nodes,0,2)
- self.is_network_split=False
self.sync_all()
- def run_test (self):
+ def run_test(self):
# Check that there's no UTXO on none of the nodes
assert_equal(len(self.nodes[0].listunspent()), 0)
@@ -52,13 +51,33 @@ class WalletTest (BitcoinTestFramework):
assert_equal(self.nodes[2].getbalance(), 0)
# Check that only first and second nodes have UTXOs
- assert_equal(len(self.nodes[0].listunspent()), 1)
+ utxos = self.nodes[0].listunspent()
+ assert_equal(len(utxos), 1)
assert_equal(len(self.nodes[1].listunspent()), 1)
assert_equal(len(self.nodes[2].listunspent()), 0)
# Send 21 BTC from 0 to 2 using sendtoaddress call.
self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 11)
- self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 10)
+ mempool_txid = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 10)
+
+ self.log.info("test gettxout")
+ # utxo spent in mempool should be visible if you exclude mempool
+ # but invisible if you include mempool
+ confirmed_txid, confirmed_index = utxos[0]["txid"], utxos[0]["vout"]
+ txout = self.nodes[0].gettxout(confirmed_txid, confirmed_index, False)
+ assert_equal(txout['value'], 50)
+ txout = self.nodes[0].gettxout(confirmed_txid, confirmed_index, True)
+ assert txout is None
+ # new utxo from mempool should be invisible if you exclude mempool
+ # but visible if you include mempool
+ txout = self.nodes[0].gettxout(mempool_txid, 0, False)
+ assert txout is None
+ txout1 = self.nodes[0].gettxout(mempool_txid, 0, True)
+ txout2 = self.nodes[0].gettxout(mempool_txid, 1, True)
+ # note the mempool tx will have randomly assigned indices
+ # but 10 will go to node2 and the rest will go to node0
+ balance = self.nodes[0].getbalance()
+ assert_equal(set([txout1['value'], txout2['value']]), set([10, balance]))
walletinfo = self.nodes[0].getwalletinfo()
assert_equal(walletinfo['immature_balance'], 0)
diff --git a/test/functional/walletbackup.py b/test/functional/walletbackup.py
index af1718572f..0492132af6 100755
--- a/test/functional/walletbackup.py
+++ b/test/functional/walletbackup.py
@@ -44,14 +44,12 @@ class WalletBackupTest(BitcoinTestFramework):
# nodes 1, 2,3 are spenders, let's give them a keypool=100
self.extra_args = [["-keypool=100"], ["-keypool=100"], ["-keypool=100"], []]
- # This mirrors how the network was setup in the bash test
def setup_network(self, split=False):
- self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, self.extra_args)
+ self.setup_nodes()
connect_nodes(self.nodes[0], 3)
connect_nodes(self.nodes[1], 3)
connect_nodes(self.nodes[2], 3)
connect_nodes(self.nodes[2], 0)
- self.is_network_split=False
self.sync_all()
def one_send(self, from_node, to_address):
diff --git a/test/functional/zapwallettxes.py b/test/functional/zapwallettxes.py
index ce446e44a3..7987edeb54 100755
--- a/test/functional/zapwallettxes.py
+++ b/test/functional/zapwallettxes.py
@@ -23,13 +23,9 @@ class ZapWalletTXesTest (BitcoinTestFramework):
self.setup_clean_chain = True
self.num_nodes = 3
- def setup_network(self, split=False):
- self.nodes = start_nodes(self.num_nodes, self.options.tmpdir)
- connect_nodes_bi(self.nodes,0,1)
- connect_nodes_bi(self.nodes,1,2)
+ def setup_network(self):
+ super().setup_network()
connect_nodes_bi(self.nodes,0,2)
- self.is_network_split=False
- self.sync_all()
def run_test (self):
self.log.info("Mining blocks...")
diff --git a/test/functional/zmq_test.py b/test/functional/zmq_test.py
index 9e27b46381..a72630406e 100755
--- a/test/functional/zmq_test.py
+++ b/test/functional/zmq_test.py
@@ -3,11 +3,13 @@
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test the ZMQ API."""
+import configparser
+import os
+import struct
+import sys
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
-import zmq
-import struct
class ZMQTest (BitcoinTestFramework):
@@ -18,12 +20,27 @@ class ZMQTest (BitcoinTestFramework):
port = 28332
def setup_nodes(self):
+ # Try to import python3-zmq. Skip this test if the import fails.
+ try:
+ import zmq
+ except ImportError:
+ self.log.warning("python3-zmq module not available. Skipping zmq tests!")
+ sys.exit(self.TEST_EXIT_SKIPPED)
+
+ # Check that bitcoin has been built with ZMQ enabled
+ config = configparser.ConfigParser()
+ config.read_file(open(os.path.dirname(__file__) + "/config.ini"))
+
+ if not config["components"].getboolean("ENABLE_ZMQ"):
+ self.log.warning("bitcoind has not been built with zmq enabled. Skipping zmq tests!")
+ sys.exit(self.TEST_EXIT_SKIPPED)
+
self.zmqContext = zmq.Context()
self.zmqSubSocket = self.zmqContext.socket(zmq.SUB)
self.zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"hashblock")
self.zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"hashtx")
self.zmqSubSocket.connect("tcp://127.0.0.1:%i" % self.port)
- return start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[
+ self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[
['-zmqpubhashtx=tcp://127.0.0.1:'+str(self.port), '-zmqpubhashblock=tcp://127.0.0.1:'+str(self.port)],
[],
[],
diff --git a/test/util/data/blanktxv1.json b/test/util/data/blanktxv1.json
index 51c25a5a98..9fe2de649b 100644
--- a/test/util/data/blanktxv1.json
+++ b/test/util/data/blanktxv1.json
@@ -2,6 +2,8 @@
"txid": "d21633ba23f70118185227be58a63527675641ad37967e2aa461559f577aec43",
"hash": "d21633ba23f70118185227be58a63527675641ad37967e2aa461559f577aec43",
"version": 1,
+ "size": 10,
+ "vsize": 10,
"locktime": 0,
"vin": [
],
diff --git a/test/util/data/blanktxv2.json b/test/util/data/blanktxv2.json
index 266919f445..e97626e421 100644
--- a/test/util/data/blanktxv2.json
+++ b/test/util/data/blanktxv2.json
@@ -2,6 +2,8 @@
"txid": "4ebd325a4b394cff8c57e8317ccf5a8d0e2bdf1b8526f8aad6c8e43d8240621a",
"hash": "4ebd325a4b394cff8c57e8317ccf5a8d0e2bdf1b8526f8aad6c8e43d8240621a",
"version": 2,
+ "size": 10,
+ "vsize": 10,
"locktime": 0,
"vin": [
],
diff --git a/test/util/data/tt-delin1-out.json b/test/util/data/tt-delin1-out.json
index 712a2c27f8..f6dfbb51cc 100644
--- a/test/util/data/tt-delin1-out.json
+++ b/test/util/data/tt-delin1-out.json
@@ -2,6 +2,8 @@
"txid": "81b2035be1da1abe745c6141174a73d151009ec17b3d5ebffa2e177408c50dfd",
"hash": "81b2035be1da1abe745c6141174a73d151009ec17b3d5ebffa2e177408c50dfd",
"version": 1,
+ "size": 3040,
+ "vsize": 3040,
"locktime": 0,
"vin": [
{
diff --git a/test/util/data/tt-delout1-out.json b/test/util/data/tt-delout1-out.json
index afc4e95762..6769ed79ff 100644
--- a/test/util/data/tt-delout1-out.json
+++ b/test/util/data/tt-delout1-out.json
@@ -2,6 +2,8 @@
"txid": "c46ccd75b5050e942b2e86a3648f843f525fe6fc000bf0534ba5973063354493",
"hash": "c46ccd75b5050e942b2e86a3648f843f525fe6fc000bf0534ba5973063354493",
"version": 1,
+ "size": 3155,
+ "vsize": 3155,
"locktime": 0,
"vin": [
{
diff --git a/test/util/data/tt-locktime317000-out.json b/test/util/data/tt-locktime317000-out.json
index 2b9075f8ac..82b64df075 100644
--- a/test/util/data/tt-locktime317000-out.json
+++ b/test/util/data/tt-locktime317000-out.json
@@ -2,6 +2,8 @@
"txid": "aded538f642c17e15f4d3306b8be7e1a4d1ae0c4616d641ab51ea09ba65e5cb5",
"hash": "aded538f642c17e15f4d3306b8be7e1a4d1ae0c4616d641ab51ea09ba65e5cb5",
"version": 1,
+ "size": 3189,
+ "vsize": 3189,
"locktime": 317000,
"vin": [
{
diff --git a/test/util/data/txcreate1.json b/test/util/data/txcreate1.json
index f83e036f33..36741044c9 100644
--- a/test/util/data/txcreate1.json
+++ b/test/util/data/txcreate1.json
@@ -2,6 +2,8 @@
"txid": "fe7d174f42dce0cffa7a527e9bc8368956057619ec817648f6138b98f2533e8f",
"hash": "fe7d174f42dce0cffa7a527e9bc8368956057619ec817648f6138b98f2533e8f",
"version": 2,
+ "size": 201,
+ "vsize": 201,
"locktime": 0,
"vin": [
{
diff --git a/test/util/data/txcreate2.json b/test/util/data/txcreate2.json
index fb5e177db7..23fe7ace67 100644
--- a/test/util/data/txcreate2.json
+++ b/test/util/data/txcreate2.json
@@ -2,6 +2,8 @@
"txid": "0481afb29931341d0d7861d8a2f6f26456fa042abf54a23e96440ed7946e0715",
"hash": "0481afb29931341d0d7861d8a2f6f26456fa042abf54a23e96440ed7946e0715",
"version": 2,
+ "size": 19,
+ "vsize": 19,
"locktime": 0,
"vin": [
],
diff --git a/test/util/data/txcreatedata1.json b/test/util/data/txcreatedata1.json
index 760518d30a..e65a1859eb 100644
--- a/test/util/data/txcreatedata1.json
+++ b/test/util/data/txcreatedata1.json
@@ -2,6 +2,8 @@
"txid": "07894b4d12fe7853dd911402db1620920d261b9627c447f931417d330c25f06e",
"hash": "07894b4d12fe7853dd911402db1620920d261b9627c447f931417d330c25f06e",
"version": 1,
+ "size": 176,
+ "vsize": 176,
"locktime": 0,
"vin": [
{
diff --git a/test/util/data/txcreatedata2.json b/test/util/data/txcreatedata2.json
index 3c6da40f90..8f1544e1c0 100644
--- a/test/util/data/txcreatedata2.json
+++ b/test/util/data/txcreatedata2.json
@@ -2,6 +2,8 @@
"txid": "c14b007fa3a6c1e7765919c1d14c1cfc2b8642c3a5d3be4b1fa8c4ccfec98bb0",
"hash": "c14b007fa3a6c1e7765919c1d14c1cfc2b8642c3a5d3be4b1fa8c4ccfec98bb0",
"version": 2,
+ "size": 176,
+ "vsize": 176,
"locktime": 0,
"vin": [
{
diff --git a/test/util/data/txcreatedata_seq0.json b/test/util/data/txcreatedata_seq0.json
index d272a4c447..e52401f418 100644
--- a/test/util/data/txcreatedata_seq0.json
+++ b/test/util/data/txcreatedata_seq0.json
@@ -2,6 +2,8 @@
"txid": "8df6ed527472542dd5e137c242a7c5a9f337ac34f7b257ae4af886aeaebb51b0",
"hash": "8df6ed527472542dd5e137c242a7c5a9f337ac34f7b257ae4af886aeaebb51b0",
"version": 2,
+ "size": 85,
+ "vsize": 85,
"locktime": 0,
"vin": [
{
diff --git a/test/util/data/txcreatedata_seq1.json b/test/util/data/txcreatedata_seq1.json
index d323255418..093ff4a56b 100644
--- a/test/util/data/txcreatedata_seq1.json
+++ b/test/util/data/txcreatedata_seq1.json
@@ -2,6 +2,8 @@
"txid": "c4dea671b0d7b48f8ab10bc46650e8329d3c5766931f548f513847a19f5ba75b",
"hash": "c4dea671b0d7b48f8ab10bc46650e8329d3c5766931f548f513847a19f5ba75b",
"version": 1,
+ "size": 126,
+ "vsize": 126,
"locktime": 0,
"vin": [
{
diff --git a/test/util/data/txcreatemultisig1.json b/test/util/data/txcreatemultisig1.json
index f6ce43c202..0cc530836a 100644
--- a/test/util/data/txcreatemultisig1.json
+++ b/test/util/data/txcreatemultisig1.json
@@ -2,6 +2,8 @@
"txid": "0d1d4edfc217d9db3ab6a9298f26a52eae3c52f55a6cb8ccbc14f7c727572894",
"hash": "0d1d4edfc217d9db3ab6a9298f26a52eae3c52f55a6cb8ccbc14f7c727572894",
"version": 1,
+ "size": 124,
+ "vsize": 124,
"locktime": 0,
"vin": [
],
diff --git a/test/util/data/txcreatemultisig2.json b/test/util/data/txcreatemultisig2.json
index e09d22060f..8ad2ffdc65 100644
--- a/test/util/data/txcreatemultisig2.json
+++ b/test/util/data/txcreatemultisig2.json
@@ -2,6 +2,8 @@
"txid": "0d861f278a3b7bce7cb5a88d71e6e6a903336f95ad5a2c29b295b63835b6eee3",
"hash": "0d861f278a3b7bce7cb5a88d71e6e6a903336f95ad5a2c29b295b63835b6eee3",
"version": 1,
+ "size": 42,
+ "vsize": 42,
"locktime": 0,
"vin": [
],
diff --git a/test/util/data/txcreatemultisig3.json b/test/util/data/txcreatemultisig3.json
index 88e32bd310..086bf44b8a 100644
--- a/test/util/data/txcreatemultisig3.json
+++ b/test/util/data/txcreatemultisig3.json
@@ -2,6 +2,8 @@
"txid": "ccc552220b46a3b5140048b03395987ce4f0fa1ddf8c635bba1fa44e0f8c1d7f",
"hash": "ccc552220b46a3b5140048b03395987ce4f0fa1ddf8c635bba1fa44e0f8c1d7f",
"version": 1,
+ "size": 53,
+ "vsize": 53,
"locktime": 0,
"vin": [
],
diff --git a/test/util/data/txcreatemultisig4.json b/test/util/data/txcreatemultisig4.json
index fc69c7269c..d23ccc045e 100644
--- a/test/util/data/txcreatemultisig4.json
+++ b/test/util/data/txcreatemultisig4.json
@@ -2,6 +2,8 @@
"txid": "5e8b1cc73234e208d4b7ca9075f136b908c34101be7a048df4ba9ac758b61567",
"hash": "5e8b1cc73234e208d4b7ca9075f136b908c34101be7a048df4ba9ac758b61567",
"version": 1,
+ "size": 42,
+ "vsize": 42,
"locktime": 0,
"vin": [
],
diff --git a/test/util/data/txcreateoutpubkey1.json b/test/util/data/txcreateoutpubkey1.json
index 6019fa2dcd..f10aaecf7a 100644
--- a/test/util/data/txcreateoutpubkey1.json
+++ b/test/util/data/txcreateoutpubkey1.json
@@ -2,6 +2,8 @@
"txid": "f42b38ac12e3fafc96ba1a9ba70cbfe326744aef75df5fb9db5d6e2855ca415f",
"hash": "f42b38ac12e3fafc96ba1a9ba70cbfe326744aef75df5fb9db5d6e2855ca415f",
"version": 1,
+ "size": 54,
+ "vsize": 54,
"locktime": 0,
"vin": [
],
diff --git a/test/util/data/txcreateoutpubkey2.json b/test/util/data/txcreateoutpubkey2.json
index 6fc3d57527..5a473b76c3 100644
--- a/test/util/data/txcreateoutpubkey2.json
+++ b/test/util/data/txcreateoutpubkey2.json
@@ -2,6 +2,8 @@
"txid": "70f2a088cde460e677415fa1fb71895e90c231e6ed38ed203a35b6f848e9cc73",
"hash": "70f2a088cde460e677415fa1fb71895e90c231e6ed38ed203a35b6f848e9cc73",
"version": 1,
+ "size": 41,
+ "vsize": 41,
"locktime": 0,
"vin": [
],
diff --git a/test/util/data/txcreateoutpubkey3.json b/test/util/data/txcreateoutpubkey3.json
index a1a25fc834..b8389b8f7e 100644
--- a/test/util/data/txcreateoutpubkey3.json
+++ b/test/util/data/txcreateoutpubkey3.json
@@ -2,6 +2,8 @@
"txid": "bfc7e898ee9f6a9652d7b8cca147e2da134502e2ada0f279ed634fc8cf833f8c",
"hash": "bfc7e898ee9f6a9652d7b8cca147e2da134502e2ada0f279ed634fc8cf833f8c",
"version": 1,
+ "size": 42,
+ "vsize": 42,
"locktime": 0,
"vin": [
],
diff --git a/test/util/data/txcreatescript1.json b/test/util/data/txcreatescript1.json
index 8ffecba411..823168e9fb 100644
--- a/test/util/data/txcreatescript1.json
+++ b/test/util/data/txcreatescript1.json
@@ -2,6 +2,8 @@
"txid": "f0851b68202f736b792649cfc960259c2374badcb644ab20cac726b5f72f61c9",
"hash": "f0851b68202f736b792649cfc960259c2374badcb644ab20cac726b5f72f61c9",
"version": 1,
+ "size": 20,
+ "vsize": 20,
"locktime": 0,
"vin": [
],
diff --git a/test/util/data/txcreatescript2.json b/test/util/data/txcreatescript2.json
index 41eb69f1af..d4c7e10c78 100644
--- a/test/util/data/txcreatescript2.json
+++ b/test/util/data/txcreatescript2.json
@@ -2,6 +2,8 @@
"txid": "6e07a7cc075e0703f32ee8c4e5373fe654bfbc315148fda364e1be286ff290d0",
"hash": "6e07a7cc075e0703f32ee8c4e5373fe654bfbc315148fda364e1be286ff290d0",
"version": 1,
+ "size": 42,
+ "vsize": 42,
"locktime": 0,
"vin": [
],
diff --git a/test/util/data/txcreatescript3.json b/test/util/data/txcreatescript3.json
index 90e7e27f9f..001e69511f 100644
--- a/test/util/data/txcreatescript3.json
+++ b/test/util/data/txcreatescript3.json
@@ -2,6 +2,8 @@
"txid": "8a234037b088e987c877030efc83374a07441c321bf9dc6dd2f206bc26507df8",
"hash": "8a234037b088e987c877030efc83374a07441c321bf9dc6dd2f206bc26507df8",
"version": 1,
+ "size": 53,
+ "vsize": 53,
"locktime": 0,
"vin": [
],
diff --git a/test/util/data/txcreatescript4.json b/test/util/data/txcreatescript4.json
index 11783751a4..20094bcd44 100644
--- a/test/util/data/txcreatescript4.json
+++ b/test/util/data/txcreatescript4.json
@@ -2,6 +2,8 @@
"txid": "24225cf5e9391100d6b218134b9f03383ca4c880a1f634ac12990cf28b66adbc",
"hash": "24225cf5e9391100d6b218134b9f03383ca4c880a1f634ac12990cf28b66adbc",
"version": 1,
+ "size": 42,
+ "vsize": 42,
"locktime": 0,
"vin": [
],
diff --git a/test/util/data/txcreatesignv1.json b/test/util/data/txcreatesignv1.json
index ff39e71b40..519d3ab066 100644
--- a/test/util/data/txcreatesignv1.json
+++ b/test/util/data/txcreatesignv1.json
@@ -2,6 +2,8 @@
"txid": "977e7cd286cb72cd470d539ba6cb48400f8f387d97451d45cdb8819437a303af",
"hash": "977e7cd286cb72cd470d539ba6cb48400f8f387d97451d45cdb8819437a303af",
"version": 1,
+ "size": 224,
+ "vsize": 224,
"locktime": 0,
"vin": [
{