aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml4
-rw-r--r--Makefile.am3
-rw-r--r--configure.ac28
-rwxr-xr-xcontrib/devtools/check-doc.py46
-rwxr-xr-xcontrib/devtools/clang-format-diff.py20
-rwxr-xr-xcontrib/devtools/github-merge.py18
-rwxr-xr-xcontrib/devtools/lint-include-guards.sh29
-rwxr-xr-xcontrib/devtools/lint-python.sh3
-rwxr-xr-xcontrib/devtools/lint-tests.sh19
-rwxr-xr-xcontrib/devtools/optimize-pngs.py30
-rwxr-xr-xcontrib/devtools/security-check.py47
-rwxr-xr-xcontrib/devtools/symbol-check.py69
-rwxr-xr-xcontrib/devtools/update-translations.py3
-rwxr-xr-xcontrib/macdeploy/custom_dsstore.py3
-rwxr-xr-xcontrib/macdeploy/macdeployqtplus27
-rw-r--r--contrib/testgen/base58.py20
-rwxr-xr-xcontrib/testgen/gen_base58_test_vectors.py25
-rw-r--r--contrib/verify-commits/README.md21
-rw-r--r--contrib/verify-commits/allow-revsig-commits400
-rw-r--r--contrib/verify-commits/trusted-git-root2
-rwxr-xr-xcontrib/verify-commits/verify-commits.sh32
-rw-r--r--doc/build-osx.md2
-rw-r--r--doc/developer-notes.md249
-rw-r--r--doc/release-notes.md6
-rw-r--r--doc/tor.md4
-rwxr-xr-xshare/qt/extract_strings_qt.py3
-rwxr-xr-xshare/rpcauth/rpcauth.py2
-rw-r--r--src/Makefile.am13
-rw-r--r--src/Makefile.qt.include4
-rw-r--r--src/Makefile.qttest.include2
-rw-r--r--src/addrdb.cpp6
-rw-r--r--src/bech32.h5
-rw-r--r--src/bench/bench_bitcoin.cpp2
-rw-r--r--src/bench/perf.h6
-rw-r--r--src/bench/verify_script.cpp2
-rw-r--r--src/bitcoin-cli.cpp3
-rw-r--r--src/bitcoin-tx.cpp4
-rw-r--r--src/bitcoind.cpp6
-rw-r--r--src/blockencodings.h6
-rw-r--r--src/chainparams.cpp1
-rw-r--r--src/chainparamsbase.cpp1
-rw-r--r--src/compressor.cpp30
-rw-r--r--src/compressor.h31
-rw-r--r--src/consensus/merkle.h6
-rw-r--r--src/dbwrapper.cpp32
-rw-r--r--src/httprpc.cpp5
-rw-r--r--src/httpserver.cpp1
-rw-r--r--src/init.cpp89
-rw-r--r--src/init.h10
-rw-r--r--src/interface/README.md17
-rw-r--r--src/interface/handler.cpp33
-rw-r--r--src/interface/handler.h35
-rw-r--r--src/interface/node.cpp308
-rw-r--r--src/interface/node.h259
-rw-r--r--src/interface/wallet.cpp441
-rw-r--r--src/interface/wallet.h352
-rw-r--r--src/key_io.h6
-rw-r--r--src/keystore.cpp6
-rw-r--r--src/keystore.h15
-rw-r--r--src/memusage.h1
-rw-r--r--src/miner.cpp1
-rw-r--r--src/net.cpp1
-rw-r--r--src/net_processing.cpp2
-rw-r--r--src/net_processing.h6
-rw-r--r--src/netaddress.h8
-rw-r--r--src/policy/fees.h9
-rw-r--r--src/policy/policy.cpp4
-rw-r--r--src/policy/rbf.cpp8
-rw-r--r--src/policy/rbf.h8
-rw-r--r--src/protocol.h6
-rw-r--r--src/pubkey.h5
-rw-r--r--src/qt/addresstablemodel.cpp59
-rw-r--r--src/qt/addresstablemodel.h7
-rw-r--r--src/qt/bantablemodel.cpp11
-rw-r--r--src/qt/bantablemodel.h9
-rw-r--r--src/qt/bitcoin.cpp110
-rw-r--r--src/qt/bitcoingui.cpp41
-rw-r--r--src/qt/bitcoingui.h12
-rw-r--r--src/qt/clientmodel.cpp160
-rw-r--r--src/qt/clientmodel.h46
-rw-r--r--src/qt/coincontroldialog.cpp75
-rw-r--r--src/qt/guiutil.cpp15
-rw-r--r--src/qt/guiutil.h10
-rw-r--r--src/qt/intro.cpp8
-rw-r--r--src/qt/intro.h6
-rw-r--r--src/qt/optionsdialog.cpp7
-rw-r--r--src/qt/optionsmodel.cpp36
-rw-r--r--src/qt/optionsmodel.h9
-rw-r--r--src/qt/overviewpage.cpp54
-rw-r--r--src/qt/overviewpage.h12
-rw-r--r--src/qt/paymentserver.cpp32
-rw-r--r--src/qt/paymentserver.h8
-rw-r--r--src/qt/peertablemodel.cpp36
-rw-r--r--src/qt/peertablemodel.h9
-rw-r--r--src/qt/receivecoinsdialog.cpp6
-rw-r--r--src/qt/recentrequeststablemodel.cpp6
-rw-r--r--src/qt/recentrequeststablemodel.h4
-rw-r--r--src/qt/rpcconsole.cpp54
-rw-r--r--src/qt/rpcconsole.h13
-rw-r--r--src/qt/sendcoinsdialog.cpp48
-rw-r--r--src/qt/sendcoinsdialog.h3
-rw-r--r--src/qt/sendcoinsentry.cpp4
-rw-r--r--src/qt/sendcoinsentry.h2
-rw-r--r--src/qt/signverifymessagedialog.cpp2
-rw-r--r--src/qt/splashscreen.cpp40
-rw-r--r--src/qt/splashscreen.h20
-rw-r--r--src/qt/test/paymentrequestdata.h5
-rw-r--r--src/qt/test/paymentservertests.cpp6
-rw-r--r--src/qt/test/rpcnestedtests.cpp84
-rw-r--r--src/qt/test/rpcnestedtests.h6
-rw-r--r--src/qt/test/wallettests.cpp12
-rw-r--r--src/qt/trafficgraphwidget.cpp13
-rw-r--r--src/qt/transactiondesc.cpp138
-rw-r--r--src/qt/transactiondesc.h12
-rw-r--r--src/qt/transactionrecord.cpp86
-rw-r--r--src/qt/transactionrecord.h16
-rw-r--r--src/qt/transactiontablemodel.cpp100
-rw-r--r--src/qt/transactiontablemodel.h13
-rw-r--r--src/qt/transactionview.cpp10
-rw-r--r--src/qt/utilitydialog.cpp5
-rw-r--r--src/qt/utilitydialog.h6
-rw-r--r--src/qt/walletmodel.cpp341
-rw-r--r--src/qt/walletmodel.h67
-rw-r--r--src/qt/walletmodeltransaction.cpp20
-rw-r--r--src/qt/walletmodeltransaction.h17
-rw-r--r--src/qt/walletview.cpp5
-rw-r--r--src/rest.cpp92
-rw-r--r--src/rpc/blockchain.cpp21
-rw-r--r--src/rpc/client.cpp2
-rw-r--r--src/rpc/client.h6
-rw-r--r--src/rpc/mining.cpp12
-rw-r--r--src/rpc/protocol.h6
-rw-r--r--src/rpc/rawtransaction.cpp94
-rw-r--r--src/rpc/register.h6
-rw-r--r--src/rpc/server.cpp6
-rw-r--r--src/rpc/server.h7
-rw-r--r--src/script/bitcoinconsensus.h6
-rw-r--r--src/script/interpreter.cpp28
-rw-r--r--src/script/interpreter.h6
-rw-r--r--src/script/ismine.cpp10
-rw-r--r--src/script/ismine.h10
-rw-r--r--src/script/sign.cpp39
-rw-r--r--src/script/sign.h33
-rw-r--r--src/script/standard.cpp4
-rw-r--r--src/serialize.h10
-rw-r--r--src/support/lockedpool.cpp1
-rw-r--r--src/sync.cpp1
-rw-r--r--src/test/allocator_tests.cpp2
-rw-r--r--src/test/blockchain_tests.cpp2
-rw-r--r--src/test/compress_tests.cpp8
-rw-r--r--src/test/data/script_tests.json28
-rw-r--r--src/test/data/tx_invalid.json4
-rw-r--r--src/test/dbwrapper_tests.cpp2
-rw-r--r--src/test/multisig_tests.cpp2
-rw-r--r--src/test/net_tests.cpp8
-rw-r--r--src/test/prevector_tests.cpp2
-rw-r--r--src/test/script_tests.cpp134
-rw-r--r--src/test/serialize_tests.cpp16
-rw-r--r--src/test/sighash_tests.cpp4
-rw-r--r--src/test/streams_tests.cpp8
-rw-r--r--src/test/test_bitcoin.cpp2
-rw-r--r--src/test/test_bitcoin.h2
-rw-r--r--src/test/test_bitcoin_fuzzy.cpp1
-rw-r--r--src/test/test_bitcoin_main.cpp2
-rw-r--r--src/test/transaction_tests.cpp2
-rw-r--r--src/test/txvalidationcache_tests.cpp8
-rw-r--r--src/test/util_tests.cpp65
-rw-r--r--src/test/versionbits_tests.cpp20
-rw-r--r--src/txdb.cpp2
-rw-r--r--src/txdb.h1
-rw-r--r--src/txmempool.cpp4
-rw-r--r--src/txmempool.h4
-rw-r--r--src/util.cpp137
-rw-r--r--src/util.h24
-rw-r--r--src/validation.cpp108
-rw-r--r--src/validation.h8
-rw-r--r--src/versionbits.cpp28
-rw-r--r--src/versionbits.h18
-rw-r--r--src/wallet/coinselection.h6
-rw-r--r--src/wallet/crypter.h4
-rw-r--r--src/wallet/db.cpp10
-rw-r--r--src/wallet/db.h3
-rw-r--r--src/wallet/init.cpp62
-rw-r--r--src/wallet/init.h43
-rw-r--r--src/wallet/rpcdump.cpp4
-rw-r--r--src/wallet/rpcwallet.cpp4
-rw-r--r--src/wallet/test/accounting_tests.cpp2
-rw-r--r--src/wallet/test/coinselector_tests.cpp2
-rw-r--r--src/wallet/test/crypto_tests.cpp2
-rw-r--r--src/wallet/test/wallet_test_fixture.h9
-rw-r--r--src/wallet/test/wallet_tests.cpp1
-rw-r--r--src/wallet/wallet.cpp43
-rw-r--r--src/wallet/wallet.h50
-rw-r--r--src/wallet/walletdb.cpp42
-rw-r--r--src/wallet/walletdb.h14
-rw-r--r--src/wallet/walletutil.h6
-rw-r--r--src/walletinitinterface.h37
-rw-r--r--test/functional/README.md52
-rwxr-xr-xtest/functional/feature_bip9_softforks.py283
-rwxr-xr-xtest/functional/feature_block.py1308
-rwxr-xr-xtest/functional/feature_blocksdir.py36
-rwxr-xr-xtest/functional/feature_config_args.py5
-rwxr-xr-xtest/functional/feature_csv_activation.py566
-rwxr-xr-xtest/functional/feature_fee_estimation.py8
-rwxr-xr-xtest/functional/feature_help.py42
-rwxr-xr-xtest/functional/feature_logging.py6
-rwxr-xr-xtest/functional/feature_pruning.py4
-rwxr-xr-xtest/functional/feature_uacomment.py5
-rwxr-xr-xtest/functional/interface_rest.py427
-rwxr-xr-xtest/functional/mempool_accept.py293
-rwxr-xr-xtest/functional/p2p_segwit.py6
-rwxr-xr-xtest/functional/p2p_sendheaders.py9
-rwxr-xr-xtest/functional/rpc_net.py34
-rw-r--r--test/functional/test_framework/authproxy.py2
-rw-r--r--test/functional/test_framework/blockstore.py160
-rwxr-xr-xtest/functional/test_framework/comptool.py397
-rwxr-xr-xtest/functional/test_framework/messages.py5
-rwxr-xr-xtest/functional/test_framework/mininode.py4
-rwxr-xr-xtest/functional/test_framework/test_framework.py29
-rwxr-xr-xtest/functional/test_framework/test_node.py30
-rw-r--r--test/functional/test_framework/util.py13
-rwxr-xr-xtest/functional/test_runner.py20
-rwxr-xr-xtest/functional/wallet_bumpfee.py4
-rwxr-xr-xtest/functional/wallet_importmulti.py8
-rwxr-xr-xtest/functional/wallet_multiwallet.py12
225 files changed, 5812 insertions, 4203 deletions
diff --git a/.travis.yml b/.travis.yml
index 6e3fc5144c..80265c0353 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -8,8 +8,6 @@ cache:
- depends/built
- depends/sdk-sources
- $HOME/.ccache
-git:
- depth: 1
env:
global:
- MAKEJOBS=-j3
@@ -66,7 +64,7 @@ before_script:
- if [ "$NEED_XVFB" = 1 ]; then export DISPLAY=:99.0; /sbin/start-stop-daemon --start --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac; fi
script:
- if [ "$CHECK_DOC" = 1 -a "$TRAVIS_REPO_SLUG" = "bitcoin/bitcoin" -a "$TRAVIS_PULL_REQUEST" = "false" ]; then while read LINE; do travis_retry gpg --keyserver hkp://subset.pool.sks-keyservers.net --recv-keys $LINE; done < contrib/verify-commits/trusted-keys; fi
- - if [ "$CHECK_DOC" = 1 -a "$TRAVIS_REPO_SLUG" = "bitcoin/bitcoin" -a "$TRAVIS_PULL_REQUEST" = "false" ]; then contrib/verify-commits/verify-commits.sh; fi
+ - if [ "$CHECK_DOC" = 1 -a "$TRAVIS_REPO_SLUG" = "bitcoin/bitcoin" -a "$TRAVIS_EVENT_TYPE" = "cron" ]; then travis_wait 30 contrib/verify-commits/verify-commits.sh; fi
- export TRAVIS_COMMIT_LOG=`git log --format=fuller -1`
- if [ -n "$USE_SHELL" ]; then export CONFIG_SHELL="$USE_SHELL"; fi
- OUTDIR=$BASE_OUTDIR/$TRAVIS_PULL_REQUEST/$TRAVIS_JOB_NUMBER-$HOST
diff --git a/Makefile.am b/Makefile.am
index f345760f2d..f554501b2e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -293,6 +293,5 @@ clean-docs:
rm -rf doc/doxygen
clean-local: clean-docs
- rm -rf coverage_percent.txt test_bitcoin.coverage/ total.coverage/ test/tmp/ cache/ $(OSX_APP)
+ rm -rf coverage_percent.txt test_bitcoin.coverage/ total.coverage/ test/tmp/ cache/ $(OSX_APP) src/qt/moc_*
rm -rf test/functional/__pycache__ test/functional/test_framework/__pycache__ test/cache
-
diff --git a/configure.ac b/configure.ac
index 4f57750a1a..c422914a26 100644
--- a/configure.ac
+++ b/configure.ac
@@ -219,6 +219,12 @@ AC_ARG_ENABLE([debug],
[enable_debug=$enableval],
[enable_debug=no])
+# Enable different -fsanitize options
+AC_ARG_WITH([sanitizers],
+ [AS_HELP_STRING([--with-sanitizers],
+ [comma separated list of extra sanitizers to build with (default is none enabled)])],
+ [use_sanitizers=$withval])
+
# Enable gprof profiling
AC_ARG_ENABLE([gprof],
[AS_HELP_STRING([--enable-gprof],
@@ -247,6 +253,26 @@ if test "x$enable_debug" = xyes; then
fi
fi
+if test x$use_sanitizers != x; then
+ # First check if the compiler accepts flags. If an incompatible pair like
+ # -fsanitize=address,thread is used here, this check will fail. This will also
+ # fail if a bad argument is passed, e.g. -fsanitize=undfeined
+ AX_CHECK_COMPILE_FLAG(
+ [[-fsanitize=$use_sanitizers]],
+ [[SANITIZER_CXXFLAGS=-fsanitize=$use_sanitizers]],
+ [AC_MSG_ERROR([compiler did not accept requested flags])])
+
+ # Some compilers (e.g. GCC) require additional libraries like libasan,
+ # libtsan, libubsan, etc. Make sure linking still works with the sanitize
+ # flag. This is a separate check so we can give a better error message when
+ # the sanitize flags are supported by the compiler but the actual sanitizer
+ # libs are missing.
+ AX_CHECK_LINK_FLAG(
+ [[-fsanitize=$use_sanitizers]],
+ [[SANITIZER_LDFLAGS=-fsanitize=$use_sanitizers]],
+ [AC_MSG_ERROR([linker did not accept requested flags, you are missing required libraries])])
+fi
+
ERROR_CXXFLAGS=
if test "x$enable_werror" = "xyes"; then
if test "x$CXXFLAG_WERROR" = "x"; then
@@ -1258,6 +1284,8 @@ AC_SUBST(HARDENED_CPPFLAGS)
AC_SUBST(HARDENED_LDFLAGS)
AC_SUBST(PIC_FLAGS)
AC_SUBST(PIE_FLAGS)
+AC_SUBST(SANITIZER_CXXFLAGS)
+AC_SUBST(SANITIZER_LDFLAGS)
AC_SUBST(SSE42_CXXFLAGS)
AC_SUBST(LIBTOOL_APP_LDFLAGS)
AC_SUBST(USE_UPNP)
diff --git a/contrib/devtools/check-doc.py b/contrib/devtools/check-doc.py
index f164ea9322..0c2e1a24be 100755
--- a/contrib/devtools/check-doc.py
+++ b/contrib/devtools/check-doc.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# Copyright (c) 2015-2017 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -16,31 +16,33 @@ import sys
FOLDER_GREP = 'src'
FOLDER_TEST = 'src/test/'
-CMD_ROOT_DIR = '`git rev-parse --show-toplevel`/%s' % FOLDER_GREP
-CMD_GREP_ARGS = r"egrep -r -I '(map(Multi)?Args(\.count\(|\[)|Get(Bool)?Arg\()\"\-[^\"]+?\"' %s | grep -v '%s'" % (CMD_ROOT_DIR, FOLDER_TEST)
-CMD_GREP_DOCS = r"egrep -r -I 'HelpMessageOpt\(\"\-[^\"=]+?(=|\")' %s" % (CMD_ROOT_DIR)
-REGEX_ARG = re.compile(r'(?:map(?:Multi)?Args(?:\.count\(|\[)|Get(?:Bool)?Arg\()\"(\-[^\"]+?)\"')
-REGEX_DOC = re.compile(r'HelpMessageOpt\(\"(\-[^\"=]+?)(?:=|\")')
+REGEX_ARG = '(?:ForceSet|SoftSet|Get|Is)(?:Bool)?Args?(?:Set)?\("(-[^"]+)"'
+REGEX_DOC = 'HelpMessageOpt\("(-[^"=]+?)(?:=|")'
+CMD_ROOT_DIR = '`git rev-parse --show-toplevel`/{}'.format(FOLDER_GREP)
+CMD_GREP_ARGS = r"git grep --perl-regexp '{}' -- {} ':(exclude){}'".format(REGEX_ARG, CMD_ROOT_DIR, FOLDER_TEST)
+CMD_GREP_DOCS = r"git grep --perl-regexp '{}' {}".format(REGEX_DOC, CMD_ROOT_DIR)
# list unsupported, deprecated and duplicate args as they need no documentation
SET_DOC_OPTIONAL = set(['-rpcssl', '-benchmark', '-h', '-help', '-socks', '-tor', '-debugnet', '-whitelistalwaysrelay', '-prematurewitness', '-walletprematurewitness', '-promiscuousmempoolflags', '-blockminsize', '-dbcrashratio', '-forcecompactdb', '-usehd'])
+
def main():
- used = check_output(CMD_GREP_ARGS, shell=True)
- docd = check_output(CMD_GREP_DOCS, shell=True)
-
- args_used = set(re.findall(REGEX_ARG,used))
- args_docd = set(re.findall(REGEX_DOC,docd)).union(SET_DOC_OPTIONAL)
- args_need_doc = args_used.difference(args_docd)
- args_unknown = args_docd.difference(args_used)
-
- print "Args used : %s" % len(args_used)
- print "Args documented : %s" % len(args_docd)
- print "Args undocumented: %s" % len(args_need_doc)
- print args_need_doc
- print "Args unknown : %s" % len(args_unknown)
- print args_unknown
-
- sys.exit(len(args_need_doc))
+ used = check_output(CMD_GREP_ARGS, shell=True, universal_newlines=True)
+ docd = check_output(CMD_GREP_DOCS, shell=True, universal_newlines=True)
+
+ args_used = set(re.findall(re.compile(REGEX_ARG), used))
+ args_docd = set(re.findall(re.compile(REGEX_DOC), docd)).union(SET_DOC_OPTIONAL)
+ args_need_doc = args_used.difference(args_docd)
+ args_unknown = args_docd.difference(args_used)
+
+ print("Args used : {}".format(len(args_used)))
+ print("Args documented : {}".format(len(args_docd)))
+ print("Args undocumented: {}".format(len(args_need_doc)))
+ print(args_need_doc)
+ print("Args unknown : {}".format(len(args_unknown)))
+ print(args_unknown)
+
+ sys.exit(len(args_need_doc))
+
if __name__ == "__main__":
main()
diff --git a/contrib/devtools/clang-format-diff.py b/contrib/devtools/clang-format-diff.py
index 7ea49b65e1..5402870fba 100755
--- a/contrib/devtools/clang-format-diff.py
+++ b/contrib/devtools/clang-format-diff.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
#
#===- clang-format-diff.py - ClangFormat Diff Reformatter ----*- python -*--===#
#
@@ -69,10 +69,9 @@ Example usage for git/svn users:
import argparse
import difflib
+import io
import re
-import string
import subprocess
-import StringIO
import sys
@@ -133,9 +132,9 @@ def main():
['-lines', str(start_line) + ':' + str(end_line)])
# Reformat files containing changes in place.
- for filename, lines in lines_by_file.iteritems():
+ for filename, lines in lines_by_file.items():
if args.i and args.verbose:
- print 'Formatting', filename
+ print('Formatting {}'.format(filename))
command = [binary, filename]
if args.i:
command.append('-i')
@@ -143,8 +142,11 @@ def main():
command.append('-sort-includes')
command.extend(lines)
command.extend(['-style=file', '-fallback-style=none'])
- p = subprocess.Popen(command, stdout=subprocess.PIPE,
- stderr=None, stdin=subprocess.PIPE)
+ p = subprocess.Popen(command,
+ stdout=subprocess.PIPE,
+ stderr=None,
+ stdin=subprocess.PIPE,
+ universal_newlines=True)
stdout, stderr = p.communicate()
if p.returncode != 0:
sys.exit(p.returncode)
@@ -152,11 +154,11 @@ def main():
if not args.i:
with open(filename) as f:
code = f.readlines()
- formatted_code = StringIO.StringIO(stdout).readlines()
+ formatted_code = io.StringIO(stdout).readlines()
diff = difflib.unified_diff(code, formatted_code,
filename, filename,
'(before formatting)', '(after formatting)')
- diff_string = string.join(diff, '')
+ diff_string = ''.join(diff)
if len(diff_string) > 0:
sys.stdout.write(diff_string)
diff --git a/contrib/devtools/github-merge.py b/contrib/devtools/github-merge.py
index 2941d2cb6d..9c666673cf 100755
--- a/contrib/devtools/github-merge.py
+++ b/contrib/devtools/github-merge.py
@@ -46,7 +46,7 @@ def git_config_get(option, default=None):
'''
try:
return subprocess.check_output([GIT,'config','--get',option]).rstrip().decode('utf-8')
- except subprocess.CalledProcessError as e:
+ except subprocess.CalledProcessError:
return default
def retrieve_pr_info(repo,pull):
@@ -193,23 +193,23 @@ def main():
devnull = open(os.devnull,'w')
try:
subprocess.check_call([GIT,'checkout','-q',branch])
- except subprocess.CalledProcessError as e:
+ except subprocess.CalledProcessError:
print("ERROR: Cannot check out branch %s." % (branch), file=stderr)
sys.exit(3)
try:
subprocess.check_call([GIT,'fetch','-q',host_repo,'+refs/pull/'+pull+'/*:refs/heads/pull/'+pull+'/*',
'+refs/heads/'+branch+':refs/heads/'+base_branch])
- except subprocess.CalledProcessError as e:
+ except subprocess.CalledProcessError:
print("ERROR: Cannot find pull request #%s or branch %s on %s." % (pull,branch,host_repo), file=stderr)
sys.exit(3)
try:
subprocess.check_call([GIT,'log','-q','-1','refs/heads/'+head_branch], stdout=devnull, stderr=stdout)
- except subprocess.CalledProcessError as e:
+ except subprocess.CalledProcessError:
print("ERROR: Cannot find head of pull request #%s on %s." % (pull,host_repo), file=stderr)
sys.exit(3)
try:
subprocess.check_call([GIT,'log','-q','-1','refs/heads/'+merge_branch], stdout=devnull, stderr=stdout)
- except subprocess.CalledProcessError as e:
+ except subprocess.CalledProcessError:
print("ERROR: Cannot find merge of pull request #%s on %s." % (pull,host_repo), file=stderr)
sys.exit(3)
subprocess.check_call([GIT,'checkout','-q',base_branch])
@@ -230,7 +230,7 @@ def main():
message += '\n\nPull request description:\n\n ' + body.replace('\n', '\n ') + '\n'
try:
subprocess.check_call([GIT,'merge','-q','--commit','--no-edit','--no-ff','-m',message.encode('utf-8'),head_branch])
- except subprocess.CalledProcessError as e:
+ except subprocess.CalledProcessError:
print("ERROR: Cannot be merged cleanly.",file=stderr)
subprocess.check_call([GIT,'merge','--abort'])
sys.exit(4)
@@ -249,12 +249,12 @@ def main():
try:
first_sha512 = tree_sha512sum()
message += '\n\nTree-SHA512: ' + first_sha512
- except subprocess.CalledProcessError as e:
+ except subprocess.CalledProcessError:
print("ERROR: Unable to compute tree hash")
sys.exit(4)
try:
subprocess.check_call([GIT,'commit','--amend','-m',message.encode('utf-8')])
- except subprocess.CalledProcessError as e:
+ except subprocess.CalledProcessError:
print("ERROR: Cannot update message.", file=stderr)
sys.exit(4)
@@ -299,7 +299,7 @@ def main():
try:
subprocess.check_call([GIT,'commit','-q','--gpg-sign','--amend','--no-edit'])
break
- except subprocess.CalledProcessError as e:
+ except subprocess.CalledProcessError:
print("Error while signing, asking again.",file=stderr)
elif reply == 'x':
print("Not signing off on merge, exiting.",file=stderr)
diff --git a/contrib/devtools/lint-include-guards.sh b/contrib/devtools/lint-include-guards.sh
new file mode 100755
index 0000000000..6a0dd556bb
--- /dev/null
+++ b/contrib/devtools/lint-include-guards.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+#
+# Copyright (c) 2018 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#
+# Check include guards.
+
+HEADER_ID_PREFIX="BITCOIN_"
+HEADER_ID_SUFFIX="_H"
+
+REGEXP_EXCLUDE_FILES_WITH_PREFIX="src/(crypto/ctaes/|leveldb/|secp256k1/|tinyformat.h|univalue/)"
+
+EXIT_CODE=0
+for HEADER_FILE in $(git ls-files -- "*.h" | grep -vE "^${REGEXP_EXCLUDE_FILES_WITH_PREFIX}")
+do
+ HEADER_ID_BASE=$(cut -f2- -d/ <<< "${HEADER_FILE}" | sed "s/\.h$//g" | tr / _ | tr "[:lower:]" "[:upper:]")
+ HEADER_ID="${HEADER_ID_PREFIX}${HEADER_ID_BASE}${HEADER_ID_SUFFIX}"
+ if [[ $(grep -cE "^#(ifndef|define) ${HEADER_ID}" "${HEADER_FILE}") != 2 ]]; then
+ echo "${HEADER_FILE} seems to be missing the expected include guard:"
+ echo " #ifndef ${HEADER_ID}"
+ echo " #define ${HEADER_ID}"
+ echo " ..."
+ echo " #endif // ${HEADER_ID}"
+ echo
+ EXIT_CODE=1
+ fi
+done
+exit ${EXIT_CODE}
diff --git a/contrib/devtools/lint-python.sh b/contrib/devtools/lint-python.sh
index e2c9d775a6..1469ce1640 100755
--- a/contrib/devtools/lint-python.sh
+++ b/contrib/devtools/lint-python.sh
@@ -52,6 +52,7 @@
# F822 undefined name name in __all__
# F823 local variable name … referenced before assignment
# F831 duplicate argument name in function definition
+# F841 local variable 'foo' is assigned to but never used
# W292 no newline at end of file
# W504 line break after binary operator
# W601 .has_key() is deprecated, use "in"
@@ -60,4 +61,4 @@
# W604 backticks are deprecated, use "repr()"
# W605 invalid escape sequence "x"
-flake8 --ignore=B,C,E,F,I,N,W --select=E112,E113,E115,E116,E125,E131,E133,E223,E224,E271,E272,E273,E274,E275,E304,E306,E502,E702,E703,E714,E721,E741,E742,E743,F401,F402,F404,F406,F407,F601,F602,F621,F622,F631,F701,F702,F703,F704,F705,F706,F707,F811,F812,F822,F823,F831,W292,W504,W601,W602,W603,W604,W605 .
+flake8 --ignore=B,C,E,F,I,N,W --select=E112,E113,E115,E116,E125,E131,E133,E223,E224,E271,E272,E273,E274,E275,E304,E306,E502,E702,E703,E714,E721,E741,E742,E743,F401,F402,F404,F406,F407,F601,F602,F621,F622,F631,F701,F702,F703,F704,F705,F706,F707,F811,F812,F822,F823,F831,F841,W292,W504,W601,W602,W603,W604,W605 .
diff --git a/contrib/devtools/lint-tests.sh b/contrib/devtools/lint-tests.sh
new file mode 100755
index 0000000000..dd1a3ebdc4
--- /dev/null
+++ b/contrib/devtools/lint-tests.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (c) 2018 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#
+# Check the test suite naming convention
+
+NAMING_INCONSISTENCIES=$(git grep -E '^BOOST_FIXTURE_TEST_SUITE\(' -- \
+ "src/test/**.cpp" "src/wallet/test/**.cpp" | \
+ grep -vE '/(.*?)\.cpp:BOOST_FIXTURE_TEST_SUITE\(\1, .*\)$')
+if [[ ${NAMING_INCONSISTENCIES} != "" ]]; then
+ echo "The test suite in file src/test/foo_tests.cpp should be named"
+ echo "\"foo_tests\". Please make sure the following test suites follow"
+ echo "that convention:"
+ echo
+ echo "${NAMING_INCONSISTENCIES}"
+ exit 1
+fi
diff --git a/contrib/devtools/optimize-pngs.py b/contrib/devtools/optimize-pngs.py
index 5cb3bb6f75..565b199125 100755
--- a/contrib/devtools/optimize-pngs.py
+++ b/contrib/devtools/optimize-pngs.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# Copyright (c) 2014-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.
@@ -10,7 +10,7 @@ import os
import sys
import subprocess
import hashlib
-from PIL import Image
+from PIL import Image # pip3 install Pillow
def file_hash(filename):
'''Return hash of raw file contents'''
@@ -27,7 +27,7 @@ def content_hash(filename):
pngcrush = 'pngcrush'
git = 'git'
folders = ["src/qt/res/movies", "src/qt/res/icons", "share/pixmaps"]
-basePath = subprocess.check_output([git, 'rev-parse', '--show-toplevel']).rstrip('\n')
+basePath = subprocess.check_output([git, 'rev-parse', '--show-toplevel'], universal_newlines=True).rstrip('\n')
totalSaveBytes = 0
noHashChange = True
@@ -37,42 +37,40 @@ for folder in folders:
for file in os.listdir(absFolder):
extension = os.path.splitext(file)[1]
if extension.lower() == '.png':
- print("optimizing "+file+"..."),
+ print("optimizing {}...".format(file), end =' ')
file_path = os.path.join(absFolder, file)
fileMetaMap = {'file' : file, 'osize': os.path.getsize(file_path), 'sha256Old' : file_hash(file_path)}
fileMetaMap['contentHashPre'] = content_hash(file_path)
- pngCrushOutput = ""
try:
- pngCrushOutput = subprocess.check_output(
- [pngcrush, "-brute", "-ow", "-rem", "gAMA", "-rem", "cHRM", "-rem", "iCCP", "-rem", "sRGB", "-rem", "alla", "-rem", "text", file_path],
- stderr=subprocess.STDOUT).rstrip('\n')
+ subprocess.call([pngcrush, "-brute", "-ow", "-rem", "gAMA", "-rem", "cHRM", "-rem", "iCCP", "-rem", "sRGB", "-rem", "alla", "-rem", "text", file_path],
+ stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
except:
- print "pngcrush is not installed, aborting..."
+ print("pngcrush is not installed, aborting...")
sys.exit(0)
#verify
- if "Not a PNG file" in subprocess.check_output([pngcrush, "-n", "-v", file_path], stderr=subprocess.STDOUT):
- print "PNG file "+file+" is corrupted after crushing, check out pngcursh version"
+ if "Not a PNG file" in subprocess.check_output([pngcrush, "-n", "-v", file_path], stderr=subprocess.STDOUT, universal_newlines=True):
+ print("PNG file "+file+" is corrupted after crushing, check out pngcursh version")
sys.exit(1)
fileMetaMap['sha256New'] = file_hash(file_path)
fileMetaMap['contentHashPost'] = content_hash(file_path)
if fileMetaMap['contentHashPre'] != fileMetaMap['contentHashPost']:
- print "Image contents of PNG file "+file+" before and after crushing don't match"
+ print("Image contents of PNG file {} before and after crushing don't match".format(file))
sys.exit(1)
fileMetaMap['psize'] = os.path.getsize(file_path)
outputArray.append(fileMetaMap)
- print("done\n"),
+ print("done")
-print "summary:\n+++++++++++++++++"
+print("summary:\n+++++++++++++++++")
for fileDict in outputArray:
oldHash = fileDict['sha256Old']
newHash = fileDict['sha256New']
totalSaveBytes += fileDict['osize'] - fileDict['psize']
noHashChange = noHashChange and (oldHash == newHash)
- print fileDict['file']+"\n size diff from: "+str(fileDict['osize'])+" to: "+str(fileDict['psize'])+"\n old sha256: "+oldHash+"\n new sha256: "+newHash+"\n"
+ print(fileDict['file']+"\n size diff from: "+str(fileDict['osize'])+" to: "+str(fileDict['psize'])+"\n old sha256: "+oldHash+"\n new sha256: "+newHash+"\n")
-print "completed. Checksum stable: "+str(noHashChange)+". Total reduction: "+str(totalSaveBytes)+" bytes"
+print("completed. Checksum stable: "+str(noHashChange)+". Total reduction: "+str(totalSaveBytes)+" bytes")
diff --git a/contrib/devtools/security-check.py b/contrib/devtools/security-check.py
index 1613f704df..0f2099953f 100755
--- a/contrib/devtools/security-check.py
+++ b/contrib/devtools/security-check.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# Copyright (c) 2015-2017 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -8,7 +8,6 @@ Exit status will be 0 if successful, and the program will be silent.
Otherwise the exit status will be 1 and it will log which executables failed which checks.
Needs `readelf` (for ELF) and `objdump` (for PE).
'''
-from __future__ import division,print_function,unicode_literals
import subprocess
import sys
import os
@@ -21,38 +20,38 @@ def check_ELF_PIE(executable):
'''
Check for position independent executable (PIE), allowing for address space randomization.
'''
- p = subprocess.Popen([READELF_CMD, '-h', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
+ p = subprocess.Popen([READELF_CMD, '-h', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True)
(stdout, stderr) = p.communicate()
if p.returncode:
raise IOError('Error opening file')
ok = False
- for line in stdout.split(b'\n'):
+ for line in stdout.splitlines():
line = line.split()
- if len(line)>=2 and line[0] == b'Type:' and line[1] == b'DYN':
+ if len(line)>=2 and line[0] == 'Type:' and line[1] == 'DYN':
ok = True
return ok
def get_ELF_program_headers(executable):
'''Return type and flags for ELF program headers'''
- p = subprocess.Popen([READELF_CMD, '-l', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
+ p = subprocess.Popen([READELF_CMD, '-l', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True)
(stdout, stderr) = p.communicate()
if p.returncode:
raise IOError('Error opening file')
in_headers = False
count = 0
headers = []
- for line in stdout.split(b'\n'):
- if line.startswith(b'Program Headers:'):
+ for line in stdout.splitlines():
+ if line.startswith('Program Headers:'):
in_headers = True
- if line == b'':
+ if line == '':
in_headers = False
if in_headers:
if count == 1: # header line
- ofs_typ = line.find(b'Type')
- ofs_offset = line.find(b'Offset')
- ofs_flags = line.find(b'Flg')
- ofs_align = line.find(b'Align')
+ ofs_typ = line.find('Type')
+ ofs_offset = line.find('Offset')
+ ofs_flags = line.find('Flg')
+ ofs_align = line.find('Align')
if ofs_typ == -1 or ofs_offset == -1 or ofs_flags == -1 or ofs_align == -1:
raise ValueError('Cannot parse elfread -lW output')
elif count > 1:
@@ -69,9 +68,9 @@ def check_ELF_NX(executable):
have_wx = False
have_gnu_stack = False
for (typ, flags) in get_ELF_program_headers(executable):
- if typ == b'GNU_STACK':
+ if typ == 'GNU_STACK':
have_gnu_stack = True
- if b'W' in flags and b'E' in flags: # section is both writable and executable
+ if 'W' in flags and 'E' in flags: # section is both writable and executable
have_wx = True
return have_gnu_stack and not have_wx
@@ -88,17 +87,17 @@ def check_ELF_RELRO(executable):
# However, the dynamic linker need to write to this area so these are RW.
# Glibc itself takes care of mprotecting this area R after relocations are finished.
# See also http://permalink.gmane.org/gmane.comp.gnu.binutils/71347
- if typ == b'GNU_RELRO':
+ if typ == 'GNU_RELRO':
have_gnu_relro = True
have_bindnow = False
- p = subprocess.Popen([READELF_CMD, '-d', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
+ p = subprocess.Popen([READELF_CMD, '-d', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True)
(stdout, stderr) = p.communicate()
if p.returncode:
raise IOError('Error opening file')
- for line in stdout.split(b'\n'):
+ for line in stdout.splitlines():
tokens = line.split()
- if len(tokens)>1 and tokens[1] == b'(BIND_NOW)' or (len(tokens)>2 and tokens[1] == b'(FLAGS)' and b'BIND_NOW' in tokens[2]):
+ if len(tokens)>1 and tokens[1] == '(BIND_NOW)' or (len(tokens)>2 and tokens[1] == '(FLAGS)' and 'BIND_NOW' in tokens[2]):
have_bindnow = True
return have_gnu_relro and have_bindnow
@@ -106,13 +105,13 @@ def check_ELF_Canary(executable):
'''
Check for use of stack canary
'''
- p = subprocess.Popen([READELF_CMD, '--dyn-syms', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
+ p = subprocess.Popen([READELF_CMD, '--dyn-syms', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True)
(stdout, stderr) = p.communicate()
if p.returncode:
raise IOError('Error opening file')
ok = False
- for line in stdout.split(b'\n'):
- if b'__stack_chk_fail' in line:
+ for line in stdout.splitlines():
+ if '__stack_chk_fail' in line:
ok = True
return ok
@@ -122,13 +121,13 @@ def get_PE_dll_characteristics(executable):
Returns a tuple (arch,bits) where arch is 'i386:x86-64' or 'i386'
and bits is the DllCharacteristics value.
'''
- p = subprocess.Popen([OBJDUMP_CMD, '-x', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
+ p = subprocess.Popen([OBJDUMP_CMD, '-x', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True)
(stdout, stderr) = p.communicate()
if p.returncode:
raise IOError('Error opening file')
arch = ''
bits = 0
- for line in stdout.split('\n'):
+ for line in stdout.splitlines():
tokens = line.split()
if len(tokens)>=2 and tokens[0] == 'architecture:':
arch = tokens[1].rstrip(',')
diff --git a/contrib/devtools/symbol-check.py b/contrib/devtools/symbol-check.py
index 98daa1bd76..3a67319eaa 100755
--- a/contrib/devtools/symbol-check.py
+++ b/contrib/devtools/symbol-check.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# Copyright (c) 2014 Wladimir J. van der Laan
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -11,7 +11,6 @@ Example usage:
find ../gitian-builder/build -type f -executable | xargs python contrib/devtools/symbol-check.py
'''
-from __future__ import division, print_function, unicode_literals
import subprocess
import re
import sys
@@ -47,28 +46,28 @@ MAX_VERSIONS = {
# Ignore symbols that are exported as part of every executable
IGNORE_EXPORTS = {
-b'_edata', b'_end', b'_init', b'__bss_start', b'_fini', b'_IO_stdin_used'
+'_edata', '_end', '_init', '__bss_start', '_fini', '_IO_stdin_used'
}
READELF_CMD = os.getenv('READELF', '/usr/bin/readelf')
CPPFILT_CMD = os.getenv('CPPFILT', '/usr/bin/c++filt')
# Allowed NEEDED libraries
ALLOWED_LIBRARIES = {
# bitcoind and bitcoin-qt
-b'libgcc_s.so.1', # GCC base support
-b'libc.so.6', # C library
-b'libpthread.so.0', # threading
-b'libanl.so.1', # DNS resolve
-b'libm.so.6', # math library
-b'librt.so.1', # real-time (clock)
-b'ld-linux-x86-64.so.2', # 64-bit dynamic linker
-b'ld-linux.so.2', # 32-bit dynamic linker
+'libgcc_s.so.1', # GCC base support
+'libc.so.6', # C library
+'libpthread.so.0', # threading
+'libanl.so.1', # DNS resolve
+'libm.so.6', # math library
+'librt.so.1', # real-time (clock)
+'ld-linux-x86-64.so.2', # 64-bit dynamic linker
+'ld-linux.so.2', # 32-bit dynamic linker
# bitcoin-qt only
-b'libX11-xcb.so.1', # part of X11
-b'libX11.so.6', # part of X11
-b'libxcb.so.1', # part of X11
-b'libfontconfig.so.1', # font support
-b'libfreetype.so.6', # font parsing
-b'libdl.so.2' # programming interface to dynamic linker
+'libX11-xcb.so.1', # part of X11
+'libX11.so.6', # part of X11
+'libxcb.so.1', # part of X11
+'libfontconfig.so.1', # font support
+'libfreetype.so.6', # font parsing
+'libdl.so.2' # programming interface to dynamic linker
}
class CPPFilt(object):
@@ -78,10 +77,10 @@ class CPPFilt(object):
Use a pipe to the 'c++filt' command.
'''
def __init__(self):
- self.proc = subprocess.Popen(CPPFILT_CMD, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
+ self.proc = subprocess.Popen(CPPFILT_CMD, stdin=subprocess.PIPE, stdout=subprocess.PIPE, universal_newlines=True)
def __call__(self, mangled):
- self.proc.stdin.write(mangled + b'\n')
+ self.proc.stdin.write(mangled + '\n')
self.proc.stdin.flush()
return self.proc.stdout.readline().rstrip()
@@ -95,43 +94,43 @@ def read_symbols(executable, imports=True):
Parse an ELF executable and return a list of (symbol,version) tuples
for dynamic, imported symbols.
'''
- p = subprocess.Popen([READELF_CMD, '--dyn-syms', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
+ p = subprocess.Popen([READELF_CMD, '--dyn-syms', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True)
(stdout, stderr) = p.communicate()
if p.returncode:
raise IOError('Could not read symbols for %s: %s' % (executable, stderr.strip()))
syms = []
- for line in stdout.split(b'\n'):
+ for line in stdout.splitlines():
line = line.split()
- if len(line)>7 and re.match(b'[0-9]+:$', line[0]):
- (sym, _, version) = line[7].partition(b'@')
- is_import = line[6] == b'UND'
- if version.startswith(b'@'):
+ if len(line)>7 and re.match('[0-9]+:$', line[0]):
+ (sym, _, version) = line[7].partition('@')
+ is_import = line[6] == 'UND'
+ if version.startswith('@'):
version = version[1:]
if is_import == imports:
syms.append((sym, version))
return syms
def check_version(max_versions, version):
- if b'_' in version:
- (lib, _, ver) = version.rpartition(b'_')
+ if '_' in version:
+ (lib, _, ver) = version.rpartition('_')
else:
lib = version
ver = '0'
- ver = tuple([int(x) for x in ver.split(b'.')])
+ ver = tuple([int(x) for x in ver.split('.')])
if not lib in max_versions:
return False
return ver <= max_versions[lib]
def read_libraries(filename):
- p = subprocess.Popen([READELF_CMD, '-d', '-W', filename], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
+ p = subprocess.Popen([READELF_CMD, '-d', '-W', filename], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True)
(stdout, stderr) = p.communicate()
if p.returncode:
raise IOError('Error opening file')
libraries = []
- for line in stdout.split(b'\n'):
+ for line in stdout.splitlines():
tokens = line.split()
- if len(tokens)>2 and tokens[1] == b'(NEEDED)':
- match = re.match(b'^Shared library: \[(.*)\]$', b' '.join(tokens[2:]))
+ if len(tokens)>2 and tokens[1] == '(NEEDED)':
+ match = re.match('^Shared library: \[(.*)\]$', ' '.join(tokens[2:]))
if match:
libraries.append(match.group(1))
else:
@@ -145,18 +144,18 @@ if __name__ == '__main__':
# Check imported symbols
for sym,version in read_symbols(filename, True):
if version and not check_version(MAX_VERSIONS, version):
- print('%s: symbol %s from unsupported version %s' % (filename, cppfilt(sym).decode('utf-8'), version.decode('utf-8')))
+ print('%s: symbol %s from unsupported version %s' % (filename, cppfilt(sym), version))
retval = 1
# Check exported symbols
for sym,version in read_symbols(filename, False):
if sym in IGNORE_EXPORTS:
continue
- print('%s: export of symbol %s not allowed' % (filename, cppfilt(sym).decode('utf-8')))
+ print('%s: export of symbol %s not allowed' % (filename, cppfilt(sym)))
retval = 1
# Check dependency libraries
for library_name in read_libraries(filename):
if library_name not in ALLOWED_LIBRARIES:
- print('%s: NEEDED library %s is not allowed' % (filename, library_name.decode('utf-8')))
+ print('%s: NEEDED library %s is not allowed' % (filename, library_name))
retval = 1
sys.exit(retval)
diff --git a/contrib/devtools/update-translations.py b/contrib/devtools/update-translations.py
index e1924749d2..b36e6968bf 100755
--- a/contrib/devtools/update-translations.py
+++ b/contrib/devtools/update-translations.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# Copyright (c) 2014 Wladimir J. van der Laan
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -15,7 +15,6 @@ It will do the following automatically:
TODO:
- auto-add new translations to the build system according to the translation process
'''
-from __future__ import division, print_function
import subprocess
import re
import sys
diff --git a/contrib/macdeploy/custom_dsstore.py b/contrib/macdeploy/custom_dsstore.py
index e6ecabace1..b29fc71765 100755
--- a/contrib/macdeploy/custom_dsstore.py
+++ b/contrib/macdeploy/custom_dsstore.py
@@ -1,8 +1,7 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# Copyright (c) 2013-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.
-from __future__ import division,print_function,unicode_literals
import biplist
from ds_store import DSStore
from mac_alias import Alias
diff --git a/contrib/macdeploy/macdeployqtplus b/contrib/macdeploy/macdeployqtplus
index 23a568ad13..17ce6c44f9 100755
--- a/contrib/macdeploy/macdeployqtplus
+++ b/contrib/macdeploy/macdeployqtplus
@@ -1,5 +1,4 @@
-#!/usr/bin/env python
-from __future__ import division, print_function, unicode_literals
+#!/usr/bin/env python3
#
# Copyright (C) 2011 Patrick "p2k" Schneider <me@p2k-network.org>
#
@@ -203,7 +202,7 @@ def getFrameworks(binaryPath, verbose):
if verbose >= 3:
print("Inspecting with otool: " + binaryPath)
otoolbin=os.getenv("OTOOL", "otool")
- otool = subprocess.Popen([otoolbin, "-L", binaryPath], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ otool = subprocess.Popen([otoolbin, "-L", binaryPath], stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
o_stdout, o_stderr = otool.communicate()
if otool.returncode != 0:
if verbose >= 1:
@@ -211,7 +210,7 @@ def getFrameworks(binaryPath, verbose):
sys.stderr.flush()
raise RuntimeError("otool failed with return code %d" % otool.returncode)
- otoolLines = o_stdout.decode().split("\n")
+ otoolLines = o_stdout.split("\n")
otoolLines.pop(0) # First line is the inspected binary
if ".framework" in binaryPath or binaryPath.endswith(".dylib"):
otoolLines.pop(0) # Frameworks and dylibs list themselves as a dependency.
@@ -714,22 +713,6 @@ elif config.sign:
if config.dmg is not None:
- #Patch in check_output for Python 2.6
- if "check_output" not in dir( subprocess ):
- def f(*popenargs, **kwargs):
- if 'stdout' in kwargs:
- raise ValueError('stdout argument not allowed, it will be overridden.')
- process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs)
- output, unused_err = process.communicate()
- retcode = process.poll()
- if retcode:
- cmd = kwargs.get("args")
- if cmd is None:
- cmd = popenargs[0]
- raise CalledProcessError(retcode, cmd)
- return output
- subprocess.check_output = f
-
def runHDIUtil(verb, image_basename, **kwargs):
hdiutil_args = ["hdiutil", verb, image_basename + ".dmg"]
if "capture_stdout" in kwargs:
@@ -747,7 +730,7 @@ if config.dmg is not None:
if not value is True:
hdiutil_args.append(str(value))
- return run(hdiutil_args)
+ return run(hdiutil_args, universal_newlines=True)
if verbose >= 2:
if fancy is None:
@@ -789,7 +772,7 @@ if config.dmg is not None:
except subprocess.CalledProcessError as e:
sys.exit(e.returncode)
- m = re.search("/Volumes/(.+$)", output.decode())
+ m = re.search("/Volumes/(.+$)", output)
disk_root = m.group(0)
disk_name = m.group(1)
diff --git a/contrib/testgen/base58.py b/contrib/testgen/base58.py
index 816d40b49c..0dbb79a707 100644
--- a/contrib/testgen/base58.py
+++ b/contrib/testgen/base58.py
@@ -28,7 +28,9 @@ def b58encode(v):
"""
long_value = 0
for (i, c) in enumerate(v[::-1]):
- long_value += (256**i) * ord(c)
+ if isinstance(c, str):
+ c = ord(c)
+ long_value += (256**i) * c
result = ''
while long_value >= __b58base:
@@ -41,7 +43,7 @@ def b58encode(v):
# leading 0-bytes in the input become leading-1s
nPad = 0
for c in v:
- if c == '\0': nPad += 1
+ if c == 0: nPad += 1
else: break
return (__b58chars[0]*nPad) + result
@@ -50,8 +52,10 @@ def b58decode(v, length = None):
""" decode v into a string of len bytes
"""
long_value = 0
- for (i, c) in enumerate(v[::-1]):
- long_value += __b58chars.find(c) * (__b58base**i)
+ for i, c in enumerate(v[::-1]):
+ pos = __b58chars.find(c)
+ assert pos != -1
+ long_value += pos * (__b58base**i)
result = bytes()
while long_value >= 256:
@@ -62,10 +66,12 @@ def b58decode(v, length = None):
nPad = 0
for c in v:
- if c == __b58chars[0]: nPad += 1
- else: break
+ if c == __b58chars[0]:
+ nPad += 1
+ continue
+ break
- result = chr(0)*nPad + result
+ result = bytes(nPad) + result
if length is not None and len(result) != length:
return None
diff --git a/contrib/testgen/gen_base58_test_vectors.py b/contrib/testgen/gen_base58_test_vectors.py
index 8e6a5d5819..4351605786 100755
--- a/contrib/testgen/gen_base58_test_vectors.py
+++ b/contrib/testgen/gen_base58_test_vectors.py
@@ -1,11 +1,11 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# Copyright (c) 2012-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.
'''
Generate valid and invalid base58 address and private key test vectors.
-Usage:
+Usage:
gen_base58_test_vectors.py valid 50 > ../../src/test/data/base58_keys_valid.json
gen_base58_test_vectors.py invalid 50 > ../../src/test/data/base58_keys_invalid.json
'''
@@ -46,8 +46,8 @@ def is_valid(v):
if result is None:
return False
for template in templates:
- prefix = str(bytearray(template[0]))
- suffix = str(bytearray(template[2]))
+ prefix = bytearray(template[0])
+ suffix = bytearray(template[2])
if result.startswith(prefix) and result.endswith(suffix):
if (len(result) - len(prefix) - len(suffix)) == template[1]:
return True
@@ -57,20 +57,23 @@ def gen_valid_vectors():
'''Generate valid test vectors'''
while True:
for template in templates:
- prefix = str(bytearray(template[0]))
- payload = os.urandom(template[1])
- suffix = str(bytearray(template[2]))
+ prefix = bytearray(template[0])
+ payload = bytearray(os.urandom(template[1]))
+ suffix = bytearray(template[2])
rv = b58encode_chk(prefix + payload + suffix)
assert is_valid(rv)
- metadata = dict([(x,y) for (x,y) in zip(metadata_keys,template[3]) if y is not None])
- yield (rv, b2a_hex(payload), metadata)
+ metadata = {x: y for x, y in zip(metadata_keys,template[3]) if y is not None}
+ hexrepr = b2a_hex(payload)
+ if isinstance(hexrepr, bytes):
+ hexrepr = hexrepr.decode('utf8')
+ yield (rv, hexrepr, metadata)
def gen_invalid_vector(template, corrupt_prefix, randomize_payload_size, corrupt_suffix):
'''Generate possibly invalid vector'''
if corrupt_prefix:
prefix = os.urandom(1)
else:
- prefix = str(bytearray(template[0]))
+ prefix = bytearray(template[0])
if randomize_payload_size:
payload = os.urandom(max(int(random.expovariate(0.5)), 50))
@@ -80,7 +83,7 @@ def gen_invalid_vector(template, corrupt_prefix, randomize_payload_size, corrupt
if corrupt_suffix:
suffix = os.urandom(len(template[2]))
else:
- suffix = str(bytearray(template[2]))
+ suffix = bytearray(template[2])
return b58encode_chk(prefix + payload + suffix)
diff --git a/contrib/verify-commits/README.md b/contrib/verify-commits/README.md
index e9e3f65da2..fa492fdd27 100644
--- a/contrib/verify-commits/README.md
+++ b/contrib/verify-commits/README.md
@@ -24,3 +24,24 @@ keys:
Note that the above isn't a good UI/UX yet, and needs significant improvements
to make it more convenient and reduce the chance of errors; pull-reqs
improving this process would be much appreciated.
+
+Configuration files
+-------------------
+
+* `trusted-git-root`: This file should contain a single git commit hash which is the first unsigned git commit (hence it is the "root of trust").
+* `trusted-sha512-root-commit`: This file should contain a single git commit hash which is the first commit without a SHA512 root commitment.
+* `trusted-keys`: This file should contain a \n-delimited list of all PGP fingerprints of authorized commit signers (primary, not subkeys).
+* `allow-revsig-commits`: This file should contain a \n-delimited list of git commit hashes. See next section for more info.
+
+Key expiry/revocation
+---------------------
+
+When a key (or subkey) which has signed old commits expires or is revoked,
+verify-commits will start failing to verify all commits which were signed by
+said key. In order to avoid bumping the root-of-trust `trusted-git-root`
+file, individual commits which were signed by such a key can be added to the
+`allow-revsig-commits` file. That way, the PGP signatures are still verified
+but no new commits can be signed by any expired/revoked key. To easily build a
+list of commits which need to be added, verify-commits.sh can be edited to test
+each commit with BITCOIN_VERIFY_COMMITS_ALLOW_REVSIG set to both 1 and 0, and
+those which need it set to 1 printed.
diff --git a/contrib/verify-commits/allow-revsig-commits b/contrib/verify-commits/allow-revsig-commits
index f0088cdca4..3abf82e529 100644
--- a/contrib/verify-commits/allow-revsig-commits
+++ b/contrib/verify-commits/allow-revsig-commits
@@ -102,3 +102,403 @@ bafd075c5e6a1088ef0f1aa0b0b224e026a3d3e0
c8d2473e6cb042e7275a10c49d3f6a4a91bf0166
386f4385ab04b0b2c3d47bddc0dc0f2de7354964
9f33dba05c01ecc5c56eb1284ab7d64d42f55171
+7466a26cab5d66665991433947964a638f5b957e
+b43aba89e356ff95b706e80d4802f60fc46a569a
+02b7e8319aef2a870264ad4fa2e3bb18664dcc36
+f686002a8eba820a40ac2f34a6e8f57b2b5cc54c
+2b1c50b9352ab1dc40b0f877db23c1fa4048fae3
+2405ce1df043f778b8efb9205009500cbc17313a
+4ad3b3c72c73d61e0a0cab541dca20acf651320d
+4ba3d4f4393d81148422d24d222fe7ed00130194
+8ee5c7b747171e335793c74cd9d2f7491da58164
+872c921c0a208b04bd0713758e52fcab5b7c1684
+00d1680498c5550e7db1f359202d3433a092fafd
+585db41e9ab7a6fb262c8bad7f427cdbdc497188
+18462960c0f13bd07d8f52b61e7d7bc17e991eea
+0630974647dacaf25e7fcb7f9cbb785bb078ede6
+0f58d7f3d62f012f2584f5e781fc73de4763dd9e
+3d16f581538b0974853e820508e8b3093269d2fd
+66e91420ab233cf1dac64504e0dc129019bf8c0d
+d8d9162f5bad39b2720dd2b2da237c6159e4755f
+29fad97c320c892ab6a480c81e2078ec22ab354b
+791c3ea61b4e49fd46a1a71b84ca99ddf69d2ff7
+a312e201ba56742499a5480b5f2115f01505c217
+ce56fdd2e8cdf94fd0ab76d71adbfa755e23ce7d
+480f42630cbd598c04fa59ee0e406f56904ecffb
+6012f1caf744ac9b53383d7d10a8f1b70ca2c0e1
+ded6a2afa549f693dcabb430ce0862f8631360c8
+07090c5339436f856e79a8036d1c85deeb453803
+0e265916d1c6a63e4a3821dab9db597b5ec64b46
+e4ffcacc2187d3419c8ea12b82fb06d82d8751d2
+e117cfe45eee9169409e74a44ef4a866be25bc35
+dcfe218626b05204e9fbc95ba5d95ca0eb72ec9b
+23481fa50301201ef5a60675ef899aa6ce94ca03
+27c59dc502f29cf1d76290556c21e366145e3b2e
+4a62ddd01873d18dbca96c81d756be1020249b45
+a233fb4f1d037e68ff70eef3a9f5b7bf1d631918
+b2089c51cc4af2f7e1c0ec75be9449ee222b1d69
+c997f8808256521397f1c003bb1e9896fee6eaa0
+5dc00f68c49c46a380a98d06233f90528b8e2557
+fe53d5f3636aed064823bc220d828c7ff08d1d52
+935eb8de039dec65669a96a1c3b86f4b03a1b86c
+0277173b1defb63216d40a8d8805ae6d5d563c26
+2a30e67d20f76bbcd9a7d445f616f005316e0a1a
+d32528e733f2711b34dbc41fbb2bb0f153bf7e9a
+4cad91663df381d0dff8526f3b4aa74569dfb626
+1b06ed136f17b526360617a70026aed5ded5746c
+895fbd768f0c89cea3f78acac58b233d4e3a145e
+f0295becbf3ef1fb78095306408789253fe0c114
+8d573198638e52e2dbd9abc609861430f9d2bcc3
+9d9c4185fadaf243bb97c226e2fef16b65299699
+eebe4580bc8d6484d79ecb24dd87412221cf2ea7
+9cf6393a4f82b9c81d3b4b468a17a89db10531a2
+598a9c4e4dcd03c6d80fba005de729a6a3aeba7e
+6970b30c6f1d2be7947295fe18f2390649b17a4b
+f359afcc410432ed5d30001acda0c66741ee8935
+126000ba9e7ff16271be2f4eef3df99ade8d624f
+b5e4b9b5100ec15217d43edb5f4149439f4b20a5
+b987ca4ee495a7fff82f0ac14ef0753bfb7586e2
+b03013396cb2f4bf25746388b3982a2c3616e16b
+9a97f39afaa890caa7987c6bc001b9a66e3e74e8
+cad504bf4c302f7a72e0a0e191f3fdbafda7340f
+45cf8a03cb57b8639a8d47323bde46ba22d9eeaf
+b7450cdbd89a1c862f4d4d8bf093f8a0b5448f9c
+0910cbe4ef31eb95fd76c7c2f820419fe64a3150
+92a810d04b906722c9efe60e3997243c71ff3d4c
+45173fa6fca9537abb0a0554f731d14b9f89c456
+fd4ca17360e6fc0c9bb76bf6b5b07c9102c12728
+ddff3447f29b62d79a33f728791f42fa9436216e
+36a5a4404836da323c755523fbd27563a8e84f94
+c991b304dee368f506cfee27ddaa333f1f82c518
+d38d1a3e75aa97ffa8755ddd431754a6d0942964
+a332a7d5a15214015f9553fdb2bcf80a1a4b8dc0
+604e08c83cf58ca7e7cda2ab284c1ace7bb12977
+18a1bbad98bd4321f15e7921d9aec91661499d90
+8049241e226c16bd07b029c0cb4b62ac40f0c923
+797441ee995aac59f55d59a93ecb55e8ecbe7dbc
+62fdf9b07087b80d2142799bdd2324f61483359d
+f60b4ad57912b78a96af08046a503f7905610a8c
+13e31dd6548d64a5992f439e74bb424bf88aca04
+fbce66a982679b5409a295be5c99a2eef429cabf
+9f2c2dba21855b8cb9b193b1819be73fa4a23a99
+a89221873a3ee2451c73b41bbe2d99d36f439d31
+3d6ad407770e13958e157bf026cae0bfb9254899
+901ba3e3819405306414628306746552b0aa1d28
+7a43fbb959c38e025e558e472ad57de357539894
+0d89fa0877930c6c8a539a656c1009ad8ab6755b
+54aedc013744c86b11157423fa3cffc9a51eef02
+f0c1f8abb0182da557d07372b938f3a0a4bb906f
+4ed818060ecf4a38a02c8cb48f6cbc78d2ee7708
+3bdf242fc68a8d767932c6214455d4d413effbc9
+5e468994fbb349e8eefc996954a31a67a34aaa15
+41aa9c4a801a01eca1fad22a7095372d23dace60
+2adbddb03840ad71e843c6c4a207a13e871cd1d4
+13e352dc53dec0127c5f94a60055d0ca829420dc
+95e14dc81dd30ee0d396ad08dca9a6980d16eee1
+61fb80660f73e5aa5b69302ecc7ac33da206ba5a
+05a761932edd05cf94ffe938908baf058f38632a
+ee92243e66f2df03b3a759a8ffb75dc06f0cea0d
+22cdf93c062eeaa0f8f9d6220f01b67240073dfb
+76b33491596736ca804e3a29bd8398d7a1516ab7
+6e4e98ee8ce2da3cca2e2fd210e9e8dbc9b1c936
+c838283ecdfb9490425bb071b7c22e542de46c7c
+5e3f5e4f25b65b583d3bfefac9e1148035781089
+f7388e93d3dd91a90239aedac4ec58404f103a2e
+0a2f46b0158b6fc7244a585913b0925c0acf707f
+dd561667cb7ccbbfed3134b05a565971ef6f5873
+6f01dcf63873a5e42798635ab4026c9a5f9fa213
+70fec9e36bcd1a3d93df019be084aaf89cecd7d7
+f9b74ef3fc74fd7d2aa94560820341f03cda8e12
+998c3046fab2b52bc9f141cfb588a18c05506a86
+89cc4f905e30b913ca20e4192d538cc5cbe2c38d
+87d90efd69b64f769116956a5db89e536e9e3714
+5aeaa9ccd1568a77e075dbe2bd2435bd60c87c91
+bfb270acfa30713dc8c968bb9ee40cf5a2360359
+1b8c88451b0554502435d3883c528ad0aad1b09b
+57ee73990f1ce29916adfd99f93eae1ccea1a43b
+808c84f89d0edcef9ddaab0b849a382719f6ec9e
+14b860bf64020451ced823b859da8cb912278ab9
+c63364610f4a041df1c1bd81d01b1f6856160749
+92eadc395071876d77f3babddc056b4325bdbabc
+e93fff1463ae906fc986bf98c3b118c82f171546
+9ccafb1d7bdd172a9b963444072a844da379c4f7
+b4a509a3f817121c3df98ddfd96b2769e18a3e5a
+dbc4ae03963014ab4b7957d62ba59dbd8f938c33
+8ddf60db7ad636b6a31b590251c671ded635fa1d
+f199b8a33d9443a258a1f49a1a29674cd9ee9a20
+e542728cde676f218c552d841d0af29b92f9800b
+763231051596b8e3455b839911ad6a3a1f1c3c74
+ff4cd6075b12fb32b9a906deea3ed033e3f9560a
+9c3c9cdae3e20b5bdea91a0631edac5116bbc89f
+93d20a734d2ee873832bed8ca5c05cf8e539c53c
+ef8340d25f7c5dd5682bdecea97ce84cfce1493c
+69c7ecef405d168f658a9cc7996da84c17f61e66
+4ce2f3d0d33346e9f0e96851689ee6550b2a72e3
+44e1fd926cfb0df0fbd8c41de8cd65ed8d5d6e18
+d6d2c8503c4039b682196d83a67dc28359c10c5c
+ae233c4ec3d14a97c6195059f52873cdba2b4755
+0f399a9ff227896265cafab9b2e9fab6cdb9b5b9
+f4ed44ab4a8f9a87ba678d5fd1449fbf636103dc
+7fcd61b2613c211bb042a82a889655178be6a212
+42973f834445d7735738bdba8847812ba3c34d95
+8df48b36ed3201d938b9974ecbee455d7dc2fb84
+96ac26e56627f0c24213fcd3a1cce9fc95f1f661
+cce94c518a46b7b0006f984bbe4d69e8749182d2
+801dd40666d1e6009920ad3ff755c7bb993b2a62
+ce829855cfca103dde55661fa1524e66b139d063
+b148803b181e30213e8a7f3bd89c8239e9dcb866
+c377feaad87f8109f85da6caf62602b30c20effc
+b37cab65c63e051ebc5b491da9bd687581df94df
+16e41844e7d6c5876d2caaeef6010656950c6ec5
+ee50c9e48786dea0d9df2e45805c25565c100fe3
+11dacc6154c42bc6fe3ba94c1823f8a46e4fe81a
+791a0e6ddade27d1b69f4861a6640de60b9553cf
+638e6c59da4fad987c437592174b188510193b2e
+52f8877525d5238f3440e73710507be889d14127
+2a56baf395bf11835d784c4f8634f4525deed6a1
+bc561b4b7d6a3f71649d37d5eb9047c29efa2b13
+31809d6f8514c4a8d5677e947e3f1ebb0db210b9
+a31e9ad4f027955d43c04a05517244647e250161
+777519bd96f68c18150a0f5942f8f97a91937f5e
+4eb1f39d421024d9666cec61deaf96715ffae4c6
+50fae68d416b4b8ec4ca192923dfd5ae9ea42773
+ce665863b137ac4a7470cf006a92aa7694faca71
+81f8c0378b2ab5ea0d7b65635cb529bd3c69127c
+108222b9c323a05cc9339368f10ddd0859f62b43
+28f788e47e58f2b462351d6989348a4e1a241b2b
+d81dccf191a48a6b59c3747d7b4ccbe3535dde40
+a90e6d2bffc422ddcdb771c53aac0bceb970a2c4
+91e49c51f1aecc9e1d75457f4920d52a4b0a133c
+60dd9cc470584960431de425e2a9ffbed0e8034a
+ede386c2193fc31351e193b3a8cf30030d6be62c
+a084767b40c0d3ba8fa8f8d60f1e8d99a9dc3457
+3f726c99f819f97f2ab21b94d34c6b3129cd883a
+77fc469fc78cdd87c29f398d46ac58dbb9ef62c0
+4ae6d0fbef60ccbecf8f23bb482e201b3678f7a3
+8858b6ddd3bce9daa08da6e05de3ca863a399c15
+22e301a3d56dc9e6878380ee92c7d19ca43119d2
+c484ec6c9b85ca4e331e395c564ae232fd0681dd
+a46a671e253528e450bd57645c400bf761da07ab
+655970d9c60ae6850daf452457e14e21047c0e1b
+b6a48914c50631914192aa11b19205436a9c664d
+7db65c363a0cc6ca7cdb04de9a973ab70013baad
+6366941275344dac7e2130b0c972e90117d37ed0
+4fb2586661471a1572c2df2a5a091011d45eb7c4
+d7be7b39fa1021ec4518186afe145ee948e12a94
+85aec87b11ec41295558175c63f1f5a849460fdf
+aeb31756276034dd506fdf97c8aaade0e7e584f5
+ac016e17d20253129a0287cee7e1d06b7ef15966
+bf74d377fb8e20140da6eac1407414928384bcea
+2c811e08db651a4aed6ea0f7c1972d60de6de8ab
+e5d26e47c7a482c072a7fe47bb84c56854734184
+96a63a3e0cefe920819bd42add0041837b1214a1
+e526ca6284b9e13be1b912b80dd73a34e739b539
+ecd21357f16106e541e9c2854ead2a906659b938
+4b5a7ce0c301ad971f383eb60f61bf9b4026efda
+929fd7276c0f0c30b9416f61a6f5f35d763d81e4
+fa8a0639f7b0ce04030b72b4d5be4f0aa36fc5cb
+f1f1605c22a6283bbfd757055fcf2b584a857709
+0c173a15ca1bf20999f74987988985508c9de463
+df0793f324e33066cc746c0cb1d053d35733d626
+2b0179d8a9b75397937126b36114df0dddeab40c
+bf0a08be281dc42241e7f264c2a20515eb4781bb
+3895e25a77363ae8b49358fb793f50fa8b271e2d
+1fc783fc08bc078239537535f174ab8a489772c0
+1d4805ce04645f3203b0cfd3d66ea710e7433eb4
+d3b58704d1d325875fc605580c1c02b825c1bbcc
+ed88e3194c4bc43aeafef929da7b419d03dea1ad
+dd07f47b79628668e29cc0143b21e790100ee445
+65cc7aacfbfc7b747926375280a1d839e88d576b
+080ec5209172ac9605f1434559dbb3c1e012b10a
+416af3edf5b5ab265acf95568f2bc9eabd3d96de
+e0a7801223fd573863939e76cb633f1dcc2d22c4
+4bc853b50fd9127687eb9e4f3b679dd261a4fa96
+c68a9a69278aa194fed96bd9733d32af3690a11e
+c38f540298f0e188df5ed68fd56c623b9ac8331b
+643fa0b22d70e459d7f7ec3d728ae4811dc5158f
+e053e05c130549f43953f1d70e724dc9ce3e1b85
+75e898c094eea533d1dfaf141c6afccc3072c49f
+2805d606bc46bf5589093a1b92d3542c13ce50c2
+32751807c9c06011eb689cba56b401a6302699c0
+30853e16d332816752dafcfca92147c7ffef5b54
+bea5b00cfe95cd37832305c0f93c339a22a7d79d
+c871f323b418fac27bf834843ca26985010df53f
+329fc1dce7a1c372c8b10c2f2f8732b2c60daff0
+1aefc94dd78d6e0c9209cb09fc16f53dedf42108
+8e5725666b519b61fcdc3141da5c6a57c1959909
+a4ca0b042365061020627a8c045cddacea3312ec
+8bd16ee12fc8ef6723e0572c29b979c15b92b4f4
+87abe20fc118721cc5efdbd94a8462468cd1da2b
+4b766fcdd4ca16399075d1e081a321b3b05ce516
+f6241b3e420e19f3f0507cbbc872fe9218916a02
+7ee523604851af62c0a47c07ee023a8710ef32f7
+776ba233e939fe41a74c6b2632b93a0679a32c71
+6a796b2b53fe542e0f340f250f4f20d69efed8d0
+23d78c4dd01bc74ba35db3e3df95280f6f1b2e22
+f4b15e2de97c4f8cdbb40bef4c9d0ab2807974d9
+fff72de5bf8ac7b70208e655f237b80e70e18851
+170bc2c381f86a523de2fc8b71d62ade66303c0d
+314ebdfcb38d4b4c977579f787d5e1a20d068c94
+e9274839bf316b1972d80d28e45759f898edbf86
+75171f099e82e3527d7c3469b15891bd92227ec2
+3c5e6c94caf40395e031fbde44a0cca46fdd76ec
+dc8fc0c73bebbc1c48ac5540026030c9cc00ec23
+492d22f92919d8d9d59568318c26c1e2ac4890cc
+80c3a734298e824f9321c4efdd446086a3baad89
+47535d7c3ec79c5978cdcc03a5351ddbbb22538d
+1b25b6df0f08f7474228c5b6ed13b58682e1e440
+c530c15180631cea95e9c292cf7fabde9dca9db3
+2723bcdce3248417e98e6c43207bef74d34076c1
+ed22eb4a62bd8d5369aaec87d4cbdc03c9f16368
+9111df9673beb6d6616d491a5478f09b5f14d040
+d86bb075bf6d1e78c1e4f3dd38b0ea828ef5ecfe
+50a1cc0f0aef1514b917a5a3f4476967170b429d
+6ce733747e160ca699711f2c47e686284ca9aa07
+b44adf92342ad4f9c343ba29c081a91687932936
+88799ea1b1c08f4bc1a487c9e3c2effd5e1650ae
+080d7c700fc3291560d79fc590e05b8e2bad984f
+12af74b289f8cdc6caf850dc6c802f9936b1e8b3
+8e4f7e72410df3ba430082c7cf385f26fd75b033
+8ac80412867118172dc4172494304e19969e9489
+f2734c2828f69d9cfd535e5eab0592a7674b2b61
+0b9fb682890b8fe10cec54072b809a5efe57d33d
+5b029aaedb5fcf7cadd249607dd28eb3f233ab8c
+79af9fbd8c3c0e54702a9c92b171f134bd4466c8
+c412fd805ddf3282dc2e1f28e30f51ffcb1f1da2
+111849345bb5140f86b48e730ceab4bff45fa2e9
+a0b1e57b20a17177ed5a9a54e4a8aab597a546b4
+ca209230c8e73745cf8cfc79f500c9c46e103306
+a230b0588788dbe1ac84622aea169c577b381241
+dfef6b6af08097f0676a2323085558fbbd3c48c6
+3192e5278abca7c1f3b4a2a7f77a0ce941c73985
+7c7ddd9ead99a8b5033a1a5d4698032c9e2b3a92
+10b930dde8f14e9cb661810e97a33bbf144fc55c
+9225de2cf652fe2bf6e50636824cdb641546f57d
+598ef9c44b3ea2cc142c175f077b493f39f5ba22
+c49355c7170a64bdd7864cc3ba9a64916b67fe7c
+857d1e171e051b254a617f27b39f6a551054cee2
+21833f9456f6ad5bc06321ad6d9590f42ce0195c
+8910b4717e5bb946ee6988f7fe9fd461f53a5935
+5703dff0939f05c7457cebd6fc61d88ab13afe41
+8bfa13b15b84cb372950fb7b25a1080173060b6a
+ac23a7c1f19b3d8c326ffe75c8e13edf285f90fe
+19be26afe3d04783a92d032b55bf3fb1e2ae63cc
+f7ec7cfd38b543ba81ac7bed5b77f9a19739460b
+36afd4db4442c45d4078b1a7ad16a1872b5bee0d
+88c2ae3ed2bb5d367dd408c9255cd8f1e7a36c7d
+a13a417cdcfdfd1f1b3bf997bb6ffe6e69b096b9
+d6064a89ac97dc0d2ce9da3982e1a4e25afaeda8
+7146d96de3e15a80cafbab2af48ff6f65d8e41bb
+5628c70f2a44567695e5331fe2293c5b7f35b629
+7ff4a538a8682cdf02a4bcd6f15499c841001b73
+aa5fa642b0e7ce2ea55e2298886f212f11a8894e
+8efd1c820b9a782d8608d54d924658536178295c
+50a226563cd8d7c0a5e8448e87fede0eb72a8354
+b860915f8b0dae98e57a254d11575ea41f5c5a79
+d304fef3746039183f51b3ac8f4774dcf3a64f59
+53ab12d9318d5d195ccc77028b0e3ae66dc6e1fd
+668de70be039a4f1ffcf20aeae2a22ee71fc55a8
+0fea960ca917b73aff853fe88476174c8a313863
+f89502306dcf6393a2c7b0efbb0fa728fc582137
+ff58b1c3bdff5e5f687f10f9e40ce495ca49674e
+0b96abc35f1a9d46a27eeddd7df418d107c29c57
+b0b57a17306a7e963a4fe463f84e2b150a00a859
+4105cb6fd964ad13099ca83b1fdf3d35f3961f74
+23281a4dc3afc42a001346caec4dbb8193f0bb53
+8daf103fa138f9a184448ebf1c2e03b9dbd96f21
+02e5308c1b9f3771bbe49bc5036215fa2bd66aa9
+a65ced1a66575c652baf5084644b8647f531be8c
+2456a835f0bc7796d9ff71f64837fa6790e2b7cc
+9ec1330b455c1ab2eb6b89f8a2ab885677d4ae8a
+0b738075bd43fbd4410e30a51e0498cbfd2b7513
+98c80e374b84e5a9c2d5c36889a0b1ebed5b814b
+25720fc394e27a951bcad26095fb5a711bfacb8f
+4cfd57d2e38207d78722ce8c9274ba8dd700d1cc
+0fc1c31a878e93d938c67db3f958e82e3c39659f
+df1ab5b4d67b46b5e9e840b1fbe0ff02520831f9
+5bc3b6cede8dabdf3f4f27ddb03723cbb7cde51a
+c2ea1e6561caba3abffce361abc800822b9e0efe
+caa2f106d704ec3ade63498031dd58d34510bc76
+dce853ef76ef90c46d84294225088d595467d08c
+dbc8a8c86ae50059fddb2d6834fa5f0c9bbf9b71
+0f921e6a0492c4e9f037a9ed91f474885032d68c
+041331e1da23e4136fd046ed870cdcc177464176
+e6ba5068f107ac234576e77cedbd748b665369c2
+76fcd9d5034143a5b041766552670d19f926097d
+72bf1b3d0962304850a3ef5fe375db4bff1d0a39
+919db037f1f5cc73cdcaef92dd9cb0e7f5c8dec3
+c36229b0b2e9d4554053f5c9fc451ac29a493b1f
+9e4bb312e6958d2baa309ba670e5eed1523c6f47
+d7ba4a233bd5a6f8fadee681c68a995e23fe36d7
+98514988a3d3e8b7dbf0463884a5c38f5ed5562d
+5412c08c3cf13577566064edd04da021c37b7cbe
+31bcc667863f368157efa1143a78623a5db8f0d1
+7bd1aa566fb4a4fe194f209085649f2c722b0cff
+c4522e71c7e1d8ecfd70112e9375b9d00d6733a8
+e22f409f18881b63a8e747036584a71217f40e6e
+97ec6e5c9098a1240655cfcab05b6cd5eedb6cd1
+bc121b0eb19713ec72002b5be03ba5ac35903a17
+c98f6b3d93a2cc1b49a6db425ea2b661089d0f9e
+0de7fd36de57a68e543b4c1f184fba192c398c73
+e662d281b837c25b2b70525aa8fe8af894339823
+44adf683ad232db8ce0cb89b3e236a1f5944cfb0
+cb2ed300a89ebf9f0654da869ced665ed8b2abe7
+0a6d48d9ed60b0b02177059ab116f8f46d2cbed3
+b42291334651fff46dbfe5947a726f65cb9d7dfe
+e5364991daecb73aca3bb5ac37f2619d7a89211b
+4a2b170c075ce703cbdc82519a48016a9ee3f99c
+924de0bd75a7f75df65d7d15f9d1587a2e794abf
+1253f8692fc3a11be9430685cd405236a68df6c3
+2b799ae9e1e0a540f9a5971ddf27d83254668279
+c9bdf9a75f9fde8cd011e4aa94be4ed4347078a3
+3d69ecb4edeb80003a1a41442e320898a30dbd9c
+f08222e882b18c1f279308636e03beceece2dbf1
+23e03f8d26d7bd03273a5dcbdcfe3905dfb49ffb
+03dd707dc027fbf6f24120213f8eb66571600374
+d0754799698de2c032abcb8198ee5d5401063213
+072116fceb2294b97d1c40f79305f2e3ff71812b
+e66cc1d58e16bf1650dd6479fed64ecaca8c6098
+f137753a2dcd8229f89d1d1ac28039364e5850b4
+61d191fbf953700ba8aeadc9c8cf4c195efbd10c
+76f3c02fb01a6df98fbd8c16ac21d159d4649d37
+6013c73b3312e11b447ed387426749014716f820
+6faffb8a83db3f209a303a4464dbdd597faad5a4
+cc9e8aca5f950c78dcfeff63c441ba993c1fe12f
+8ca69a2a88a77eb06149fa049ab1a7e6de38b321
+2f71490d21796594ca6f55e375558944de9db5a0
+08cc5fd666456cb476467473ed1880c90c92dedb
+e31a43c725ebe641d7c219c3886eee18eebf0bb8
+52b5a8785de760a204b2b0aab19dfaf79c2c3ff0
+483e8e4f4875a1a621ec9e9df2880d3037d95ed7
+1e5799c52535a3fc20e885916f1e7ed33ecc7f46
+a82e5d8220bbc8b5d786bed99b0876f530b9b7cc
+7fe6c5c993706e8395cdaf7977bee793c06f48f3
+2a0836f6d5e7c1d7e97bedb0e0ea33dcaf981f77
+ddc308068d69c6c9aa629ee3c4ce75e1d1cf08b5
+ec139a5621a9c9f03e1988391a3c7c6c5d849776
+c01a6c48b982d625fd9f4f69005878781d3d56fa
+95a983d56dbda457e3bf8766d59bac74c7aa5699
+760741a00833876976389ed7a6b73f36ee5b4c13
+6e5e5abba6f8bbbe61c22795df440dfafcfdc378
+cf2cecb18779ce83de9adebf382dff1c19b12840
+af9b7a9f2f73b1a2f9728106774dd13e8d1cdd8d
+115735d547fdeade822f547eb3e8c8f9961a9b07
+c2c69edf37b5c02aafa01d0407dadbf5ef8751b5
+a072d1a83787e786d074a4b5871b0b961781f7c6
+ed2cd59e258f756b2eaed7909a60956ade6ef7ee
+ae5575ba41c8a782805afb1c08730343cfc22397
+6ff2c8d29f6b5a5c2ce63f0a16f3bb0dbd049451
+a80de15113166354cdf208e3d8b6e25f4511a591
+06bd4f637f15e769f088d9051a5af94bbb0217a3
+6700cc993cc07fb0f5b8b577ff8c4afcf0b18274
+37f9a1f627c0995d89b62923e75cd092600894f9
+8844ef15ded02d5ed86fb95aaf251235fcef2396
+1b87e5b5b184a0a6c683eda23b36393822b57f03
+e2bf830bb6c1bfa038c943dd6f5d92a406bd723f
+423ca302a3ee87000530da3c105f269b8fabece7
+4e14afe42fdd468d5de11df8cc13defdcb8e83f8
+3e90fe6534206412ea22beaa445cf20d28fbe718
+88b77c7da0a672c89e24df37ea6e9085b4e2a05c
+0ad104190465d8d65c2344bbe10dcf3df025d86c
+5c7df7022bcd360e6af00b9458b1a3fd54e1cc9a
+59ad56851a342d2c62f6b38bf15002b23ab439e1
diff --git a/contrib/verify-commits/trusted-git-root b/contrib/verify-commits/trusted-git-root
index e560b98d02..c60f8ab695 100644
--- a/contrib/verify-commits/trusted-git-root
+++ b/contrib/verify-commits/trusted-git-root
@@ -1 +1 @@
-11049f4fe62606d1b0380a9ef800ac130f0fbadf
+82bcf405f6db1d55b684a1f63a4aabad376cdad7
diff --git a/contrib/verify-commits/verify-commits.sh b/contrib/verify-commits/verify-commits.sh
index 532b97a438..6415eea4d5 100755
--- a/contrib/verify-commits/verify-commits.sh
+++ b/contrib/verify-commits/verify-commits.sh
@@ -35,6 +35,8 @@ NO_SHA1=1
PREV_COMMIT=""
INITIAL_COMMIT="${CURRENT_COMMIT}"
+BRANCH="$(git rev-parse --abbrev-ref HEAD)"
+
while true; do
if [ "$CURRENT_COMMIT" = $VERIFIED_ROOT ]; then
echo "There is a valid path from \"$INITIAL_COMMIT\" to $VERIFIED_ROOT where all commits are signed!"
@@ -123,9 +125,29 @@ while true; do
fi
PARENTS=$(git show -s --format=format:%P "$CURRENT_COMMIT")
- for PARENT in $PARENTS; do
- PREV_COMMIT="$CURRENT_COMMIT"
- CURRENT_COMMIT="$PARENT"
- break
- done
+ PARENT1=${PARENTS%% *}
+ PARENT2=""
+ if [ "x$PARENT1" != "x$PARENTS" ]; then
+ PARENTX=${PARENTS#* }
+ PARENT2=${PARENTX%% *}
+ if [ "x$PARENT2" != "x$PARENTX" ]; then
+ echo "Commit $CURRENT_COMMIT is an octopus merge" > /dev/stderr
+ exit 1
+ fi
+ fi
+ if [ "x$PARENT2" != "x" ]; then
+ CURRENT_TREE="$(git show --format="%T" "$CURRENT_COMMIT")"
+ git checkout --force --quiet "$PARENT1"
+ git merge --no-ff --quiet "$PARENT2" >/dev/null
+ RECREATED_TREE="$(git show --format="%T" HEAD)"
+ if [ "$CURRENT_TREE" != "$RECREATED_TREE" ]; then
+ echo "Merge commit $CURRENT_COMMIT is not clean" > /dev/stderr
+ git diff "$CURRENT_COMMIT"
+ git checkout --force --quiet "$BRANCH"
+ exit 1
+ fi
+ git checkout --force --quiet "$BRANCH"
+ fi
+ PREV_COMMIT="$CURRENT_COMMIT"
+ CURRENT_COMMIT="$PARENT1"
done
diff --git a/doc/build-osx.md b/doc/build-osx.md
index f19e4f0b8d..e52a770ced 100644
--- a/doc/build-osx.md
+++ b/doc/build-osx.md
@@ -16,7 +16,7 @@ Then install [Homebrew](https://brew.sh).
Dependencies
----------------------
- brew install automake berkeley-db4 libtool boost miniupnpc openssl pkg-config protobuf python qt libevent
+ brew install automake berkeley-db4 libtool boost miniupnpc openssl pkg-config protobuf python qt libevent qrencode
See [dependencies.md](dependencies.md) for a complete overview.
diff --git a/doc/developer-notes.md b/doc/developer-notes.md
index 8f06ee4eca..c9fe3a7ab4 100644
--- a/doc/developer-notes.md
+++ b/doc/developer-notes.md
@@ -1,6 +1,43 @@
Developer Notes
===============
+<!-- markdown-toc start -->
+**Table of Contents**
+
+- [Developer Notes](#developer-notes)
+ - [Coding Style](#coding-style)
+ - [Doxygen comments](#doxygen-comments)
+ - [Development tips and tricks](#development-tips-and-tricks)
+ - [Compiling for debugging](#compiling-for-debugging)
+ - [Compiling for gprof profiling](#compiling-for-gprof-profiling)
+ - [debug.log](#debuglog)
+ - [Testnet and Regtest modes](#testnet-and-regtest-modes)
+ - [DEBUG_LOCKORDER](#debug_lockorder)
+ - [Valgrind suppressions file](#valgrind-suppressions-file)
+ - [Compiling for test coverage](#compiling-for-test-coverage)
+ - [Locking/mutex usage notes](#lockingmutex-usage-notes)
+ - [Threads](#threads)
+ - [Ignoring IDE/editor files](#ignoring-ideeditor-files)
+- [Development guidelines](#development-guidelines)
+ - [General Bitcoin Core](#general-bitcoin-core)
+ - [Wallet](#wallet)
+ - [General C++](#general-c)
+ - [C++ data structures](#c-data-structures)
+ - [Strings and formatting](#strings-and-formatting)
+ - [Variable names](#variable-names)
+ - [Threads and synchronization](#threads-and-synchronization)
+ - [Source code organization](#source-code-organization)
+ - [GUI](#gui)
+ - [Subtrees](#subtrees)
+ - [Git and GitHub tips](#git-and-github-tips)
+ - [Scripted diffs](#scripted-diffs)
+ - [RPC interface guidelines](#rpc-interface-guidelines)
+
+<!-- markdown-toc end -->
+
+Coding Style
+---------------
+
Various coding styles have been used during the history of the codebase,
and the result is not very consistent. However, we're now trying to converge to
a single style, which is specified below. When writing patches, favor the new
@@ -34,11 +71,14 @@ code.
- Constant names are all uppercase, and use `_` to separate words.
- Class names, function names and method names are UpperCamelCase
(PascalCase). Do not prefix class names with `C`.
+ - Test suite naming convention: The Boost test suite in file
+ `src/test/foo_tests.cpp` should be named `foo_tests`.
- **Miscellaneous**
- `++i` is preferred over `i++`.
- `nullptr` is preferred over `NULL` or `(void*)0`.
- `static_assert` is preferred over `assert` where possible. Generally; compile-time checking is preferred over run-time checking.
+ - `enum class` is preferred over `enum` where possible. Scoped enumerations avoid two potential pitfalls/problems with traditional C++ enumerations: implicit conversions to int, and name clashes due to enumerators being exported to the surrounding scope.
Block style example:
```c++
@@ -137,43 +177,44 @@ Documentation can be generated with `make docs` and cleaned up with `make clean-
Development tips and tricks
---------------------------
-**compiling for debugging**
+### Compiling for debugging
-Run configure with the --enable-debug option, then make. Or run configure with
-CXXFLAGS="-g -ggdb -O0" or whatever debug flags you need.
+Run configure with `--enable-debug` to add additional compiler flags that
+produce better debugging builds.
-**compiling for gprof profiling**
+### Compiling for gprof profiling
-Run configure with the --enable-gprof option, then make.
+Run configure with the `--enable-gprof` option, then make.
-**debug.log**
+### debug.log
If the code is behaving strangely, take a look in the debug.log file in the data directory;
error and debugging messages are written there.
-The -debug=... command-line option controls debugging; running with just -debug or -debug=1 will turn
+The `-debug=...` command-line option controls debugging; running with just `-debug` or `-debug=1` will turn
on all categories (and give you a very large debug.log file).
-The Qt code routes qDebug() output to debug.log under category "qt": run with -debug=qt
+The Qt code routes `qDebug()` output to debug.log under category "qt": run with `-debug=qt`
to see it.
-**testnet and regtest modes**
+### Testnet and Regtest modes
-Run with the -testnet option to run with "play bitcoins" on the test network, if you
+Run with the `-testnet` option to run with "play bitcoins" on the test network, if you
are testing multi-machine code that needs to operate across the internet.
-If you are testing something that can run on one machine, run with the -regtest option.
-In regression test mode, blocks can be created on-demand; see test/functional/ for tests
-that run in -regtest mode.
+If you are testing something that can run on one machine, run with the `-regtest` option.
+In regression test mode, blocks can be created on-demand; see [test/functional/](/test/functional) for tests
+that run in `-regtest` mode.
-**DEBUG_LOCKORDER**
+### DEBUG_LOCKORDER
-Bitcoin Core is a multithreaded application, and deadlocks or other multithreading bugs
-can be very difficult to track down. Compiling with -DDEBUG_LOCKORDER (configure
-CXXFLAGS="-DDEBUG_LOCKORDER -g") inserts run-time checks to keep track of which locks
-are held, and adds warnings to the debug.log file if inconsistencies are detected.
+Bitcoin Core is a multi-threaded application, and deadlocks or other
+multi-threading bugs can be very difficult to track down. The `--enable-debug`
+configure option adds `-DDEBUG_LOCKORDER` to the compiler flags. This inserts
+run-time checks to keep track of which locks are held, and adds warnings to the
+debug.log file if inconsistencies are detected.
-**Valgrind suppressions file**
+### Valgrind suppressions file
Valgrind is a programming tool for memory debugging, memory leak detection, and
profiling. The repo contains a Valgrind suppressions file
@@ -188,7 +229,7 @@ $ valgrind --suppressions=contrib/valgrind.supp --leak-check=full \
$ valgrind -v --leak-check=full src/bitcoind -printtoconsole
```
-**compiling for test coverage**
+### Compiling for test coverage
LCOV can be used to generate a test coverage report based upon `make check`
execution. LCOV must be installed on your system (e.g. the `lcov` package
@@ -204,22 +245,73 @@ make cov
# A coverage report will now be accessible at `./test_bitcoin.coverage/index.html`.
```
+**Sanitizers**
+
+Bitcoin can be compiled with various "sanitizers" enabled, which add
+instrumentation for issues regarding things like memory safety, thread race
+conditions, or undefined behavior. This is controlled with the
+`--with-sanitizers` configure flag, which should be a comma separated list of
+sanitizers to enable. The sanitizer list should correspond to supported
+`-fsanitize=` options in your compiler. These sanitizers have runtime overhead,
+so they are most useful when testing changes or producing debugging builds.
+
+Some examples:
+
+```bash
+# Enable both the address sanitizer and the undefined behavior sanitizer
+./configure --with-sanitizers=address,undefined
+
+# Enable the thread sanitizer
+./configure --with-sanitizers=thread
+```
+
+If you are compiling with GCC you will typically need to install corresponding
+"san" libraries to actually compile with these flags, e.g. libasan for the
+address sanitizer, libtsan for the thread sanitizer, and libubsan for the
+undefined sanitizer. If you are missing required libraries, the configure script
+will fail with a linker error when testing the sanitizer flags.
+
+The test suite should pass cleanly with the `thread` and `undefined` sanitizers,
+but there are a number of known problems when using the `address` sanitizer. The
+address sanitizer is known to fail in
+[sha256_sse4::Transform](/src/crypto/sha256_sse4.cpp) which makes it unusable
+unless you also use `--disable-asm` when running configure. We would like to fix
+sanitizer issues, so please send pull requests if you can fix any errors found
+by the address sanitizer (or any other sanitizer).
+
+Not all sanitizer options can be enabled at the same time, e.g. trying to build
+with `--with-sanitizers=address,thread` will fail in the configure script as
+these sanitizers are mutually incompatible. Refer to your compiler manual to
+learn more about these options and which sanitizers are supported by your
+compiler.
+
+Additional resources:
+
+ * [AddressSanitizer](https://clang.llvm.org/docs/AddressSanitizer.html)
+ * [LeakSanitizer](https://clang.llvm.org/docs/LeakSanitizer.html)
+ * [MemorySanitizer](https://clang.llvm.org/docs/MemorySanitizer.html)
+ * [ThreadSanitizer](https://clang.llvm.org/docs/ThreadSanitizer.html)
+ * [UndefinedBehaviorSanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html)
+ * [GCC Instrumentation Options](https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html)
+ * [Google Sanitizers Wiki](https://github.com/google/sanitizers/wiki)
+ * [Issue #12691: Enable -fsanitize flags in Travis](https://github.com/bitcoin/bitcoin/issues/12691)
+
Locking/mutex usage notes
-------------------------
The code is multi-threaded, and uses mutexes and the
-LOCK/TRY_LOCK macros to protect data structures.
+`LOCK` and `TRY_LOCK` macros to protect data structures.
-Deadlocks due to inconsistent lock ordering (thread 1 locks cs_main
-and then cs_wallet, while thread 2 locks them in the opposite order:
-result, deadlock as each waits for the other to release its lock) are
-a problem. Compile with -DDEBUG_LOCKORDER to get lock order
-inconsistencies reported in the debug.log file.
+Deadlocks due to inconsistent lock ordering (thread 1 locks `cs_main` and then
+`cs_wallet`, while thread 2 locks them in the opposite order: result, deadlock
+as each waits for the other to release its lock) are a problem. Compile with
+`-DDEBUG_LOCKORDER` (or use `--enable-debug`) to get lock order inconsistencies
+reported in the debug.log file.
Re-architecting the core code so there are better-defined interfaces
between the various components is a goal, with any necessary locking
-done by the components (e.g. see the self-contained CKeyStore class
-and its cs_KeyStore lock for example).
+done by the components (e.g. see the self-contained `CBasicKeyStore` class
+and its `cs_KeyStore` lock for example).
Threads
-------
@@ -358,12 +450,21 @@ C++ data structures
- Vector bounds checking is only enabled in debug mode. Do not rely on it
-- Make sure that constructors initialize all fields. If this is skipped for a
- good reason (i.e., optimization on the critical path), add an explicit
- comment about this
+- Initialize all non-static class members where they are defined.
+ If this is skipped for a good reason (i.e., optimization on the critical
+ path), add an explicit comment about this
- *Rationale*: Ensure determinism by avoiding accidental use of uninitialized
values. Also, static analyzers balk about this.
+ Initializing the members in the declaration makes it easy to
+ spot uninitialized ones.
+
+```cpp
+class A
+{
+ uint32_t m_count{0};
+}
+```
- By default, declare single-argument constructors `explicit`.
@@ -382,18 +483,6 @@ C++ data structures
- *Rationale*: Easier to understand what is happening, thus easier to spot mistakes, even for those
that are not language lawyers
-- Initialize all non-static class members where they are defined
-
- - *Rationale*: Initializing the members in the declaration makes it easy to spot uninitialized ones,
- and avoids accidentally reading uninitialized memory
-
-```cpp
-class A
-{
- uint32_t m_count{0};
-}
-```
-
Strings and formatting
------------------------
@@ -514,6 +603,16 @@ namespace {
source file into account. This allows quoted includes to stand out more when
the location of the source file actually is relevant.
+- Use include guards to avoid the problem of double inclusion. The header file
+ `foo/bar.h` should use the include guard identifier `BITCOIN_FOO_BAR_H`, e.g.
+
+```c++
+#ifndef BITCOIN_FOO_BAR_H
+#define BITCOIN_FOO_BAR_H
+...
+#endif // BITCOIN_FOO_BAR_H
+```
+
GUI
-----
@@ -523,6 +622,19 @@ GUI
should not interact with the user. That's where View classes come in. The converse also
holds: try to not directly access core data structures from Views.
+- Avoid adding slow or blocking code in the GUI thread. In particular do not
+ add new `interface::Node` and `interface::Wallet` method calls, even if they
+ may be fast now, in case they are changed to lock or communicate across
+ processes in the future.
+
+ Prefer to offload work from the GUI thread to worker threads (see
+ `RPCExecutor` in console code as an example) or take other steps (see
+ https://doc.qt.io/archives/qq/qq27-responsive-guis.html) to keep the GUI
+ responsive.
+
+ - *Rationale*: Blocking the GUI thread can increase latency, and lead to
+ hangs and deadlocks.
+
Subtrees
----------
@@ -542,7 +654,10 @@ its upstream repository.
Current subtrees include:
- src/leveldb
- - Upstream at https://github.com/google/leveldb ; Maintained by Google, but open important PRs to Core to avoid delay
+ - Upstream at https://github.com/google/leveldb ; Maintained by Google, but
+ open important PRs to Core to avoid delay.
+ - **Note**: Follow the instructions in [Upgrading LevelDB](#upgrading-leveldb) when
+ merging upstream changes to the leveldb subtree.
- src/libsecp256k1
- Upstream at https://github.com/bitcoin-core/secp256k1/ ; actively maintaned by Core contributors.
@@ -553,6 +668,52 @@ Current subtrees include:
- src/univalue
- Upstream at https://github.com/jgarzik/univalue ; report important PRs to Core to avoid delay.
+Upgrading LevelDB
+---------------------
+
+Extra care must be taken when upgrading LevelDB. This section explains issues
+you must be aware of.
+
+### File Descriptor Counts
+
+In most configurations we use the default LevelDB value for `max_open_files`,
+which is 1000 at the time of this writing. If LevelDB actually uses this many
+file descriptors it will cause problems with Bitcoin's `select()` loop, because
+it may cause new sockets to be created where the fd value is >= 1024. For this
+reason, on 64-bit Unix systems we rely on an internal LevelDB optimization that
+uses `mmap()` + `close()` to open table files without actually retaining
+references to the table file descriptors. If you are upgrading LevelDB, you must
+sanity check the changes to make sure that this assumption remains valid.
+
+In addition to reviewing the upstream changes in `env_posix.cc`, you can use `lsof` to
+check this. For example, on Linux this command will show open `.ldb` file counts:
+
+```bash
+$ lsof -p $(pidof bitcoind) |\
+ awk 'BEGIN { fd=0; mem=0; } /ldb$/ { if ($4 == "mem") mem++; else fd++ } END { printf "mem = %s, fd = %s\n", mem, fd}'
+mem = 119, fd = 0
+```
+
+The `mem` value shows how many files are mmap'ed, and the `fd` value shows you
+many file descriptors these files are using. You should check that `fd` is a
+small number (usually 0 on 64-bit hosts).
+
+See the notes in the `SetMaxOpenFiles()` function in `dbwrapper.cc` for more
+details.
+
+### Consensus Compatibility
+
+It is possible for LevelDB changes to inadvertently change consensus
+compatibility between nodes. This happened in Bitcoin 0.8 (when LevelDB was
+first introduced). When upgrading LevelDB you should review the upstream changes
+to check for issues affecting consensus compatibility.
+
+For example, if LevelDB had a bug that accidentally prevented a key from being
+returned in an edge case, and that bug was fixed upstream, the bug "fix" would
+be an incompatible consensus change. In this situation the correct behavior
+would be to revert the upstream fix before applying the updates to Bitcoin's
+copy of LevelDB. In general you should be wary of any upstream changes affecting
+what data is returned from LevelDB queries.
Git and GitHub tips
---------------------
diff --git a/doc/release-notes.md b/doc/release-notes.md
index 740afd5137..0a72f3fe4a 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -74,6 +74,7 @@ RPC changes
add `label` fields to returned JSON objects that previously only had
`account` fields.
- `sendmany` now shuffles outputs to improve privacy, so any previously expected behavior with regards to output ordering can no longer be relied upon.
+- The new RPC `testmempoolaccept` can be used to test acceptance of a transaction to the mempool without adding it.
External wallet files
---------------------
@@ -117,6 +118,11 @@ deprecated in V0.15.1, and has now been removed. Miners should use the
`-blockmaxweight` option if they want to limit the weight of their blocks'
weights.
+Python Support
+--------------
+
+Support for Python 2 has been discontinued for all test files and tools.
+
Credits
=======
diff --git a/doc/tor.md b/doc/tor.md
index a05979fca8..931c83abdd 100644
--- a/doc/tor.md
+++ b/doc/tor.md
@@ -31,7 +31,7 @@ outgoing connections be anonymized, but more is possible.
In a typical situation, this suffices to run behind a Tor proxy:
- ./bitcoin -proxy=127.0.0.1:9050
+ ./bitcoind -proxy=127.0.0.1:9050
2. Run a bitcoin hidden server
@@ -86,7 +86,7 @@ and open port 8333 on your firewall (or use -upnp).
If you only want to use Tor to reach onion addresses, but not use it as a proxy
for normal IPv4/IPv6 communication, use:
- ./bitcoin -onion=127.0.0.1:9050 -externalip=57qr3yd1nyntf5k.onion -discover
+ ./bitcoind -onion=127.0.0.1:9050 -externalip=57qr3yd1nyntf5k.onion -discover
3. Automatically listen on Tor
--------------------------------
diff --git a/share/qt/extract_strings_qt.py b/share/qt/extract_strings_qt.py
index fbf3ea3436..e8f0820ca8 100755
--- a/share/qt/extract_strings_qt.py
+++ b/share/qt/extract_strings_qt.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# Copyright (c) 2012-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.
@@ -6,7 +6,6 @@
Extract _("...") strings for translation and convert to Qt stringdefs so that
they can be picked up by Qt linguist.
'''
-from __future__ import division,print_function,unicode_literals
from subprocess import Popen, PIPE
import operator
import os
diff --git a/share/rpcauth/rpcauth.py b/share/rpcauth/rpcauth.py
index 7baad85582..d6580281d4 100755
--- a/share/rpcauth/rpcauth.py
+++ b/share/rpcauth/rpcauth.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# Copyright (c) 2015-2017 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/Makefile.am b/src/Makefile.am
index 7c2fe56d9d..d2cfdc104b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -4,9 +4,10 @@
DIST_SUBDIRS = secp256k1 univalue
-AM_LDFLAGS = $(PTHREAD_CFLAGS) $(LIBTOOL_LDFLAGS) $(HARDENED_LDFLAGS) $(GPROF_LDFLAGS)
-AM_CXXFLAGS = $(HARDENED_CXXFLAGS) $(ERROR_CXXFLAGS) $(GPROF_CXXFLAGS)
+AM_LDFLAGS = $(PTHREAD_CFLAGS) $(LIBTOOL_LDFLAGS) $(HARDENED_LDFLAGS) $(GPROF_LDFLAGS) $(SANITIZER_LDFLAGS)
+AM_CXXFLAGS = $(HARDENED_CXXFLAGS) $(ERROR_CXXFLAGS) $(GPROF_CXXFLAGS) $(SANITIZER_CXXFLAGS)
AM_CPPFLAGS = $(HARDENED_CPPFLAGS)
+AM_LIBTOOLFLAGS = --preserve-dup-deps
EXTRA_LIBRARIES =
if EMBEDDED_UNIVALUE
@@ -104,6 +105,9 @@ BITCOIN_CORE_H = \
httpserver.h \
indirectmap.h \
init.h \
+ interface/handler.h \
+ interface/node.h \
+ interface/wallet.h \
key.h \
key_io.h \
keystore.h \
@@ -162,12 +166,12 @@ BITCOIN_CORE_H = \
validation.h \
validationinterface.h \
versionbits.h \
+ walletinitinterface.h \
wallet/coincontrol.h \
wallet/crypter.h \
wallet/db.h \
wallet/feebumper.h \
wallet/fees.h \
- wallet/init.h \
wallet/rpcwallet.h \
wallet/wallet.h \
wallet/walletdb.h \
@@ -244,6 +248,7 @@ endif
libbitcoin_wallet_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
libbitcoin_wallet_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_wallet_a_SOURCES = \
+ interface/wallet.cpp \
wallet/crypter.cpp \
wallet/db.cpp \
wallet/feebumper.cpp \
@@ -356,6 +361,8 @@ libbitcoin_util_a_SOURCES = \
compat/glibcxx_sanity.cpp \
compat/strnlen.cpp \
fs.cpp \
+ interface/handler.cpp \
+ interface/node.cpp \
random.cpp \
rpc/protocol.cpp \
rpc/util.cpp \
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index 0bdde06772..38eb12ce0d 100644
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -402,7 +402,7 @@ if TARGET_WINDOWS
endif
qt_bitcoin_qt_LDADD = qt/libbitcoinqt.a $(LIBBITCOIN_SERVER)
if ENABLE_WALLET
-qt_bitcoin_qt_LDADD += $(LIBBITCOIN_WALLET)
+qt_bitcoin_qt_LDADD += $(LIBBITCOIN_UTIL) $(LIBBITCOIN_WALLET)
endif
if ENABLE_ZMQ
qt_bitcoin_qt_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
@@ -411,7 +411,7 @@ qt_bitcoin_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL)
$(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QR_LIBS) $(PROTOBUF_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) \
$(EVENT_PTHREADS_LIBS) $(EVENT_LIBS)
qt_bitcoin_qt_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-qt_bitcoin_qt_LIBTOOLFLAGS = --tag CXX
+qt_bitcoin_qt_LIBTOOLFLAGS = $(AM_LIBTOOLFLAGS) --tag CXX
#locale/foo.ts -> locale/foo.qm
QT_QM=$(QT_TS:.ts=.qm)
diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include
index ea2ed17472..1279152198 100644
--- a/src/Makefile.qttest.include
+++ b/src/Makefile.qttest.include
@@ -52,7 +52,7 @@ nodist_qt_test_test_bitcoin_qt_SOURCES = $(TEST_QT_MOC_CPP)
qt_test_test_bitcoin_qt_LDADD = $(LIBBITCOINQT) $(LIBBITCOIN_SERVER)
if ENABLE_WALLET
-qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_WALLET)
+qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_UTIL) $(LIBBITCOIN_WALLET)
endif
if ENABLE_ZMQ
qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
diff --git a/src/addrdb.cpp b/src/addrdb.cpp
index 675f3c28af..e4620e63c6 100644
--- a/src/addrdb.cpp
+++ b/src/addrdb.cpp
@@ -22,8 +22,8 @@ bool SerializeDB(Stream& stream, const Data& data)
// Write and commit header, data
try {
CHashWriter hasher(SER_DISK, CLIENT_VERSION);
- stream << FLATDATA(Params().MessageStart()) << data;
- hasher << FLATDATA(Params().MessageStart()) << data;
+ stream << Params().MessageStart() << data;
+ hasher << Params().MessageStart() << data;
stream << hasher.GetHash();
} catch (const std::exception& e) {
return error("%s: Serialize or I/O error - %s", __func__, e.what());
@@ -66,7 +66,7 @@ bool DeserializeDB(Stream& stream, Data& data, bool fCheckSum = true)
CHashVerifier<Stream> verifier(&stream);
// de-serialize file header (network specific magic number) and ..
unsigned char pchMsgTmp[4];
- verifier >> FLATDATA(pchMsgTmp);
+ verifier >> pchMsgTmp;
// ... verify the network matches ours
if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp)))
return error("%s: Invalid network magic number", __func__);
diff --git a/src/bech32.h b/src/bech32.h
index 7ef7b22213..2e2823e974 100644
--- a/src/bech32.h
+++ b/src/bech32.h
@@ -9,6 +9,9 @@
//
// For more information, see BIP 173.
+#ifndef BITCOIN_BECH32_H
+#define BITCOIN_BECH32_H
+
#include <stdint.h>
#include <string>
#include <vector>
@@ -23,3 +26,5 @@ std::string Encode(const std::string& hrp, const std::vector<uint8_t>& values);
std::pair<std::string, std::vector<uint8_t>> Decode(const std::string& str);
} // namespace bech32
+
+#endif // BITCOIN_BECH32_H
diff --git a/src/bench/bench_bitcoin.cpp b/src/bench/bench_bitcoin.cpp
index fc92a46c0f..1d87883522 100644
--- a/src/bench/bench_bitcoin.cpp
+++ b/src/bench/bench_bitcoin.cpp
@@ -27,7 +27,7 @@ main(int argc, char** argv)
{
gArgs.ParseParameters(argc, argv);
- if (gArgs.IsArgSet("-?") || gArgs.IsArgSet("-h") || gArgs.IsArgSet("-help")) {
+ if (HelpRequested(gArgs)) {
std::cout << HelpMessageGroup(_("Options:"))
<< HelpMessageOpt("-?", _("Print this help message and exit"))
<< HelpMessageOpt("-list", _("List benchmarks without executing them. Can be combined with -scaling and -filter"))
diff --git a/src/bench/perf.h b/src/bench/perf.h
index 681bd0c8a2..73ea8b9647 100644
--- a/src/bench/perf.h
+++ b/src/bench/perf.h
@@ -3,8 +3,8 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
/** Functions for measurement of CPU cycles */
-#ifndef H_PERF
-#define H_PERF
+#ifndef BITCOIN_BENCH_PERF_H
+#define BITCOIN_BENCH_PERF_H
#include <stdint.h>
@@ -34,4 +34,4 @@ uint64_t perf_cpucycles(void);
void perf_init(void);
void perf_fini(void);
-#endif // H_PERF
+#endif // BITCOIN_BENCH_PERF_H
diff --git a/src/bench/verify_script.cpp b/src/bench/verify_script.cpp
index 29dedeef0b..705fa368a5 100644
--- a/src/bench/verify_script.cpp
+++ b/src/bench/verify_script.cpp
@@ -75,7 +75,7 @@ static void VerifyScriptBench(benchmark::State& state)
CMutableTransaction txSpend = BuildSpendingTransaction(scriptSig, txCredit);
CScriptWitness& witness = txSpend.vin[0].scriptWitness;
witness.stack.emplace_back();
- key.Sign(SignatureHash(witScriptPubkey, txSpend, 0, SIGHASH_ALL, txCredit.vout[0].nValue, SIGVERSION_WITNESS_V0), witness.stack.back(), 0);
+ key.Sign(SignatureHash(witScriptPubkey, txSpend, 0, SIGHASH_ALL, txCredit.vout[0].nValue, SigVersion::WITNESS_V0), witness.stack.back(), 0);
witness.stack.back().push_back(static_cast<unsigned char>(SIGHASH_ALL));
witness.stack.push_back(ToByteVector(pubkey));
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index 8bdc210997..618b0d16bc 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -15,6 +15,7 @@
#include <util.h>
#include <utilstrencodings.h>
+#include <memory>
#include <stdio.h>
#include <event2/buffer.h>
@@ -82,7 +83,7 @@ static int AppInitRPC(int argc, char* argv[])
// Parameters
//
gArgs.ParseParameters(argc, argv);
- if (argc<2 || gArgs.IsArgSet("-?") || gArgs.IsArgSet("-h") || gArgs.IsArgSet("-help") || gArgs.IsArgSet("-version")) {
+ if (argc < 2 || HelpRequested(gArgs) || gArgs.IsArgSet("-version")) {
std::string strUsage = strprintf(_("%s RPC client version"), _(PACKAGE_NAME)) + " " + FormatFullVersion() + "\n";
if (!gArgs.IsArgSet("-version")) {
strUsage += "\n" + _("Usage:") + "\n" +
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index 8218e883a6..65f8177daf 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -22,6 +22,7 @@
#include <utilmoneystr.h>
#include <utilstrencodings.h>
+#include <memory>
#include <stdio.h>
#include <boost/algorithm/string.hpp>
@@ -51,8 +52,7 @@ static int AppInitRawTx(int argc, char* argv[])
fCreateBlank = gArgs.GetBoolArg("-create", false);
- if (argc<2 || gArgs.IsArgSet("-?") || gArgs.IsArgSet("-h") || gArgs.IsArgSet("-help"))
- {
+ if (argc < 2 || HelpRequested(gArgs)) {
// First part of help message is specific to this utility
std::string strUsage = strprintf(_("%s bitcoin-tx utility version"), _(PACKAGE_NAME)) + " " + FormatFullVersion() + "\n\n" +
_("Usage:") + "\n" +
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index d3eb60725f..b00c2a6308 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -18,6 +18,7 @@
#include <httpserver.h>
#include <httprpc.h>
#include <utilstrencodings.h>
+#include <walletinitinterface.h>
#include <boost/thread.hpp>
@@ -66,8 +67,7 @@ bool AppInit(int argc, char* argv[])
gArgs.ParseParameters(argc, argv);
// Process help and version before taking care about datadir
- if (gArgs.IsArgSet("-?") || gArgs.IsArgSet("-h") || gArgs.IsArgSet("-help") || gArgs.IsArgSet("-version"))
- {
+ if (HelpRequested(gArgs) || gArgs.IsArgSet("-version")) {
std::string strUsage = strprintf(_("%s Daemon"), _(PACKAGE_NAME)) + " " + _("version") + " " + FormatFullVersion() + "\n";
if (gArgs.IsArgSet("-version"))
@@ -79,7 +79,7 @@ bool AppInit(int argc, char* argv[])
strUsage += "\n" + _("Usage:") + "\n" +
" bitcoind [options] " + strprintf(_("Start %s Daemon"), _(PACKAGE_NAME)) + "\n";
- strUsage += "\n" + HelpMessage(HMM_BITCOIND);
+ strUsage += "\n" + HelpMessage(HelpMessageMode::BITCOIND);
}
fprintf(stdout, "%s", strUsage.c_str());
diff --git a/src/blockencodings.h b/src/blockencodings.h
index f80821aa65..3a0828b307 100644
--- a/src/blockencodings.h
+++ b/src/blockencodings.h
@@ -2,8 +2,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#ifndef BITCOIN_BLOCK_ENCODINGS_H
-#define BITCOIN_BLOCK_ENCODINGS_H
+#ifndef BITCOIN_BLOCKENCODINGS_H
+#define BITCOIN_BLOCKENCODINGS_H
#include <primitives/block.h>
@@ -206,4 +206,4 @@ public:
ReadStatus FillBlock(CBlock& block, const std::vector<CTransactionRef>& vtx_missing);
};
-#endif
+#endif // BITCOIN_BLOCKENCODINGS_H
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index 6067503b0b..adf8e6ae5b 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -11,6 +11,7 @@
#include <utilstrencodings.h>
#include <assert.h>
+#include <memory>
#include <chainparamsseeds.h>
diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp
index a04258fd40..726616e650 100644
--- a/src/chainparamsbase.cpp
+++ b/src/chainparamsbase.cpp
@@ -9,6 +9,7 @@
#include <util.h>
#include <assert.h>
+#include <memory>
const std::string CBaseChainParams::MAIN = "main";
const std::string CBaseChainParams::TESTNET = "test";
diff --git a/src/compressor.cpp b/src/compressor.cpp
index 86de2900e9..da639a01af 100644
--- a/src/compressor.cpp
+++ b/src/compressor.cpp
@@ -9,7 +9,15 @@
#include <pubkey.h>
#include <script/standard.h>
-bool CScriptCompressor::IsToKeyID(CKeyID &hash) const
+/*
+ * These check for scripts for which a special case with a shorter encoding is defined.
+ * They are implemented separately from the CScript test, as these test for exact byte
+ * sequence correspondences, and are more strict. For example, IsToPubKey also verifies
+ * whether the public key is valid (as invalid ones cannot be represented in compressed
+ * form).
+ */
+
+static bool IsToKeyID(const CScript& script, CKeyID &hash)
{
if (script.size() == 25 && script[0] == OP_DUP && script[1] == OP_HASH160
&& script[2] == 20 && script[23] == OP_EQUALVERIFY
@@ -20,7 +28,7 @@ bool CScriptCompressor::IsToKeyID(CKeyID &hash) const
return false;
}
-bool CScriptCompressor::IsToScriptID(CScriptID &hash) const
+static bool IsToScriptID(const CScript& script, CScriptID &hash)
{
if (script.size() == 23 && script[0] == OP_HASH160 && script[1] == 20
&& script[22] == OP_EQUAL) {
@@ -30,7 +38,7 @@ bool CScriptCompressor::IsToScriptID(CScriptID &hash) const
return false;
}
-bool CScriptCompressor::IsToPubKey(CPubKey &pubkey) const
+static bool IsToPubKey(const CScript& script, CPubKey &pubkey)
{
if (script.size() == 35 && script[0] == 33 && script[34] == OP_CHECKSIG
&& (script[1] == 0x02 || script[1] == 0x03)) {
@@ -45,24 +53,24 @@ bool CScriptCompressor::IsToPubKey(CPubKey &pubkey) const
return false;
}
-bool CScriptCompressor::Compress(std::vector<unsigned char> &out) const
+bool CompressScript(const CScript& script, std::vector<unsigned char> &out)
{
CKeyID keyID;
- if (IsToKeyID(keyID)) {
+ if (IsToKeyID(script, keyID)) {
out.resize(21);
out[0] = 0x00;
memcpy(&out[1], &keyID, 20);
return true;
}
CScriptID scriptID;
- if (IsToScriptID(scriptID)) {
+ if (IsToScriptID(script, scriptID)) {
out.resize(21);
out[0] = 0x01;
memcpy(&out[1], &scriptID, 20);
return true;
}
CPubKey pubkey;
- if (IsToPubKey(pubkey)) {
+ if (IsToPubKey(script, pubkey)) {
out.resize(33);
memcpy(&out[1], &pubkey[1], 32);
if (pubkey[0] == 0x02 || pubkey[0] == 0x03) {
@@ -76,7 +84,7 @@ bool CScriptCompressor::Compress(std::vector<unsigned char> &out) const
return false;
}
-unsigned int CScriptCompressor::GetSpecialSize(unsigned int nSize) const
+unsigned int GetSpecialScriptSize(unsigned int nSize)
{
if (nSize == 0 || nSize == 1)
return 20;
@@ -85,7 +93,7 @@ unsigned int CScriptCompressor::GetSpecialSize(unsigned int nSize) const
return 0;
}
-bool CScriptCompressor::Decompress(unsigned int nSize, const std::vector<unsigned char> &in)
+bool DecompressScript(CScript& script, unsigned int nSize, const std::vector<unsigned char> &in)
{
switch(nSize) {
case 0x00:
@@ -139,7 +147,7 @@ bool CScriptCompressor::Decompress(unsigned int nSize, const std::vector<unsigne
// * if e==9, we only know the resulting number is not zero, so output 1 + 10*(n - 1) + 9
// (this is decodable, as d is in [1-9] and e is in [0-9])
-uint64_t CTxOutCompressor::CompressAmount(uint64_t n)
+uint64_t CompressAmount(uint64_t n)
{
if (n == 0)
return 0;
@@ -158,7 +166,7 @@ uint64_t CTxOutCompressor::CompressAmount(uint64_t n)
}
}
-uint64_t CTxOutCompressor::DecompressAmount(uint64_t x)
+uint64_t DecompressAmount(uint64_t x)
{
// x = 0 OR x = 1+10*(9*n + d - 1) + e OR x = 1+10*(n - 1) + 9
if (x == 0)
diff --git a/src/compressor.h b/src/compressor.h
index 6fcecd27e9..561c8e66d0 100644
--- a/src/compressor.h
+++ b/src/compressor.h
@@ -14,6 +14,13 @@ class CKeyID;
class CPubKey;
class CScriptID;
+bool CompressScript(const CScript& script, std::vector<unsigned char> &out);
+unsigned int GetSpecialScriptSize(unsigned int nSize);
+bool DecompressScript(CScript& script, unsigned int nSize, const std::vector<unsigned char> &out);
+
+uint64_t CompressAmount(uint64_t nAmount);
+uint64_t DecompressAmount(uint64_t nAmount);
+
/** Compact serializer for scripts.
*
* It detects common cases and encodes them much more efficiently.
@@ -37,28 +44,13 @@ private:
static const unsigned int nSpecialScripts = 6;
CScript &script;
-protected:
- /**
- * These check for scripts for which a special case with a shorter encoding is defined.
- * They are implemented separately from the CScript test, as these test for exact byte
- * sequence correspondences, and are more strict. For example, IsToPubKey also verifies
- * whether the public key is valid (as invalid ones cannot be represented in compressed
- * form).
- */
- bool IsToKeyID(CKeyID &hash) const;
- bool IsToScriptID(CScriptID &hash) const;
- bool IsToPubKey(CPubKey &pubkey) const;
-
- bool Compress(std::vector<unsigned char> &out) const;
- unsigned int GetSpecialSize(unsigned int nSize) const;
- bool Decompress(unsigned int nSize, const std::vector<unsigned char> &out);
public:
explicit CScriptCompressor(CScript &scriptIn) : script(scriptIn) { }
template<typename Stream>
void Serialize(Stream &s) const {
std::vector<unsigned char> compr;
- if (Compress(compr)) {
+ if (CompressScript(script, compr)) {
s << CFlatData(compr);
return;
}
@@ -72,9 +64,9 @@ public:
unsigned int nSize = 0;
s >> VARINT(nSize);
if (nSize < nSpecialScripts) {
- std::vector<unsigned char> vch(GetSpecialSize(nSize), 0x00);
+ std::vector<unsigned char> vch(GetSpecialScriptSize(nSize), 0x00);
s >> CFlatData(vch);
- Decompress(nSize, vch);
+ DecompressScript(script, nSize, vch);
return;
}
nSize -= nSpecialScripts;
@@ -96,9 +88,6 @@ private:
CTxOut &txout;
public:
- static uint64_t CompressAmount(uint64_t nAmount);
- static uint64_t DecompressAmount(uint64_t nAmount);
-
explicit CTxOutCompressor(CTxOut &txoutIn) : txout(txoutIn) { }
ADD_SERIALIZE_METHODS;
diff --git a/src/consensus/merkle.h b/src/consensus/merkle.h
index d57bb3412e..0afb73adb5 100644
--- a/src/consensus/merkle.h
+++ b/src/consensus/merkle.h
@@ -2,8 +2,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#ifndef BITCOIN_MERKLE
-#define BITCOIN_MERKLE
+#ifndef BITCOIN_CONSENSUS_MERKLE_H
+#define BITCOIN_CONSENSUS_MERKLE_H
#include <stdint.h>
#include <vector>
@@ -35,4 +35,4 @@ uint256 BlockWitnessMerkleRoot(const CBlock& block, bool* mutated = nullptr);
*/
std::vector<uint256> BlockMerkleBranch(const CBlock& block, uint32_t position);
-#endif
+#endif // BITCOIN_CONSENSUS_MERKLE_H
diff --git a/src/dbwrapper.cpp b/src/dbwrapper.cpp
index fb0d4215a2..ca446c92fe 100644
--- a/src/dbwrapper.cpp
+++ b/src/dbwrapper.cpp
@@ -4,6 +4,7 @@
#include <dbwrapper.h>
+#include <memory>
#include <random.h>
#include <leveldb/cache.h>
@@ -71,6 +72,31 @@ public:
}
};
+static void SetMaxOpenFiles(leveldb::Options *options) {
+ // On most platforms the default setting of max_open_files (which is 1000)
+ // is optimal. On Windows using a large file count is OK because the handles
+ // do not interfere with select() loops. On 64-bit Unix hosts this value is
+ // also OK, because up to that amount LevelDB will use an mmap
+ // implementation that does not use extra file descriptors (the fds are
+ // closed after being mmaped).
+ //
+ // Increasing the value beyond the default is dangerous because LevelDB will
+ // fall back to a non-mmap implementation when the file count is too large.
+ // On 32-bit Unix host we should decrease the value because the handles use
+ // up real fds, and we want to avoid fd exhaustion issues.
+ //
+ // See PR #12495 for further discussion.
+
+ int default_open_files = options->max_open_files;
+#ifndef WIN32
+ if (sizeof(void*) < 8) {
+ options->max_open_files = 64;
+ }
+#endif
+ LogPrint(BCLog::LEVELDB, "LevelDB using max_open_files=%d (default=%d)\n",
+ options->max_open_files, default_open_files);
+}
+
static leveldb::Options GetOptions(size_t nCacheSize)
{
leveldb::Options options;
@@ -78,13 +104,13 @@ static leveldb::Options GetOptions(size_t nCacheSize)
options.write_buffer_size = nCacheSize / 4; // up to two write buffers may be held in memory simultaneously
options.filter_policy = leveldb::NewBloomFilterPolicy(10);
options.compression = leveldb::kNoCompression;
- options.max_open_files = 64;
options.info_log = new CBitcoinLevelDBLogger();
if (leveldb::kMajorVersion > 1 || (leveldb::kMajorVersion == 1 && leveldb::kMinorVersion >= 16)) {
// LevelDB versions before 1.16 consider short writes to be corruption. Only trigger error
// on corruption in later versions.
options.paranoid_checks = true;
}
+ SetMaxOpenFiles(&options);
return options;
}
@@ -159,12 +185,12 @@ bool CDBWrapper::WriteBatch(CDBBatch& batch, bool fSync)
const bool log_memory = LogAcceptCategory(BCLog::LEVELDB);
double mem_before = 0;
if (log_memory) {
- mem_before = DynamicMemoryUsage() / 1024 / 1024;
+ mem_before = DynamicMemoryUsage() / 1024.0 / 1024;
}
leveldb::Status status = pdb->Write(fSync ? syncoptions : writeoptions, &batch.batch);
dbwrapper_private::HandleError(status);
if (log_memory) {
- double mem_after = DynamicMemoryUsage() / 1024 / 1024;
+ double mem_after = DynamicMemoryUsage() / 1024.0 / 1024;
LogPrint(BCLog::LEVELDB, "WriteBatch memory usage: db=%s, before=%.1fMiB, after=%.1fMiB\n",
m_name, mem_before, mem_after);
}
diff --git a/src/httprpc.cpp b/src/httprpc.cpp
index ed0a030701..de2437943e 100644
--- a/src/httprpc.cpp
+++ b/src/httprpc.cpp
@@ -17,6 +17,8 @@
#include <crypto/hmac_sha256.h>
#include <stdio.h>
+#include <memory>
+
#include <boost/algorithm/string.hpp> // boost::trim
/** WWW-Authenticate to present with 401 Unauthorized response */
@@ -158,8 +160,9 @@ static bool HTTPReq_JSONRPC(HTTPRequest* req, const std::string &)
}
JSONRPCRequest jreq;
+ jreq.peerAddr = req->GetPeer().ToString();
if (!RPCAuthorized(authHeader.second, jreq.authUser)) {
- LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", req->GetPeer().ToString());
+ LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", jreq.peerAddr);
/* Deter brute-forcing
If this results in a DoS the user really
diff --git a/src/httpserver.cpp b/src/httpserver.cpp
index 36db530c82..b8b338dfbc 100644
--- a/src/httpserver.cpp
+++ b/src/httpserver.cpp
@@ -13,6 +13,7 @@
#include <sync.h>
#include <ui_interface.h>
+#include <memory>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
diff --git a/src/init.cpp b/src/init.cpp
index 62767edbf8..811e59e362 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -43,10 +43,8 @@
#include <util.h>
#include <utilmoneystr.h>
#include <validationinterface.h>
-#ifdef ENABLE_WALLET
-#include <wallet/init.h>
-#endif
#include <warnings.h>
+#include <walletinitinterface.h>
#include <stdint.h>
#include <stdio.h>
#include <memory>
@@ -75,6 +73,25 @@ static const bool DEFAULT_STOPAFTERBLOCKIMPORT = false;
std::unique_ptr<CConnman> g_connman;
std::unique_ptr<PeerLogicValidation> peerLogic;
+#if !(ENABLE_WALLET)
+class DummyWalletInit : public WalletInitInterface {
+public:
+
+ std::string GetHelpString(bool showDebug) override {return std::string{};}
+ bool ParameterInteraction() override {return true;}
+ void RegisterRPC(CRPCTable &) override {}
+ bool Verify() override {return true;}
+ bool Open() override {return true;}
+ void Start(CScheduler& scheduler) override {}
+ void Flush() override {}
+ void Stop() override {}
+ void Close() override {}
+};
+
+static DummyWalletInit g_dummy_wallet_init;
+WalletInitInterface* const g_wallet_init_interface = &g_dummy_wallet_init;
+#endif
+
#if ENABLE_ZMQ
static CZMQNotificationInterface* pzmqNotificationInterface = nullptr;
#endif
@@ -116,7 +133,6 @@ static const char* FEE_ESTIMATES_FILENAME="fee_estimates.dat";
//
std::atomic<bool> fRequestShutdown(false);
-std::atomic<bool> fDumpMempoolLater(false);
void StartShutdown()
{
@@ -189,9 +205,7 @@ void Shutdown()
StopREST();
StopRPC();
StopHTTPServer();
-#ifdef ENABLE_WALLET
- FlushWallets();
-#endif
+ g_wallet_init_interface->Flush();
StopMapPort();
// Because these depend on each-other, we make sure that neither can be
@@ -208,7 +222,7 @@ void Shutdown()
threadGroup.interrupt_all();
threadGroup.join_all();
- if (fDumpMempoolLater && gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
+ if (g_is_mempool_loaded && gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
DumpMempool();
}
@@ -249,9 +263,7 @@ void Shutdown()
pcoinsdbview.reset();
pblocktree.reset();
}
-#ifdef ENABLE_WALLET
- StopWallets();
-#endif
+ g_wallet_init_interface->Stop();
#if ENABLE_ZMQ
if (pzmqNotificationInterface) {
@@ -271,9 +283,7 @@ void Shutdown()
UnregisterAllValidationInterfaces();
GetMainSignals().UnregisterBackgroundSignalScheduler();
GetMainSignals().UnregisterWithMempoolSignals(mempool);
-#ifdef ENABLE_WALLET
- CloseWallets();
-#endif
+ g_wallet_init_interface->Close();
globalVerifyHandle.reset();
ECC_Stop();
LogPrintf("%s: done\n", __func__);
@@ -333,12 +343,13 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-version", _("Print version and exit"));
strUsage += HelpMessageOpt("-alertnotify=<cmd>", _("Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)"));
strUsage +=HelpMessageOpt("-assumevalid=<hex>", strprintf(_("If this block is in the chain assume that it and its ancestors are valid and potentially skip their script verification (0 to verify all, default: %s, testnet: %s)"), defaultChainParams->GetConsensus().defaultAssumeValid.GetHex(), testnetChainParams->GetConsensus().defaultAssumeValid.GetHex()));
+ strUsage += HelpMessageOpt("-blocksdir=<dir>", _("Specify blocks directory (default: <datadir>/blocks)"));
strUsage += HelpMessageOpt("-blocknotify=<cmd>", _("Execute command when the best block changes (%s in cmd is replaced by block hash)"));
strUsage += HelpMessageOpt("-blockreconstructionextratxn=<n>", strprintf(_("Extra transactions to keep in memory for compact block reconstructions (default: %u)"), DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN));
if (showDebug)
strUsage += HelpMessageOpt("-blocksonly", strprintf(_("Whether to operate in a blocks only mode (default: %u)"), DEFAULT_BLOCKSONLY));
strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)"), BITCOIN_CONF_FILENAME));
- if (mode == HMM_BITCOIND)
+ if (mode == HelpMessageMode::BITCOIND)
{
#if HAVE_DECL_DAEMON
strUsage += HelpMessageOpt("-daemon", _("Run in the background as a daemon and accept commands"));
@@ -415,9 +426,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-whitelist=<IP address or network>", _("Whitelist peers connecting from the given IP address (e.g. 1.2.3.4) or CIDR notated network (e.g. 1.2.3.0/24). Can be specified multiple times.") +
" " + _("Whitelisted peers cannot be DoS banned and their transactions are always relayed, even if they are already in the mempool, useful e.g. for a gateway"));
-#ifdef ENABLE_WALLET
- strUsage += GetWalletHelpString(showDebug);
-#endif
+ strUsage += g_wallet_init_interface->GetHelpString(showDebug);
#if ENABLE_ZMQ
strUsage += HelpMessageGroup(_("ZeroMQ notification options:"));
@@ -428,18 +437,16 @@ std::string HelpMessage(HelpMessageMode mode)
#endif
strUsage += HelpMessageGroup(_("Debugging/Testing options:"));
- if (showDebug)
- {
+ if (showDebug) {
strUsage += HelpMessageOpt("-checkblocks=<n>", strprintf(_("How many blocks to check at startup (default: %u, 0 = all)"), DEFAULT_CHECKBLOCKS));
strUsage += HelpMessageOpt("-checklevel=<n>", strprintf(_("How thorough the block verification of -checkblocks is (0-4, default: %u)"), DEFAULT_CHECKLEVEL));
- strUsage += HelpMessageOpt("-checkblockindex", strprintf("Do a full consistency check for mapBlockIndex, setBlockIndexCandidates, chainActive and mapBlocksUnlinked occasionally. Also sets -checkmempool (default: %u)", defaultChainParams->DefaultConsistencyChecks()));
+ strUsage += HelpMessageOpt("-checkblockindex", strprintf("Do a full consistency check for mapBlockIndex, setBlockIndexCandidates, chainActive and mapBlocksUnlinked occasionally. (default: %u)", defaultChainParams->DefaultConsistencyChecks()));
strUsage += HelpMessageOpt("-checkmempool=<n>", strprintf("Run checks every <n> transactions (default: %u)", defaultChainParams->DefaultConsistencyChecks()));
strUsage += HelpMessageOpt("-checkpoints", strprintf("Disable expensive verification for known chain history (default: %u)", DEFAULT_CHECKPOINTS_ENABLED));
strUsage += HelpMessageOpt("-disablesafemode", strprintf("Disable safemode, override a real safe mode event (default: %u)", DEFAULT_DISABLE_SAFEMODE));
strUsage += HelpMessageOpt("-deprecatedrpc=<method>", "Allows deprecated RPC method(s) to be used");
strUsage += HelpMessageOpt("-testsafemode", strprintf("Force safe mode (default: %u)", DEFAULT_TESTSAFEMODE));
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));
@@ -594,7 +601,7 @@ void CleanupBlockRevFiles()
// 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");
- fs::path blocksdir = GetDataDir() / "blocks";
+ fs::path blocksdir = GetBlocksDir();
for (fs::directory_iterator it(blocksdir); it != fs::directory_iterator(); it++) {
if (fs::is_regular_file(*it) &&
it->path().filename().string().length() == 12 &&
@@ -679,7 +686,7 @@ void ThreadImport(std::vector<fs::path> vImportFiles)
// scan for better chains in the block chain database, that are not yet connected in the active best chain
CValidationState state;
if (!ActivateBestChain(state, chainparams)) {
- LogPrintf("Failed to connect best block");
+ LogPrintf("Failed to connect best block\n");
StartShutdown();
return;
}
@@ -692,8 +699,8 @@ void ThreadImport(std::vector<fs::path> vImportFiles)
} // End scope of CImportingNow
if (gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
LoadMempool();
- fDumpMempoolLater = !fRequestShutdown;
}
+ g_is_mempool_loaded = !fRequestShutdown;
}
/** Sanity checks
@@ -898,6 +905,10 @@ bool AppInitParameterInteraction()
// also see: InitParameterInteraction()
+ if (!fs::is_directory(GetBlocksDir(false))) {
+ return InitError(strprintf(_("Specified blocks directory \"%s\" does not exist.\n"), gArgs.GetArg("-blocksdir", "").c_str()));
+ }
+
// if using block pruning, then disallow txindex
if (gArgs.GetArg("-prune", 0)) {
if (gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX))
@@ -1083,10 +1094,7 @@ bool AppInitParameterInteraction()
return InitError(strprintf("acceptnonstdtxn is not currently supported for %s chain", chainparams.NetworkIDString()));
nBytesPerSigOp = gArgs.GetArg("-bytespersigop", nBytesPerSigOp);
-#ifdef ENABLE_WALLET
- if (!WalletParameterInteraction())
- return false;
-#endif
+ if (!g_wallet_init_interface->ParameterInteraction()) return false;
fIsBareMultisigStd = gArgs.GetBoolArg("-permitbaremultisig", DEFAULT_PERMIT_BAREMULTISIG);
fAcceptDatacarrier = gArgs.GetBoolArg("-datacarrier", DEFAULT_ACCEPT_DATACARRIER);
@@ -1252,9 +1260,7 @@ bool AppInitMain()
* available in the GUI RPC console even if external calls are disabled.
*/
RegisterAllCoreRPCCommands(tableRPC);
-#ifdef ENABLE_WALLET
- RegisterWalletRPC(tableRPC);
-#endif
+ g_wallet_init_interface->RegisterRPC(tableRPC);
/* Start the RPC server already. It will be started in "warmup" mode
* and not really process calls already (but it will signify connections
@@ -1271,10 +1277,8 @@ bool AppInitMain()
int64_t nStart;
// ********************************************************* Step 5: verify wallet database integrity
-#ifdef ENABLE_WALLET
- if (!VerifyWallets())
- return false;
-#endif
+ if (!g_wallet_init_interface->Verify()) return false;
+
// ********************************************************* Step 6: network initialization
// Note that we absolutely cannot open any actual connections
// until the very end ("start node") as the UTXO/block state
@@ -1592,12 +1596,7 @@ bool AppInitMain()
fFeeEstimatesInitialized = true;
// ********************************************************* Step 8: load wallet
-#ifdef ENABLE_WALLET
- if (!OpenWallets())
- return false;
-#else
- LogPrintf("No wallet support compiled in!\n");
-#endif
+ if (!g_wallet_init_interface->Open()) return false;
// ********************************************************* Step 9: data directory maintenance
@@ -1623,7 +1622,7 @@ bool AppInitMain()
// ********************************************************* Step 10: import blocks
- if (!CheckDiskSpace())
+ if (!CheckDiskSpace() && !CheckDiskSpace(0, true))
return false;
// Either install a handler to notify us when genesis activates, or set fHaveGenesis directly.
@@ -1743,9 +1742,7 @@ bool AppInitMain()
SetRPCWarmupFinished();
uiInterface.InitMessage(_("Done loading"));
-#ifdef ENABLE_WALLET
- StartWallets(scheduler);
-#endif
+ g_wallet_init_interface->Start(scheduler);
return true;
}
diff --git a/src/init.h b/src/init.h
index 33f97a55a5..829c110112 100644
--- a/src/init.h
+++ b/src/init.h
@@ -6,11 +6,15 @@
#ifndef BITCOIN_INIT_H
#define BITCOIN_INIT_H
+#include <memory>
#include <string>
class CScheduler;
class CWallet;
+class WalletInitInterface;
+extern WalletInitInterface* const g_wallet_init_interface;
+
namespace boost
{
class thread_group;
@@ -57,9 +61,9 @@ bool AppInitLockDataDirectory();
bool AppInitMain();
/** The help message mode determines what help message to show */
-enum HelpMessageMode {
- HMM_BITCOIND,
- HMM_BITCOIN_QT
+enum class HelpMessageMode {
+ BITCOIND,
+ BITCOIN_QT
};
/** Help for options shared between UI and daemon (for -help) */
diff --git a/src/interface/README.md b/src/interface/README.md
new file mode 100644
index 0000000000..e93b91d23c
--- /dev/null
+++ b/src/interface/README.md
@@ -0,0 +1,17 @@
+# Internal c++ interfaces
+
+The following interfaces are defined here:
+
+* [`Chain`](chain.h) — used by wallet to access blockchain and mempool state. Added in [#10973](https://github.com/bitcoin/bitcoin/pull/10973).
+
+* [`Chain::Client`](chain.h) — used by node to start & stop `Chain` clients. Added in [#10973](https://github.com/bitcoin/bitcoin/pull/10973).
+
+* [`Node`](node.h) — used by GUI to start & stop bitcoin node. Added in [#10244](https://github.com/bitcoin/bitcoin/pull/10244).
+
+* [`Wallet`](wallet.h) — used by GUI to access wallets. Added in [#10244](https://github.com/bitcoin/bitcoin/pull/10244).
+
+* [`Handler`](handler.h) — returned by `handleEvent` methods on interfaces above and used to manage lifetimes of event handlers.
+
+* [`Init`](init.h) — used by multiprocess code to access interfaces above on startup. Added in [#10102](https://github.com/bitcoin/bitcoin/pull/10102).
+
+The interfaces above define boundaries between major components of bitcoin code (node, wallet, and gui), making it possible for them to run in different processes, and be tested, developed, and understood independently. These interfaces are not currently designed to be stable or to be used externally.
diff --git a/src/interface/handler.cpp b/src/interface/handler.cpp
new file mode 100644
index 0000000000..4b27f374f4
--- /dev/null
+++ b/src/interface/handler.cpp
@@ -0,0 +1,33 @@
+// Copyright (c) 2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <interface/handler.h>
+
+#include <util.h>
+
+#include <boost/signals2/connection.hpp>
+#include <memory>
+#include <utility>
+
+namespace interface {
+namespace {
+
+class HandlerImpl : public Handler
+{
+public:
+ HandlerImpl(boost::signals2::connection connection) : m_connection(std::move(connection)) {}
+
+ void disconnect() override { m_connection.disconnect(); }
+
+ boost::signals2::scoped_connection m_connection;
+};
+
+} // namespace
+
+std::unique_ptr<Handler> MakeHandler(boost::signals2::connection connection)
+{
+ return MakeUnique<HandlerImpl>(std::move(connection));
+}
+
+} // namespace interface
diff --git a/src/interface/handler.h b/src/interface/handler.h
new file mode 100644
index 0000000000..a76334bfbf
--- /dev/null
+++ b/src/interface/handler.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_INTERFACE_HANDLER_H
+#define BITCOIN_INTERFACE_HANDLER_H
+
+#include <memory>
+
+namespace boost {
+namespace signals2 {
+class connection;
+} // namespace signals2
+} // namespace boost
+
+namespace interface {
+
+//! Generic interface for managing an event handler or callback function
+//! registered with another interface. Has a single disconnect method to cancel
+//! the registration and prevent any future notifications.
+class Handler
+{
+public:
+ virtual ~Handler() {}
+
+ //! Disconnect the handler.
+ virtual void disconnect() = 0;
+};
+
+//! Return handler wrapping a boost signal connection.
+std::unique_ptr<Handler> MakeHandler(boost::signals2::connection connection);
+
+} // namespace interface
+
+#endif // BITCOIN_INTERFACE_HANDLER_H
diff --git a/src/interface/node.cpp b/src/interface/node.cpp
new file mode 100644
index 0000000000..f04da4e176
--- /dev/null
+++ b/src/interface/node.cpp
@@ -0,0 +1,308 @@
+// Copyright (c) 2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <interface/node.h>
+
+#include <addrdb.h>
+#include <amount.h>
+#include <chain.h>
+#include <chainparams.h>
+#include <init.h>
+#include <interface/handler.h>
+#include <interface/wallet.h>
+#include <net.h>
+#include <net_processing.h>
+#include <netaddress.h>
+#include <netbase.h>
+#include <policy/feerate.h>
+#include <policy/fees.h>
+#include <policy/policy.h>
+#include <primitives/block.h>
+#include <rpc/server.h>
+#include <scheduler.h>
+#include <sync.h>
+#include <txmempool.h>
+#include <ui_interface.h>
+#include <util.h>
+#include <validation.h>
+#include <warnings.h>
+
+#if defined(HAVE_CONFIG_H)
+#include <config/bitcoin-config.h>
+#endif
+#ifdef ENABLE_WALLET
+#include <wallet/fees.h>
+#include <wallet/wallet.h>
+#define CHECK_WALLET(x) x
+#else
+#define CHECK_WALLET(x) throw std::logic_error("Wallet function called in non-wallet build.")
+#endif
+
+#include <atomic>
+#include <boost/thread/thread.hpp>
+#include <univalue.h>
+
+namespace interface {
+namespace {
+
+class NodeImpl : public Node
+{
+ void parseParameters(int argc, const char* const argv[]) override
+ {
+ gArgs.ParseParameters(argc, argv);
+ }
+ void readConfigFile(const std::string& conf_path) override { gArgs.ReadConfigFile(conf_path); }
+ bool softSetArg(const std::string& arg, const std::string& value) override { return gArgs.SoftSetArg(arg, value); }
+ bool softSetBoolArg(const std::string& arg, bool value) override { return gArgs.SoftSetBoolArg(arg, value); }
+ void selectParams(const std::string& network) override { SelectParams(network); }
+ std::string getNetwork() override { return Params().NetworkIDString(); }
+ void initLogging() override { InitLogging(); }
+ void initParameterInteraction() override { InitParameterInteraction(); }
+ std::string getWarnings(const std::string& type) override { return GetWarnings(type); }
+ uint32_t getLogCategories() override { return ::logCategories; }
+ bool baseInitialize() override
+ {
+ return AppInitBasicSetup() && AppInitParameterInteraction() && AppInitSanityChecks() &&
+ AppInitLockDataDirectory();
+ }
+ bool appInitMain() override { return AppInitMain(); }
+ void appShutdown() override
+ {
+ Interrupt();
+ Shutdown();
+ }
+ void startShutdown() override { StartShutdown(); }
+ bool shutdownRequested() override { return ShutdownRequested(); }
+ void mapPort(bool use_upnp) override
+ {
+ if (use_upnp) {
+ StartMapPort();
+ } else {
+ InterruptMapPort();
+ StopMapPort();
+ }
+ }
+ std::string helpMessage(HelpMessageMode mode) override { return HelpMessage(mode); }
+ bool getProxy(Network net, proxyType& proxy_info) override { return GetProxy(net, proxy_info); }
+ size_t getNodeCount(CConnman::NumConnections flags) override
+ {
+ return g_connman ? g_connman->GetNodeCount(flags) : 0;
+ }
+ bool getNodesStats(NodesStats& stats) override
+ {
+ stats.clear();
+
+ if (g_connman) {
+ std::vector<CNodeStats> stats_temp;
+ g_connman->GetNodeStats(stats_temp);
+
+ stats.reserve(stats_temp.size());
+ for (auto& node_stats_temp : stats_temp) {
+ stats.emplace_back(std::move(node_stats_temp), false, CNodeStateStats());
+ }
+
+ // Try to retrieve the CNodeStateStats for each node.
+ TRY_LOCK(::cs_main, lockMain);
+ if (lockMain) {
+ for (auto& node_stats : stats) {
+ std::get<1>(node_stats) =
+ GetNodeStateStats(std::get<0>(node_stats).nodeid, std::get<2>(node_stats));
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+ bool getBanned(banmap_t& banmap) override
+ {
+ if (g_connman) {
+ g_connman->GetBanned(banmap);
+ return true;
+ }
+ return false;
+ }
+ bool ban(const CNetAddr& net_addr, BanReason reason, int64_t ban_time_offset) override
+ {
+ if (g_connman) {
+ g_connman->Ban(net_addr, reason, ban_time_offset);
+ return true;
+ }
+ return false;
+ }
+ bool unban(const CSubNet& ip) override
+ {
+ if (g_connman) {
+ g_connman->Unban(ip);
+ return true;
+ }
+ return false;
+ }
+ bool disconnect(NodeId id) override
+ {
+ if (g_connman) {
+ return g_connman->DisconnectNode(id);
+ }
+ return false;
+ }
+ int64_t getTotalBytesRecv() override { return g_connman ? g_connman->GetTotalBytesRecv() : 0; }
+ int64_t getTotalBytesSent() override { return g_connman ? g_connman->GetTotalBytesSent() : 0; }
+ size_t getMempoolSize() override { return ::mempool.size(); }
+ size_t getMempoolDynamicUsage() override { return ::mempool.DynamicMemoryUsage(); }
+ bool getHeaderTip(int& height, int64_t& block_time) override
+ {
+ LOCK(::cs_main);
+ if (::pindexBestHeader) {
+ height = ::pindexBestHeader->nHeight;
+ block_time = ::pindexBestHeader->GetBlockTime();
+ return true;
+ }
+ return false;
+ }
+ int getNumBlocks() override
+ {
+ LOCK(::cs_main);
+ return ::chainActive.Height();
+ }
+ int64_t getLastBlockTime() override
+ {
+ LOCK(::cs_main);
+ if (::chainActive.Tip()) {
+ return ::chainActive.Tip()->GetBlockTime();
+ }
+ return Params().GenesisBlock().GetBlockTime(); // Genesis block's time of current network
+ }
+ double getVerificationProgress() override
+ {
+ const CBlockIndex* tip;
+ {
+ LOCK(::cs_main);
+ tip = ::chainActive.Tip();
+ }
+ return GuessVerificationProgress(Params().TxData(), tip);
+ }
+ bool isInitialBlockDownload() override { return IsInitialBlockDownload(); }
+ bool getReindex() override { return ::fReindex; }
+ bool getImporting() override { return ::fImporting; }
+ void setNetworkActive(bool active) override
+ {
+ if (g_connman) {
+ g_connman->SetNetworkActive(active);
+ }
+ }
+ bool getNetworkActive() override { return g_connman && g_connman->GetNetworkActive(); }
+ unsigned int getTxConfirmTarget() override { CHECK_WALLET(return ::nTxConfirmTarget); }
+ CAmount getRequiredFee(unsigned int tx_bytes) override { CHECK_WALLET(return GetRequiredFee(tx_bytes)); }
+ CAmount getMinimumFee(unsigned int tx_bytes,
+ const CCoinControl& coin_control,
+ int* returned_target,
+ FeeReason* reason) override
+ {
+ FeeCalculation fee_calc;
+ CAmount result;
+ CHECK_WALLET(result = GetMinimumFee(tx_bytes, coin_control, ::mempool, ::feeEstimator, &fee_calc));
+ if (returned_target) *returned_target = fee_calc.returnedTarget;
+ if (reason) *reason = fee_calc.reason;
+ return result;
+ }
+ CAmount getMaxTxFee() override { return ::maxTxFee; }
+ CFeeRate estimateSmartFee(int num_blocks, bool conservative, int* returned_target = nullptr) override
+ {
+ FeeCalculation fee_calc;
+ CFeeRate result = ::feeEstimator.estimateSmartFee(num_blocks, &fee_calc, conservative);
+ if (returned_target) {
+ *returned_target = fee_calc.returnedTarget;
+ }
+ return result;
+ }
+ CFeeRate getDustRelayFee() override { return ::dustRelayFee; }
+ CFeeRate getFallbackFee() override { CHECK_WALLET(return CWallet::fallbackFee); }
+ CFeeRate getPayTxFee() override { CHECK_WALLET(return ::payTxFee); }
+ void setPayTxFee(CFeeRate rate) override { CHECK_WALLET(::payTxFee = rate); }
+ UniValue executeRpc(const std::string& command, const UniValue& params, const std::string& uri) override
+ {
+ JSONRPCRequest req;
+ req.params = params;
+ req.strMethod = command;
+ req.URI = uri;
+ return ::tableRPC.execute(req);
+ }
+ std::vector<std::string> listRpcCommands() override { return ::tableRPC.listCommands(); }
+ void rpcSetTimerInterfaceIfUnset(RPCTimerInterface* iface) override { RPCSetTimerInterfaceIfUnset(iface); }
+ void rpcUnsetTimerInterface(RPCTimerInterface* iface) override { RPCUnsetTimerInterface(iface); }
+ bool getUnspentOutput(const COutPoint& output, Coin& coin) override
+ {
+ LOCK(::cs_main);
+ return ::pcoinsTip->GetCoin(output, coin);
+ }
+ std::vector<std::unique_ptr<Wallet>> getWallets() override
+ {
+#ifdef ENABLE_WALLET
+ std::vector<std::unique_ptr<Wallet>> wallets;
+ for (CWalletRef wallet : ::vpwallets) {
+ wallets.emplace_back(MakeWallet(*wallet));
+ }
+ return wallets;
+#else
+ throw std::logic_error("Node::getWallets() called in non-wallet build.");
+#endif
+ }
+ std::unique_ptr<Handler> handleInitMessage(InitMessageFn fn) override
+ {
+ return MakeHandler(::uiInterface.InitMessage.connect(fn));
+ }
+ std::unique_ptr<Handler> handleMessageBox(MessageBoxFn fn) override
+ {
+ return MakeHandler(::uiInterface.ThreadSafeMessageBox.connect(fn));
+ }
+ std::unique_ptr<Handler> handleQuestion(QuestionFn fn) override
+ {
+ return MakeHandler(::uiInterface.ThreadSafeQuestion.connect(fn));
+ }
+ std::unique_ptr<Handler> handleShowProgress(ShowProgressFn fn) override
+ {
+ return MakeHandler(::uiInterface.ShowProgress.connect(fn));
+ }
+ std::unique_ptr<Handler> handleLoadWallet(LoadWalletFn fn) override
+ {
+ CHECK_WALLET(
+ return MakeHandler(::uiInterface.LoadWallet.connect([fn](CWallet* wallet) { fn(MakeWallet(*wallet)); })));
+ }
+ std::unique_ptr<Handler> handleNotifyNumConnectionsChanged(NotifyNumConnectionsChangedFn fn) override
+ {
+ return MakeHandler(::uiInterface.NotifyNumConnectionsChanged.connect(fn));
+ }
+ std::unique_ptr<Handler> handleNotifyNetworkActiveChanged(NotifyNetworkActiveChangedFn fn) override
+ {
+ return MakeHandler(::uiInterface.NotifyNetworkActiveChanged.connect(fn));
+ }
+ std::unique_ptr<Handler> handleNotifyAlertChanged(NotifyAlertChangedFn fn) override
+ {
+ return MakeHandler(::uiInterface.NotifyAlertChanged.connect(fn));
+ }
+ std::unique_ptr<Handler> handleBannedListChanged(BannedListChangedFn fn) override
+ {
+ return MakeHandler(::uiInterface.BannedListChanged.connect(fn));
+ }
+ std::unique_ptr<Handler> handleNotifyBlockTip(NotifyBlockTipFn fn) override
+ {
+ return MakeHandler(::uiInterface.NotifyBlockTip.connect([fn](bool initial_download, const CBlockIndex* block) {
+ fn(initial_download, block->nHeight, block->GetBlockTime(),
+ GuessVerificationProgress(Params().TxData(), block));
+ }));
+ }
+ std::unique_ptr<Handler> handleNotifyHeaderTip(NotifyHeaderTipFn fn) override
+ {
+ return MakeHandler(
+ ::uiInterface.NotifyHeaderTip.connect([fn](bool initial_download, const CBlockIndex* block) {
+ fn(initial_download, block->nHeight, block->GetBlockTime(),
+ GuessVerificationProgress(Params().TxData(), block));
+ }));
+ }
+};
+
+} // namespace
+
+std::unique_ptr<Node> MakeNode() { return MakeUnique<NodeImpl>(); }
+
+} // namespace interface
diff --git a/src/interface/node.h b/src/interface/node.h
new file mode 100644
index 0000000000..6ae7f9e46c
--- /dev/null
+++ b/src/interface/node.h
@@ -0,0 +1,259 @@
+// Copyright (c) 2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_INTERFACE_NODE_H
+#define BITCOIN_INTERFACE_NODE_H
+
+#include <addrdb.h> // For banmap_t
+#include <amount.h> // For CAmount
+#include <init.h> // For HelpMessageMode
+#include <net.h> // For CConnman::NumConnections
+#include <netaddress.h> // For Network
+
+#include <functional>
+#include <memory>
+#include <stddef.h>
+#include <stdint.h>
+#include <string>
+#include <tuple>
+#include <vector>
+
+class CCoinControl;
+class CFeeRate;
+class CNodeStats;
+class Coin;
+class RPCTimerInterface;
+class UniValue;
+class proxyType;
+enum class FeeReason;
+struct CNodeStateStats;
+
+namespace interface {
+
+class Handler;
+class Wallet;
+
+//! Top-level interface for a bitcoin node (bitcoind process).
+class Node
+{
+public:
+ virtual ~Node() {}
+
+ //! Set command line arguments.
+ virtual void parseParameters(int argc, const char* const argv[]) = 0;
+
+ //! Set a command line argument if it doesn't already have a value
+ virtual bool softSetArg(const std::string& arg, const std::string& value) = 0;
+
+ //! Set a command line boolean argument if it doesn't already have a value
+ virtual bool softSetBoolArg(const std::string& arg, bool value) = 0;
+
+ //! Load settings from configuration file.
+ virtual void readConfigFile(const std::string& conf_path) = 0;
+
+ //! Choose network parameters.
+ virtual void selectParams(const std::string& network) = 0;
+
+ //! Get network name.
+ virtual std::string getNetwork() = 0;
+
+ //! Init logging.
+ virtual void initLogging() = 0;
+
+ //! Init parameter interaction.
+ virtual void initParameterInteraction() = 0;
+
+ //! Get warnings.
+ virtual std::string getWarnings(const std::string& type) = 0;
+
+ // Get log flags.
+ virtual uint32_t getLogCategories() = 0;
+
+ //! Initialize app dependencies.
+ virtual bool baseInitialize() = 0;
+
+ //! Start node.
+ virtual bool appInitMain() = 0;
+
+ //! Stop node.
+ virtual void appShutdown() = 0;
+
+ //! Start shutdown.
+ virtual void startShutdown() = 0;
+
+ //! Return whether shutdown was requested.
+ virtual bool shutdownRequested() = 0;
+
+ //! Get help message string.
+ virtual std::string helpMessage(HelpMessageMode mode) = 0;
+
+ //! Map port.
+ virtual void mapPort(bool use_upnp) = 0;
+
+ //! Get proxy.
+ virtual bool getProxy(Network net, proxyType& proxy_info) = 0;
+
+ //! Get number of connections.
+ virtual size_t getNodeCount(CConnman::NumConnections flags) = 0;
+
+ //! Get stats for connected nodes.
+ using NodesStats = std::vector<std::tuple<CNodeStats, bool, CNodeStateStats>>;
+ virtual bool getNodesStats(NodesStats& stats) = 0;
+
+ //! Get ban map entries.
+ virtual bool getBanned(banmap_t& banmap) = 0;
+
+ //! Ban node.
+ virtual bool ban(const CNetAddr& net_addr, BanReason reason, int64_t ban_time_offset) = 0;
+
+ //! Unban node.
+ virtual bool unban(const CSubNet& ip) = 0;
+
+ //! Disconnect node.
+ virtual bool disconnect(NodeId id) = 0;
+
+ //! Get total bytes recv.
+ virtual int64_t getTotalBytesRecv() = 0;
+
+ //! Get total bytes sent.
+ virtual int64_t getTotalBytesSent() = 0;
+
+ //! Get mempool size.
+ virtual size_t getMempoolSize() = 0;
+
+ //! Get mempool dynamic usage.
+ virtual size_t getMempoolDynamicUsage() = 0;
+
+ //! Get header tip height and time.
+ virtual bool getHeaderTip(int& height, int64_t& block_time) = 0;
+
+ //! Get num blocks.
+ virtual int getNumBlocks() = 0;
+
+ //! Get last block time.
+ virtual int64_t getLastBlockTime() = 0;
+
+ //! Get verification progress.
+ virtual double getVerificationProgress() = 0;
+
+ //! Is initial block download.
+ virtual bool isInitialBlockDownload() = 0;
+
+ //! Get reindex.
+ virtual bool getReindex() = 0;
+
+ //! Get importing.
+ virtual bool getImporting() = 0;
+
+ //! Set network active.
+ virtual void setNetworkActive(bool active) = 0;
+
+ //! Get network active.
+ virtual bool getNetworkActive() = 0;
+
+ //! Get tx confirm target.
+ virtual unsigned int getTxConfirmTarget() = 0;
+
+ //! Get required fee.
+ virtual CAmount getRequiredFee(unsigned int tx_bytes) = 0;
+
+ //! Get minimum fee.
+ virtual CAmount getMinimumFee(unsigned int tx_bytes,
+ const CCoinControl& coin_control,
+ int* returned_target,
+ FeeReason* reason) = 0;
+
+ //! Get max tx fee.
+ virtual CAmount getMaxTxFee() = 0;
+
+ //! Estimate smart fee.
+ virtual CFeeRate estimateSmartFee(int num_blocks, bool conservative, int* returned_target = nullptr) = 0;
+
+ //! Get dust relay fee.
+ virtual CFeeRate getDustRelayFee() = 0;
+
+ //! Get fallback fee.
+ virtual CFeeRate getFallbackFee() = 0;
+
+ //! Get pay tx fee.
+ virtual CFeeRate getPayTxFee() = 0;
+
+ //! Set pay tx fee.
+ virtual void setPayTxFee(CFeeRate rate) = 0;
+
+ //! Execute rpc command.
+ virtual UniValue executeRpc(const std::string& command, const UniValue& params, const std::string& uri) = 0;
+
+ //! List rpc commands.
+ virtual std::vector<std::string> listRpcCommands() = 0;
+
+ //! Set RPC timer interface if unset.
+ virtual void rpcSetTimerInterfaceIfUnset(RPCTimerInterface* iface) = 0;
+
+ //! Unset RPC timer interface.
+ virtual void rpcUnsetTimerInterface(RPCTimerInterface* iface) = 0;
+
+ //! Get unspent outputs associated with a transaction.
+ virtual bool getUnspentOutput(const COutPoint& output, Coin& coin) = 0;
+
+ //! Return interfaces for accessing wallets (if any).
+ virtual std::vector<std::unique_ptr<Wallet>> getWallets() = 0;
+
+ //! Register handler for init messages.
+ using InitMessageFn = std::function<void(const std::string& message)>;
+ virtual std::unique_ptr<Handler> handleInitMessage(InitMessageFn fn) = 0;
+
+ //! Register handler for message box messages.
+ using MessageBoxFn =
+ std::function<bool(const std::string& message, const std::string& caption, unsigned int style)>;
+ virtual std::unique_ptr<Handler> handleMessageBox(MessageBoxFn fn) = 0;
+
+ //! Register handler for question messages.
+ using QuestionFn = std::function<bool(const std::string& message,
+ const std::string& non_interactive_message,
+ const std::string& caption,
+ unsigned int style)>;
+ virtual std::unique_ptr<Handler> handleQuestion(QuestionFn fn) = 0;
+
+ //! Register handler for progress messages.
+ using ShowProgressFn = std::function<void(const std::string& title, int progress, bool resume_possible)>;
+ virtual std::unique_ptr<Handler> handleShowProgress(ShowProgressFn fn) = 0;
+
+ //! Register handler for load wallet messages.
+ using LoadWalletFn = std::function<void(std::unique_ptr<Wallet> wallet)>;
+ virtual std::unique_ptr<Handler> handleLoadWallet(LoadWalletFn fn) = 0;
+
+ //! Register handler for number of connections changed messages.
+ using NotifyNumConnectionsChangedFn = std::function<void(int new_num_connections)>;
+ virtual std::unique_ptr<Handler> handleNotifyNumConnectionsChanged(NotifyNumConnectionsChangedFn fn) = 0;
+
+ //! Register handler for network active messages.
+ using NotifyNetworkActiveChangedFn = std::function<void(bool network_active)>;
+ virtual std::unique_ptr<Handler> handleNotifyNetworkActiveChanged(NotifyNetworkActiveChangedFn fn) = 0;
+
+ //! Register handler for notify alert messages.
+ using NotifyAlertChangedFn = std::function<void()>;
+ virtual std::unique_ptr<Handler> handleNotifyAlertChanged(NotifyAlertChangedFn fn) = 0;
+
+ //! Register handler for ban list messages.
+ using BannedListChangedFn = std::function<void()>;
+ virtual std::unique_ptr<Handler> handleBannedListChanged(BannedListChangedFn fn) = 0;
+
+ //! Register handler for block tip messages.
+ using NotifyBlockTipFn =
+ std::function<void(bool initial_download, int height, int64_t block_time, double verification_progress)>;
+ virtual std::unique_ptr<Handler> handleNotifyBlockTip(NotifyBlockTipFn fn) = 0;
+
+ //! Register handler for header tip messages.
+ using NotifyHeaderTipFn =
+ std::function<void(bool initial_download, int height, int64_t block_time, double verification_progress)>;
+ virtual std::unique_ptr<Handler> handleNotifyHeaderTip(NotifyHeaderTipFn fn) = 0;
+};
+
+//! Return implementation of Node interface.
+std::unique_ptr<Node> MakeNode();
+
+} // namespace interface
+
+#endif // BITCOIN_INTERFACE_NODE_H
diff --git a/src/interface/wallet.cpp b/src/interface/wallet.cpp
new file mode 100644
index 0000000000..a6bce7d3de
--- /dev/null
+++ b/src/interface/wallet.cpp
@@ -0,0 +1,441 @@
+// Copyright (c) 2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <interface/wallet.h>
+
+#include <amount.h>
+#include <chain.h>
+#include <consensus/validation.h>
+#include <interface/handler.h>
+#include <net.h>
+#include <policy/policy.h>
+#include <primitives/transaction.h>
+#include <script/ismine.h>
+#include <script/standard.h>
+#include <support/allocators/secure.h>
+#include <sync.h>
+#include <timedata.h>
+#include <ui_interface.h>
+#include <uint256.h>
+#include <validation.h>
+#include <wallet/feebumper.h>
+#include <wallet/wallet.h>
+
+#include <memory>
+
+namespace interface {
+namespace {
+
+class PendingWalletTxImpl : public PendingWalletTx
+{
+public:
+ PendingWalletTxImpl(CWallet& wallet) : m_wallet(wallet), m_key(&wallet) {}
+
+ const CTransaction& get() override { return *m_tx; }
+
+ int64_t getVirtualSize() override { return GetVirtualTransactionSize(*m_tx); }
+
+ bool commit(WalletValueMap value_map,
+ WalletOrderForm order_form,
+ std::string from_account,
+ std::string& reject_reason) override
+ {
+ LOCK2(cs_main, m_wallet.cs_wallet);
+ CValidationState state;
+ if (!m_wallet.CommitTransaction(m_tx, std::move(value_map), std::move(order_form), std::move(from_account), m_key, g_connman.get(), state)) {
+ reject_reason = state.GetRejectReason();
+ return false;
+ }
+ return true;
+ }
+
+ CTransactionRef m_tx;
+ CWallet& m_wallet;
+ CReserveKey m_key;
+};
+
+//! Construct wallet tx struct.
+WalletTx MakeWalletTx(CWallet& wallet, const CWalletTx& wtx)
+{
+ WalletTx result;
+ result.tx = wtx.tx;
+ result.txin_is_mine.reserve(wtx.tx->vin.size());
+ for (const auto& txin : wtx.tx->vin) {
+ result.txin_is_mine.emplace_back(wallet.IsMine(txin));
+ }
+ result.txout_is_mine.reserve(wtx.tx->vout.size());
+ result.txout_address.reserve(wtx.tx->vout.size());
+ result.txout_address_is_mine.reserve(wtx.tx->vout.size());
+ for (const auto& txout : wtx.tx->vout) {
+ result.txout_is_mine.emplace_back(wallet.IsMine(txout));
+ result.txout_address.emplace_back();
+ result.txout_address_is_mine.emplace_back(ExtractDestination(txout.scriptPubKey, result.txout_address.back()) ?
+ IsMine(wallet, result.txout_address.back()) :
+ ISMINE_NO);
+ }
+ result.credit = wtx.GetCredit(ISMINE_ALL);
+ result.debit = wtx.GetDebit(ISMINE_ALL);
+ result.change = wtx.GetChange();
+ result.time = wtx.GetTxTime();
+ result.value_map = wtx.mapValue;
+ result.is_coinbase = wtx.IsCoinBase();
+ return result;
+}
+
+//! Construct wallet tx status struct.
+WalletTxStatus MakeWalletTxStatus(const CWalletTx& wtx)
+{
+ WalletTxStatus result;
+ auto mi = ::mapBlockIndex.find(wtx.hashBlock);
+ CBlockIndex* block = mi != ::mapBlockIndex.end() ? mi->second : nullptr;
+ result.block_height = (block ? block->nHeight : std::numeric_limits<int>::max()),
+ result.blocks_to_maturity = wtx.GetBlocksToMaturity();
+ result.depth_in_main_chain = wtx.GetDepthInMainChain();
+ result.request_count = wtx.GetRequestCount();
+ result.time_received = wtx.nTimeReceived;
+ result.lock_time = wtx.tx->nLockTime;
+ result.is_final = CheckFinalTx(*wtx.tx);
+ result.is_trusted = wtx.IsTrusted();
+ result.is_abandoned = wtx.isAbandoned();
+ result.is_coinbase = wtx.IsCoinBase();
+ result.is_in_main_chain = wtx.IsInMainChain();
+ return result;
+}
+
+//! Construct wallet TxOut struct.
+WalletTxOut MakeWalletTxOut(CWallet& wallet, const CWalletTx& wtx, int n, int depth)
+{
+ WalletTxOut result;
+ result.txout = wtx.tx->vout[n];
+ result.time = wtx.GetTxTime();
+ result.depth_in_main_chain = depth;
+ result.is_spent = wallet.IsSpent(wtx.GetHash(), n);
+ return result;
+}
+
+class WalletImpl : public Wallet
+{
+public:
+ WalletImpl(CWallet& wallet) : m_wallet(wallet) {}
+
+ bool encryptWallet(const SecureString& wallet_passphrase) override
+ {
+ return m_wallet.EncryptWallet(wallet_passphrase);
+ }
+ bool isCrypted() override { return m_wallet.IsCrypted(); }
+ bool lock() override { return m_wallet.Lock(); }
+ bool unlock(const SecureString& wallet_passphrase) override { return m_wallet.Unlock(wallet_passphrase); }
+ bool isLocked() override { return m_wallet.IsLocked(); }
+ bool changeWalletPassphrase(const SecureString& old_wallet_passphrase,
+ const SecureString& new_wallet_passphrase) override
+ {
+ return m_wallet.ChangeWalletPassphrase(old_wallet_passphrase, new_wallet_passphrase);
+ }
+ bool backupWallet(const std::string& filename) override { return m_wallet.BackupWallet(filename); }
+ std::string getWalletName() override { return m_wallet.GetName(); }
+ bool getKeyFromPool(bool internal, CPubKey& pub_key) override
+ {
+ return m_wallet.GetKeyFromPool(pub_key, internal);
+ }
+ bool getPubKey(const CKeyID& address, CPubKey& pub_key) override { return m_wallet.GetPubKey(address, pub_key); }
+ bool getPrivKey(const CKeyID& address, CKey& key) override { return m_wallet.GetKey(address, key); }
+ bool isSpendable(const CTxDestination& dest) override { return IsMine(m_wallet, dest) & ISMINE_SPENDABLE; }
+ bool haveWatchOnly() override { return m_wallet.HaveWatchOnly(); };
+ bool setAddressBook(const CTxDestination& dest, const std::string& name, const std::string& purpose) override
+ {
+ return m_wallet.SetAddressBook(dest, name, purpose);
+ }
+ bool delAddressBook(const CTxDestination& dest) override
+ {
+ return m_wallet.DelAddressBook(dest);
+ }
+ bool getAddress(const CTxDestination& dest, std::string* name, isminetype* is_mine) override
+ {
+ LOCK(m_wallet.cs_wallet);
+ auto it = m_wallet.mapAddressBook.find(dest);
+ if (it == m_wallet.mapAddressBook.end()) {
+ return false;
+ }
+ if (name) {
+ *name = it->second.name;
+ }
+ if (is_mine) {
+ *is_mine = IsMine(m_wallet, dest);
+ }
+ return true;
+ }
+ std::vector<WalletAddress> getAddresses() override
+ {
+ LOCK(m_wallet.cs_wallet);
+ std::vector<WalletAddress> result;
+ for (const auto& item : m_wallet.mapAddressBook) {
+ result.emplace_back(item.first, IsMine(m_wallet, item.first), item.second.name, item.second.purpose);
+ }
+ return result;
+ }
+ void learnRelatedScripts(const CPubKey& key, OutputType type) override { m_wallet.LearnRelatedScripts(key, type); }
+ bool addDestData(const CTxDestination& dest, const std::string& key, const std::string& value) override
+ {
+ LOCK(m_wallet.cs_wallet);
+ return m_wallet.AddDestData(dest, key, value);
+ }
+ bool eraseDestData(const CTxDestination& dest, const std::string& key) override
+ {
+ LOCK(m_wallet.cs_wallet);
+ return m_wallet.EraseDestData(dest, key);
+ }
+ std::vector<std::string> getDestValues(const std::string& prefix) override
+ {
+ return m_wallet.GetDestValues(prefix);
+ }
+ void lockCoin(const COutPoint& output) override
+ {
+ LOCK2(cs_main, m_wallet.cs_wallet);
+ return m_wallet.LockCoin(output);
+ }
+ void unlockCoin(const COutPoint& output) override
+ {
+ LOCK2(cs_main, m_wallet.cs_wallet);
+ return m_wallet.UnlockCoin(output);
+ }
+ bool isLockedCoin(const COutPoint& output) override
+ {
+ LOCK2(cs_main, m_wallet.cs_wallet);
+ return m_wallet.IsLockedCoin(output.hash, output.n);
+ }
+ void listLockedCoins(std::vector<COutPoint>& outputs) override
+ {
+ LOCK2(cs_main, m_wallet.cs_wallet);
+ return m_wallet.ListLockedCoins(outputs);
+ }
+ std::unique_ptr<PendingWalletTx> createTransaction(const std::vector<CRecipient>& recipients,
+ const CCoinControl& coin_control,
+ bool sign,
+ int& change_pos,
+ CAmount& fee,
+ std::string& fail_reason) override
+ {
+ LOCK2(cs_main, m_wallet.cs_wallet);
+ auto pending = MakeUnique<PendingWalletTxImpl>(m_wallet);
+ if (!m_wallet.CreateTransaction(recipients, pending->m_tx, pending->m_key, fee, change_pos,
+ fail_reason, coin_control, sign)) {
+ return {};
+ }
+ return std::move(pending);
+ }
+ bool transactionCanBeAbandoned(const uint256& txid) override { return m_wallet.TransactionCanBeAbandoned(txid); }
+ bool abandonTransaction(const uint256& txid) override
+ {
+ LOCK2(cs_main, m_wallet.cs_wallet);
+ return m_wallet.AbandonTransaction(txid);
+ }
+ bool transactionCanBeBumped(const uint256& txid) override
+ {
+ return feebumper::TransactionCanBeBumped(&m_wallet, txid);
+ }
+ bool createBumpTransaction(const uint256& txid,
+ const CCoinControl& coin_control,
+ CAmount total_fee,
+ std::vector<std::string>& errors,
+ CAmount& old_fee,
+ CAmount& new_fee,
+ CMutableTransaction& mtx) override
+ {
+ return feebumper::CreateTransaction(&m_wallet, txid, coin_control, total_fee, errors, old_fee, new_fee, mtx) ==
+ feebumper::Result::OK;
+ }
+ bool signBumpTransaction(CMutableTransaction& mtx) override { return feebumper::SignTransaction(&m_wallet, mtx); }
+ bool commitBumpTransaction(const uint256& txid,
+ CMutableTransaction&& mtx,
+ std::vector<std::string>& errors,
+ uint256& bumped_txid) override
+ {
+ return feebumper::CommitTransaction(&m_wallet, txid, std::move(mtx), errors, bumped_txid) ==
+ feebumper::Result::OK;
+ }
+ CTransactionRef getTx(const uint256& txid) override
+ {
+ LOCK2(::cs_main, m_wallet.cs_wallet);
+ auto mi = m_wallet.mapWallet.find(txid);
+ if (mi != m_wallet.mapWallet.end()) {
+ return mi->second.tx;
+ }
+ return {};
+ }
+ WalletTx getWalletTx(const uint256& txid) override
+ {
+ LOCK2(::cs_main, m_wallet.cs_wallet);
+ auto mi = m_wallet.mapWallet.find(txid);
+ if (mi != m_wallet.mapWallet.end()) {
+ return MakeWalletTx(m_wallet, mi->second);
+ }
+ return {};
+ }
+ std::vector<WalletTx> getWalletTxs() override
+ {
+ LOCK2(::cs_main, m_wallet.cs_wallet);
+ std::vector<WalletTx> result;
+ result.reserve(m_wallet.mapWallet.size());
+ for (const auto& entry : m_wallet.mapWallet) {
+ result.emplace_back(MakeWalletTx(m_wallet, entry.second));
+ }
+ return result;
+ }
+ bool tryGetTxStatus(const uint256& txid,
+ interface::WalletTxStatus& tx_status,
+ int& num_blocks,
+ int64_t& adjusted_time) override
+ {
+ TRY_LOCK(::cs_main, locked_chain);
+ if (!locked_chain) {
+ return false;
+ }
+ TRY_LOCK(m_wallet.cs_wallet, locked_wallet);
+ if (!locked_wallet) {
+ return false;
+ }
+ auto mi = m_wallet.mapWallet.find(txid);
+ if (mi == m_wallet.mapWallet.end()) {
+ return false;
+ }
+ num_blocks = ::chainActive.Height();
+ adjusted_time = GetAdjustedTime();
+ tx_status = MakeWalletTxStatus(mi->second);
+ return true;
+ }
+ WalletTx getWalletTxDetails(const uint256& txid,
+ WalletTxStatus& tx_status,
+ WalletOrderForm& order_form,
+ bool& in_mempool,
+ int& num_blocks,
+ int64_t& adjusted_time) override
+ {
+ LOCK2(::cs_main, m_wallet.cs_wallet);
+ auto mi = m_wallet.mapWallet.find(txid);
+ if (mi != m_wallet.mapWallet.end()) {
+ num_blocks = ::chainActive.Height();
+ adjusted_time = GetAdjustedTime();
+ in_mempool = mi->second.InMempool();
+ order_form = mi->second.vOrderForm;
+ tx_status = MakeWalletTxStatus(mi->second);
+ return MakeWalletTx(m_wallet, mi->second);
+ }
+ return {};
+ }
+ WalletBalances getBalances() override
+ {
+ WalletBalances result;
+ result.balance = m_wallet.GetBalance();
+ result.unconfirmed_balance = m_wallet.GetUnconfirmedBalance();
+ result.immature_balance = m_wallet.GetImmatureBalance();
+ result.have_watch_only = m_wallet.HaveWatchOnly();
+ if (result.have_watch_only) {
+ result.watch_only_balance = m_wallet.GetWatchOnlyBalance();
+ result.unconfirmed_watch_only_balance = m_wallet.GetUnconfirmedWatchOnlyBalance();
+ result.immature_watch_only_balance = m_wallet.GetImmatureWatchOnlyBalance();
+ }
+ return result;
+ }
+ bool tryGetBalances(WalletBalances& balances, int& num_blocks) override
+ {
+ TRY_LOCK(cs_main, locked_chain);
+ if (!locked_chain) return false;
+ TRY_LOCK(m_wallet.cs_wallet, locked_wallet);
+ if (!locked_wallet) {
+ return false;
+ }
+ balances = getBalances();
+ num_blocks = ::chainActive.Height();
+ return true;
+ }
+ CAmount getBalance() override { return m_wallet.GetBalance(); }
+ CAmount getAvailableBalance(const CCoinControl& coin_control) override
+ {
+ return m_wallet.GetAvailableBalance(&coin_control);
+ }
+ isminetype txinIsMine(const CTxIn& txin) override
+ {
+ LOCK2(::cs_main, m_wallet.cs_wallet);
+ return m_wallet.IsMine(txin);
+ }
+ isminetype txoutIsMine(const CTxOut& txout) override
+ {
+ LOCK2(::cs_main, m_wallet.cs_wallet);
+ return m_wallet.IsMine(txout);
+ }
+ CAmount getDebit(const CTxIn& txin, isminefilter filter) override
+ {
+ LOCK2(::cs_main, m_wallet.cs_wallet);
+ return m_wallet.GetDebit(txin, filter);
+ }
+ CAmount getCredit(const CTxOut& txout, isminefilter filter) override
+ {
+ LOCK2(::cs_main, m_wallet.cs_wallet);
+ return m_wallet.GetCredit(txout, filter);
+ }
+ CoinsList listCoins() override
+ {
+ LOCK2(::cs_main, m_wallet.cs_wallet);
+ CoinsList result;
+ for (const auto& entry : m_wallet.ListCoins()) {
+ auto& group = result[entry.first];
+ for (const auto& coin : entry.second) {
+ group.emplace_back(
+ COutPoint(coin.tx->GetHash(), coin.i), MakeWalletTxOut(m_wallet, *coin.tx, coin.i, coin.nDepth));
+ }
+ }
+ return result;
+ }
+ std::vector<WalletTxOut> getCoins(const std::vector<COutPoint>& outputs) override
+ {
+ LOCK2(::cs_main, m_wallet.cs_wallet);
+ std::vector<WalletTxOut> result;
+ result.reserve(outputs.size());
+ for (const auto& output : outputs) {
+ result.emplace_back();
+ auto it = m_wallet.mapWallet.find(output.hash);
+ if (it != m_wallet.mapWallet.end()) {
+ int depth = it->second.GetDepthInMainChain();
+ if (depth >= 0) {
+ result.back() = MakeWalletTxOut(m_wallet, it->second, output.n, depth);
+ }
+ }
+ }
+ return result;
+ }
+ bool hdEnabled() override { return m_wallet.IsHDEnabled(); }
+ OutputType getDefaultAddressType() override { return m_wallet.m_default_address_type; }
+ OutputType getDefaultChangeType() override { return m_wallet.m_default_change_type; }
+ std::unique_ptr<Handler> handleShowProgress(ShowProgressFn fn) override
+ {
+ return MakeHandler(m_wallet.ShowProgress.connect(fn));
+ }
+ std::unique_ptr<Handler> handleStatusChanged(StatusChangedFn fn) override
+ {
+ return MakeHandler(m_wallet.NotifyStatusChanged.connect([fn](CCryptoKeyStore*) { fn(); }));
+ }
+ std::unique_ptr<Handler> handleAddressBookChanged(AddressBookChangedFn fn) override
+ {
+ return MakeHandler(m_wallet.NotifyAddressBookChanged.connect(
+ [fn](CWallet*, const CTxDestination& address, const std::string& label, bool is_mine,
+ const std::string& purpose, ChangeType status) { fn(address, label, is_mine, purpose, status); }));
+ }
+ std::unique_ptr<Handler> handleTransactionChanged(TransactionChangedFn fn) override
+ {
+ return MakeHandler(m_wallet.NotifyTransactionChanged.connect(
+ [fn, this](CWallet*, const uint256& txid, ChangeType status) { fn(txid, status); }));
+ }
+ std::unique_ptr<Handler> handleWatchOnlyChanged(WatchOnlyChangedFn fn) override
+ {
+ return MakeHandler(m_wallet.NotifyWatchonlyChanged.connect(fn));
+ }
+
+ CWallet& m_wallet;
+};
+
+} // namespace
+
+std::unique_ptr<Wallet> MakeWallet(CWallet& wallet) { return MakeUnique<WalletImpl>(wallet); }
+
+} // namespace interface
diff --git a/src/interface/wallet.h b/src/interface/wallet.h
new file mode 100644
index 0000000000..b66ed63398
--- /dev/null
+++ b/src/interface/wallet.h
@@ -0,0 +1,352 @@
+// Copyright (c) 2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_INTERFACE_WALLET_H
+#define BITCOIN_INTERFACE_WALLET_H
+
+#include <amount.h> // For CAmount
+#include <pubkey.h> // For CTxDestination (CKeyID and CScriptID)
+#include <script/ismine.h> // For isminefilter, isminetype
+#include <script/standard.h> // For CTxDestination
+#include <support/allocators/secure.h> // For SecureString
+#include <ui_interface.h> // For ChangeType
+
+#include <functional>
+#include <map>
+#include <memory>
+#include <stdint.h>
+#include <string>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+class CCoinControl;
+class CKey;
+class CWallet;
+enum class OutputType;
+struct CRecipient;
+
+namespace interface {
+
+class Handler;
+class PendingWalletTx;
+struct WalletAddress;
+struct WalletBalances;
+struct WalletTx;
+struct WalletTxOut;
+struct WalletTxStatus;
+
+using WalletOrderForm = std::vector<std::pair<std::string, std::string>>;
+using WalletValueMap = std::map<std::string, std::string>;
+
+//! Interface for accessing a wallet.
+class Wallet
+{
+public:
+ virtual ~Wallet() {}
+
+ //! Encrypt wallet.
+ virtual bool encryptWallet(const SecureString& wallet_passphrase) = 0;
+
+ //! Return whether wallet is encrypted.
+ virtual bool isCrypted() = 0;
+
+ //! Lock wallet.
+ virtual bool lock() = 0;
+
+ //! Unlock wallet.
+ virtual bool unlock(const SecureString& wallet_passphrase) = 0;
+
+ //! Return whether wallet is locked.
+ virtual bool isLocked() = 0;
+
+ //! Change wallet passphrase.
+ virtual bool changeWalletPassphrase(const SecureString& old_wallet_passphrase,
+ const SecureString& new_wallet_passphrase) = 0;
+
+ //! Back up wallet.
+ virtual bool backupWallet(const std::string& filename) = 0;
+
+ //! Get wallet name.
+ virtual std::string getWalletName() = 0;
+
+ // Get key from pool.
+ virtual bool getKeyFromPool(bool internal, CPubKey& pub_key) = 0;
+
+ //! Get public key.
+ virtual bool getPubKey(const CKeyID& address, CPubKey& pub_key) = 0;
+
+ //! Get private key.
+ virtual bool getPrivKey(const CKeyID& address, CKey& key) = 0;
+
+ //! Return whether wallet has private key.
+ virtual bool isSpendable(const CTxDestination& dest) = 0;
+
+ //! Return whether wallet has watch only keys.
+ virtual bool haveWatchOnly() = 0;
+
+ //! Add or update address.
+ virtual bool setAddressBook(const CTxDestination& dest, const std::string& name, const std::string& purpose) = 0;
+
+ // Remove address.
+ virtual bool delAddressBook(const CTxDestination& dest) = 0;
+
+ //! Look up address in wallet, return whether exists.
+ virtual bool getAddress(const CTxDestination& dest,
+ std::string* name = nullptr,
+ isminetype* is_mine = nullptr) = 0;
+
+ //! Get wallet address list.
+ virtual std::vector<WalletAddress> getAddresses() = 0;
+
+ //! Add scripts to key store so old so software versions opening the wallet
+ //! database can detect payments to newer address types.
+ virtual void learnRelatedScripts(const CPubKey& key, OutputType type) = 0;
+
+ //! Add dest data.
+ virtual bool addDestData(const CTxDestination& dest, const std::string& key, const std::string& value) = 0;
+
+ //! Erase dest data.
+ virtual bool eraseDestData(const CTxDestination& dest, const std::string& key) = 0;
+
+ //! Get dest values with prefix.
+ virtual std::vector<std::string> getDestValues(const std::string& prefix) = 0;
+
+ //! Lock coin.
+ virtual void lockCoin(const COutPoint& output) = 0;
+
+ //! Unlock coin.
+ virtual void unlockCoin(const COutPoint& output) = 0;
+
+ //! Return whether coin is locked.
+ virtual bool isLockedCoin(const COutPoint& output) = 0;
+
+ //! List locked coins.
+ virtual void listLockedCoins(std::vector<COutPoint>& outputs) = 0;
+
+ //! Create transaction.
+ virtual std::unique_ptr<PendingWalletTx> createTransaction(const std::vector<CRecipient>& recipients,
+ const CCoinControl& coin_control,
+ bool sign,
+ int& change_pos,
+ CAmount& fee,
+ std::string& fail_reason) = 0;
+
+ //! Return whether transaction can be abandoned.
+ virtual bool transactionCanBeAbandoned(const uint256& txid) = 0;
+
+ //! Abandon transaction.
+ virtual bool abandonTransaction(const uint256& txid) = 0;
+
+ //! Return whether transaction can be bumped.
+ virtual bool transactionCanBeBumped(const uint256& txid) = 0;
+
+ //! Create bump transaction.
+ virtual bool createBumpTransaction(const uint256& txid,
+ const CCoinControl& coin_control,
+ CAmount total_fee,
+ std::vector<std::string>& errors,
+ CAmount& old_fee,
+ CAmount& new_fee,
+ CMutableTransaction& mtx) = 0;
+
+ //! Sign bump transaction.
+ virtual bool signBumpTransaction(CMutableTransaction& mtx) = 0;
+
+ //! Commit bump transaction.
+ virtual bool commitBumpTransaction(const uint256& txid,
+ CMutableTransaction&& mtx,
+ std::vector<std::string>& errors,
+ uint256& bumped_txid) = 0;
+
+ //! Get a transaction.
+ virtual CTransactionRef getTx(const uint256& txid) = 0;
+
+ //! Get transaction information.
+ virtual WalletTx getWalletTx(const uint256& txid) = 0;
+
+ //! Get list of all wallet transactions.
+ virtual std::vector<WalletTx> getWalletTxs() = 0;
+
+ //! Try to get updated status for a particular transaction, if possible without blocking.
+ virtual bool tryGetTxStatus(const uint256& txid,
+ WalletTxStatus& tx_status,
+ int& num_blocks,
+ int64_t& adjusted_time) = 0;
+
+ //! Get transaction details.
+ virtual WalletTx getWalletTxDetails(const uint256& txid,
+ WalletTxStatus& tx_status,
+ WalletOrderForm& order_form,
+ bool& in_mempool,
+ int& num_blocks,
+ int64_t& adjusted_time) = 0;
+
+ //! Get balances.
+ virtual WalletBalances getBalances() = 0;
+
+ //! Get balances if possible without blocking.
+ virtual bool tryGetBalances(WalletBalances& balances, int& num_blocks) = 0;
+
+ //! Get balance.
+ virtual CAmount getBalance() = 0;
+
+ //! Get available balance.
+ virtual CAmount getAvailableBalance(const CCoinControl& coin_control) = 0;
+
+ //! Return whether transaction input belongs to wallet.
+ virtual isminetype txinIsMine(const CTxIn& txin) = 0;
+
+ //! Return whether transaction output belongs to wallet.
+ virtual isminetype txoutIsMine(const CTxOut& txout) = 0;
+
+ //! Return debit amount if transaction input belongs to wallet.
+ virtual CAmount getDebit(const CTxIn& txin, isminefilter filter) = 0;
+
+ //! Return credit amount if transaction input belongs to wallet.
+ virtual CAmount getCredit(const CTxOut& txout, isminefilter filter) = 0;
+
+ //! Return AvailableCoins + LockedCoins grouped by wallet address.
+ //! (put change in one group with wallet address)
+ using CoinsList = std::map<CTxDestination, std::vector<std::tuple<COutPoint, WalletTxOut>>>;
+ virtual CoinsList listCoins() = 0;
+
+ //! Return wallet transaction output information.
+ virtual std::vector<WalletTxOut> getCoins(const std::vector<COutPoint>& outputs) = 0;
+
+ // Return whether HD enabled.
+ virtual bool hdEnabled() = 0;
+
+ // Get default address type.
+ virtual OutputType getDefaultAddressType() = 0;
+
+ // Get default change type.
+ virtual OutputType getDefaultChangeType() = 0;
+
+ //! Register handler for show progress messages.
+ using ShowProgressFn = std::function<void(const std::string& title, int progress)>;
+ virtual std::unique_ptr<Handler> handleShowProgress(ShowProgressFn fn) = 0;
+
+ //! Register handler for status changed messages.
+ using StatusChangedFn = std::function<void()>;
+ virtual std::unique_ptr<Handler> handleStatusChanged(StatusChangedFn fn) = 0;
+
+ //! Register handler for address book changed messages.
+ using AddressBookChangedFn = std::function<void(const CTxDestination& address,
+ const std::string& label,
+ bool is_mine,
+ const std::string& purpose,
+ ChangeType status)>;
+ virtual std::unique_ptr<Handler> handleAddressBookChanged(AddressBookChangedFn fn) = 0;
+
+ //! Register handler for transaction changed messages.
+ using TransactionChangedFn = std::function<void(const uint256& txid, ChangeType status)>;
+ virtual std::unique_ptr<Handler> handleTransactionChanged(TransactionChangedFn fn) = 0;
+
+ //! Register handler for watchonly changed messages.
+ using WatchOnlyChangedFn = std::function<void(bool have_watch_only)>;
+ virtual std::unique_ptr<Handler> handleWatchOnlyChanged(WatchOnlyChangedFn fn) = 0;
+};
+
+//! Tracking object returned by CreateTransaction and passed to CommitTransaction.
+class PendingWalletTx
+{
+public:
+ virtual ~PendingWalletTx() {}
+
+ //! Get transaction data.
+ virtual const CTransaction& get() = 0;
+
+ //! Get virtual transaction size.
+ virtual int64_t getVirtualSize() = 0;
+
+ //! Send pending transaction and commit to wallet.
+ virtual bool commit(WalletValueMap value_map,
+ WalletOrderForm order_form,
+ std::string from_account,
+ std::string& reject_reason) = 0;
+};
+
+//! Information about one wallet address.
+struct WalletAddress
+{
+ CTxDestination dest;
+ isminetype is_mine;
+ std::string name;
+ std::string purpose;
+
+ WalletAddress(CTxDestination dest, isminetype is_mine, std::string name, std::string purpose)
+ : dest(std::move(dest)), is_mine(is_mine), name(std::move(name)), purpose(std::move(purpose))
+ {
+ }
+};
+
+//! Collection of wallet balances.
+struct WalletBalances
+{
+ CAmount balance = 0;
+ CAmount unconfirmed_balance = 0;
+ CAmount immature_balance = 0;
+ bool have_watch_only = false;
+ CAmount watch_only_balance = 0;
+ CAmount unconfirmed_watch_only_balance = 0;
+ CAmount immature_watch_only_balance = 0;
+
+ bool balanceChanged(const WalletBalances& prev) const
+ {
+ return balance != prev.balance || unconfirmed_balance != prev.unconfirmed_balance ||
+ immature_balance != prev.immature_balance || watch_only_balance != prev.watch_only_balance ||
+ unconfirmed_watch_only_balance != prev.unconfirmed_watch_only_balance ||
+ immature_watch_only_balance != prev.immature_watch_only_balance;
+ }
+};
+
+// Wallet transaction information.
+struct WalletTx
+{
+ CTransactionRef tx;
+ std::vector<isminetype> txin_is_mine;
+ std::vector<isminetype> txout_is_mine;
+ std::vector<CTxDestination> txout_address;
+ std::vector<isminetype> txout_address_is_mine;
+ CAmount credit;
+ CAmount debit;
+ CAmount change;
+ int64_t time;
+ std::map<std::string, std::string> value_map;
+ bool is_coinbase;
+};
+
+//! Updated transaction status.
+struct WalletTxStatus
+{
+ int block_height;
+ int blocks_to_maturity;
+ int depth_in_main_chain;
+ int request_count;
+ unsigned int time_received;
+ uint32_t lock_time;
+ bool is_final;
+ bool is_trusted;
+ bool is_abandoned;
+ bool is_coinbase;
+ bool is_in_main_chain;
+};
+
+//! Wallet transaction output.
+struct WalletTxOut
+{
+ CTxOut txout;
+ int64_t time;
+ int depth_in_main_chain = -1;
+ bool is_spent = false;
+};
+
+//! Return implementation of Wallet interface. This function will be undefined
+//! in builds where ENABLE_WALLET is false.
+std::unique_ptr<Wallet> MakeWallet(CWallet& wallet);
+
+} // namespace interface
+
+#endif // BITCOIN_INTERFACE_WALLET_H
diff --git a/src/key_io.h b/src/key_io.h
index 6fc9a8059a..d75b5b31c8 100644
--- a/src/key_io.h
+++ b/src/key_io.h
@@ -3,8 +3,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#ifndef BITCOIN_KEYIO_H
-#define BITCOIN_KEYIO_H
+#ifndef BITCOIN_KEY_IO_H
+#define BITCOIN_KEY_IO_H
#include <chainparams.h>
#include <key.h>
@@ -26,4 +26,4 @@ CTxDestination DecodeDestination(const std::string& str);
bool IsValidDestinationString(const std::string& str);
bool IsValidDestinationString(const std::string& str, const CChainParams& params);
-#endif // BITCOIN_KEYIO_H
+#endif // BITCOIN_KEY_IO_H
diff --git a/src/keystore.cpp b/src/keystore.cpp
index fab1b81c9a..e69d518890 100644
--- a/src/keystore.cpp
+++ b/src/keystore.cpp
@@ -7,10 +7,6 @@
#include <util.h>
-bool CKeyStore::AddKey(const CKey &key) {
- return AddKeyPubKey(key, key.GetPubKey());
-}
-
void CBasicKeyStore::ImplicitlyLearnRelatedKeyScripts(const CPubKey& pubkey)
{
AssertLockHeld(cs_KeyStore);
@@ -131,7 +127,7 @@ static bool ExtractPubKey(const CScript &dest, CPubKey& pubKeyOut)
CScript::const_iterator pc = dest.begin();
opcodetype opcode;
std::vector<unsigned char> vch;
- if (!dest.GetOp(pc, opcode, vch) || vch.size() < 33 || vch.size() > 65)
+ if (!dest.GetOp(pc, opcode, vch) || !CPubKey::ValidSize(vch))
return false;
pubKeyOut = CPubKey(vch);
if (!pubKeyOut.IsFullyValid())
diff --git a/src/keystore.h b/src/keystore.h
index ffd3238fd6..fa912cb195 100644
--- a/src/keystore.h
+++ b/src/keystore.h
@@ -9,35 +9,27 @@
#include <key.h>
#include <pubkey.h>
#include <script/script.h>
+#include <script/sign.h>
#include <script/standard.h>
#include <sync.h>
#include <boost/signals2/signal.hpp>
/** A virtual base class for key stores */
-class CKeyStore
+class CKeyStore : public SigningProvider
{
-protected:
- mutable CCriticalSection cs_KeyStore;
-
public:
- virtual ~CKeyStore() {}
-
//! Add a key to the store.
virtual bool AddKeyPubKey(const CKey &key, const CPubKey &pubkey) =0;
- virtual bool AddKey(const CKey &key);
//! Check whether a key corresponding to a given address is present in the store.
virtual bool HaveKey(const CKeyID &address) const =0;
- virtual bool GetKey(const CKeyID &address, CKey& keyOut) const =0;
virtual std::set<CKeyID> GetKeys() const =0;
- virtual bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const =0;
//! Support for BIP 0013 : see https://github.com/bitcoin/bips/blob/master/bip-0013.mediawiki
virtual bool AddCScript(const CScript& redeemScript) =0;
virtual bool HaveCScript(const CScriptID &hash) const =0;
virtual std::set<CScriptID> GetCScripts() const =0;
- virtual bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const =0;
//! Support for Watch-only addresses
virtual bool AddWatchOnly(const CScript &dest) =0;
@@ -55,6 +47,8 @@ typedef std::set<CScript> WatchOnlySet;
class CBasicKeyStore : public CKeyStore
{
protected:
+ mutable CCriticalSection cs_KeyStore;
+
KeyMap mapKeys;
WatchKeyMap mapWatchKeys;
ScriptMap mapScripts;
@@ -64,6 +58,7 @@ protected:
public:
bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override;
+ bool AddKey(const CKey &key) { return AddKeyPubKey(key, key.GetPubKey()); }
bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const override;
bool HaveKey(const CKeyID &address) const override;
std::set<CKeyID> GetKeys() const override;
diff --git a/src/memusage.h b/src/memusage.h
index fea7ecdf9f..0b92e53b48 100644
--- a/src/memusage.h
+++ b/src/memusage.h
@@ -10,6 +10,7 @@
#include <stdlib.h>
#include <map>
+#include <memory>
#include <set>
#include <vector>
#include <unordered_map>
diff --git a/src/miner.cpp b/src/miner.cpp
index 0660df928c..d2be393538 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -27,6 +27,7 @@
#include <validationinterface.h>
#include <algorithm>
+#include <memory>
#include <queue>
#include <utility>
diff --git a/src/net.cpp b/src/net.cpp
index 342dfbaeb9..f45592ab83 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -20,6 +20,7 @@
#include <ui_interface.h>
#include <utilstrencodings.h>
+#include <memory>
#ifdef WIN32
#include <string.h>
#else
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 61e6ae7448..dbdae705de 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -30,6 +30,8 @@
#include <utilmoneystr.h>
#include <utilstrencodings.h>
+#include <memory>
+
#if defined(NDEBUG)
# error "Bitcoin cannot be compiled without assertions."
#endif
diff --git a/src/net_processing.h b/src/net_processing.h
index 11543129cf..195d0d2033 100644
--- a/src/net_processing.h
+++ b/src/net_processing.h
@@ -86,9 +86,9 @@ private:
};
struct CNodeStateStats {
- int nMisbehavior;
- int nSyncHeight;
- int nCommonHeight;
+ int nMisbehavior = 0;
+ int nSyncHeight = -1;
+ int nCommonHeight = -1;
std::vector<int> vHeightInFlight;
};
diff --git a/src/netaddress.h b/src/netaddress.h
index 93bbb66491..ad6b55eb58 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -93,7 +93,7 @@ class CNetAddr
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
- READWRITE(FLATDATA(ip));
+ READWRITE(ip);
}
friend class CSubNet;
@@ -131,8 +131,8 @@ class CSubNet
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(network);
- READWRITE(FLATDATA(netmask));
- READWRITE(FLATDATA(valid));
+ READWRITE(netmask);
+ READWRITE(valid);
}
};
@@ -166,7 +166,7 @@ class CService : public CNetAddr
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
- READWRITE(FLATDATA(ip));
+ READWRITE(ip);
unsigned short portN = htons(port);
READWRITE(FLATDATA(portN));
if (ser_action.ForRead())
diff --git a/src/policy/fees.h b/src/policy/fees.h
index 5f69e989c1..8c34bee237 100644
--- a/src/policy/fees.h
+++ b/src/policy/fees.h
@@ -2,8 +2,8 @@
// Copyright (c) 2009-2017 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#ifndef BITCOIN_POLICYESTIMATOR_H
-#define BITCOIN_POLICYESTIMATOR_H
+#ifndef BITCOIN_POLICY_FEES_H
+#define BITCOIN_POLICY_FEES_H
#include <amount.h>
#include <policy/feerate.h>
@@ -12,6 +12,7 @@
#include <sync.h>
#include <map>
+#include <memory>
#include <string>
#include <vector>
@@ -68,7 +69,7 @@ class TxConfirmStats;
/* Identifier for each of the 3 different TxConfirmStats which will track
* history over different time horizons. */
-enum FeeEstimateHorizon {
+enum class FeeEstimateHorizon {
SHORT_HALFLIFE = 0,
MED_HALFLIFE = 1,
LONG_HALFLIFE = 2
@@ -294,4 +295,4 @@ private:
FastRandomContext insecure_rand;
};
-#endif /*BITCOIN_POLICYESTIMATOR_H */
+#endif // BITCOIN_POLICY_FEES_H
diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp
index db1ad4f26d..c3f65fb2ab 100644
--- a/src/policy/policy.cpp
+++ b/src/policy/policy.cpp
@@ -179,7 +179,7 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
{
std::vector<std::vector<unsigned char> > stack;
// convert the scriptSig into a stack, so we can inspect the redeemScript
- if (!EvalScript(stack, tx.vin[i].scriptSig, SCRIPT_VERIFY_NONE, BaseSignatureChecker(), SIGVERSION_BASE))
+ if (!EvalScript(stack, tx.vin[i].scriptSig, SCRIPT_VERIFY_NONE, BaseSignatureChecker(), SigVersion::BASE))
return false;
if (stack.empty())
return false;
@@ -215,7 +215,7 @@ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
// If the scriptPubKey is P2SH, we try to extract the redeemScript casually by converting the scriptSig
// into a stack. We do not check IsPushOnly nor compare the hash as these will be done later anyway.
// If the check fails at this stage, we know that this txid must be a bad one.
- if (!EvalScript(stack, tx.vin[i].scriptSig, SCRIPT_VERIFY_NONE, BaseSignatureChecker(), SIGVERSION_BASE))
+ if (!EvalScript(stack, tx.vin[i].scriptSig, SCRIPT_VERIFY_NONE, BaseSignatureChecker(), SigVersion::BASE))
return false;
if (stack.empty())
return false;
diff --git a/src/policy/rbf.cpp b/src/policy/rbf.cpp
index c5a1741608..81b2a7fadc 100644
--- a/src/policy/rbf.cpp
+++ b/src/policy/rbf.cpp
@@ -22,13 +22,13 @@ RBFTransactionState IsRBFOptIn(const CTransaction &tx, CTxMemPool &pool)
// First check the transaction itself.
if (SignalsOptInRBF(tx)) {
- return RBF_TRANSACTIONSTATE_REPLACEABLE_BIP125;
+ return RBFTransactionState::REPLACEABLE_BIP125;
}
// If this transaction is not in our mempool, then we can't be sure
// we will know about all its inputs.
if (!pool.exists(tx.GetHash())) {
- return RBF_TRANSACTIONSTATE_UNKNOWN;
+ return RBFTransactionState::UNKNOWN;
}
// If all the inputs have nSequence >= maxint-1, it still might be
@@ -40,8 +40,8 @@ RBFTransactionState IsRBFOptIn(const CTransaction &tx, CTxMemPool &pool)
for (CTxMemPool::txiter it : setAncestors) {
if (SignalsOptInRBF(it->GetTx())) {
- return RBF_TRANSACTIONSTATE_REPLACEABLE_BIP125;
+ return RBFTransactionState::REPLACEABLE_BIP125;
}
}
- return RBF_TRANSACTIONSTATE_FINAL;
+ return RBFTransactionState::FINAL;
}
diff --git a/src/policy/rbf.h b/src/policy/rbf.h
index 72f51b0f03..b10532addf 100644
--- a/src/policy/rbf.h
+++ b/src/policy/rbf.h
@@ -9,10 +9,10 @@
static const uint32_t MAX_BIP125_RBF_SEQUENCE = 0xfffffffd;
-enum RBFTransactionState {
- RBF_TRANSACTIONSTATE_UNKNOWN,
- RBF_TRANSACTIONSTATE_REPLACEABLE_BIP125,
- RBF_TRANSACTIONSTATE_FINAL
+enum class RBFTransactionState {
+ UNKNOWN,
+ REPLACEABLE_BIP125,
+ FINAL
};
// Check whether the sequence numbers on this transaction are signaling
diff --git a/src/protocol.h b/src/protocol.h
index e518d11944..a07c5ea862 100644
--- a/src/protocol.h
+++ b/src/protocol.h
@@ -48,10 +48,10 @@ public:
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action)
{
- READWRITE(FLATDATA(pchMessageStart));
- READWRITE(FLATDATA(pchCommand));
+ READWRITE(pchMessageStart);
+ READWRITE(pchCommand);
READWRITE(nMessageSize);
- READWRITE(FLATDATA(pchChecksum));
+ READWRITE(pchChecksum);
}
char pchMessageStart[MESSAGE_START_SIZE];
diff --git a/src/pubkey.h b/src/pubkey.h
index 59bf56395c..9c6c6b085e 100644
--- a/src/pubkey.h
+++ b/src/pubkey.h
@@ -70,6 +70,11 @@ private:
}
public:
+
+ bool static ValidSize(const std::vector<unsigned char> &vch) {
+ return vch.size() > 0 && GetLen(vch[0]) == vch.size();
+ }
+
//! Construct an invalid public key.
CPubKey()
{
diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp
index 801334483a..a9408895d9 100644
--- a/src/qt/addresstablemodel.cpp
+++ b/src/qt/addresstablemodel.cpp
@@ -7,6 +7,7 @@
#include <qt/guiutil.h>
#include <qt/walletmodel.h>
+#include <interface/node.h>
#include <key_io.h>
#include <wallet/wallet.h>
@@ -67,28 +68,23 @@ static AddressTableEntry::Type translateTransactionType(const QString &strPurpos
class AddressTablePriv
{
public:
- CWallet *wallet;
QList<AddressTableEntry> cachedAddressTable;
AddressTableModel *parent;
- AddressTablePriv(CWallet *_wallet, AddressTableModel *_parent):
- wallet(_wallet), parent(_parent) {}
+ AddressTablePriv(AddressTableModel *_parent):
+ parent(_parent) {}
- void refreshAddressTable()
+ void refreshAddressTable(interface::Wallet& wallet)
{
cachedAddressTable.clear();
{
- LOCK(wallet->cs_wallet);
- for (const std::pair<CTxDestination, CAddressBookData>& item : wallet->mapAddressBook)
+ for (const auto& address : wallet.getAddresses())
{
- const CTxDestination& address = item.first;
- bool fMine = IsMine(*wallet, address);
AddressTableEntry::Type addressType = translateTransactionType(
- QString::fromStdString(item.second.purpose), fMine);
- const std::string& strName = item.second.name;
+ QString::fromStdString(address.purpose), address.is_mine);
cachedAddressTable.append(AddressTableEntry(addressType,
- QString::fromStdString(strName),
- QString::fromStdString(EncodeDestination(address))));
+ QString::fromStdString(address.name),
+ QString::fromStdString(EncodeDestination(address.dest))));
}
}
// qLowerBound() and qUpperBound() require our cachedAddressTable list to be sorted in asc order
@@ -162,12 +158,12 @@ public:
}
};
-AddressTableModel::AddressTableModel(CWallet *_wallet, WalletModel *parent) :
- QAbstractTableModel(parent),walletModel(parent),wallet(_wallet),priv(0)
+AddressTableModel::AddressTableModel(WalletModel *parent) :
+ QAbstractTableModel(parent),walletModel(parent),priv(0)
{
columns << tr("Label") << tr("Address");
- priv = new AddressTablePriv(wallet, this);
- priv->refreshAddressTable();
+ priv = new AddressTablePriv(this);
+ priv->refreshAddressTable(parent->wallet());
}
AddressTableModel::~AddressTableModel()
@@ -244,7 +240,6 @@ bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value,
if(role == Qt::EditRole)
{
- LOCK(wallet->cs_wallet); /* For SetAddressBook / DelAddressBook */
CTxDestination curAddress = DecodeDestination(rec->address.toStdString());
if(index.column() == Label)
{
@@ -254,7 +249,7 @@ bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value,
editStatus = NO_CHANGES;
return false;
}
- wallet->SetAddressBook(curAddress, value.toString().toStdString(), strPurpose);
+ walletModel->wallet().setAddressBook(curAddress, value.toString().toStdString(), strPurpose);
} else if(index.column() == Address) {
CTxDestination newAddress = DecodeDestination(value.toString().toStdString());
// Refuse to set invalid address, set error status and return false
@@ -271,7 +266,7 @@ bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value,
}
// Check for duplicate addresses to prevent accidental deletion of addresses, if you try
// to paste an existing address over another address (with a different label)
- else if(wallet->mapAddressBook.count(newAddress))
+ if (walletModel->wallet().getAddress(newAddress))
{
editStatus = DUPLICATE_ADDRESS;
return false;
@@ -280,9 +275,9 @@ bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value,
else if(rec->type == AddressTableEntry::Sending)
{
// Remove old entry
- wallet->DelAddressBook(curAddress);
+ walletModel->wallet().delAddressBook(curAddress);
// Add new entry with new address
- wallet->SetAddressBook(newAddress, rec->label.toStdString(), strPurpose);
+ walletModel->wallet().setAddressBook(newAddress, value.toString().toStdString(), strPurpose);
}
}
return true;
@@ -356,8 +351,7 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con
}
// Check for duplicate addresses
{
- LOCK(wallet->cs_wallet);
- if(wallet->mapAddressBook.count(DecodeDestination(strAddress)))
+ if(walletModel->wallet().getAddress(DecodeDestination(strAddress)))
{
editStatus = DUPLICATE_ADDRESS;
return QString();
@@ -368,7 +362,7 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con
{
// Generate a new address to associate with given label
CPubKey newKey;
- if(!wallet->GetKeyFromPool(newKey))
+ if(!walletModel->wallet().getKeyFromPool(false /* internal */, newKey))
{
WalletModel::UnlockContext ctx(walletModel->requestUnlock());
if(!ctx.isValid())
@@ -377,13 +371,13 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con
editStatus = WALLET_UNLOCK_FAILURE;
return QString();
}
- if(!wallet->GetKeyFromPool(newKey))
+ if(!walletModel->wallet().getKeyFromPool(false /* internal */, newKey))
{
editStatus = KEY_GENERATION_FAILURE;
return QString();
}
}
- wallet->LearnRelatedScripts(newKey, address_type);
+ walletModel->wallet().learnRelatedScripts(newKey, address_type);
strAddress = EncodeDestination(GetDestinationForKey(newKey, address_type));
}
else
@@ -392,7 +386,7 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con
}
// Add entry
- wallet->SetAddressBook(DecodeDestination(strAddress), strLabel,
+ walletModel->wallet().setAddressBook(DecodeDestination(strAddress), strLabel,
(type == Send ? "send" : "receive"));
return QString::fromStdString(strAddress);
}
@@ -407,7 +401,7 @@ bool AddressTableModel::removeRows(int row, int count, const QModelIndex &parent
// Also refuse to remove receiving addresses.
return false;
}
- wallet->DelAddressBook(DecodeDestination(rec->address.toStdString()));
+ walletModel->wallet().delAddressBook(DecodeDestination(rec->address.toStdString()));
return true;
}
@@ -416,12 +410,11 @@ bool AddressTableModel::removeRows(int row, int count, const QModelIndex &parent
QString AddressTableModel::labelForAddress(const QString &address) const
{
{
- LOCK(wallet->cs_wallet);
CTxDestination destination = DecodeDestination(address.toStdString());
- std::map<CTxDestination, CAddressBookData>::iterator mi = wallet->mapAddressBook.find(destination);
- if (mi != wallet->mapAddressBook.end())
+ std::string name;
+ if (walletModel->wallet().getAddress(destination, &name))
{
- return QString::fromStdString(mi->second.name);
+ return QString::fromStdString(name);
}
}
return QString();
@@ -441,7 +434,7 @@ int AddressTableModel::lookupAddress(const QString &address) const
}
}
-OutputType AddressTableModel::GetDefaultAddressType() const { return wallet->m_default_address_type; };
+OutputType AddressTableModel::GetDefaultAddressType() const { return walletModel->wallet().getDefaultAddressType(); };
void AddressTableModel::emitDataChanged(int idx)
{
diff --git a/src/qt/addresstablemodel.h b/src/qt/addresstablemodel.h
index ed7a4e6f43..954f0f593e 100644
--- a/src/qt/addresstablemodel.h
+++ b/src/qt/addresstablemodel.h
@@ -13,7 +13,9 @@ enum class OutputType;
class AddressTablePriv;
class WalletModel;
-class CWallet;
+namespace interface {
+class Wallet;
+}
/**
Qt model of the address book in the core. This allows views to access and modify the address book.
@@ -23,7 +25,7 @@ class AddressTableModel : public QAbstractTableModel
Q_OBJECT
public:
- explicit AddressTableModel(CWallet *wallet, WalletModel *parent = 0);
+ explicit AddressTableModel(WalletModel *parent = 0);
~AddressTableModel();
enum ColumnIndex {
@@ -80,7 +82,6 @@ public:
private:
WalletModel *walletModel;
- CWallet *wallet;
AddressTablePriv *priv;
QStringList columns;
EditStatus editStatus;
diff --git a/src/qt/bantablemodel.cpp b/src/qt/bantablemodel.cpp
index c89c90e118..cbd67d70ab 100644
--- a/src/qt/bantablemodel.cpp
+++ b/src/qt/bantablemodel.cpp
@@ -8,6 +8,7 @@
#include <qt/guiconstants.h>
#include <qt/guiutil.h>
+#include <interface/node.h>
#include <sync.h>
#include <utiltime.h>
@@ -45,11 +46,10 @@ public:
Qt::SortOrder sortOrder;
/** Pull a full list of banned nodes from CNode into our cache */
- void refreshBanlist()
+ void refreshBanlist(interface::Node& node)
{
banmap_t banMap;
- if(g_connman)
- g_connman->GetBanned(banMap);
+ node.getBanned(banMap);
cachedBanlist.clear();
#if QT_VERSION >= 0x040700
@@ -82,8 +82,9 @@ public:
}
};
-BanTableModel::BanTableModel(ClientModel *parent) :
+BanTableModel::BanTableModel(interface::Node& node, ClientModel *parent) :
QAbstractTableModel(parent),
+ m_node(node),
clientModel(parent)
{
columns << tr("IP/Netmask") << tr("Banned Until");
@@ -168,7 +169,7 @@ QModelIndex BanTableModel::index(int row, int column, const QModelIndex &parent)
void BanTableModel::refresh()
{
Q_EMIT layoutAboutToBeChanged();
- priv->refreshBanlist();
+ priv->refreshBanlist(m_node);
Q_EMIT layoutChanged();
}
diff --git a/src/qt/bantablemodel.h b/src/qt/bantablemodel.h
index a54f8793d0..4c171e6fbe 100644
--- a/src/qt/bantablemodel.h
+++ b/src/qt/bantablemodel.h
@@ -7,12 +7,18 @@
#include <net.h>
+#include <memory>
+
#include <QAbstractTableModel>
#include <QStringList>
class ClientModel;
class BanTablePriv;
+namespace interface {
+ class Node;
+}
+
struct CCombinedBan {
CSubNet subnet;
CBanEntry banEntry;
@@ -39,7 +45,7 @@ class BanTableModel : public QAbstractTableModel
Q_OBJECT
public:
- explicit BanTableModel(ClientModel *parent = 0);
+ explicit BanTableModel(interface::Node& node, ClientModel *parent = 0);
~BanTableModel();
void startAutoRefresh();
void stopAutoRefresh();
@@ -65,6 +71,7 @@ public Q_SLOTS:
void refresh();
private:
+ interface::Node& m_node;
ClientModel *clientModel;
QStringList columns;
std::unique_ptr<BanTablePriv> priv;
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index df1be35c8d..d164ca435c 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -27,16 +27,17 @@
#endif
#include <init.h>
+#include <interface/handler.h>
+#include <interface/node.h>
#include <rpc/server.h>
#include <ui_interface.h>
#include <uint256.h>
#include <util.h>
#include <warnings.h>
-#ifdef ENABLE_WALLET
-#include <wallet/wallet.h>
-#endif
+#include <walletinitinterface.h>
+#include <memory>
#include <stdint.h>
#include <boost/thread.hpp>
@@ -178,11 +179,7 @@ class BitcoinCore: public QObject
{
Q_OBJECT
public:
- explicit BitcoinCore();
- /** Basic initialization, before starting initialization/shutdown thread.
- * Return true on success.
- */
- static bool baseInitialize();
+ explicit BitcoinCore(interface::Node& node);
public Q_SLOTS:
void initialize();
@@ -194,9 +191,10 @@ Q_SIGNALS:
void runawayException(const QString &message);
private:
-
/// Pass fatal exception message to UI thread
void handleRunawayException(const std::exception *e);
+
+ interface::Node& m_node;
};
/** Main Bitcoin application object */
@@ -204,7 +202,7 @@ class BitcoinApplication: public QApplication
{
Q_OBJECT
public:
- explicit BitcoinApplication(int &argc, char **argv);
+ explicit BitcoinApplication(interface::Node& node, int &argc, char **argv);
~BitcoinApplication();
#ifdef ENABLE_WALLET
@@ -245,6 +243,7 @@ Q_SIGNALS:
private:
QThread *coreThread;
+ interface::Node& m_node;
OptionsModel *optionsModel;
ClientModel *clientModel;
BitcoinGUI *window;
@@ -262,36 +261,15 @@ private:
#include <qt/bitcoin.moc>
-BitcoinCore::BitcoinCore():
- QObject()
+BitcoinCore::BitcoinCore(interface::Node& node) :
+ QObject(), m_node(node)
{
}
void BitcoinCore::handleRunawayException(const std::exception *e)
{
PrintExceptionContinue(e, "Runaway exception");
- Q_EMIT runawayException(QString::fromStdString(GetWarnings("gui")));
-}
-
-bool BitcoinCore::baseInitialize()
-{
- if (!AppInitBasicSetup())
- {
- return false;
- }
- if (!AppInitParameterInteraction())
- {
- return false;
- }
- if (!AppInitSanityChecks())
- {
- return false;
- }
- if (!AppInitLockDataDirectory())
- {
- return false;
- }
- return true;
+ Q_EMIT runawayException(QString::fromStdString(m_node.getWarnings("gui")));
}
void BitcoinCore::initialize()
@@ -299,7 +277,7 @@ void BitcoinCore::initialize()
try
{
qDebug() << __func__ << ": Running initialization in thread";
- bool rv = AppInitMain();
+ bool rv = m_node.appInitMain();
Q_EMIT initializeResult(rv);
} catch (const std::exception& e) {
handleRunawayException(&e);
@@ -313,8 +291,7 @@ void BitcoinCore::shutdown()
try
{
qDebug() << __func__ << ": Running Shutdown in thread";
- Interrupt();
- Shutdown();
+ m_node.appShutdown();
qDebug() << __func__ << ": Shutdown finished";
Q_EMIT shutdownResult();
} catch (const std::exception& e) {
@@ -324,9 +301,10 @@ void BitcoinCore::shutdown()
}
}
-BitcoinApplication::BitcoinApplication(int &argc, char **argv):
+BitcoinApplication::BitcoinApplication(interface::Node& node, int &argc, char **argv):
QApplication(argc, argv),
coreThread(0),
+ m_node(node),
optionsModel(0),
clientModel(0),
window(0),
@@ -381,12 +359,12 @@ void BitcoinApplication::createPaymentServer()
void BitcoinApplication::createOptionsModel(bool resetSettings)
{
- optionsModel = new OptionsModel(nullptr, resetSettings);
+ optionsModel = new OptionsModel(m_node, nullptr, resetSettings);
}
void BitcoinApplication::createWindow(const NetworkStyle *networkStyle)
{
- window = new BitcoinGUI(platformStyle, networkStyle, 0);
+ window = new BitcoinGUI(m_node, platformStyle, networkStyle, 0);
pollShutdownTimer = new QTimer(window);
connect(pollShutdownTimer, SIGNAL(timeout()), window, SLOT(detectShutdown()));
@@ -394,7 +372,7 @@ void BitcoinApplication::createWindow(const NetworkStyle *networkStyle)
void BitcoinApplication::createSplashScreen(const NetworkStyle *networkStyle)
{
- SplashScreen *splash = new SplashScreen(0, networkStyle);
+ SplashScreen *splash = new SplashScreen(m_node, 0, networkStyle);
// We don't hold a direct pointer to the splash screen after creation, but the splash
// screen will take care of deleting itself when slotFinish happens.
splash->show();
@@ -407,7 +385,7 @@ void BitcoinApplication::startThread()
if(coreThread)
return;
coreThread = new QThread(this);
- BitcoinCore *executor = new BitcoinCore();
+ BitcoinCore *executor = new BitcoinCore(m_node);
executor->moveToThread(coreThread);
/* communication to and from thread */
@@ -425,8 +403,8 @@ void BitcoinApplication::startThread()
void BitcoinApplication::parameterSetup()
{
- InitLogging();
- InitParameterInteraction();
+ m_node.initLogging();
+ m_node.initParameterInteraction();
}
void BitcoinApplication::requestInitialize()
@@ -459,7 +437,7 @@ void BitcoinApplication::requestShutdown()
delete clientModel;
clientModel = 0;
- StartShutdown();
+ m_node.startShutdown();
// Request shutdown from core thread
Q_EMIT requestedShutdown();
@@ -479,13 +457,14 @@ void BitcoinApplication::initializeResult(bool success)
paymentServer->setOptionsModel(optionsModel);
#endif
- clientModel = new ClientModel(optionsModel);
+ clientModel = new ClientModel(m_node, optionsModel);
window->setClientModel(clientModel);
#ifdef ENABLE_WALLET
bool fFirstWallet = true;
- for (CWalletRef pwallet : vpwallets) {
- WalletModel * const walletModel = new WalletModel(platformStyle, pwallet, optionsModel);
+ auto wallets = m_node.getWallets();
+ for (auto& wallet : wallets) {
+ WalletModel * const walletModel = new WalletModel(std::move(wallet), m_node, platformStyle, optionsModel);
window->addWallet(walletModel);
if (fFirstWallet) {
@@ -493,8 +472,8 @@ void BitcoinApplication::initializeResult(bool success)
fFirstWallet = false;
}
- connect(walletModel, SIGNAL(coinsSent(CWallet*,SendCoinsRecipient,QByteArray)),
- paymentServer, SLOT(fetchPaymentACK(CWallet*,const SendCoinsRecipient&,QByteArray)));
+ connect(walletModel, SIGNAL(coinsSent(WalletModel*,SendCoinsRecipient,QByteArray)),
+ paymentServer, SLOT(fetchPaymentACK(WalletModel*,const SendCoinsRecipient&,QByteArray)));
m_wallet_models.push_back(walletModel);
}
@@ -553,9 +532,11 @@ int main(int argc, char *argv[])
{
SetupEnvironment();
+ std::unique_ptr<interface::Node> node = interface::MakeNode();
+
/// 1. Parse command-line options. These take precedence over anything else.
// Command-line options take precedence:
- gArgs.ParseParameters(argc, argv);
+ node->parseParameters(argc, argv);
// Do not refer to data directory yet, this can be overridden by Intro::pickDataDirectory
@@ -569,7 +550,7 @@ int main(int argc, char *argv[])
Q_INIT_RESOURCE(bitcoin);
Q_INIT_RESOURCE(bitcoin_locale);
- BitcoinApplication app(argc, argv);
+ BitcoinApplication app(*node, argc, argv);
#if QT_VERSION > 0x050100
// Generate high-dpi pixmaps
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
@@ -611,19 +592,18 @@ int main(int argc, char *argv[])
// Show help message immediately after parsing command-line options (for "-lang") and setting locale,
// but before showing splash screen.
- if (gArgs.IsArgSet("-?") || gArgs.IsArgSet("-h") || gArgs.IsArgSet("-help") || gArgs.IsArgSet("-version"))
- {
- HelpMessageDialog help(nullptr, gArgs.IsArgSet("-version"));
+ if (HelpRequested(gArgs) || gArgs.IsArgSet("-version")) {
+ HelpMessageDialog help(*node, nullptr, gArgs.IsArgSet("-version"));
help.showOrPrint();
return EXIT_SUCCESS;
}
/// 5. Now that settings and translations are available, ask user for data directory
// User language is set up: pick a data directory
- if (!Intro::pickDataDirectory())
+ if (!Intro::pickDataDirectory(*node))
return EXIT_SUCCESS;
- /// 6. Determine availability of data directory and parse bitcoin.conf
+ /// 6. Determine availability of data and blocks directory and parse bitcoin.conf
/// - Do not call GetDataDir(true) before this step finishes
if (!fs::is_directory(GetDataDir(false)))
{
@@ -632,7 +612,7 @@ int main(int argc, char *argv[])
return EXIT_FAILURE;
}
try {
- gArgs.ReadConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME));
+ node->readConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME));
} catch (const std::exception& e) {
QMessageBox::critical(0, QObject::tr(PACKAGE_NAME),
QObject::tr("Error: Cannot parse configuration file: %1. Only use key=value syntax.").arg(e.what()));
@@ -647,14 +627,14 @@ int main(int argc, char *argv[])
// Check for -testnet or -regtest parameter (Params() calls are only valid after this clause)
try {
- SelectParams(ChainNameFromCommandLine());
+ node->selectParams(ChainNameFromCommandLine());
} catch(std::exception &e) {
QMessageBox::critical(0, QObject::tr(PACKAGE_NAME), QObject::tr("Error: %1").arg(e.what()));
return EXIT_FAILURE;
}
#ifdef ENABLE_WALLET
// Parse URIs on command line -- this can affect Params()
- PaymentServer::ipcParseCommandLine(argc, argv);
+ PaymentServer::ipcParseCommandLine(*node, argc, argv);
#endif
QScopedPointer<const NetworkStyle> networkStyle(NetworkStyle::instantiate(QString::fromStdString(Params().NetworkIDString())));
@@ -696,10 +676,10 @@ int main(int argc, char *argv[])
// Allow parameter interaction before we create the options model
app.parameterSetup();
// Load GUI settings from QSettings
- app.createOptionsModel(gArgs.IsArgSet("-resetguisettings"));
+ app.createOptionsModel(gArgs.GetBoolArg("-resetguisettings", false));
// Subscribe to global signals from core
- uiInterface.InitMessage.connect(InitMessage);
+ std::unique_ptr<interface::Handler> handler = node->handleInitMessage(InitMessage);
if (gArgs.GetBoolArg("-splash", DEFAULT_SPLASHSCREEN) && !gArgs.GetBoolArg("-min", false))
app.createSplashScreen(networkStyle.data());
@@ -711,7 +691,7 @@ int main(int argc, char *argv[])
// Perform base initialization before spinning up initialization/shutdown thread
// This is acceptable because this function only contains steps that are quick to execute,
// so the GUI thread won't be held up.
- if (BitcoinCore::baseInitialize()) {
+ if (node->baseInitialize()) {
app.requestInitialize();
#if defined(Q_OS_WIN) && QT_VERSION >= 0x050000
WinShutdownMonitor::registerShutdownBlockReason(QObject::tr("%1 didn't yet exit safely...").arg(QObject::tr(PACKAGE_NAME)), (HWND)app.getMainWinId());
@@ -726,10 +706,10 @@ int main(int argc, char *argv[])
}
} catch (const std::exception& e) {
PrintExceptionContinue(&e, "Runaway exception");
- app.handleRunawayException(QString::fromStdString(GetWarnings("gui")));
+ app.handleRunawayException(QString::fromStdString(node->getWarnings("gui")));
} catch (...) {
PrintExceptionContinue(nullptr, "Runaway exception");
- app.handleRunawayException(QString::fromStdString(GetWarnings("gui")));
+ app.handleRunawayException(QString::fromStdString(node->getWarnings("gui")));
}
return rv;
}
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 4707fcceb9..63a2a200b2 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -30,6 +30,8 @@
#include <chainparams.h>
#include <init.h>
+#include <interface/handler.h>
+#include <interface/node.h>
#include <ui_interface.h>
#include <util.h>
@@ -72,9 +74,10 @@ const std::string BitcoinGUI::DEFAULT_UIPLATFORM =
#endif
;
-BitcoinGUI::BitcoinGUI(const PlatformStyle *_platformStyle, const NetworkStyle *networkStyle, QWidget *parent) :
+BitcoinGUI::BitcoinGUI(interface::Node& node, const PlatformStyle *_platformStyle, const NetworkStyle *networkStyle, QWidget *parent) :
QMainWindow(parent),
enableWallet(false),
+ m_node(node),
clientModel(0),
walletFrame(0),
unitDisplayControl(0),
@@ -149,8 +152,8 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *_platformStyle, const NetworkStyle *
setUnifiedTitleAndToolBarOnMac(true);
#endif
- rpcConsole = new RPCConsole(_platformStyle, 0);
- helpMessageDialog = new HelpMessageDialog(this, false);
+ rpcConsole = new RPCConsole(node, _platformStyle, 0);
+ helpMessageDialog = new HelpMessageDialog(node, this, false);
#ifdef ENABLE_WALLET
if(enableWallet)
{
@@ -490,7 +493,7 @@ void BitcoinGUI::setClientModel(ClientModel *_clientModel)
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(nullptr), false);
+ setNumBlocks(m_node.getNumBlocks(), QDateTime::fromTime_t(m_node.getLastBlockTime()), m_node.getVerificationProgress(), false);
connect(_clientModel, SIGNAL(numBlocksChanged(int,QDateTime,double,bool)), this, SLOT(setNumBlocks(int,QDateTime,double,bool)));
// Receive and report messages from client model
@@ -665,7 +668,7 @@ void BitcoinGUI::aboutClicked()
if(!clientModel)
return;
- HelpMessageDialog dlg(this, true);
+ HelpMessageDialog dlg(m_node, this, true);
dlg.exec();
}
@@ -748,7 +751,7 @@ void BitcoinGUI::updateNetworkState()
QString tooltip;
- if (clientModel->getNetworkActive()) {
+ if (m_node.getNetworkActive()) {
tooltip = tr("%n active connection(s) to Bitcoin network", "", count) + QString(".<br>") + tr("Click to disable network activity.");
} else {
tooltip = tr("Network activity disabled.") + QString("<br>") + tr("Click to enable network activity again.");
@@ -799,7 +802,7 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVer
// Acquire current block source
enum BlockSource blockSource = clientModel->getBlockSource();
switch (blockSource) {
- case BLOCK_SOURCE_NETWORK:
+ case BlockSource::NETWORK:
if (header) {
updateHeadersSyncProgressLabel();
return;
@@ -807,17 +810,17 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVer
progressBarLabel->setText(tr("Synchronizing with network..."));
updateHeadersSyncProgressLabel();
break;
- case BLOCK_SOURCE_DISK:
+ case BlockSource::DISK:
if (header) {
progressBarLabel->setText(tr("Indexing blocks on disk..."));
} else {
progressBarLabel->setText(tr("Processing blocks on disk..."));
}
break;
- case BLOCK_SOURCE_REINDEX:
+ case BlockSource::REINDEX:
progressBarLabel->setText(tr("Reindexing blocks on disk..."));
break;
- case BLOCK_SOURCE_NONE:
+ case BlockSource::NONE:
if (header) {
return;
}
@@ -1007,7 +1010,7 @@ void BitcoinGUI::incomingTransaction(const QString& date, int unit, const CAmoun
// On new transaction, make an info balloon
QString msg = tr("Date: %1\n").arg(date) +
tr("Amount: %1\n").arg(BitcoinUnits::formatWithUnit(unit, amount, true));
- if (WalletModel::isMultiwallet() && !walletName.isEmpty()) {
+ if (m_node.getWallets().size() > 1 && !walletName.isEmpty()) {
msg += tr("Wallet: %1\n").arg(walletName);
}
msg += tr("Type: %1\n").arg(type);
@@ -1113,7 +1116,7 @@ void BitcoinGUI::updateWalletStatus()
}
WalletModel * const walletModel = walletView->getWalletModel();
setEncryptionStatus(walletModel->getEncryptionStatus());
- setHDStatus(walletModel->hdEnabled());
+ setHDStatus(walletModel->wallet().hdEnabled());
}
#endif // ENABLE_WALLET
@@ -1149,7 +1152,7 @@ void BitcoinGUI::toggleHidden()
void BitcoinGUI::detectShutdown()
{
- if (ShutdownRequested())
+ if (m_node.shutdownRequested())
{
if(rpcConsole)
rpcConsole->hide();
@@ -1214,22 +1217,20 @@ static bool ThreadSafeMessageBox(BitcoinGUI *gui, const std::string& message, co
void BitcoinGUI::subscribeToCoreSignals()
{
// Connect signals to client
- uiInterface.ThreadSafeMessageBox.connect(boost::bind(ThreadSafeMessageBox, this, _1, _2, _3));
- uiInterface.ThreadSafeQuestion.connect(boost::bind(ThreadSafeMessageBox, this, _1, _3, _4));
+ m_handler_message_box = m_node.handleMessageBox(boost::bind(ThreadSafeMessageBox, this, _1, _2, _3));
+ m_handler_question = m_node.handleQuestion(boost::bind(ThreadSafeMessageBox, this, _1, _3, _4));
}
void BitcoinGUI::unsubscribeFromCoreSignals()
{
// Disconnect signals from client
- uiInterface.ThreadSafeMessageBox.disconnect(boost::bind(ThreadSafeMessageBox, this, _1, _2, _3));
- uiInterface.ThreadSafeQuestion.disconnect(boost::bind(ThreadSafeMessageBox, this, _1, _3, _4));
+ m_handler_message_box->disconnect();
+ m_handler_question->disconnect();
}
void BitcoinGUI::toggleNetworkActive()
{
- if (clientModel) {
- clientModel->setNetworkActive(!clientModel->getNetworkActive());
- }
+ m_node.setNetworkActive(!m_node.getNetworkActive());
}
UnitDisplayStatusBarControl::UnitDisplayStatusBarControl(const PlatformStyle *platformStyle) :
diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h
index b9e92f2d5b..3a4b25d804 100644
--- a/src/qt/bitcoingui.h
+++ b/src/qt/bitcoingui.h
@@ -18,6 +18,8 @@
#include <QPoint>
#include <QSystemTrayIcon>
+#include <memory>
+
class ClientModel;
class NetworkStyle;
class Notificator;
@@ -31,6 +33,11 @@ class WalletModel;
class HelpMessageDialog;
class ModalOverlay;
+namespace interface {
+class Handler;
+class Node;
+}
+
QT_BEGIN_NAMESPACE
class QAction;
class QComboBox;
@@ -49,7 +56,7 @@ class BitcoinGUI : public QMainWindow
public:
static const std::string DEFAULT_UIPLATFORM;
- explicit BitcoinGUI(const PlatformStyle *platformStyle, const NetworkStyle *networkStyle, QWidget *parent = 0);
+ explicit BitcoinGUI(interface::Node& node, const PlatformStyle *platformStyle, const NetworkStyle *networkStyle, QWidget *parent = 0);
~BitcoinGUI();
/** Set the client model.
@@ -76,6 +83,9 @@ protected:
bool eventFilter(QObject *object, QEvent *event);
private:
+ interface::Node& m_node;
+ std::unique_ptr<interface::Handler> m_handler_message_box;
+ std::unique_ptr<interface::Handler> m_handler_question;
ClientModel *clientModel;
WalletFrame *walletFrame;
diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp
index eaf2896bc3..1dbfc815b6 100644
--- a/src/qt/clientmodel.cpp
+++ b/src/qt/clientmodel.cpp
@@ -13,6 +13,8 @@
#include <chainparams.h>
#include <checkpoints.h>
#include <clientversion.h>
+#include <interface/handler.h>
+#include <interface/node.h>
#include <validation.h>
#include <net.h>
#include <txmempool.h>
@@ -30,8 +32,9 @@ class CBlockIndex;
static int64_t nLastHeaderTipUpdateNotification = 0;
static int64_t nLastBlockTipUpdateNotification = 0;
-ClientModel::ClientModel(OptionsModel *_optionsModel, QObject *parent) :
+ClientModel::ClientModel(interface::Node& node, OptionsModel *_optionsModel, QObject *parent) :
QObject(parent),
+ m_node(node),
optionsModel(_optionsModel),
peerTableModel(0),
banTableModel(0),
@@ -39,8 +42,8 @@ ClientModel::ClientModel(OptionsModel *_optionsModel, QObject *parent) :
{
cachedBestHeaderHeight = -1;
cachedBestHeaderTime = -1;
- peerTableModel = new PeerTableModel(this);
- banTableModel = new BanTableModel(this);
+ peerTableModel = new PeerTableModel(m_node, this);
+ banTableModel = new BanTableModel(m_node, this);
pollTimer = new QTimer(this);
connect(pollTimer, SIGNAL(timeout()), this, SLOT(updateTimer()));
pollTimer->start(MODEL_UPDATE_DELAY);
@@ -64,15 +67,7 @@ int ClientModel::getNumConnections(unsigned int flags) const
else if (flags == CONNECTIONS_ALL)
connections = CConnman::CONNECTIONS_ALL;
- if(g_connman)
- return g_connman->GetNodeCount(connections);
- return 0;
-}
-
-int ClientModel::getNumBlocks() const
-{
- LOCK(cs_main);
- return chainActive.Height();
+ return m_node.getNodeCount(connections);
}
int ClientModel::getHeaderTipHeight() const
@@ -80,10 +75,11 @@ int ClientModel::getHeaderTipHeight() const
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();
+ int height;
+ int64_t blockTime;
+ if (m_node.getHeaderTip(height, blockTime)) {
+ cachedBestHeaderHeight = height;
+ cachedBestHeaderTime = blockTime;
}
}
return cachedBestHeaderHeight;
@@ -92,66 +88,22 @@ int ClientModel::getHeaderTipHeight() const
int64_t ClientModel::getHeaderTipTime() const
{
if (cachedBestHeaderTime == -1) {
- LOCK(cs_main);
- if (pindexBestHeader) {
- cachedBestHeaderHeight = pindexBestHeader->nHeight;
- cachedBestHeaderTime = pindexBestHeader->GetBlockTime();
+ int height;
+ int64_t blockTime;
+ if (m_node.getHeaderTip(height, blockTime)) {
+ cachedBestHeaderHeight = height;
+ cachedBestHeaderTime = blockTime;
}
}
return cachedBestHeaderTime;
}
-quint64 ClientModel::getTotalBytesRecv() const
-{
- if(!g_connman)
- return 0;
- return g_connman->GetTotalBytesRecv();
-}
-
-quint64 ClientModel::getTotalBytesSent() const
-{
- if(!g_connman)
- return 0;
- return g_connman->GetTotalBytesSent();
-}
-
-QDateTime ClientModel::getLastBlockDate() const
-{
- LOCK(cs_main);
-
- if (chainActive.Tip())
- return QDateTime::fromTime_t(chainActive.Tip()->GetBlockTime());
-
- return QDateTime::fromTime_t(Params().GenesisBlock().GetBlockTime()); // Genesis block's time of current network
-}
-
-long ClientModel::getMempoolSize() const
-{
- return mempool.size();
-}
-
-size_t ClientModel::getMempoolDynamicUsage() const
-{
- return mempool.DynamicMemoryUsage();
-}
-
-double ClientModel::getVerificationProgress(const CBlockIndex *tipIn) const
-{
- CBlockIndex *tip = const_cast<CBlockIndex *>(tipIn);
- LOCK(cs_main);
- if (!tip)
- {
- tip = chainActive.Tip();
- }
- return GuessVerificationProgress(Params().TxData(), tip);
-}
-
void ClientModel::updateTimer()
{
// no locking required at this point
// the following calls will acquire the required lock
- Q_EMIT mempoolSizeChanged(getMempoolSize(), getMempoolDynamicUsage());
- Q_EMIT bytesChanged(getTotalBytesRecv(), getTotalBytesSent());
+ Q_EMIT mempoolSizeChanged(m_node.getMempoolSize(), m_node.getMempoolDynamicUsage());
+ Q_EMIT bytesChanged(m_node.getTotalBytesRecv(), m_node.getTotalBytesSent());
}
void ClientModel::updateNumConnections(int numConnections)
@@ -169,41 +121,21 @@ void ClientModel::updateAlert()
Q_EMIT alertsChanged(getStatusBarWarnings());
}
-bool ClientModel::inInitialBlockDownload() const
-{
- return IsInitialBlockDownload();
-}
-
enum BlockSource ClientModel::getBlockSource() const
{
- if (fReindex)
- return BLOCK_SOURCE_REINDEX;
- else if (fImporting)
- return BLOCK_SOURCE_DISK;
+ if (m_node.getReindex())
+ return BlockSource::REINDEX;
+ else if (m_node.getImporting())
+ return BlockSource::DISK;
else if (getNumConnections() > 0)
- return BLOCK_SOURCE_NETWORK;
+ return BlockSource::NETWORK;
- return BLOCK_SOURCE_NONE;
-}
-
-void ClientModel::setNetworkActive(bool active)
-{
- if (g_connman) {
- g_connman->SetNetworkActive(active);
- }
-}
-
-bool ClientModel::getNetworkActive() const
-{
- if (g_connman) {
- return g_connman->GetNetworkActive();
- }
- return false;
+ return BlockSource::NONE;
}
QString ClientModel::getStatusBarWarnings() const
{
- return QString::fromStdString(GetWarnings("gui"));
+ return QString::fromStdString(m_node.getWarnings("gui"));
}
OptionsModel *ClientModel::getOptionsModel()
@@ -285,7 +217,7 @@ static void BannedListChanged(ClientModel *clientmodel)
QMetaObject::invokeMethod(clientmodel, "updateBanlist", Qt::QueuedConnection);
}
-static void BlockTipChanged(ClientModel *clientmodel, bool initialSync, const CBlockIndex *pIndex, bool fHeader)
+static void BlockTipChanged(ClientModel *clientmodel, bool initialSync, int height, int64_t blockTime, double verificationProgress, bool fHeader)
{
// lock free async UI updates in case we have a new block tip
// during initial sync, only update the UI if the last update
@@ -298,16 +230,16 @@ static void BlockTipChanged(ClientModel *clientmodel, bool initialSync, const CB
if (fHeader) {
// cache best headers time and height to reduce future cs_main locks
- clientmodel->cachedBestHeaderHeight = pIndex->nHeight;
- clientmodel->cachedBestHeaderTime = pIndex->GetBlockTime();
+ clientmodel->cachedBestHeaderHeight = height;
+ clientmodel->cachedBestHeaderTime = blockTime;
}
// if we are in-sync, update the UI regardless of last update time
if (!initialSync || now - nLastUpdateNotification > MODEL_UPDATE_DELAY) {
//pass an async signal to the UI thread
QMetaObject::invokeMethod(clientmodel, "numBlocksChanged", Qt::QueuedConnection,
- Q_ARG(int, pIndex->nHeight),
- Q_ARG(QDateTime, QDateTime::fromTime_t(pIndex->GetBlockTime())),
- Q_ARG(double, clientmodel->getVerificationProgress(pIndex)),
+ Q_ARG(int, height),
+ Q_ARG(QDateTime, QDateTime::fromTime_t(blockTime)),
+ Q_ARG(double, verificationProgress),
Q_ARG(bool, fHeader));
nLastUpdateNotification = now;
}
@@ -316,23 +248,23 @@ static void BlockTipChanged(ClientModel *clientmodel, bool initialSync, const CB
void ClientModel::subscribeToCoreSignals()
{
// Connect signals to client
- uiInterface.ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2));
- uiInterface.NotifyNumConnectionsChanged.connect(boost::bind(NotifyNumConnectionsChanged, this, _1));
- uiInterface.NotifyNetworkActiveChanged.connect(boost::bind(NotifyNetworkActiveChanged, this, _1));
- uiInterface.NotifyAlertChanged.connect(boost::bind(NotifyAlertChanged, this));
- uiInterface.BannedListChanged.connect(boost::bind(BannedListChanged, this));
- uiInterface.NotifyBlockTip.connect(boost::bind(BlockTipChanged, this, _1, _2, false));
- uiInterface.NotifyHeaderTip.connect(boost::bind(BlockTipChanged, this, _1, _2, true));
+ m_handler_show_progress = m_node.handleShowProgress(boost::bind(ShowProgress, this, _1, _2));
+ m_handler_notify_num_connections_changed = m_node.handleNotifyNumConnectionsChanged(boost::bind(NotifyNumConnectionsChanged, this, _1));
+ m_handler_notify_network_active_changed = m_node.handleNotifyNetworkActiveChanged(boost::bind(NotifyNetworkActiveChanged, this, _1));
+ m_handler_notify_alert_changed = m_node.handleNotifyAlertChanged(boost::bind(NotifyAlertChanged, this));
+ m_handler_banned_list_changed = m_node.handleBannedListChanged(boost::bind(BannedListChanged, this));
+ m_handler_notify_block_tip = m_node.handleNotifyBlockTip(boost::bind(BlockTipChanged, this, _1, _2, _3, _4, false));
+ m_handler_notify_header_tip = m_node.handleNotifyHeaderTip(boost::bind(BlockTipChanged, this, _1, _2, _3, _4, true));
}
void ClientModel::unsubscribeFromCoreSignals()
{
// Disconnect signals from client
- uiInterface.ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2));
- uiInterface.NotifyNumConnectionsChanged.disconnect(boost::bind(NotifyNumConnectionsChanged, this, _1));
- uiInterface.NotifyNetworkActiveChanged.disconnect(boost::bind(NotifyNetworkActiveChanged, this, _1));
- uiInterface.NotifyAlertChanged.disconnect(boost::bind(NotifyAlertChanged, this));
- uiInterface.BannedListChanged.disconnect(boost::bind(BannedListChanged, this));
- uiInterface.NotifyBlockTip.disconnect(boost::bind(BlockTipChanged, this, _1, _2, false));
- uiInterface.NotifyHeaderTip.disconnect(boost::bind(BlockTipChanged, this, _1, _2, true));
+ m_handler_show_progress->disconnect();
+ m_handler_notify_num_connections_changed->disconnect();
+ m_handler_notify_network_active_changed->disconnect();
+ m_handler_notify_alert_changed->disconnect();
+ m_handler_banned_list_changed->disconnect();
+ m_handler_notify_block_tip->disconnect();
+ m_handler_notify_header_tip->disconnect();
}
diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h
index 99ec2365a9..9faa10b87e 100644
--- a/src/qt/clientmodel.h
+++ b/src/qt/clientmodel.h
@@ -9,6 +9,7 @@
#include <QDateTime>
#include <atomic>
+#include <memory>
class BanTableModel;
class OptionsModel;
@@ -16,15 +17,20 @@ class PeerTableModel;
class CBlockIndex;
+namespace interface {
+class Handler;
+class Node;
+}
+
QT_BEGIN_NAMESPACE
class QTimer;
QT_END_NAMESPACE
-enum BlockSource {
- BLOCK_SOURCE_NONE,
- BLOCK_SOURCE_REINDEX,
- BLOCK_SOURCE_DISK,
- BLOCK_SOURCE_NETWORK
+enum class BlockSource {
+ NONE,
+ REINDEX,
+ DISK,
+ NETWORK
};
enum NumConnections {
@@ -40,37 +46,21 @@ class ClientModel : public QObject
Q_OBJECT
public:
- explicit ClientModel(OptionsModel *optionsModel, QObject *parent = 0);
+ explicit ClientModel(interface::Node& node, OptionsModel *optionsModel, QObject *parent = 0);
~ClientModel();
+ interface::Node& node() const { return m_node; }
OptionsModel *getOptionsModel();
PeerTableModel *getPeerTableModel();
BanTableModel *getBanTableModel();
//! Return number of connections, default is in- and outbound (total)
int getNumConnections(unsigned int flags = CONNECTIONS_ALL) const;
- int getNumBlocks() const;
int getHeaderTipHeight() const;
int64_t getHeaderTipTime() const;
- //! Return number of transactions in the mempool
- long getMempoolSize() const;
- //! Return the dynamic memory usage of the mempool
- size_t getMempoolDynamicUsage() const;
-
- quint64 getTotalBytesRecv() const;
- quint64 getTotalBytesSent() const;
-
- double getVerificationProgress(const CBlockIndex *tip) const;
- QDateTime getLastBlockDate() const;
-
- //! Return true if core is doing initial block download
- bool inInitialBlockDownload() const;
+
//! Returns enum BlockSource of the current importing/syncing state
enum BlockSource getBlockSource() const;
- //! Return true if network activity in core is enabled
- bool getNetworkActive() const;
- //! Toggle network activity state in core
- void setNetworkActive(bool active);
//! Return warnings to be displayed in status bar
QString getStatusBarWarnings() const;
@@ -85,6 +75,14 @@ public:
mutable std::atomic<int64_t> cachedBestHeaderTime;
private:
+ interface::Node& m_node;
+ std::unique_ptr<interface::Handler> m_handler_show_progress;
+ std::unique_ptr<interface::Handler> m_handler_notify_num_connections_changed;
+ std::unique_ptr<interface::Handler> m_handler_notify_network_active_changed;
+ std::unique_ptr<interface::Handler> m_handler_notify_alert_changed;
+ std::unique_ptr<interface::Handler> m_handler_banned_list_changed;
+ std::unique_ptr<interface::Handler> m_handler_notify_block_tip;
+ std::unique_ptr<interface::Handler> m_handler_notify_header_tip;
OptionsModel *optionsModel;
PeerTableModel *peerTableModel;
BanTableModel *banTableModel;
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index a45e9f85c1..2081d6ca08 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -14,7 +14,7 @@
#include <qt/walletmodel.h>
#include <wallet/coincontrol.h>
-#include <init.h>
+#include <interface/node.h>
#include <key_io.h>
#include <policy/fees.h>
#include <policy/policy.h>
@@ -209,7 +209,7 @@ void CoinControlDialog::showMenu(const QPoint &point)
if (item->text(COLUMN_TXHASH).length() == 64) // transaction hash is 64 characters (this means it is a child node, so it is not a parent node in tree mode)
{
copyTransactionHashAction->setEnabled(true);
- if (model->isLockedCoin(uint256S(item->text(COLUMN_TXHASH).toStdString()), item->text(COLUMN_VOUT_INDEX).toUInt()))
+ if (model->wallet().isLockedCoin(COutPoint(uint256S(item->text(COLUMN_TXHASH).toStdString()), item->text(COLUMN_VOUT_INDEX).toUInt())))
{
lockAction->setEnabled(false);
unlockAction->setEnabled(true);
@@ -269,7 +269,7 @@ void CoinControlDialog::lockCoin()
contextMenuItem->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
COutPoint outpt(uint256S(contextMenuItem->text(COLUMN_TXHASH).toStdString()), contextMenuItem->text(COLUMN_VOUT_INDEX).toUInt());
- model->lockCoin(outpt);
+ model->wallet().lockCoin(outpt);
contextMenuItem->setDisabled(true);
contextMenuItem->setIcon(COLUMN_CHECKBOX, platformStyle->SingleColorIcon(":/icons/lock_closed"));
updateLabelLocked();
@@ -279,7 +279,7 @@ void CoinControlDialog::lockCoin()
void CoinControlDialog::unlockCoin()
{
COutPoint outpt(uint256S(contextMenuItem->text(COLUMN_TXHASH).toStdString()), contextMenuItem->text(COLUMN_VOUT_INDEX).toUInt());
- model->unlockCoin(outpt);
+ model->wallet().unlockCoin(outpt);
contextMenuItem->setDisabled(false);
contextMenuItem->setIcon(COLUMN_CHECKBOX, QIcon());
updateLabelLocked();
@@ -405,7 +405,7 @@ void CoinControlDialog::viewItemChanged(QTreeWidgetItem* item, int column)
void CoinControlDialog::updateLabelLocked()
{
std::vector<COutPoint> vOutpts;
- model->listLockedCoins(vOutpts);
+ model->wallet().listLockedCoins(vOutpts);
if (vOutpts.size() > 0)
{
ui->labelLocked->setText(tr("(%1 locked)").arg(vOutpts.size()));
@@ -431,7 +431,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
{
CTxOut txout(amount, static_cast<CScript>(std::vector<unsigned char>(24, 0)));
txDummy.vout.push_back(txout);
- fDust |= IsDust(txout, ::dustRelayFee);
+ fDust |= IsDust(txout, model->node().getDustRelayFee());
}
}
@@ -445,16 +445,16 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
bool fWitness = false;
std::vector<COutPoint> vCoinControl;
- std::vector<COutput> vOutputs;
coinControl()->ListSelected(vCoinControl);
- model->getOutputs(vCoinControl, vOutputs);
- for (const COutput& out : vOutputs) {
+ size_t i = 0;
+ for (const auto& out : model->wallet().getCoins(vCoinControl)) {
+ if (out.depth_in_main_chain < 0) continue;
+
// unselect already spent, very unlikely scenario, this could happen
// when selected are spent elsewhere, like rpc or another computer
- uint256 txhash = out.tx->GetHash();
- COutPoint outpt(txhash, out.i);
- if (model->isSpent(outpt))
+ const COutPoint& outpt = vCoinControl[i++];
+ if (out.is_spent)
{
coinControl()->UnSelect(outpt);
continue;
@@ -464,22 +464,22 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
nQuantity++;
// Amount
- nAmount += out.tx->tx->vout[out.i].nValue;
+ nAmount += out.txout.nValue;
// Bytes
CTxDestination address;
int witnessversion = 0;
std::vector<unsigned char> witnessprogram;
- if (out.tx->tx->vout[out.i].scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram))
+ if (out.txout.scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram))
{
nBytesInputs += (32 + 4 + 1 + (107 / WITNESS_SCALE_FACTOR) + 4);
fWitness = true;
}
- else if(ExtractDestination(out.tx->tx->vout[out.i].scriptPubKey, address))
+ else if(ExtractDestination(out.txout.scriptPubKey, address))
{
CPubKey pubkey;
CKeyID *keyid = boost::get<CKeyID>(&address);
- if (keyid && model->getPubKey(*keyid, pubkey))
+ if (keyid && model->wallet().getPubKey(*keyid, pubkey))
{
nBytesInputs += (pubkey.IsCompressed() ? 148 : 180);
}
@@ -509,7 +509,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
nBytes -= 34;
// Fee
- nPayFee = GetMinimumFee(nBytes, *coinControl(), ::mempool, ::feeEstimator, nullptr /* FeeCalculation */);
+ nPayFee = model->node().getMinimumFee(nBytes, *coinControl(), nullptr /* returned_target */, nullptr /* reason */);
if (nPayAmount > 0)
{
@@ -521,7 +521,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
if (nChange > 0 && nChange < MIN_CHANGE)
{
CTxOut txout(nChange, static_cast<CScript>(std::vector<unsigned char>(24, 0)));
- if (IsDust(txout, ::dustRelayFee))
+ if (IsDust(txout, model->node().getDustRelayFee()))
{
nPayFee += nChange;
nChange = 0;
@@ -621,13 +621,10 @@ void CoinControlDialog::updateView()
int nDisplayUnit = model->getOptionsModel()->getDisplayUnit();
- std::map<QString, std::vector<COutput> > mapCoins;
- model->listCoins(mapCoins);
-
- for (const std::pair<QString, std::vector<COutput>>& coins : mapCoins) {
+ for (const auto& coins : model->wallet().listCoins()) {
CCoinControlWidgetItem *itemWalletAddress = new CCoinControlWidgetItem();
itemWalletAddress->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
- QString sWalletAddress = coins.first;
+ QString sWalletAddress = QString::fromStdString(EncodeDestination(coins.first));
QString sWalletLabel = model->getAddressTableModel()->labelForAddress(sWalletAddress);
if (sWalletLabel.isEmpty())
sWalletLabel = tr("(no label)");
@@ -649,8 +646,10 @@ void CoinControlDialog::updateView()
CAmount nSum = 0;
int nChildren = 0;
- for (const COutput& out : coins.second) {
- nSum += out.tx->tx->vout[out.i].nValue;
+ for (const auto& outpair : coins.second) {
+ const COutPoint& output = std::get<0>(outpair);
+ const interface::WalletTxOut& out = std::get<1>(outpair);
+ nSum += out.txout.nValue;
nChildren++;
CCoinControlWidgetItem *itemOutput;
@@ -662,7 +661,7 @@ void CoinControlDialog::updateView()
// address
CTxDestination outputAddress;
QString sAddress = "";
- if(ExtractDestination(out.tx->tx->vout[out.i].scriptPubKey, outputAddress))
+ if(ExtractDestination(out.txout.scriptPubKey, outputAddress))
{
sAddress = QString::fromStdString(EncodeDestination(outputAddress));
@@ -687,35 +686,33 @@ void CoinControlDialog::updateView()
}
// amount
- itemOutput->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, out.tx->tx->vout[out.i].nValue));
- itemOutput->setData(COLUMN_AMOUNT, Qt::UserRole, QVariant((qlonglong)out.tx->tx->vout[out.i].nValue)); // padding so that sorting works correctly
+ itemOutput->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, out.txout.nValue));
+ itemOutput->setData(COLUMN_AMOUNT, Qt::UserRole, QVariant((qlonglong)out.txout.nValue)); // padding so that sorting works correctly
// date
- itemOutput->setText(COLUMN_DATE, GUIUtil::dateTimeStr(out.tx->GetTxTime()));
- itemOutput->setData(COLUMN_DATE, Qt::UserRole, QVariant((qlonglong)out.tx->GetTxTime()));
+ itemOutput->setText(COLUMN_DATE, GUIUtil::dateTimeStr(out.time));
+ itemOutput->setData(COLUMN_DATE, Qt::UserRole, QVariant((qlonglong)out.time));
// confirmations
- itemOutput->setText(COLUMN_CONFIRMATIONS, QString::number(out.nDepth));
- itemOutput->setData(COLUMN_CONFIRMATIONS, Qt::UserRole, QVariant((qlonglong)out.nDepth));
+ itemOutput->setText(COLUMN_CONFIRMATIONS, QString::number(out.depth_in_main_chain));
+ itemOutput->setData(COLUMN_CONFIRMATIONS, Qt::UserRole, QVariant((qlonglong)out.depth_in_main_chain));
// transaction hash
- uint256 txhash = out.tx->GetHash();
- itemOutput->setText(COLUMN_TXHASH, QString::fromStdString(txhash.GetHex()));
+ itemOutput->setText(COLUMN_TXHASH, QString::fromStdString(output.hash.GetHex()));
// vout index
- itemOutput->setText(COLUMN_VOUT_INDEX, QString::number(out.i));
+ itemOutput->setText(COLUMN_VOUT_INDEX, QString::number(output.n));
// disable locked coins
- if (model->isLockedCoin(txhash, out.i))
+ if (model->wallet().isLockedCoin(output))
{
- COutPoint outpt(txhash, out.i);
- coinControl()->UnSelect(outpt); // just to be sure
+ coinControl()->UnSelect(output); // just to be sure
itemOutput->setDisabled(true);
itemOutput->setIcon(COLUMN_CHECKBOX, platformStyle->SingleColorIcon(":/icons/lock_closed"));
}
// set checkbox
- if (coinControl()->IsSelected(COutPoint(txhash, out.i)))
+ if (coinControl()->IsSelected(output))
itemOutput->setCheckState(COLUMN_CHECKBOX, Qt::Checked);
}
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index 16ef27a36a..3501f97908 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -13,7 +13,7 @@
#include <chainparams.h>
#include <primitives/transaction.h>
#include <key_io.h>
-#include <init.h>
+#include <interface/node.h>
#include <policy/policy.h>
#include <protocol.h>
#include <script/script.h>
@@ -137,15 +137,6 @@ void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent)
widget->setCheckValidator(new BitcoinAddressCheckValidator(parent));
}
-void setupAmountWidget(QLineEdit *widget, QWidget *parent)
-{
- QDoubleValidator *amountValidator = new QDoubleValidator(parent);
- amountValidator->setDecimals(8);
- amountValidator->setBottom(0.0);
- widget->setValidator(amountValidator);
- widget->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
-}
-
bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out)
{
// return if URI is not valid or is no bitcoin: URI
@@ -241,12 +232,12 @@ QString formatBitcoinURI(const SendCoinsRecipient &info)
return ret;
}
-bool isDust(const QString& address, const CAmount& amount)
+bool isDust(interface::Node& node, const QString& address, const CAmount& amount)
{
CTxDestination dest = DecodeDestination(address.toStdString());
CScript script = GetScriptForDestination(dest);
CTxOut txOut(amount, script);
- return IsDust(txOut, ::dustRelayFee);
+ return IsDust(txOut, node.getDustRelayFee());
}
QString HtmlEscape(const QString& str, bool fMultiLine)
diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h
index 4b856986da..40037edb9d 100644
--- a/src/qt/guiutil.h
+++ b/src/qt/guiutil.h
@@ -20,6 +20,11 @@
class QValidatedLineEdit;
class SendCoinsRecipient;
+namespace interface
+{
+ class Node;
+}
+
QT_BEGIN_NAMESPACE
class QAbstractItemView;
class QDateTime;
@@ -40,9 +45,8 @@ namespace GUIUtil
// Return a monospace font
QFont fixedPitchFont();
- // Set up widgets for address and amounts
+ // Set up widget for address
void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent);
- void setupAmountWidget(QLineEdit *widget, QWidget *parent);
// Parse "bitcoin:" URI into recipient object, return true on successful parsing
bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out);
@@ -50,7 +54,7 @@ namespace GUIUtil
QString formatBitcoinURI(const SendCoinsRecipient &info);
// Returns true if given address+amount meets "dust" definition
- bool isDust(const QString& address, const CAmount& amount);
+ bool isDust(interface::Node& node, const QString& address, const CAmount& amount);
// HTML escaping for rich text controls
QString HtmlEscape(const QString& str, bool fMultiLine=false);
diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp
index e69f196238..4eb7482018 100644
--- a/src/qt/intro.cpp
+++ b/src/qt/intro.cpp
@@ -12,6 +12,7 @@
#include <qt/guiutil.h>
+#include <interface/node.h>
#include <util.h>
#include <QFileDialog>
@@ -186,7 +187,7 @@ QString Intro::getDefaultDataDirectory()
return GUIUtil::boostPathToQString(GetDefaultDataDir());
}
-bool Intro::pickDataDirectory()
+bool Intro::pickDataDirectory(interface::Node& node)
{
QSettings settings;
/* If data directory provided on command line, no need to look at settings
@@ -233,8 +234,9 @@ bool Intro::pickDataDirectory()
* override -datadir in the bitcoin.conf file in the default data directory
* (to be consistent with bitcoind behavior)
*/
- if(dataDir != getDefaultDataDirectory())
- gArgs.SoftSetArg("-datadir", GUIUtil::qstringToBoostPath(dataDir).string()); // use OS locale for path setting
+ if(dataDir != getDefaultDataDirectory()) {
+ node.softSetArg("-datadir", GUIUtil::qstringToBoostPath(dataDir).string()); // use OS locale for path setting
+ }
return true;
}
diff --git a/src/qt/intro.h b/src/qt/intro.h
index 5b428b379c..07d2025bb6 100644
--- a/src/qt/intro.h
+++ b/src/qt/intro.h
@@ -13,6 +13,10 @@ static const bool DEFAULT_CHOOSE_DATADIR = false;
class FreespaceChecker;
+namespace interface {
+ class Node;
+}
+
namespace Ui {
class Intro;
}
@@ -41,7 +45,7 @@ public:
* @note do NOT call global GetDataDir() before calling this function, this
* will cause the wrong path to be cached.
*/
- static bool pickDataDirectory();
+ static bool pickDataDirectory(interface::Node& node);
/**
* Determine default data directory for operating system.
diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp
index 5bef473c63..438e9e70f1 100644
--- a/src/qt/optionsdialog.cpp
+++ b/src/qt/optionsdialog.cpp
@@ -13,6 +13,7 @@
#include <qt/guiutil.h>
#include <qt/optionsmodel.h>
+#include <interface/node.h>
#include <validation.h> // for DEFAULT_SCRIPTCHECK_THREADS and MAX_SCRIPTCHECK_THREADS
#include <netbase.h>
#include <txdb.h> // for -dbcache defaults
@@ -313,17 +314,17 @@ void OptionsDialog::updateDefaultProxyNets()
std::string strProxy;
QString strDefaultProxyGUI;
- GetProxy(NET_IPV4, proxy);
+ model->node().getProxy(NET_IPV4, proxy);
strProxy = proxy.proxy.ToStringIP() + ":" + proxy.proxy.ToStringPort();
strDefaultProxyGUI = ui->proxyIp->text() + ":" + ui->proxyPort->text();
(strProxy == strDefaultProxyGUI.toStdString()) ? ui->proxyReachIPv4->setChecked(true) : ui->proxyReachIPv4->setChecked(false);
- GetProxy(NET_IPV6, proxy);
+ model->node().getProxy(NET_IPV6, proxy);
strProxy = proxy.proxy.ToStringIP() + ":" + proxy.proxy.ToStringPort();
strDefaultProxyGUI = ui->proxyIp->text() + ":" + ui->proxyPort->text();
(strProxy == strDefaultProxyGUI.toStdString()) ? ui->proxyReachIPv6->setChecked(true) : ui->proxyReachIPv6->setChecked(false);
- GetProxy(NET_TOR, proxy);
+ model->node().getProxy(NET_TOR, proxy);
strProxy = proxy.proxy.ToStringIP() + ":" + proxy.proxy.ToStringPort();
strDefaultProxyGUI = ui->proxyIp->text() + ":" + ui->proxyPort->text();
(strProxy == strDefaultProxyGUI.toStdString()) ? ui->proxyReachTor->setChecked(true) : ui->proxyReachTor->setChecked(false);
diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp
index 909be1c264..d8197b6ec6 100644
--- a/src/qt/optionsmodel.cpp
+++ b/src/qt/optionsmodel.cpp
@@ -11,26 +11,21 @@
#include <qt/bitcoinunits.h>
#include <qt/guiutil.h>
-#include <init.h>
+#include <interface/node.h>
#include <validation.h> // For DEFAULT_SCRIPTCHECK_THREADS
#include <net.h>
#include <netbase.h>
#include <txdb.h> // for -dbcache defaults
#include <qt/intro.h>
-#ifdef ENABLE_WALLET
-#include <wallet/wallet.h>
-#include <wallet/walletdb.h>
-#endif
-
#include <QNetworkProxy>
#include <QSettings>
#include <QStringList>
const char *DEFAULT_GUI_PROXY_HOST = "127.0.0.1";
-OptionsModel::OptionsModel(QObject *parent, bool resetSettings) :
- QAbstractListModel(parent)
+OptionsModel::OptionsModel(interface::Node& node, QObject *parent, bool resetSettings) :
+ QAbstractListModel(parent), m_node(node)
{
Init(resetSettings);
}
@@ -93,12 +88,12 @@ void OptionsModel::Init(bool resetSettings)
// Main
if (!settings.contains("nDatabaseCache"))
settings.setValue("nDatabaseCache", (qint64)nDefaultDbCache);
- if (!gArgs.SoftSetArg("-dbcache", settings.value("nDatabaseCache").toString().toStdString()))
+ if (!m_node.softSetArg("-dbcache", settings.value("nDatabaseCache").toString().toStdString()))
addOverriddenOption("-dbcache");
if (!settings.contains("nThreadsScriptVerif"))
settings.setValue("nThreadsScriptVerif", DEFAULT_SCRIPTCHECK_THREADS);
- if (!gArgs.SoftSetArg("-par", settings.value("nThreadsScriptVerif").toString().toStdString()))
+ if (!m_node.softSetArg("-par", settings.value("nThreadsScriptVerif").toString().toStdString()))
addOverriddenOption("-par");
if (!settings.contains("strDataDir"))
@@ -108,19 +103,19 @@ void OptionsModel::Init(bool resetSettings)
#ifdef ENABLE_WALLET
if (!settings.contains("bSpendZeroConfChange"))
settings.setValue("bSpendZeroConfChange", true);
- if (!gArgs.SoftSetBoolArg("-spendzeroconfchange", settings.value("bSpendZeroConfChange").toBool()))
+ if (!m_node.softSetBoolArg("-spendzeroconfchange", settings.value("bSpendZeroConfChange").toBool()))
addOverriddenOption("-spendzeroconfchange");
#endif
// Network
if (!settings.contains("fUseUPnP"))
settings.setValue("fUseUPnP", DEFAULT_UPNP);
- if (!gArgs.SoftSetBoolArg("-upnp", settings.value("fUseUPnP").toBool()))
+ if (!m_node.softSetBoolArg("-upnp", settings.value("fUseUPnP").toBool()))
addOverriddenOption("-upnp");
if (!settings.contains("fListen"))
settings.setValue("fListen", DEFAULT_LISTEN);
- if (!gArgs.SoftSetBoolArg("-listen", settings.value("fListen").toBool()))
+ if (!m_node.softSetBoolArg("-listen", settings.value("fListen").toBool()))
addOverriddenOption("-listen");
if (!settings.contains("fUseProxy"))
@@ -128,7 +123,7 @@ void OptionsModel::Init(bool resetSettings)
if (!settings.contains("addrProxy"))
settings.setValue("addrProxy", QString("%1:%2").arg(DEFAULT_GUI_PROXY_HOST, DEFAULT_GUI_PROXY_PORT));
// Only try to set -proxy, if user has enabled fUseProxy
- if (settings.value("fUseProxy").toBool() && !gArgs.SoftSetArg("-proxy", settings.value("addrProxy").toString().toStdString()))
+ if (settings.value("fUseProxy").toBool() && !m_node.softSetArg("-proxy", settings.value("addrProxy").toString().toStdString()))
addOverriddenOption("-proxy");
else if(!settings.value("fUseProxy").toBool() && !gArgs.GetArg("-proxy", "").empty())
addOverriddenOption("-proxy");
@@ -138,7 +133,7 @@ void OptionsModel::Init(bool resetSettings)
if (!settings.contains("addrSeparateProxyTor"))
settings.setValue("addrSeparateProxyTor", QString("%1:%2").arg(DEFAULT_GUI_PROXY_HOST, DEFAULT_GUI_PROXY_PORT));
// Only try to set -onion, if user has enabled fUseSeparateProxyTor
- if (settings.value("fUseSeparateProxyTor").toBool() && !gArgs.SoftSetArg("-onion", settings.value("addrSeparateProxyTor").toString().toStdString()))
+ if (settings.value("fUseSeparateProxyTor").toBool() && !m_node.softSetArg("-onion", settings.value("addrSeparateProxyTor").toString().toStdString()))
addOverriddenOption("-onion");
else if(!settings.value("fUseSeparateProxyTor").toBool() && !gArgs.GetArg("-onion", "").empty())
addOverriddenOption("-onion");
@@ -146,7 +141,7 @@ void OptionsModel::Init(bool resetSettings)
// Display
if (!settings.contains("language"))
settings.setValue("language", "");
- if (!gArgs.SoftSetArg("-lang", settings.value("language").toString().toStdString()))
+ if (!m_node.softSetArg("-lang", settings.value("language").toString().toStdString()))
addOverriddenOption("-lang");
language = settings.value("language").toString();
@@ -315,12 +310,7 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in
break;
case MapPortUPnP: // core option - can be changed on-the-fly
settings.setValue("fUseUPnP", value.toBool());
- if (value.toBool()) {
- StartMapPort();
- } else {
- InterruptMapPort();
- StopMapPort();
- }
+ m_node.mapPort(value.toBool());
break;
case MinimizeOnClose:
fMinimizeOnClose = value.toBool();
@@ -453,7 +443,7 @@ bool OptionsModel::getProxySettings(QNetworkProxy& proxy) const
// Directly query current base proxy, because
// GUI settings can be overridden with -proxy.
proxyType curProxy;
- if (GetProxy(NET_IPV4, curProxy)) {
+ if (m_node.getProxy(NET_IPV4, curProxy)) {
proxy.setType(QNetworkProxy::Socks5Proxy);
proxy.setHostName(QString::fromStdString(curProxy.proxy.ToStringIP()));
proxy.setPort(curProxy.proxy.GetPort());
diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h
index 3f50541eb4..96c6b8fa45 100644
--- a/src/qt/optionsmodel.h
+++ b/src/qt/optionsmodel.h
@@ -9,6 +9,10 @@
#include <QAbstractListModel>
+namespace interface {
+class Node;
+}
+
QT_BEGIN_NAMESPACE
class QNetworkProxy;
QT_END_NAMESPACE
@@ -27,7 +31,7 @@ class OptionsModel : public QAbstractListModel
Q_OBJECT
public:
- explicit OptionsModel(QObject *parent = 0, bool resetSettings = false);
+ explicit OptionsModel(interface::Node& node, QObject *parent = 0, bool resetSettings = false);
enum OptionID {
StartAtStartup, // bool
@@ -75,7 +79,10 @@ public:
void setRestartRequired(bool fRequired);
bool isRestartRequired() const;
+ interface::Node& node() const { return m_node; }
+
private:
+ interface::Node& m_node;
/* Qt-only settings */
bool fHideTrayIcon;
bool fMinimizeToTray;
diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp
index d1c9f17961..76d0220648 100644
--- a/src/qt/overviewpage.cpp
+++ b/src/qt/overviewpage.cpp
@@ -21,6 +21,8 @@
#define DECORATION_SIZE 54
#define NUM_ITEMS 5
+Q_DECLARE_METATYPE(interface::WalletBalances)
+
class TxViewDelegate : public QAbstractItemDelegate
{
Q_OBJECT
@@ -113,16 +115,12 @@ OverviewPage::OverviewPage(const PlatformStyle *platformStyle, QWidget *parent)
ui(new Ui::OverviewPage),
clientModel(0),
walletModel(0),
- currentBalance(-1),
- currentUnconfirmedBalance(-1),
- currentImmatureBalance(-1),
- currentWatchOnlyBalance(-1),
- currentWatchUnconfBalance(-1),
- currentWatchImmatureBalance(-1),
txdelegate(new TxViewDelegate(platformStyle, this))
{
ui->setupUi(this);
+ m_balances.balance = -1;
+
// use a SingleColorIcon for the "out of sync warning" icon
QIcon icon = platformStyle->SingleColorIcon(":/icons/warning");
icon.addPixmap(icon.pixmap(QSize(64,64), QIcon::Normal), QIcon::Disabled); // also set the disabled icon because we are using a disabled QPushButton to work around missing HiDPI support of QLabel (https://bugreports.qt.io/browse/QTBUG-42503)
@@ -159,28 +157,23 @@ OverviewPage::~OverviewPage()
delete ui;
}
-void OverviewPage::setBalance(const CAmount& balance, const CAmount& unconfirmedBalance, const CAmount& immatureBalance, const CAmount& watchOnlyBalance, const CAmount& watchUnconfBalance, const CAmount& watchImmatureBalance)
+void OverviewPage::setBalance(const interface::WalletBalances& balances)
{
int unit = walletModel->getOptionsModel()->getDisplayUnit();
- currentBalance = balance;
- currentUnconfirmedBalance = unconfirmedBalance;
- currentImmatureBalance = immatureBalance;
- currentWatchOnlyBalance = watchOnlyBalance;
- currentWatchUnconfBalance = watchUnconfBalance;
- currentWatchImmatureBalance = watchImmatureBalance;
- ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balance, false, BitcoinUnits::separatorAlways));
- ui->labelUnconfirmed->setText(BitcoinUnits::formatWithUnit(unit, unconfirmedBalance, false, BitcoinUnits::separatorAlways));
- ui->labelImmature->setText(BitcoinUnits::formatWithUnit(unit, immatureBalance, false, BitcoinUnits::separatorAlways));
- ui->labelTotal->setText(BitcoinUnits::formatWithUnit(unit, balance + unconfirmedBalance + immatureBalance, false, BitcoinUnits::separatorAlways));
- ui->labelWatchAvailable->setText(BitcoinUnits::formatWithUnit(unit, watchOnlyBalance, false, BitcoinUnits::separatorAlways));
- ui->labelWatchPending->setText(BitcoinUnits::formatWithUnit(unit, watchUnconfBalance, false, BitcoinUnits::separatorAlways));
- ui->labelWatchImmature->setText(BitcoinUnits::formatWithUnit(unit, watchImmatureBalance, false, BitcoinUnits::separatorAlways));
- ui->labelWatchTotal->setText(BitcoinUnits::formatWithUnit(unit, watchOnlyBalance + watchUnconfBalance + watchImmatureBalance, false, BitcoinUnits::separatorAlways));
+ m_balances = balances;
+ ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balances.balance, false, BitcoinUnits::separatorAlways));
+ ui->labelUnconfirmed->setText(BitcoinUnits::formatWithUnit(unit, balances.unconfirmed_balance, false, BitcoinUnits::separatorAlways));
+ ui->labelImmature->setText(BitcoinUnits::formatWithUnit(unit, balances.immature_balance, false, BitcoinUnits::separatorAlways));
+ ui->labelTotal->setText(BitcoinUnits::formatWithUnit(unit, balances.balance + balances.unconfirmed_balance + balances.immature_balance, false, BitcoinUnits::separatorAlways));
+ ui->labelWatchAvailable->setText(BitcoinUnits::formatWithUnit(unit, balances.watch_only_balance, false, BitcoinUnits::separatorAlways));
+ ui->labelWatchPending->setText(BitcoinUnits::formatWithUnit(unit, balances.unconfirmed_watch_only_balance, false, BitcoinUnits::separatorAlways));
+ ui->labelWatchImmature->setText(BitcoinUnits::formatWithUnit(unit, balances.immature_watch_only_balance, false, BitcoinUnits::separatorAlways));
+ ui->labelWatchTotal->setText(BitcoinUnits::formatWithUnit(unit, balances.watch_only_balance + balances.unconfirmed_watch_only_balance + balances.immature_watch_only_balance, false, BitcoinUnits::separatorAlways));
// only show immature (newly mined) balance if it's non-zero, so as not to complicate things
// for the non-mining users
- bool showImmature = immatureBalance != 0;
- bool showWatchOnlyImmature = watchImmatureBalance != 0;
+ bool showImmature = balances.immature_balance != 0;
+ bool showWatchOnlyImmature = balances.immature_watch_only_balance != 0;
// for symmetry reasons also show immature label when the watch-only one is shown
ui->labelImmature->setVisible(showImmature || showWatchOnlyImmature);
@@ -231,13 +224,14 @@ void OverviewPage::setWalletModel(WalletModel *model)
ui->listTransactions->setModelColumn(TransactionTableModel::ToAddress);
// Keep up to date with wallet
- setBalance(model->getBalance(), model->getUnconfirmedBalance(), model->getImmatureBalance(),
- model->getWatchBalance(), model->getWatchUnconfirmedBalance(), model->getWatchImmatureBalance());
- connect(model, SIGNAL(balanceChanged(CAmount,CAmount,CAmount,CAmount,CAmount,CAmount)), this, SLOT(setBalance(CAmount,CAmount,CAmount,CAmount,CAmount,CAmount)));
+ interface::Wallet& wallet = model->wallet();
+ interface::WalletBalances balances = wallet.getBalances();
+ setBalance(balances);
+ connect(model, SIGNAL(balanceChanged(interface::WalletBalances)), this, SLOT(setBalance(interface::WalletBalances)));
connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
- updateWatchOnlyLabels(model->haveWatchOnly());
+ updateWatchOnlyLabels(wallet.haveWatchOnly());
connect(model, SIGNAL(notifyWatchonlyChanged(bool)), this, SLOT(updateWatchOnlyLabels(bool)));
}
@@ -249,9 +243,9 @@ void OverviewPage::updateDisplayUnit()
{
if(walletModel && walletModel->getOptionsModel())
{
- if(currentBalance != -1)
- setBalance(currentBalance, currentUnconfirmedBalance, currentImmatureBalance,
- currentWatchOnlyBalance, currentWatchUnconfBalance, currentWatchImmatureBalance);
+ if (m_balances.balance != -1) {
+ setBalance(m_balances);
+ }
// Update txdelegate->unit with the current unit
txdelegate->unit = walletModel->getOptionsModel()->getDisplayUnit();
diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h
index 0ce9f98c8c..36333536e5 100644
--- a/src/qt/overviewpage.h
+++ b/src/qt/overviewpage.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_QT_OVERVIEWPAGE_H
#define BITCOIN_QT_OVERVIEWPAGE_H
-#include <amount.h>
+#include <interface/wallet.h>
#include <QWidget>
#include <memory>
@@ -38,8 +38,7 @@ public:
void showOutOfSyncWarning(bool fShow);
public Q_SLOTS:
- void setBalance(const CAmount& balance, const CAmount& unconfirmedBalance, const CAmount& immatureBalance,
- const CAmount& watchOnlyBalance, const CAmount& watchUnconfBalance, const CAmount& watchImmatureBalance);
+ void setBalance(const interface::WalletBalances& balances);
Q_SIGNALS:
void transactionClicked(const QModelIndex &index);
@@ -49,12 +48,7 @@ private:
Ui::OverviewPage *ui;
ClientModel *clientModel;
WalletModel *walletModel;
- CAmount currentBalance;
- CAmount currentUnconfirmedBalance;
- CAmount currentImmatureBalance;
- CAmount currentWatchOnlyBalance;
- CAmount currentWatchUnconfBalance;
- CAmount currentWatchImmatureBalance;
+ interface::WalletBalances m_balances;
TxViewDelegate *txdelegate;
std::unique_ptr<TransactionFilterProxy> filter;
diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp
index 65ef250440..926a699754 100644
--- a/src/qt/paymentserver.cpp
+++ b/src/qt/paymentserver.cpp
@@ -9,6 +9,7 @@
#include <qt/optionsmodel.h>
#include <chainparams.h>
+#include <interface/node.h>
#include <policy/policy.h>
#include <key_io.h>
#include <ui_interface.h>
@@ -16,6 +17,7 @@
#include <wallet/wallet.h>
#include <cstdlib>
+#include <memory>
#include <openssl/x509_vfy.h>
@@ -199,7 +201,7 @@ void PaymentServer::LoadRootCAs(X509_STORE* _store)
// Warning: ipcSendCommandLine() is called early in init,
// so don't use "Q_EMIT message()", but "QMessageBox::"!
//
-void PaymentServer::ipcParseCommandLine(int argc, char* argv[])
+void PaymentServer::ipcParseCommandLine(interface::Node& node, int argc, char* argv[])
{
for (int i = 1; i < argc; i++)
{
@@ -221,11 +223,11 @@ void PaymentServer::ipcParseCommandLine(int argc, char* argv[])
auto tempChainParams = CreateChainParams(CBaseChainParams::MAIN);
if (IsValidDestinationString(r.address.toStdString(), *tempChainParams)) {
- SelectParams(CBaseChainParams::MAIN);
+ node.selectParams(CBaseChainParams::MAIN);
} else {
tempChainParams = CreateChainParams(CBaseChainParams::TESTNET);
if (IsValidDestinationString(r.address.toStdString(), *tempChainParams)) {
- SelectParams(CBaseChainParams::TESTNET);
+ node.selectParams(CBaseChainParams::TESTNET);
}
}
}
@@ -239,11 +241,11 @@ void PaymentServer::ipcParseCommandLine(int argc, char* argv[])
{
if (request.getDetails().network() == "main")
{
- SelectParams(CBaseChainParams::MAIN);
+ node.selectParams(CBaseChainParams::MAIN);
}
else if (request.getDetails().network() == "test")
{
- SelectParams(CBaseChainParams::TESTNET);
+ node.selectParams(CBaseChainParams::TESTNET);
}
}
}
@@ -526,7 +528,7 @@ bool PaymentServer::processPaymentRequest(const PaymentRequestPlus& request, Sen
if (request.IsInitialized()) {
// Payment request network matches client network?
- if (!verifyNetwork(request.getDetails())) {
+ if (!verifyNetwork(optionsModel->node(), request.getDetails())) {
Q_EMIT message(tr("Payment request rejected"), tr("Payment request network doesn't match client network."),
CClientUIInterface::MSG_ERROR);
@@ -583,7 +585,7 @@ bool PaymentServer::processPaymentRequest(const PaymentRequestPlus& request, Sen
// Extract and check amounts
CTxOut txOut(sendingTo.second, sendingTo.first);
- if (IsDust(txOut, ::dustRelayFee)) {
+ if (IsDust(txOut, optionsModel->node().getDustRelayFee())) {
Q_EMIT message(tr("Payment request error"), tr("Requested payment amount of %1 is too small (considered dust).")
.arg(BitcoinUnits::formatWithUnit(optionsModel->getDisplayUnit(), sendingTo.second)),
CClientUIInterface::MSG_ERROR);
@@ -621,7 +623,7 @@ void PaymentServer::fetchRequest(const QUrl& url)
netManager->get(netRequest);
}
-void PaymentServer::fetchPaymentACK(CWallet* wallet, const SendCoinsRecipient& recipient, QByteArray transaction)
+void PaymentServer::fetchPaymentACK(WalletModel* walletModel, const SendCoinsRecipient& recipient, QByteArray transaction)
{
const payments::PaymentDetails& details = recipient.paymentRequest.getDetails();
if (!details.has_payment_url())
@@ -640,17 +642,17 @@ void PaymentServer::fetchPaymentACK(CWallet* wallet, const SendCoinsRecipient& r
// Create a new refund address, or re-use:
CPubKey newKey;
- if (wallet->GetKeyFromPool(newKey)) {
+ if (walletModel->wallet().getKeyFromPool(false /* internal */, newKey)) {
// BIP70 requests encode the scriptPubKey directly, so we are not restricted to address
// types supported by the receiver. As a result, we choose the address format we also
// use for change. Despite an actual payment and not change, this is a close match:
// it's the output type we use subject to privacy issues, but not restricted by what
// other software supports.
- const OutputType change_type = wallet->m_default_change_type != OutputType::NONE ? wallet->m_default_change_type : wallet->m_default_address_type;
- wallet->LearnRelatedScripts(newKey, change_type);
+ const OutputType change_type = walletModel->wallet().getDefaultChangeType() != OutputType::NONE ? walletModel->wallet().getDefaultChangeType() : walletModel->wallet().getDefaultAddressType();
+ walletModel->wallet().learnRelatedScripts(newKey, change_type);
CTxDestination dest = GetDestinationForKey(newKey, change_type);
std::string label = tr("Refund from %1").arg(recipient.authenticatedMerchant).toStdString();
- wallet->SetAddressBook(dest, label, "refund");
+ walletModel->wallet().setAddressBook(dest, label, "refund");
CScript s = GetScriptForDestination(dest);
payments::Output* refund_to = payment.add_refund_to();
@@ -758,14 +760,14 @@ void PaymentServer::handlePaymentACK(const QString& paymentACKMsg)
Q_EMIT message(tr("Payment acknowledged"), paymentACKMsg, CClientUIInterface::ICON_INFORMATION | CClientUIInterface::MODAL);
}
-bool PaymentServer::verifyNetwork(const payments::PaymentDetails& requestDetails)
+bool PaymentServer::verifyNetwork(interface::Node& node, const payments::PaymentDetails& requestDetails)
{
- bool fVerified = requestDetails.network() == Params().NetworkIDString();
+ bool fVerified = requestDetails.network() == node.getNetwork();
if (!fVerified) {
qWarning() << QString("PaymentServer::%1: Payment request network \"%2\" doesn't match client network \"%3\".")
.arg(__func__)
.arg(QString::fromStdString(requestDetails.network()))
- .arg(QString::fromStdString(Params().NetworkIDString()));
+ .arg(QString::fromStdString(node.getNetwork()));
}
return fVerified;
}
diff --git a/src/qt/paymentserver.h b/src/qt/paymentserver.h
index e262fc6bf7..e139899e22 100644
--- a/src/qt/paymentserver.h
+++ b/src/qt/paymentserver.h
@@ -40,8 +40,6 @@
class OptionsModel;
-class CWallet;
-
QT_BEGIN_NAMESPACE
class QApplication;
class QByteArray;
@@ -62,7 +60,7 @@ class PaymentServer : public QObject
public:
// Parse URIs on command line
// Returns false on error
- static void ipcParseCommandLine(int argc, char *argv[]);
+ static void ipcParseCommandLine(interface::Node& node, int argc, char *argv[]);
// Returns true if there were URIs on the command line
// which were successfully sent to an already-running
@@ -89,7 +87,7 @@ public:
void setOptionsModel(OptionsModel *optionsModel);
// Verify that the payment request network matches the client network
- static bool verifyNetwork(const payments::PaymentDetails& requestDetails);
+ static bool verifyNetwork(interface::Node& node, const payments::PaymentDetails& requestDetails);
// Verify if the payment request is expired
static bool verifyExpired(const payments::PaymentDetails& requestDetails);
// Verify the payment request size is valid as per BIP70
@@ -113,7 +111,7 @@ public Q_SLOTS:
void uiReady();
// Submit Payment message to a merchant, get back PaymentACK:
- void fetchPaymentACK(CWallet* wallet, const SendCoinsRecipient& recipient, QByteArray transaction);
+ void fetchPaymentACK(WalletModel* walletModel, const SendCoinsRecipient& recipient, QByteArray transaction);
// Handle an incoming URI, URI with local file scheme or file
void handleURIOrFile(const QString& s);
diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp
index 0a57dcfca3..f33db8e761 100644
--- a/src/qt/peertablemodel.cpp
+++ b/src/qt/peertablemodel.cpp
@@ -8,6 +8,7 @@
#include <qt/guiconstants.h>
#include <qt/guiutil.h>
+#include <interface/node.h>
#include <validation.h> // for cs_main
#include <sync.h>
@@ -56,38 +57,26 @@ public:
std::map<NodeId, int> mapNodeRows;
/** Pull a full list of peers from vNodes into our cache */
- void refreshPeers()
+ void refreshPeers(interface::Node& node)
{
{
cachedNodeStats.clear();
- std::vector<CNodeStats> vstats;
- if(g_connman)
- g_connman->GetNodeStats(vstats);
+
+ interface::Node::NodesStats nodes_stats;
+ node.getNodesStats(nodes_stats);
#if QT_VERSION >= 0x040700
- cachedNodeStats.reserve(vstats.size());
+ cachedNodeStats.reserve(nodes_stats.size());
#endif
- for (const CNodeStats& nodestats : vstats)
+ for (auto& node_stats : nodes_stats)
{
CNodeCombinedStats stats;
- stats.nodeStateStats.nMisbehavior = 0;
- stats.nodeStateStats.nSyncHeight = -1;
- stats.nodeStateStats.nCommonHeight = -1;
- stats.fNodeStateStatsAvailable = false;
- stats.nodeStats = nodestats;
+ stats.nodeStats = std::get<0>(node_stats);
+ stats.fNodeStateStatsAvailable = std::get<1>(node_stats);
+ stats.nodeStateStats = std::get<2>(node_stats);
cachedNodeStats.append(stats);
}
}
- // Try to retrieve the CNodeStateStats for each node.
- {
- TRY_LOCK(cs_main, lockMain);
- if (lockMain)
- {
- for (CNodeCombinedStats &stats : cachedNodeStats)
- stats.fNodeStateStatsAvailable = GetNodeStateStats(stats.nodeStats.nodeid, stats.nodeStateStats);
- }
- }
-
if (sortColumn >= 0)
// sort cacheNodeStats (use stable sort to prevent rows jumping around unnecessarily)
qStableSort(cachedNodeStats.begin(), cachedNodeStats.end(), NodeLessThan(sortColumn, sortOrder));
@@ -113,8 +102,9 @@ public:
}
};
-PeerTableModel::PeerTableModel(ClientModel *parent) :
+PeerTableModel::PeerTableModel(interface::Node& node, ClientModel *parent) :
QAbstractTableModel(parent),
+ m_node(node),
clientModel(parent),
timer(0)
{
@@ -235,7 +225,7 @@ const CNodeCombinedStats *PeerTableModel::getNodeStats(int idx)
void PeerTableModel::refresh()
{
Q_EMIT layoutAboutToBeChanged();
- priv->refreshPeers();
+ priv->refreshPeers(m_node);
Q_EMIT layoutChanged();
}
diff --git a/src/qt/peertablemodel.h b/src/qt/peertablemodel.h
index e3c9c6e5a3..c53462c57c 100644
--- a/src/qt/peertablemodel.h
+++ b/src/qt/peertablemodel.h
@@ -8,12 +8,18 @@
#include <net_processing.h> // For CNodeStateStats
#include <net.h>
+#include <memory>
+
#include <QAbstractTableModel>
#include <QStringList>
class ClientModel;
class PeerTablePriv;
+namespace interface {
+class Node;
+}
+
QT_BEGIN_NAMESPACE
class QTimer;
QT_END_NAMESPACE
@@ -45,7 +51,7 @@ class PeerTableModel : public QAbstractTableModel
Q_OBJECT
public:
- explicit PeerTableModel(ClientModel *parent = 0);
+ explicit PeerTableModel(interface::Node& node, ClientModel *parent = 0);
~PeerTableModel();
const CNodeCombinedStats *getNodeStats(int idx);
int getRowByNodeId(NodeId nodeid);
@@ -76,6 +82,7 @@ public Q_SLOTS:
void refresh();
private:
+ interface::Node& m_node;
ClientModel *clientModel;
QStringList columns;
std::unique_ptr<PeerTablePriv> priv;
diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp
index c8b6366db0..70e11f0296 100644
--- a/src/qt/receivecoinsdialog.cpp
+++ b/src/qt/receivecoinsdialog.cpp
@@ -95,13 +95,13 @@ void ReceiveCoinsDialog::setModel(WalletModel *_model)
columnResizingFixer = new GUIUtil::TableViewLastColumnResizingFixer(tableView, AMOUNT_MINIMUM_COLUMN_WIDTH, DATE_COLUMN_WIDTH, this);
// configure bech32 checkbox, disable if launched with legacy as default:
- if (model->getDefaultAddressType() == OutputType::BECH32) {
+ if (model->wallet().getDefaultAddressType() == OutputType::BECH32) {
ui->useBech32->setCheckState(Qt::Checked);
} else {
ui->useBech32->setCheckState(Qt::Unchecked);
}
- ui->useBech32->setVisible(model->getDefaultAddressType() != OutputType::LEGACY);
+ ui->useBech32->setVisible(model->wallet().getDefaultAddressType() != OutputType::LEGACY);
}
}
@@ -144,7 +144,7 @@ void ReceiveCoinsDialog::on_receiveButton_clicked()
QString address;
QString label = ui->reqLabel->text();
/* Generate new receiving address */
- OutputType address_type = model->getDefaultAddressType();
+ OutputType address_type = model->wallet().getDefaultAddressType();
if (address_type != OutputType::LEGACY) {
address_type = ui->useBech32->isChecked() ? OutputType::BECH32 : OutputType::P2SH_SEGWIT;
}
diff --git a/src/qt/recentrequeststablemodel.cpp b/src/qt/recentrequeststablemodel.cpp
index 0dd7d46960..1c910926d4 100644
--- a/src/qt/recentrequeststablemodel.cpp
+++ b/src/qt/recentrequeststablemodel.cpp
@@ -12,10 +12,9 @@
#include <streams.h>
-RecentRequestsTableModel::RecentRequestsTableModel(CWallet *wallet, WalletModel *parent) :
+RecentRequestsTableModel::RecentRequestsTableModel(WalletModel *parent) :
QAbstractTableModel(parent), walletModel(parent)
{
- Q_UNUSED(wallet);
nReceiveRequestsMaxId = 0;
// Load entries from wallet
@@ -139,10 +138,9 @@ bool RecentRequestsTableModel::removeRows(int row, int count, const QModelIndex
if(count > 0 && row >= 0 && (row+count) <= list.size())
{
- const RecentRequestEntry *rec;
for (int i = 0; i < count; ++i)
{
- rec = &list[row+i];
+ const RecentRequestEntry* rec = &list[row+i];
if (!walletModel->saveReceiveRequest(rec->recipient.address.toStdString(), rec->id, ""))
return false;
}
diff --git a/src/qt/recentrequeststablemodel.h b/src/qt/recentrequeststablemodel.h
index ebad98cee8..80c7834a19 100644
--- a/src/qt/recentrequeststablemodel.h
+++ b/src/qt/recentrequeststablemodel.h
@@ -11,8 +11,6 @@
#include <QStringList>
#include <QDateTime>
-class CWallet;
-
class RecentRequestEntry
{
public:
@@ -60,7 +58,7 @@ class RecentRequestsTableModel: public QAbstractTableModel
Q_OBJECT
public:
- explicit RecentRequestsTableModel(CWallet *wallet, WalletModel *parent);
+ explicit RecentRequestsTableModel(WalletModel *parent);
~RecentRequestsTableModel();
enum ColumnIndex {
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index c41e19f6f5..745055b944 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -14,6 +14,7 @@
#include <qt/platformstyle.h>
#include <qt/walletmodel.h>
#include <chainparams.h>
+#include <interface/node.h>
#include <netbase.h>
#include <rpc/server.h>
#include <rpc/client.h>
@@ -83,12 +84,17 @@ const QStringList historyFilter = QStringList()
class RPCExecutor : public QObject
{
Q_OBJECT
+public:
+ RPCExecutor(interface::Node& node) : m_node(node) {}
public Q_SLOTS:
void request(const QString &command, const QString &walletID);
Q_SIGNALS:
void reply(int category, const QString &command);
+
+private:
+ interface::Node& m_node;
};
/** Class for handling RPC timers
@@ -140,13 +146,14 @@ public:
* - Within double quotes, only escape \c " and backslashes before a \c " or another backslash
* - Within single quotes, no escaping is possible and no special interpretation takes place
*
+ * @param[in] node optional node to execute command on
* @param[out] result stringified Result from the executed command(chain)
* @param[in] strCommand Command line to split
* @param[in] fExecute set true if you want the command to be executed
* @param[out] pstrFilteredOut Command line, filtered to remove any sensitive data
*/
-bool RPCConsole::RPCParseCommandLine(std::string &strResult, const std::string &strCommand, const bool fExecute, std::string * const pstrFilteredOut, const std::string *walletID)
+bool RPCConsole::RPCParseCommandLine(interface::Node* node, std::string &strResult, const std::string &strCommand, const bool fExecute, std::string * const pstrFilteredOut, const std::string *walletID)
{
std::vector< std::vector<std::string> > stack;
stack.push_back(std::vector<std::string>());
@@ -300,16 +307,17 @@ bool RPCConsole::RPCParseCommandLine(std::string &strResult, const std::string &
if (fExecute) {
// Convert argument list to JSON objects in method-dependent way,
// and pass it along with the method name to the dispatcher.
- JSONRPCRequest req;
- req.params = RPCConvertValues(stack.back()[0], std::vector<std::string>(stack.back().begin() + 1, stack.back().end()));
- req.strMethod = stack.back()[0];
+ UniValue params = RPCConvertValues(stack.back()[0], std::vector<std::string>(stack.back().begin() + 1, stack.back().end()));
+ std::string method = stack.back()[0];
+ std::string uri;
#ifdef ENABLE_WALLET
if (walletID && !walletID->empty()) {
QByteArray encodedName = QUrl::toPercentEncoding(QString::fromStdString(*walletID));
- req.URI = "/wallet/"+std::string(encodedName.constData(), encodedName.length());
+ uri = "/wallet/"+std::string(encodedName.constData(), encodedName.length());
}
#endif
- lastResult = tableRPC.execute(req);
+ assert(node);
+ lastResult = node->executeRpc(method, params, uri);
}
state = STATE_COMMAND_EXECUTED;
@@ -416,7 +424,7 @@ void RPCExecutor::request(const QString &command, const QString &walletID)
return;
}
std::string wallet_id = walletID.toStdString();
- if(!RPCConsole::RPCExecuteCommandLine(result, executableCommand, nullptr, &wallet_id))
+ if(!RPCConsole::RPCExecuteCommandLine(m_node, result, executableCommand, nullptr, &wallet_id))
{
Q_EMIT reply(RPCConsole::CMD_ERROR, QString("Parse error: unbalanced ' or \""));
return;
@@ -443,8 +451,9 @@ void RPCExecutor::request(const QString &command, const QString &walletID)
}
}
-RPCConsole::RPCConsole(const PlatformStyle *_platformStyle, QWidget *parent) :
+RPCConsole::RPCConsole(interface::Node& node, const PlatformStyle *_platformStyle, QWidget *parent) :
QWidget(parent),
+ m_node(node),
ui(new Ui::RPCConsole),
clientModel(0),
historyPtr(0),
@@ -493,7 +502,7 @@ RPCConsole::RPCConsole(const PlatformStyle *_platformStyle, QWidget *parent) :
rpcTimerInterface = new QtRPCTimerInterface();
// avoid accidentally overwriting an existing, non QTThread
// based timer interface
- RPCSetTimerInterfaceIfUnset(rpcTimerInterface);
+ m_node.rpcSetTimerInterfaceIfUnset(rpcTimerInterface);
setTrafficGraphRange(INITIAL_TRAFFIC_GRAPH_MINS);
@@ -508,7 +517,7 @@ RPCConsole::~RPCConsole()
{
QSettings settings;
settings.setValue("RPCConsoleWindowGeometry", saveGeometry());
- RPCUnsetTimerInterface(rpcTimerInterface);
+ m_node.rpcUnsetTimerInterface(rpcTimerInterface);
delete rpcTimerInterface;
delete ui;
}
@@ -566,13 +575,14 @@ void RPCConsole::setClientModel(ClientModel *model)
setNumConnections(model->getNumConnections());
connect(model, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int)));
- setNumBlocks(model->getNumBlocks(), model->getLastBlockDate(), model->getVerificationProgress(nullptr), false);
+ interface::Node& node = clientModel->node();
+ setNumBlocks(node.getNumBlocks(), QDateTime::fromTime_t(node.getLastBlockTime()), node.getVerificationProgress(), false);
connect(model, SIGNAL(numBlocksChanged(int,QDateTime,double,bool)), this, SLOT(setNumBlocks(int,QDateTime,double,bool)));
updateNetworkState();
connect(model, SIGNAL(networkActiveChanged(bool)), this, SLOT(setNetworkActive(bool)));
- updateTrafficStats(model->getTotalBytesRecv(), model->getTotalBytesSent());
+ updateTrafficStats(node.getTotalBytesRecv(), node.getTotalBytesSent());
connect(model, SIGNAL(bytesChanged(quint64,quint64)), this, SLOT(updateTrafficStats(quint64, quint64)));
connect(model, SIGNAL(mempoolSizeChanged(long,size_t)), this, SLOT(setMempoolSize(long,size_t)));
@@ -667,7 +677,7 @@ void RPCConsole::setClientModel(ClientModel *model)
//Setup autocomplete and attach it
QStringList wordList;
- std::vector<std::string> commandList = tableRPC.listCommands();
+ std::vector<std::string> commandList = m_node.listRpcCommands();
for (size_t i = 0; i < commandList.size(); ++i)
{
wordList << commandList[i].c_str();
@@ -835,7 +845,7 @@ void RPCConsole::updateNetworkState()
connections += tr("In:") + " " + QString::number(clientModel->getNumConnections(CONNECTIONS_IN)) + " / ";
connections += tr("Out:") + " " + QString::number(clientModel->getNumConnections(CONNECTIONS_OUT)) + ")";
- if(!clientModel->getNetworkActive()) {
+ if(!clientModel->node().getNetworkActive()) {
connections += " (" + tr("Network activity disabled") + ")";
}
@@ -882,7 +892,7 @@ void RPCConsole::on_lineEdit_returnPressed()
std::string strFilteredCmd;
try {
std::string dummy;
- if (!RPCParseCommandLine(dummy, cmd.toStdString(), false, &strFilteredCmd)) {
+ if (!RPCParseCommandLine(nullptr, dummy, cmd.toStdString(), false, &strFilteredCmd)) {
// Failed to parse command, so we cannot even filter it for the history
throw std::runtime_error("Invalid command line");
}
@@ -955,7 +965,7 @@ void RPCConsole::browseHistory(int offset)
void RPCConsole::startExecutor()
{
- RPCExecutor *executor = new RPCExecutor();
+ RPCExecutor *executor = new RPCExecutor(m_node);
executor->moveToThread(&thread);
// Replies from executor object must go to this object
@@ -1181,9 +1191,6 @@ void RPCConsole::showBanTableContextMenu(const QPoint& point)
void RPCConsole::disconnectSelectedNode()
{
- if(!g_connman)
- return;
-
// Get selected peer addresses
QList<QModelIndex> nodes = GUIUtil::getEntryData(ui->peerWidget, PeerTableModel::NetNodeId);
for(int i = 0; i < nodes.count(); i++)
@@ -1191,14 +1198,14 @@ void RPCConsole::disconnectSelectedNode()
// Get currently selected peer address
NodeId id = nodes.at(i).data().toLongLong();
// Find the node, disconnect it and clear the selected node
- if(g_connman->DisconnectNode(id))
+ if(m_node.disconnect(id))
clearSelectedNode();
}
}
void RPCConsole::banSelectedNode(int bantime)
{
- if (!clientModel || !g_connman)
+ if (!clientModel)
return;
// Get selected peer addresses
@@ -1216,7 +1223,7 @@ void RPCConsole::banSelectedNode(int bantime)
// Find possible nodes, ban it and clear the selected node
const CNodeCombinedStats *stats = clientModel->getPeerTableModel()->getNodeStats(detailNodeRow);
if(stats) {
- g_connman->Ban(stats->nodeStats.addr, BanReasonManuallyAdded, bantime);
+ m_node.ban(stats->nodeStats.addr, BanReasonManuallyAdded, bantime);
}
}
clearSelectedNode();
@@ -1237,9 +1244,8 @@ void RPCConsole::unbanSelectedNode()
CSubNet possibleSubnet;
LookupSubNet(strNode.toStdString().c_str(), possibleSubnet);
- if (possibleSubnet.IsValid() && g_connman)
+ if (possibleSubnet.IsValid() && m_node.unban(possibleSubnet))
{
- g_connman->Unban(possibleSubnet);
clientModel->getBanTableModel()->refresh();
}
}
diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h
index c97260b2c3..8381301759 100644
--- a/src/qt/rpcconsole.h
+++ b/src/qt/rpcconsole.h
@@ -19,6 +19,10 @@ class PlatformStyle;
class RPCTimerInterface;
class WalletModel;
+namespace interface {
+ class Node;
+}
+
namespace Ui {
class RPCConsole;
}
@@ -34,12 +38,12 @@ class RPCConsole: public QWidget
Q_OBJECT
public:
- explicit RPCConsole(const PlatformStyle *platformStyle, QWidget *parent);
+ explicit RPCConsole(interface::Node& node, const PlatformStyle *platformStyle, QWidget *parent);
~RPCConsole();
- static bool RPCParseCommandLine(std::string &strResult, const std::string &strCommand, bool fExecute, std::string * const pstrFilteredOut = nullptr, const std::string *walletID = nullptr);
- static bool RPCExecuteCommandLine(std::string &strResult, const std::string &strCommand, std::string * const pstrFilteredOut = nullptr, const std::string *walletID = nullptr) {
- return RPCParseCommandLine(strResult, strCommand, true, pstrFilteredOut, walletID);
+ static bool RPCParseCommandLine(interface::Node* node, std::string &strResult, const std::string &strCommand, bool fExecute, std::string * const pstrFilteredOut = nullptr, const std::string *walletID = nullptr);
+ static bool RPCExecuteCommandLine(interface::Node& node, std::string &strResult, const std::string &strCommand, std::string * const pstrFilteredOut = nullptr, const std::string *walletID = nullptr) {
+ return RPCParseCommandLine(&node, strResult, strCommand, true, pstrFilteredOut, walletID);
}
void setClientModel(ClientModel *model);
@@ -140,6 +144,7 @@ private:
};
+ interface::Node& m_node;
Ui::RPCConsole *ui;
ClientModel *clientModel;
QStringList history;
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 8a52aadbb0..b4c1471a4f 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -15,9 +15,9 @@
#include <qt/sendcoinsentry.h>
#include <chainparams.h>
+#include <interface/node.h>
#include <key_io.h>
#include <wallet/coincontrol.h>
-#include <validation.h> // mempool and minRelayTxFee
#include <ui_interface.h>
#include <txmempool.h>
#include <policy/fees.h>
@@ -149,9 +149,9 @@ void SendCoinsDialog::setModel(WalletModel *_model)
}
}
- setBalance(_model->getBalance(), _model->getUnconfirmedBalance(), _model->getImmatureBalance(),
- _model->getWatchBalance(), _model->getWatchUnconfirmedBalance(), _model->getWatchImmatureBalance());
- connect(_model, SIGNAL(balanceChanged(CAmount,CAmount,CAmount,CAmount,CAmount,CAmount)), this, SLOT(setBalance(CAmount,CAmount,CAmount,CAmount,CAmount,CAmount)));
+ interface::WalletBalances balances = _model->wallet().getBalances();
+ setBalance(balances);
+ connect(_model, SIGNAL(balanceChanged(interface::WalletBalances)), this, SLOT(setBalance(interface::WalletBalances)));
connect(_model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
updateDisplayUnit();
@@ -175,7 +175,7 @@ void SendCoinsDialog::setModel(WalletModel *_model)
connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(coinControlUpdateLabels()));
connect(ui->optInRBF, SIGNAL(stateChanged(int)), this, SLOT(updateSmartFeeLabel()));
connect(ui->optInRBF, SIGNAL(stateChanged(int)), this, SLOT(coinControlUpdateLabels()));
- ui->customFee->setSingleStep(GetRequiredFee(1000));
+ ui->customFee->setSingleStep(model->node().getRequiredFee(1000));
updateFeeSectionControls();
updateMinFeeLabel();
updateSmartFeeLabel();
@@ -193,7 +193,7 @@ void SendCoinsDialog::setModel(WalletModel *_model)
settings.remove("nSmartFeeSliderPosition");
}
if (settings.value("nConfTarget").toInt() == 0)
- ui->confTargetSelector->setCurrentIndex(getIndexForConfTarget(model->getDefaultConfirmTarget()));
+ ui->confTargetSelector->setCurrentIndex(getIndexForConfTarget(model->node().getTxConfirmTarget()));
else
ui->confTargetSelector->setCurrentIndex(getIndexForConfTarget(settings.value("nConfTarget").toInt()));
}
@@ -224,7 +224,7 @@ void SendCoinsDialog::on_sendButton_clicked()
SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
if(entry)
{
- if(entry->validate())
+ if(entry->validate(model->node()))
{
recipients.append(entry->getValue());
}
@@ -372,7 +372,7 @@ void SendCoinsDialog::on_sendButton_clicked()
accept();
CoinControlDialog::coinControl()->UnSelectAll();
coinControlUpdateLabels();
- Q_EMIT coinsSent(currentTransaction.getTransaction()->GetHash());
+ Q_EMIT coinsSent(currentTransaction.getWtx()->get().GetHash());
}
fNewRecipientAllowed = true;
}
@@ -515,24 +515,17 @@ bool SendCoinsDialog::handlePaymentRequest(const SendCoinsRecipient &rv)
return true;
}
-void SendCoinsDialog::setBalance(const CAmount& balance, const CAmount& unconfirmedBalance, const CAmount& immatureBalance,
- const CAmount& watchBalance, const CAmount& watchUnconfirmedBalance, const CAmount& watchImmatureBalance)
+void SendCoinsDialog::setBalance(const interface::WalletBalances& balances)
{
- Q_UNUSED(unconfirmedBalance);
- Q_UNUSED(immatureBalance);
- Q_UNUSED(watchBalance);
- Q_UNUSED(watchUnconfirmedBalance);
- Q_UNUSED(watchImmatureBalance);
-
if(model && model->getOptionsModel())
{
- ui->labelBalance->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), balance));
+ ui->labelBalance->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), balances.balance));
}
}
void SendCoinsDialog::updateDisplayUnit()
{
- setBalance(model->getBalance(), 0, 0, 0, 0, 0);
+ setBalance(model->wallet().getBalances());
ui->customFee->setDisplayUnit(model->getOptionsModel()->getDisplayUnit());
updateMinFeeLabel();
updateSmartFeeLabel();
@@ -573,7 +566,7 @@ void SendCoinsDialog::processSendCoinsReturn(const WalletModel::SendCoinsReturn
msgParams.second = CClientUIInterface::MSG_ERROR;
break;
case WalletModel::AbsurdFee:
- msgParams.first = tr("A fee higher than %1 is considered an absurdly high fee.").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), maxTxFee));
+ msgParams.first = tr("A fee higher than %1 is considered an absurdly high fee.").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), model->node().getMaxTxFee()));
break;
case WalletModel::PaymentRequestExpired:
msgParams.first = tr("Payment request expired.");
@@ -618,7 +611,7 @@ void SendCoinsDialog::useAvailableBalance(SendCoinsEntry* entry)
}
// Calculate available amount to send.
- CAmount amount = model->getBalance(&coin_control);
+ CAmount amount = model->wallet().getAvailableBalance(coin_control);
for (int i = 0; i < ui->entries->count(); ++i) {
SendCoinsEntry* e = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
if (e && !e->isHidden() && e != entry) {
@@ -636,7 +629,7 @@ void SendCoinsDialog::useAvailableBalance(SendCoinsEntry* entry)
void SendCoinsDialog::setMinimumFee()
{
- ui->customFee->setValue(GetRequiredFee(1000));
+ ui->customFee->setValue(model->node().getRequiredFee(1000));
}
void SendCoinsDialog::updateFeeSectionControls()
@@ -668,7 +661,7 @@ void SendCoinsDialog::updateMinFeeLabel()
{
if (model && model->getOptionsModel())
ui->checkBoxMinimumFee->setText(tr("Pay only the required fee of %1").arg(
- BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), GetRequiredFee(1000)) + "/kB")
+ BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), model->node().getRequiredFee(1000)) + "/kB")
);
}
@@ -692,12 +685,13 @@ void SendCoinsDialog::updateSmartFeeLabel()
CCoinControl coin_control;
updateCoinControlState(coin_control);
coin_control.m_feerate.reset(); // Explicitly use only fee estimation rate for smart fee labels
- FeeCalculation feeCalc;
- CFeeRate feeRate = CFeeRate(GetMinimumFee(1000, coin_control, ::mempool, ::feeEstimator, &feeCalc));
+ int returned_target;
+ FeeReason reason;
+ CFeeRate feeRate = CFeeRate(model->node().getMinimumFee(1000, coin_control, &returned_target, &reason));
ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), feeRate.GetFeePerK()) + "/kB");
- if (feeCalc.reason == FeeReason::FALLBACK) {
+ if (reason == FeeReason::FALLBACK) {
ui->labelSmartFee2->show(); // (Smart fee not initialized yet. This usually takes a few blocks...)
ui->labelFeeEstimation->setText("");
ui->fallbackFeeWarningLabel->setVisible(true);
@@ -709,7 +703,7 @@ void SendCoinsDialog::updateSmartFeeLabel()
else
{
ui->labelSmartFee2->hide();
- ui->labelFeeEstimation->setText(tr("Estimated to begin confirmation within %n block(s).", "", feeCalc.returnedTarget));
+ ui->labelFeeEstimation->setText(tr("Estimated to begin confirmation within %n block(s).", "", returned_target));
ui->fallbackFeeWarningLabel->setVisible(false);
}
@@ -814,7 +808,7 @@ void SendCoinsDialog::coinControlChangeEdited(const QString& text)
}
else // Valid address
{
- if (!model->IsSpendable(dest)) {
+ if (!model->wallet().isSpendable(dest)) {
ui->labelCoinControlChangeLabel->setText(tr("Warning: Unknown change address"));
// confirmation dialog
diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h
index 48885bbcad..43757e3186 100644
--- a/src/qt/sendcoinsdialog.h
+++ b/src/qt/sendcoinsdialog.h
@@ -51,8 +51,7 @@ public Q_SLOTS:
void accept();
SendCoinsEntry *addEntry();
void updateTabsAndLabels();
- void setBalance(const CAmount& balance, const CAmount& unconfirmedBalance, const CAmount& immatureBalance,
- const CAmount& watchOnlyBalance, const CAmount& watchUnconfBalance, const CAmount& watchImmatureBalance);
+ void setBalance(const interface::WalletBalances& balances);
Q_SIGNALS:
void coinsSent(const uint256& txid);
diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp
index b7decbb69b..6a961d83ea 100644
--- a/src/qt/sendcoinsentry.cpp
+++ b/src/qt/sendcoinsentry.cpp
@@ -127,7 +127,7 @@ void SendCoinsEntry::useAvailableBalanceClicked()
Q_EMIT useAvailableBalance(this);
}
-bool SendCoinsEntry::validate()
+bool SendCoinsEntry::validate(interface::Node& node)
{
if (!model)
return false;
@@ -158,7 +158,7 @@ bool SendCoinsEntry::validate()
}
// Reject dust outputs:
- if (retval && GUIUtil::isDust(ui->payTo->text(), ui->payAmount->value())) {
+ if (retval && GUIUtil::isDust(node, ui->payTo->text(), ui->payAmount->value())) {
ui->payAmount->setValid(false);
retval = false;
}
diff --git a/src/qt/sendcoinsentry.h b/src/qt/sendcoinsentry.h
index a9fdd5938c..715f4cfde5 100644
--- a/src/qt/sendcoinsentry.h
+++ b/src/qt/sendcoinsentry.h
@@ -30,7 +30,7 @@ public:
~SendCoinsEntry();
void setModel(WalletModel *model);
- bool validate();
+ bool validate(interface::Node& node);
SendCoinsRecipient getValue();
/** Return whether the entry is still empty and unedited */
diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp
index 8dade8df79..94a3ad7987 100644
--- a/src/qt/signverifymessagedialog.cpp
+++ b/src/qt/signverifymessagedialog.cpp
@@ -140,7 +140,7 @@ void SignVerifyMessageDialog::on_signMessageButton_SM_clicked()
}
CKey key;
- if (!model->getPrivKey(*keyID, key))
+ if (!model->wallet().getPrivKey(*keyID, key))
{
ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }");
ui->statusLabel_SM->setText(tr("Private key for the entered address is not available."));
diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp
index 66e9dd0465..2475a82ef9 100644
--- a/src/qt/splashscreen.cpp
+++ b/src/qt/splashscreen.cpp
@@ -12,22 +12,21 @@
#include <clientversion.h>
#include <init.h>
+#include <interface/handler.h>
+#include <interface/node.h>
+#include <interface/wallet.h>
#include <util.h>
#include <ui_interface.h>
#include <version.h>
-#ifdef ENABLE_WALLET
-#include <wallet/wallet.h>
-#endif
-
#include <QApplication>
#include <QCloseEvent>
#include <QDesktopWidget>
#include <QPainter>
#include <QRadialGradient>
-SplashScreen::SplashScreen(Qt::WindowFlags f, const NetworkStyle *networkStyle) :
- QWidget(0, f), curAlignment(0)
+SplashScreen::SplashScreen(interface::Node& node, Qt::WindowFlags f, const NetworkStyle *networkStyle) :
+ QWidget(0, f), curAlignment(0), m_node(node)
{
// set reference point, paddings
int paddingRight = 50;
@@ -143,7 +142,7 @@ bool SplashScreen::eventFilter(QObject * obj, QEvent * ev) {
if (ev->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(ev);
if(keyEvent->text()[0] == 'q') {
- StartShutdown();
+ m_node.startShutdown();
}
}
return QObject::eventFilter(obj, ev);
@@ -177,35 +176,34 @@ static void ShowProgress(SplashScreen *splash, const std::string &title, int nPr
: _("press q to shutdown")) +
strprintf("\n%d", nProgress) + "%");
}
-
#ifdef ENABLE_WALLET
-void SplashScreen::ConnectWallet(CWallet* wallet)
+void SplashScreen::ConnectWallet(std::unique_ptr<interface::Wallet> wallet)
{
- wallet->ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2, false));
- connectedWallets.push_back(wallet);
+ m_connected_wallet_handlers.emplace_back(wallet->handleShowProgress(boost::bind(ShowProgress, this, _1, _2, false)));
+ m_connected_wallets.emplace_back(std::move(wallet));
}
#endif
void SplashScreen::subscribeToCoreSignals()
{
// Connect signals to client
- uiInterface.InitMessage.connect(boost::bind(InitMessage, this, _1));
- uiInterface.ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2, _3));
+ m_handler_init_message = m_node.handleInitMessage(boost::bind(InitMessage, this, _1));
+ m_handler_show_progress = m_node.handleShowProgress(boost::bind(ShowProgress, this, _1, _2, _3));
#ifdef ENABLE_WALLET
- uiInterface.LoadWallet.connect(boost::bind(&SplashScreen::ConnectWallet, this, _1));
+ m_handler_load_wallet = m_node.handleLoadWallet([this](std::unique_ptr<interface::Wallet> wallet) { ConnectWallet(std::move(wallet)); });
#endif
}
void SplashScreen::unsubscribeFromCoreSignals()
{
// Disconnect signals from client
- uiInterface.InitMessage.disconnect(boost::bind(InitMessage, this, _1));
- uiInterface.ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2, _3));
-#ifdef ENABLE_WALLET
- for (CWallet* const & pwallet : connectedWallets) {
- pwallet->ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2, false));
+ m_handler_init_message->disconnect();
+ m_handler_show_progress->disconnect();
+ for (auto& handler : m_connected_wallet_handlers) {
+ handler->disconnect();
}
-#endif
+ m_connected_wallet_handlers.clear();
+ m_connected_wallets.clear();
}
void SplashScreen::showMessage(const QString &message, int alignment, const QColor &color)
@@ -227,6 +225,6 @@ void SplashScreen::paintEvent(QPaintEvent *event)
void SplashScreen::closeEvent(QCloseEvent *event)
{
- StartShutdown(); // allows an "emergency" shutdown during startup
+ m_node.startShutdown(); // allows an "emergency" shutdown during startup
event->ignore();
}
diff --git a/src/qt/splashscreen.h b/src/qt/splashscreen.h
index e1568e406b..419f36f641 100644
--- a/src/qt/splashscreen.h
+++ b/src/qt/splashscreen.h
@@ -8,9 +8,16 @@
#include <functional>
#include <QSplashScreen>
-class CWallet;
+#include <memory>
+
class NetworkStyle;
+namespace interface {
+class Handler;
+class Node;
+class Wallet;
+};
+
/** Class for the splashscreen with information of the running client.
*
* @note this is intentionally not a QSplashScreen. Bitcoin Core initialization
@@ -22,7 +29,7 @@ class SplashScreen : public QWidget
Q_OBJECT
public:
- explicit SplashScreen(Qt::WindowFlags f, const NetworkStyle *networkStyle);
+ explicit SplashScreen(interface::Node& node, Qt::WindowFlags f, const NetworkStyle *networkStyle);
~SplashScreen();
protected:
@@ -45,14 +52,19 @@ private:
/** Disconnect core signals to splash screen */
void unsubscribeFromCoreSignals();
/** Connect wallet signals to splash screen */
- void ConnectWallet(CWallet*);
+ void ConnectWallet(std::unique_ptr<interface::Wallet> wallet);
QPixmap pixmap;
QString curMessage;
QColor curColor;
int curAlignment;
- QList<CWallet*> connectedWallets;
+ interface::Node& m_node;
+ std::unique_ptr<interface::Handler> m_handler_init_message;
+ std::unique_ptr<interface::Handler> m_handler_show_progress;
+ std::unique_ptr<interface::Handler> m_handler_load_wallet;
+ std::list<std::unique_ptr<interface::Wallet>> m_connected_wallets;
+ std::list<std::unique_ptr<interface::Handler>> m_connected_wallet_handlers;
};
#endif // BITCOIN_QT_SPLASHSCREEN_H
diff --git a/src/qt/test/paymentrequestdata.h b/src/qt/test/paymentrequestdata.h
index 74a2db8ea2..8e5a259f68 100644
--- a/src/qt/test/paymentrequestdata.h
+++ b/src/qt/test/paymentrequestdata.h
@@ -2,6 +2,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#ifndef BITCOIN_QT_TEST_PAYMENTREQUESTDATA_H
+#define BITCOIN_QT_TEST_PAYMENTREQUESTDATA_H
+
//
// Data for paymentservertests.cpp
//
@@ -458,3 +461,5 @@ iEBFUrBDJZU+UEezGwr7/zoECjo5ZY3PmtZcM2sILNjyweJF6XVzGqTxUw6pN6sW\
XR2T3Gy2LzRvhVA25QgGqpz0/juS2BtmNbsZPkN9gMMwKimgzc+PuCzmEKwPK9cQ\
YQ==\
";
+
+#endif // BITCOIN_QT_TEST_PAYMENTREQUESTDATA_H
diff --git a/src/qt/test/paymentservertests.cpp b/src/qt/test/paymentservertests.cpp
index 29ef4b4c9e..46e9d4d0a4 100644
--- a/src/qt/test/paymentservertests.cpp
+++ b/src/qt/test/paymentservertests.cpp
@@ -9,6 +9,7 @@
#include <amount.h>
#include <chainparams.h>
+#include <interface/node.h>
#include <random.h>
#include <script/script.h>
#include <script/standard.h>
@@ -66,7 +67,8 @@ static SendCoinsRecipient handleRequest(PaymentServer* server, std::vector<unsig
void PaymentServerTests::paymentServerTests()
{
SelectParams(CBaseChainParams::MAIN);
- OptionsModel optionsModel;
+ auto node = interface::MakeNode();
+ OptionsModel optionsModel(*node);
PaymentServer* server = new PaymentServer(nullptr, false);
X509_STORE* caStore = X509_STORE_new();
X509_STORE_add_cert(caStore, parse_b64der_cert(caCert1_BASE64));
@@ -145,7 +147,7 @@ void PaymentServerTests::paymentServerTests()
// Ensure the request is initialized, because network "main" is default, even for
// uninitialized payment requests and that will fail our test here.
QVERIFY(r.paymentRequest.IsInitialized());
- QCOMPARE(PaymentServer::verifyNetwork(r.paymentRequest.getDetails()), false);
+ QCOMPARE(PaymentServer::verifyNetwork(*node, r.paymentRequest.getDetails()), false);
// Expired payment request (expires is set to 1 = 1970-01-01 00:00:01):
data = DecodeBase64(paymentrequest2_cert2_BASE64);
diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp
index 9d0e0b97d1..467107dc4c 100644
--- a/src/qt/test/rpcnestedtests.cpp
+++ b/src/qt/test/rpcnestedtests.cpp
@@ -7,6 +7,7 @@
#include <chainparams.h>
#include <consensus/validation.h>
#include <fs.h>
+#include <interface/node.h>
#include <validation.h>
#include <rpc/register.h>
#include <rpc/server.h>
@@ -45,89 +46,90 @@ void RPCNestedTests::rpcNestedTests()
std::string result;
std::string result2;
std::string filtered;
- RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo()[chain]", &filtered); //simple result filtering with path
+ auto node = interface::MakeNode();
+ RPCConsole::RPCExecuteCommandLine(*node, result, "getblockchaininfo()[chain]", &filtered); //simple result filtering with path
QVERIFY(result=="main");
QVERIFY(filtered == "getblockchaininfo()[chain]");
- RPCConsole::RPCExecuteCommandLine(result, "getblock(getbestblockhash())"); //simple 2 level nesting
- RPCConsole::RPCExecuteCommandLine(result, "getblock(getblock(getbestblockhash())[hash], true)");
+ RPCConsole::RPCExecuteCommandLine(*node, result, "getblock(getbestblockhash())"); //simple 2 level nesting
+ RPCConsole::RPCExecuteCommandLine(*node, result, "getblock(getblock(getbestblockhash())[hash], true)");
- RPCConsole::RPCExecuteCommandLine(result, "getblock( getblock( getblock(getbestblockhash())[hash] )[hash], true)"); //4 level nesting with whitespace, filtering path and boolean parameter
+ RPCConsole::RPCExecuteCommandLine(*node, result, "getblock( getblock( getblock(getbestblockhash())[hash] )[hash], true)"); //4 level nesting with whitespace, filtering path and boolean parameter
- RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo");
+ RPCConsole::RPCExecuteCommandLine(*node, result, "getblockchaininfo");
QVERIFY(result.substr(0,1) == "{");
- RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo()");
+ RPCConsole::RPCExecuteCommandLine(*node, result, "getblockchaininfo()");
QVERIFY(result.substr(0,1) == "{");
- RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo "); //whitespace at the end will be tolerated
+ RPCConsole::RPCExecuteCommandLine(*node, result, "getblockchaininfo "); //whitespace at the end will be tolerated
QVERIFY(result.substr(0,1) == "{");
- (RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo()[\"chain\"]")); //Quote path identifier are allowed, but look after a child containing the quotes in the key
+ (RPCConsole::RPCExecuteCommandLine(*node, result, "getblockchaininfo()[\"chain\"]")); //Quote path identifier are allowed, but look after a child containing the quotes in the key
QVERIFY(result == "null");
- (RPCConsole::RPCExecuteCommandLine(result, "createrawtransaction [] {} 0")); //parameter not in brackets are allowed
- (RPCConsole::RPCExecuteCommandLine(result2, "createrawtransaction([],{},0)")); //parameter in brackets are allowed
+ (RPCConsole::RPCExecuteCommandLine(*node, result, "createrawtransaction [] {} 0")); //parameter not in brackets are allowed
+ (RPCConsole::RPCExecuteCommandLine(*node, result2, "createrawtransaction([],{},0)")); //parameter in brackets are allowed
QVERIFY(result == result2);
- (RPCConsole::RPCExecuteCommandLine(result2, "createrawtransaction( [], {} , 0 )")); //whitespace between parameters is allowed
+ (RPCConsole::RPCExecuteCommandLine(*node, result2, "createrawtransaction( [], {} , 0 )")); //whitespace between parameters is allowed
QVERIFY(result == result2);
- RPCConsole::RPCExecuteCommandLine(result, "getblock(getbestblockhash())[tx][0]", &filtered);
+ RPCConsole::RPCExecuteCommandLine(*node, result, "getblock(getbestblockhash())[tx][0]", &filtered);
QVERIFY(result == "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b");
QVERIFY(filtered == "getblock(getbestblockhash())[tx][0]");
- RPCConsole::RPCParseCommandLine(result, "importprivkey", false, &filtered);
+ RPCConsole::RPCParseCommandLine(nullptr, result, "importprivkey", false, &filtered);
QVERIFY(filtered == "importprivkey(…)");
- RPCConsole::RPCParseCommandLine(result, "signmessagewithprivkey abc", false, &filtered);
+ RPCConsole::RPCParseCommandLine(nullptr, result, "signmessagewithprivkey abc", false, &filtered);
QVERIFY(filtered == "signmessagewithprivkey(…)");
- RPCConsole::RPCParseCommandLine(result, "signmessagewithprivkey abc,def", false, &filtered);
+ RPCConsole::RPCParseCommandLine(nullptr, result, "signmessagewithprivkey abc,def", false, &filtered);
QVERIFY(filtered == "signmessagewithprivkey(…)");
- RPCConsole::RPCParseCommandLine(result, "signrawtransactionwithkey(abc)", false, &filtered);
+ RPCConsole::RPCParseCommandLine(nullptr, result, "signrawtransactionwithkey(abc)", false, &filtered);
QVERIFY(filtered == "signrawtransactionwithkey(…)");
- RPCConsole::RPCParseCommandLine(result, "walletpassphrase(help())", false, &filtered);
+ RPCConsole::RPCParseCommandLine(nullptr, result, "walletpassphrase(help())", false, &filtered);
QVERIFY(filtered == "walletpassphrase(…)");
- RPCConsole::RPCParseCommandLine(result, "walletpassphrasechange(help(walletpassphrasechange(abc)))", false, &filtered);
+ RPCConsole::RPCParseCommandLine(nullptr, result, "walletpassphrasechange(help(walletpassphrasechange(abc)))", false, &filtered);
QVERIFY(filtered == "walletpassphrasechange(…)");
- RPCConsole::RPCParseCommandLine(result, "help(encryptwallet(abc, def))", false, &filtered);
+ RPCConsole::RPCParseCommandLine(nullptr, result, "help(encryptwallet(abc, def))", false, &filtered);
QVERIFY(filtered == "help(encryptwallet(…))");
- RPCConsole::RPCParseCommandLine(result, "help(importprivkey())", false, &filtered);
+ RPCConsole::RPCParseCommandLine(nullptr, result, "help(importprivkey())", false, &filtered);
QVERIFY(filtered == "help(importprivkey(…))");
- RPCConsole::RPCParseCommandLine(result, "help(importprivkey(help()))", false, &filtered);
+ RPCConsole::RPCParseCommandLine(nullptr, result, "help(importprivkey(help()))", false, &filtered);
QVERIFY(filtered == "help(importprivkey(…))");
- RPCConsole::RPCParseCommandLine(result, "help(importprivkey(abc), walletpassphrase(def))", false, &filtered);
+ RPCConsole::RPCParseCommandLine(nullptr, result, "help(importprivkey(abc), walletpassphrase(def))", false, &filtered);
QVERIFY(filtered == "help(importprivkey(…), walletpassphrase(…))");
- RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest");
+ RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest");
QVERIFY(result == "[]");
- RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest ''");
+ RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest ''");
QVERIFY(result == "[\"\"]");
- RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest \"\"");
+ RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest \"\"");
QVERIFY(result == "[\"\"]");
- RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest '' abc");
+ RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest '' abc");
QVERIFY(result == "[\"\",\"abc\"]");
- RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest abc '' abc");
+ RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest abc '' abc");
QVERIFY(result == "[\"abc\",\"\",\"abc\"]");
- RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest abc abc");
+ RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest abc abc");
QVERIFY(result == "[\"abc\",\"abc\"]");
- RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest abc\t\tabc");
+ RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest abc\t\tabc");
QVERIFY(result == "[\"abc\",\"abc\"]");
- RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest(abc )");
+ RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest(abc )");
QVERIFY(result == "[\"abc\"]");
- RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest( abc )");
+ RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest( abc )");
QVERIFY(result == "[\"abc\"]");
- RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest( abc , cba )");
+ RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest( abc , cba )");
QVERIFY(result == "[\"abc\",\"cba\"]");
#if QT_VERSION >= 0x050300
// do the QVERIFY_EXCEPTION_THROWN checks only with Qt5.3 and higher (QVERIFY_EXCEPTION_THROWN was introduced in Qt5.3)
- QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo() .\n"), std::runtime_error); //invalid syntax
- QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo() getblockchaininfo()"), std::runtime_error); //invalid syntax
- (RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo(")); //tolerate non closing brackets if we have no arguments
- (RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo()()()")); //tolerate non command brackts
- QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo(True)"), UniValue); //invalid argument
- QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(result, "a(getblockchaininfo(True))"), UniValue); //method not found
- QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest abc,,abc"), std::runtime_error); //don't tollerate empty arguments when using ,
- QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest(abc,,abc)"), std::runtime_error); //don't tollerate empty arguments when using ,
- QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest(abc,,)"), std::runtime_error); //don't tollerate empty arguments when using ,
+ QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(*node, result, "getblockchaininfo() .\n"), std::runtime_error); //invalid syntax
+ QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(*node, result, "getblockchaininfo() getblockchaininfo()"), std::runtime_error); //invalid syntax
+ (RPCConsole::RPCExecuteCommandLine(*node, result, "getblockchaininfo(")); //tolerate non closing brackets if we have no arguments
+ (RPCConsole::RPCExecuteCommandLine(*node, result, "getblockchaininfo()()()")); //tolerate non command brackts
+ QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(*node, result, "getblockchaininfo(True)"), UniValue); //invalid argument
+ QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(*node, result, "a(getblockchaininfo(True))"), UniValue); //method not found
+ QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest abc,,abc"), std::runtime_error); //don't tollerate empty arguments when using ,
+ QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest(abc,,abc)"), std::runtime_error); //don't tollerate empty arguments when using ,
+ QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest(abc,,)"), std::runtime_error); //don't tollerate empty arguments when using ,
#endif
}
diff --git a/src/qt/test/rpcnestedtests.h b/src/qt/test/rpcnestedtests.h
index 0ce1c66f44..7b3b38f62e 100644
--- a/src/qt/test/rpcnestedtests.h
+++ b/src/qt/test/rpcnestedtests.h
@@ -2,8 +2,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#ifndef BITCOIN_QT_TEST_RPC_NESTED_TESTS_H
-#define BITCOIN_QT_TEST_RPC_NESTED_TESTS_H
+#ifndef BITCOIN_QT_TEST_RPCNESTEDTESTS_H
+#define BITCOIN_QT_TEST_RPCNESTEDTESTS_H
#include <QObject>
#include <QTest>
@@ -19,4 +19,4 @@ class RPCNestedTests : public QObject
void rpcNestedTests();
};
-#endif // BITCOIN_QT_TEST_RPC_NESTED_TESTS_H
+#endif // BITCOIN_QT_TEST_RPCNESTEDTESTS_H
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index c9898e52ca..b60c5b979f 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -1,5 +1,6 @@
#include <qt/test/wallettests.h>
+#include <interface/node.h>
#include <qt/bitcoinamountfield.h>
#include <qt/callback.h>
#include <qt/optionsmodel.h>
@@ -19,6 +20,8 @@
#include <qt/recentrequeststablemodel.h>
#include <qt/receiverequestdialog.h>
+#include <memory>
+
#include <QAbstractButton>
#include <QAction>
#include <QApplication>
@@ -175,8 +178,11 @@ void TestGUI()
std::unique_ptr<const PlatformStyle> platformStyle(PlatformStyle::instantiate("other"));
SendCoinsDialog sendCoinsDialog(platformStyle.get());
TransactionView transactionView(platformStyle.get());
- OptionsModel optionsModel;
- WalletModel walletModel(platformStyle.get(), &wallet, &optionsModel);
+ auto node = interface::MakeNode();
+ OptionsModel optionsModel(*node);
+ vpwallets.insert(vpwallets.begin(), &wallet);
+ WalletModel walletModel(std::move(node->getWallets()[0]), *node, platformStyle.get(), &optionsModel);
+ vpwallets.erase(vpwallets.begin());
sendCoinsDialog.setModel(&walletModel);
transactionView.setModel(&walletModel);
@@ -201,7 +207,7 @@ void TestGUI()
QLabel* balanceLabel = overviewPage.findChild<QLabel*>("labelBalance");
QString balanceText = balanceLabel->text();
int unit = walletModel.getOptionsModel()->getDisplayUnit();
- CAmount balance = walletModel.getBalance();
+ CAmount balance = walletModel.wallet().getBalance();
QString balanceComparison = BitcoinUnits::formatWithUnit(unit, balance, false, BitcoinUnits::separatorAlways);
QCOMPARE(balanceText, balanceComparison);
diff --git a/src/qt/trafficgraphwidget.cpp b/src/qt/trafficgraphwidget.cpp
index f869799462..ff378ed1bf 100644
--- a/src/qt/trafficgraphwidget.cpp
+++ b/src/qt/trafficgraphwidget.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 <interface/node.h>
#include <qt/trafficgraphwidget.h>
#include <qt/clientmodel.h>
@@ -35,8 +36,8 @@ void TrafficGraphWidget::setClientModel(ClientModel *model)
{
clientModel = model;
if(model) {
- nLastBytesIn = model->getTotalBytesRecv();
- nLastBytesOut = model->getTotalBytesSent();
+ nLastBytesIn = model->node().getTotalBytesRecv();
+ nLastBytesOut = model->node().getTotalBytesSent();
}
}
@@ -123,8 +124,8 @@ void TrafficGraphWidget::updateRates()
{
if(!clientModel) return;
- quint64 bytesIn = clientModel->getTotalBytesRecv(),
- bytesOut = clientModel->getTotalBytesSent();
+ quint64 bytesIn = clientModel->node().getTotalBytesRecv(),
+ bytesOut = clientModel->node().getTotalBytesSent();
float inRate = (bytesIn - nLastBytesIn) / 1024.0f * 1000 / timer->interval();
float outRate = (bytesOut - nLastBytesOut) / 1024.0f * 1000 / timer->interval();
vSamplesIn.push_front(inRate);
@@ -169,8 +170,8 @@ void TrafficGraphWidget::clear()
fMax = 0.0f;
if(clientModel) {
- nLastBytesIn = clientModel->getTotalBytesRecv();
- nLastBytesOut = clientModel->getTotalBytesSent();
+ nLastBytesIn = clientModel->node().getTotalBytesRecv();
+ nLastBytesOut = clientModel->node().getTotalBytesSent();
}
timer->start();
}
diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp
index ec5a66bc9f..409835592f 100644
--- a/src/qt/transactiondesc.cpp
+++ b/src/qt/transactiondesc.cpp
@@ -10,6 +10,7 @@
#include <qt/transactionrecord.h>
#include <consensus/consensus.h>
+#include <interface/node.h>
#include <key_io.h>
#include <validation.h>
#include <script/script.h>
@@ -22,25 +23,24 @@
#include <stdint.h>
#include <string>
-QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx)
+QString TransactionDesc::FormatTxStatus(const interface::WalletTx& wtx, const interface::WalletTxStatus& status, bool inMempool, int numBlocks, int64_t adjustedTime)
{
- AssertLockHeld(cs_main);
- if (!CheckFinalTx(*wtx.tx))
+ if (!status.is_final)
{
if (wtx.tx->nLockTime < LOCKTIME_THRESHOLD)
- return tr("Open for %n more block(s)", "", wtx.tx->nLockTime - chainActive.Height());
+ return tr("Open for %n more block(s)", "", wtx.tx->nLockTime - numBlocks);
else
return tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx.tx->nLockTime));
}
else
{
- int nDepth = wtx.GetDepthInMainChain();
+ int nDepth = status.depth_in_main_chain;
if (nDepth < 0)
return tr("conflicted with a transaction with %1 confirmations").arg(-nDepth);
- else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
+ else if (adjustedTime - status.time_received > 2 * 60 && status.request_count == 0)
return tr("%1/offline").arg(nDepth);
else if (nDepth == 0)
- return tr("0/unconfirmed, %1").arg((wtx.InMempool() ? tr("in memory pool") : tr("not in memory pool"))) + (wtx.isAbandoned() ? ", "+tr("abandoned") : "");
+ return tr("0/unconfirmed, %1").arg((inMempool ? tr("in memory pool") : tr("not in memory pool"))) + (status.is_abandoned ? ", "+tr("abandoned") : "");
else if (nDepth < 6)
return tr("%1/unconfirmed").arg(nDepth);
else
@@ -48,21 +48,27 @@ QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx)
}
}
-QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionRecord *rec, int unit)
+QString TransactionDesc::toHTML(interface::Node& node, interface::Wallet& wallet, TransactionRecord *rec, int unit)
{
+ int numBlocks;
+ int64_t adjustedTime;
+ interface::WalletTxStatus status;
+ interface::WalletOrderForm orderForm;
+ bool inMempool;
+ interface::WalletTx wtx = wallet.getWalletTxDetails(rec->hash, status, orderForm, inMempool, numBlocks, adjustedTime);
+
QString strHTML;
- LOCK2(cs_main, wallet->cs_wallet);
strHTML.reserve(4000);
strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
- int64_t nTime = wtx.GetTxTime();
- CAmount nCredit = wtx.GetCredit(ISMINE_ALL);
- CAmount nDebit = wtx.GetDebit(ISMINE_ALL);
+ int64_t nTime = wtx.time;
+ CAmount nCredit = wtx.credit;
+ CAmount nDebit = wtx.debit;
CAmount nNet = nCredit - nDebit;
- strHTML += "<b>" + tr("Status") + ":</b> " + FormatTxStatus(wtx);
- int nRequests = wtx.GetRequestCount();
+ strHTML += "<b>" + tr("Status") + ":</b> " + FormatTxStatus(wtx, status, inMempool, numBlocks, adjustedTime);
+ int nRequests = status.request_count;
if (nRequests != -1)
{
if (nRequests == 0)
@@ -77,14 +83,14 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
//
// From
//
- if (wtx.IsCoinBase())
+ if (wtx.is_coinbase)
{
strHTML += "<b>" + tr("Source") + ":</b> " + tr("Generated") + "<br>";
}
- else if (wtx.mapValue.count("from") && !wtx.mapValue["from"].empty())
+ else if (wtx.value_map.count("from") && !wtx.value_map["from"].empty())
{
// Online transaction
- strHTML += "<b>" + tr("From") + ":</b> " + GUIUtil::HtmlEscape(wtx.mapValue["from"]) + "<br>";
+ strHTML += "<b>" + tr("From") + ":</b> " + GUIUtil::HtmlEscape(wtx.value_map["from"]) + "<br>";
}
else
{
@@ -94,14 +100,16 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
// Credit
CTxDestination address = DecodeDestination(rec->address);
if (IsValidDestination(address)) {
- if (wallet->mapAddressBook.count(address))
+ std::string name;
+ isminetype ismine;
+ if (wallet.getAddress(address, &name, &ismine))
{
strHTML += "<b>" + tr("From") + ":</b> " + tr("unknown") + "<br>";
strHTML += "<b>" + tr("To") + ":</b> ";
strHTML += GUIUtil::HtmlEscape(rec->address);
- QString addressOwned = (::IsMine(*wallet, address) == ISMINE_SPENDABLE) ? tr("own address") : tr("watch-only");
- if (!wallet->mapAddressBook[address].name.empty())
- strHTML += " (" + addressOwned + ", " + tr("label") + ": " + GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + ")";
+ QString addressOwned = ismine == ISMINE_SPENDABLE ? tr("own address") : tr("watch-only");
+ if (!name.empty())
+ strHTML += " (" + addressOwned + ", " + tr("label") + ": " + GUIUtil::HtmlEscape(name) + ")";
else
strHTML += " (" + addressOwned + ")";
strHTML += "<br>";
@@ -113,31 +121,32 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
//
// To
//
- if (wtx.mapValue.count("to") && !wtx.mapValue["to"].empty())
+ if (wtx.value_map.count("to") && !wtx.value_map["to"].empty())
{
// Online transaction
- std::string strAddress = wtx.mapValue["to"];
+ std::string strAddress = wtx.value_map["to"];
strHTML += "<b>" + tr("To") + ":</b> ";
CTxDestination dest = DecodeDestination(strAddress);
- if (wallet->mapAddressBook.count(dest) && !wallet->mapAddressBook[dest].name.empty())
- strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[dest].name) + " ";
+ std::string name;
+ if (wallet.getAddress(dest, &name) && !name.empty())
+ strHTML += GUIUtil::HtmlEscape(name) + " ";
strHTML += GUIUtil::HtmlEscape(strAddress) + "<br>";
}
//
// Amount
//
- if (wtx.IsCoinBase() && nCredit == 0)
+ if (wtx.is_coinbase && nCredit == 0)
{
//
// Coinbase
//
CAmount nUnmatured = 0;
for (const CTxOut& txout : wtx.tx->vout)
- nUnmatured += wallet->GetCredit(txout, ISMINE_ALL);
+ nUnmatured += wallet.getCredit(txout, ISMINE_ALL);
strHTML += "<b>" + tr("Credit") + ":</b> ";
- if (wtx.IsInMainChain())
- strHTML += BitcoinUnits::formatHtmlWithUnit(unit, nUnmatured)+ " (" + tr("matures in %n more block(s)", "", wtx.GetBlocksToMaturity()) + ")";
+ if (status.is_in_main_chain)
+ strHTML += BitcoinUnits::formatHtmlWithUnit(unit, nUnmatured)+ " (" + tr("matures in %n more block(s)", "", status.blocks_to_maturity) + ")";
else
strHTML += "(" + tr("not accepted") + ")";
strHTML += "<br>";
@@ -152,16 +161,14 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
else
{
isminetype fAllFromMe = ISMINE_SPENDABLE;
- for (const CTxIn& txin : wtx.tx->vin)
+ for (isminetype mine : wtx.txin_is_mine)
{
- isminetype mine = wallet->IsMine(txin);
if(fAllFromMe > mine) fAllFromMe = mine;
}
isminetype fAllToMe = ISMINE_SPENDABLE;
- for (const CTxOut& txout : wtx.tx->vout)
+ for (isminetype mine : wtx.txout_is_mine)
{
- isminetype mine = wallet->IsMine(txout);
if(fAllToMe > mine) fAllToMe = mine;
}
@@ -173,22 +180,24 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
//
// Debit
//
+ auto mine = wtx.txout_is_mine.begin();
for (const CTxOut& txout : wtx.tx->vout)
{
// Ignore change
- isminetype toSelf = wallet->IsMine(txout);
+ isminetype toSelf = *(mine++);
if ((toSelf == ISMINE_SPENDABLE) && (fAllFromMe == ISMINE_SPENDABLE))
continue;
- if (!wtx.mapValue.count("to") || wtx.mapValue["to"].empty())
+ if (!wtx.value_map.count("to") || wtx.value_map["to"].empty())
{
// Offline transaction
CTxDestination address;
if (ExtractDestination(txout.scriptPubKey, address))
{
strHTML += "<b>" + tr("To") + ":</b> ";
- if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].name.empty())
- strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + " ";
+ std::string name;
+ if (wallet.getAddress(address, &name) && !name.empty())
+ strHTML += GUIUtil::HtmlEscape(name) + " ";
strHTML += GUIUtil::HtmlEscape(EncodeDestination(address));
if(toSelf == ISMINE_SPENDABLE)
strHTML += " (own address)";
@@ -206,7 +215,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
if (fAllToMe)
{
// Payment to self
- CAmount nChange = wtx.GetChange();
+ CAmount nChange = wtx.change;
CAmount nValue = nCredit - nChange;
strHTML += "<b>" + tr("Total debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -nValue) + "<br>";
strHTML += "<b>" + tr("Total credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, nValue) + "<br>";
@@ -221,12 +230,18 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
//
// Mixed debit transaction
//
- for (const CTxIn& txin : wtx.tx->vin)
- if (wallet->IsMine(txin))
- strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -wallet->GetDebit(txin, ISMINE_ALL)) + "<br>";
- for (const CTxOut& txout : wtx.tx->vout)
- if (wallet->IsMine(txout))
- strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, wallet->GetCredit(txout, ISMINE_ALL)) + "<br>";
+ auto mine = wtx.txin_is_mine.begin();
+ for (const CTxIn& txin : wtx.tx->vin) {
+ if (*(mine++)) {
+ strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -wallet.getDebit(txin, ISMINE_ALL)) + "<br>";
+ }
+ }
+ mine = wtx.txout_is_mine.begin();
+ for (const CTxOut& txout : wtx.tx->vout) {
+ if (*(mine++)) {
+ strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, wallet.getCredit(txout, ISMINE_ALL)) + "<br>";
+ }
+ }
}
}
@@ -235,10 +250,10 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
//
// Message
//
- if (wtx.mapValue.count("message") && !wtx.mapValue["message"].empty())
- strHTML += "<br><b>" + tr("Message") + ":</b><br>" + GUIUtil::HtmlEscape(wtx.mapValue["message"], true) + "<br>";
- if (wtx.mapValue.count("comment") && !wtx.mapValue["comment"].empty())
- strHTML += "<br><b>" + tr("Comment") + ":</b><br>" + GUIUtil::HtmlEscape(wtx.mapValue["comment"], true) + "<br>";
+ if (wtx.value_map.count("message") && !wtx.value_map["message"].empty())
+ strHTML += "<br><b>" + tr("Message") + ":</b><br>" + GUIUtil::HtmlEscape(wtx.value_map["message"], true) + "<br>";
+ if (wtx.value_map.count("comment") && !wtx.value_map["comment"].empty())
+ strHTML += "<br><b>" + tr("Comment") + ":</b><br>" + GUIUtil::HtmlEscape(wtx.value_map["comment"], true) + "<br>";
strHTML += "<b>" + tr("Transaction ID") + ":</b> " + rec->getTxHash() + "<br>";
strHTML += "<b>" + tr("Transaction total size") + ":</b> " + QString::number(wtx.tx->GetTotalSize()) + " bytes<br>";
@@ -246,14 +261,14 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
strHTML += "<b>" + tr("Output index") + ":</b> " + QString::number(rec->getOutputIndex()) + "<br>";
// Message from normal bitcoin:URI (bitcoin:123...?message=example)
- for (const std::pair<std::string, std::string>& r : wtx.vOrderForm)
+ for (const std::pair<std::string, std::string>& r : orderForm)
if (r.first == "Message")
strHTML += "<br><b>" + tr("Message") + ":</b><br>" + GUIUtil::HtmlEscape(r.second, true) + "<br>";
//
// PaymentRequest info:
//
- for (const std::pair<std::string, std::string>& r : wtx.vOrderForm)
+ for (const std::pair<std::string, std::string>& r : orderForm)
{
if (r.first == "PaymentRequest")
{
@@ -265,7 +280,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
}
}
- if (wtx.IsCoinBase())
+ if (wtx.is_coinbase)
{
quint32 numBlocksToMaturity = COINBASE_MATURITY + 1;
strHTML += "<br>" + tr("Generated coins must mature %1 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to \"not accepted\" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.").arg(QString::number(numBlocksToMaturity)) + "<br>";
@@ -274,15 +289,15 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
//
// Debug view
//
- if (logCategories != BCLog::NONE)
+ if (node.getLogCategories() != BCLog::NONE)
{
strHTML += "<hr><br>" + tr("Debug information") + "<br><br>";
for (const CTxIn& txin : wtx.tx->vin)
- if(wallet->IsMine(txin))
- strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -wallet->GetDebit(txin, ISMINE_ALL)) + "<br>";
+ if(wallet.txinIsMine(txin))
+ strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -wallet.getDebit(txin, ISMINE_ALL)) + "<br>";
for (const CTxOut& txout : wtx.tx->vout)
- if(wallet->IsMine(txout))
- strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, wallet->GetCredit(txout, ISMINE_ALL)) + "<br>";
+ if(wallet.txoutIsMine(txout))
+ strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, wallet.getCredit(txout, ISMINE_ALL)) + "<br>";
strHTML += "<br><b>" + tr("Transaction") + ":</b><br>";
strHTML += GUIUtil::HtmlEscape(wtx.tx->ToString(), true);
@@ -295,7 +310,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
COutPoint prevout = txin.prevout;
Coin prev;
- if(pcoinsTip->GetCoin(prevout, prev))
+ if(node.getUnspentOutput(prevout, prev))
{
{
strHTML += "<li>";
@@ -303,13 +318,14 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
CTxDestination address;
if (ExtractDestination(vout.scriptPubKey, address))
{
- if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].name.empty())
- strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + " ";
+ std::string name;
+ if (wallet.getAddress(address, &name) && !name.empty())
+ strHTML += GUIUtil::HtmlEscape(name) + " ";
strHTML += QString::fromStdString(EncodeDestination(address));
}
strHTML = strHTML + " " + tr("Amount") + "=" + BitcoinUnits::formatHtmlWithUnit(unit, vout.nValue);
- strHTML = strHTML + " IsMine=" + (wallet->IsMine(vout) & ISMINE_SPENDABLE ? tr("true") : tr("false")) + "</li>";
- strHTML = strHTML + " IsWatchOnly=" + (wallet->IsMine(vout) & ISMINE_WATCH_ONLY ? tr("true") : tr("false")) + "</li>";
+ strHTML = strHTML + " IsMine=" + (wallet.txoutIsMine(vout) & ISMINE_SPENDABLE ? tr("true") : tr("false")) + "</li>";
+ strHTML = strHTML + " IsWatchOnly=" + (wallet.txoutIsMine(vout) & ISMINE_WATCH_ONLY ? tr("true") : tr("false")) + "</li>";
}
}
}
diff --git a/src/qt/transactiondesc.h b/src/qt/transactiondesc.h
index 01b90b130f..ea9ab28ee3 100644
--- a/src/qt/transactiondesc.h
+++ b/src/qt/transactiondesc.h
@@ -10,8 +10,12 @@
class TransactionRecord;
-class CWallet;
-class CWalletTx;
+namespace interface {
+class Node;
+class Wallet;
+struct WalletTx;
+struct WalletTxStatus;
+}
/** Provide a human-readable extended HTML description of a transaction.
*/
@@ -20,12 +24,12 @@ class TransactionDesc: public QObject
Q_OBJECT
public:
- static QString toHTML(CWallet *wallet, CWalletTx &wtx, TransactionRecord *rec, int unit);
+ static QString toHTML(interface::Node& node, interface::Wallet& wallet, TransactionRecord *rec, int unit);
private:
TransactionDesc() {}
- static QString FormatTxStatus(const CWalletTx& wtx);
+ static QString FormatTxStatus(const interface::WalletTx& wtx, const interface::WalletTxStatus& status, bool inMempool, int numBlocks, int64_t adjustedTime);
};
#endif // BITCOIN_QT_TRANSACTIONDESC_H
diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp
index cc30cf747d..60ab2d57d7 100644
--- a/src/qt/transactionrecord.cpp
+++ b/src/qt/transactionrecord.cpp
@@ -5,17 +5,17 @@
#include <qt/transactionrecord.h>
#include <consensus/consensus.h>
+#include <interface/wallet.h>
#include <key_io.h>
-#include <validation.h>
#include <timedata.h>
-#include <wallet/wallet.h>
+#include <validation.h>
#include <stdint.h>
/* Return positive answer if transaction should be shown in list.
*/
-bool TransactionRecord::showTransaction(const CWalletTx &wtx)
+bool TransactionRecord::showTransaction()
{
// There are currently no cases where we hide transactions, but
// we may want to use this in the future for things like RBF.
@@ -25,17 +25,17 @@ bool TransactionRecord::showTransaction(const CWalletTx &wtx)
/*
* Decompose CWallet transaction to model transaction records.
*/
-QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *wallet, const CWalletTx &wtx)
+QList<TransactionRecord> TransactionRecord::decomposeTransaction(const interface::WalletTx& wtx)
{
QList<TransactionRecord> parts;
- int64_t nTime = wtx.GetTxTime();
- CAmount nCredit = wtx.GetCredit(ISMINE_ALL);
- CAmount nDebit = wtx.GetDebit(ISMINE_ALL);
+ int64_t nTime = wtx.time;
+ CAmount nCredit = wtx.credit;
+ CAmount nDebit = wtx.debit;
CAmount nNet = nCredit - nDebit;
- uint256 hash = wtx.GetHash();
- std::map<std::string, std::string> mapValue = wtx.mapValue;
+ uint256 hash = wtx.tx->GetHash();
+ std::map<std::string, std::string> mapValue = wtx.value_map;
- if (nNet > 0 || wtx.IsCoinBase())
+ if (nNet > 0 || wtx.is_coinbase)
{
//
// Credit
@@ -43,7 +43,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
for(unsigned int i = 0; i < wtx.tx->vout.size(); i++)
{
const CTxOut& txout = wtx.tx->vout[i];
- isminetype mine = wallet->IsMine(txout);
+ isminetype mine = wtx.txout_is_mine[i];
if(mine)
{
TransactionRecord sub(hash, nTime);
@@ -51,11 +51,11 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
sub.idx = i; // vout index
sub.credit = txout.nValue;
sub.involvesWatchAddress = mine & ISMINE_WATCH_ONLY;
- if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*wallet, address))
+ if (wtx.txout_address_is_mine[i])
{
// Received by Bitcoin Address
sub.type = TransactionRecord::RecvWithAddress;
- sub.address = EncodeDestination(address);
+ sub.address = EncodeDestination(wtx.txout_address[i]);
}
else
{
@@ -63,7 +63,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
sub.type = TransactionRecord::RecvFromOther;
sub.address = mapValue["from"];
}
- if (wtx.IsCoinBase())
+ if (wtx.is_coinbase)
{
// Generated
sub.type = TransactionRecord::Generated;
@@ -77,17 +77,15 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
{
bool involvesWatchAddress = false;
isminetype fAllFromMe = ISMINE_SPENDABLE;
- for (const CTxIn& txin : wtx.tx->vin)
+ for (isminetype mine : wtx.txin_is_mine)
{
- isminetype mine = wallet->IsMine(txin);
if(mine & ISMINE_WATCH_ONLY) involvesWatchAddress = true;
if(fAllFromMe > mine) fAllFromMe = mine;
}
isminetype fAllToMe = ISMINE_SPENDABLE;
- for (const CTxOut& txout : wtx.tx->vout)
+ for (isminetype mine : wtx.txout_is_mine)
{
- isminetype mine = wallet->IsMine(txout);
if(mine & ISMINE_WATCH_ONLY) involvesWatchAddress = true;
if(fAllToMe > mine) fAllToMe = mine;
}
@@ -95,7 +93,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
if (fAllFromMe && fAllToMe)
{
// Payment to self
- CAmount nChange = wtx.GetChange();
+ CAmount nChange = wtx.change;
parts.append(TransactionRecord(hash, nTime, TransactionRecord::SendToSelf, "",
-(nDebit - nChange), nCredit - nChange));
@@ -115,19 +113,18 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
sub.idx = nOut;
sub.involvesWatchAddress = involvesWatchAddress;
- if(wallet->IsMine(txout))
+ if(wtx.txout_is_mine[nOut])
{
// Ignore parts sent to self, as this is usually the change
// from a transaction sent back to our own address.
continue;
}
- CTxDestination address;
- if (ExtractDestination(txout.scriptPubKey, address))
+ if (!boost::get<CNoDestination>(&wtx.txout_address[nOut]))
{
// Sent to Bitcoin Address
sub.type = TransactionRecord::SendToAddress;
- sub.address = EncodeDestination(address);
+ sub.address = EncodeDestination(wtx.txout_address[nOut]);
}
else
{
@@ -161,50 +158,46 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
return parts;
}
-void TransactionRecord::updateStatus(const CWalletTx &wtx)
+void TransactionRecord::updateStatus(const interface::WalletTxStatus& wtx, int numBlocks, int64_t adjustedTime)
{
- AssertLockHeld(cs_main);
// Determine transaction status
- // Find the block the tx is in
- const CBlockIndex* pindex = LookupBlockIndex(wtx.hashBlock);
-
// Sort order, unrecorded transactions sort to the top
status.sortKey = strprintf("%010d-%01d-%010u-%03d",
- (pindex ? pindex->nHeight : std::numeric_limits<int>::max()),
- (wtx.IsCoinBase() ? 1 : 0),
- wtx.nTimeReceived,
+ wtx.block_height,
+ wtx.is_coinbase ? 1 : 0,
+ wtx.time_received,
idx);
- status.countsForBalance = wtx.IsTrusted() && !(wtx.GetBlocksToMaturity() > 0);
- status.depth = wtx.GetDepthInMainChain();
- status.cur_num_blocks = chainActive.Height();
+ status.countsForBalance = wtx.is_trusted && !(wtx.blocks_to_maturity > 0);
+ status.depth = wtx.depth_in_main_chain;
+ status.cur_num_blocks = numBlocks;
- if (!CheckFinalTx(*wtx.tx))
+ if (!wtx.is_final)
{
- if (wtx.tx->nLockTime < LOCKTIME_THRESHOLD)
+ if (wtx.lock_time < LOCKTIME_THRESHOLD)
{
status.status = TransactionStatus::OpenUntilBlock;
- status.open_for = wtx.tx->nLockTime - chainActive.Height();
+ status.open_for = wtx.lock_time - numBlocks;
}
else
{
status.status = TransactionStatus::OpenUntilDate;
- status.open_for = wtx.tx->nLockTime;
+ status.open_for = wtx.lock_time;
}
}
// For generated transactions, determine maturity
else if(type == TransactionRecord::Generated)
{
- if (wtx.GetBlocksToMaturity() > 0)
+ if (wtx.blocks_to_maturity > 0)
{
status.status = TransactionStatus::Immature;
- if (wtx.IsInMainChain())
+ if (wtx.is_in_main_chain)
{
- status.matures_in = wtx.GetBlocksToMaturity();
+ status.matures_in = wtx.blocks_to_maturity;
// Check if the block was requested by anyone
- if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
+ if (adjustedTime - wtx.time_received > 2 * 60 && wtx.request_count == 0)
status.status = TransactionStatus::MaturesWarning;
}
else
@@ -223,14 +216,14 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx)
{
status.status = TransactionStatus::Conflicted;
}
- else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
+ else if (adjustedTime - wtx.time_received > 2 * 60 && wtx.request_count == 0)
{
status.status = TransactionStatus::Offline;
}
else if (status.depth == 0)
{
status.status = TransactionStatus::Unconfirmed;
- if (wtx.isAbandoned())
+ if (wtx.is_abandoned)
status.status = TransactionStatus::Abandoned;
}
else if (status.depth < RecommendedNumConfirmations)
@@ -245,10 +238,9 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx)
status.needsUpdate = false;
}
-bool TransactionRecord::statusUpdateNeeded() const
+bool TransactionRecord::statusUpdateNeeded(int numBlocks) const
{
- AssertLockHeld(cs_main);
- return status.cur_num_blocks != chainActive.Height() || status.needsUpdate;
+ return status.cur_num_blocks != numBlocks || status.needsUpdate;
}
QString TransactionRecord::getTxHash() const
diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h
index 5321d05d15..c653584b52 100644
--- a/src/qt/transactionrecord.h
+++ b/src/qt/transactionrecord.h
@@ -11,8 +11,12 @@
#include <QList>
#include <QString>
-class CWallet;
-class CWalletTx;
+namespace interface {
+class Node;
+class Wallet;
+struct WalletTx;
+struct WalletTxStatus;
+}
/** UI model for transaction status. The transaction status is the part of a transaction that will change over time.
*/
@@ -106,8 +110,8 @@ public:
/** Decompose CWallet transaction to model transaction records.
*/
- static bool showTransaction(const CWalletTx &wtx);
- static QList<TransactionRecord> decomposeTransaction(const CWallet *wallet, const CWalletTx &wtx);
+ static bool showTransaction();
+ static QList<TransactionRecord> decomposeTransaction(const interface::WalletTx& wtx);
/** @name Immutable transaction attributes
@{*/
@@ -136,11 +140,11 @@ public:
/** Update status from core wallet tx.
*/
- void updateStatus(const CWalletTx &wtx);
+ void updateStatus(const interface::WalletTxStatus& wtx, int numBlocks, int64_t adjustedTime);
/** Return whether a status update is needed.
*/
- bool statusUpdateNeeded() const;
+ bool statusUpdateNeeded(int numBlocks) const;
};
#endif // BITCOIN_QT_TRANSACTIONRECORD_H
diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp
index 84800125fe..2148ff1728 100644
--- a/src/qt/transactiontablemodel.cpp
+++ b/src/qt/transactiontablemodel.cpp
@@ -14,11 +14,12 @@
#include <qt/walletmodel.h>
#include <core_io.h>
+#include <interface/handler.h>
+#include <interface/node.h>
#include <validation.h>
#include <sync.h>
#include <uint256.h>
#include <util.h>
-#include <wallet/wallet.h>
#include <QColor>
#include <QDateTime>
@@ -57,13 +58,11 @@ struct TxLessThan
class TransactionTablePriv
{
public:
- TransactionTablePriv(CWallet *_wallet, TransactionTableModel *_parent) :
- wallet(_wallet),
+ TransactionTablePriv(TransactionTableModel *_parent) :
parent(_parent)
{
}
- CWallet *wallet;
TransactionTableModel *parent;
/* Local cache of wallet.
@@ -74,16 +73,15 @@ public:
/* Query entire wallet anew from core.
*/
- void refreshWallet()
+ void refreshWallet(interface::Wallet& wallet)
{
qDebug() << "TransactionTablePriv::refreshWallet";
cachedWallet.clear();
{
- LOCK2(cs_main, wallet->cs_wallet);
- for (const auto& entry : wallet->mapWallet)
- {
- if (TransactionRecord::showTransaction(entry.second))
- cachedWallet.append(TransactionRecord::decomposeTransaction(wallet, entry.second));
+ for (const auto& wtx : wallet.getWalletTxs()) {
+ if (TransactionRecord::showTransaction()) {
+ cachedWallet.append(TransactionRecord::decomposeTransaction(wtx));
+ }
}
}
}
@@ -93,7 +91,7 @@ public:
Call with transaction that was added, removed or changed.
*/
- void updateWallet(const uint256 &hash, int status, bool showTransaction)
+ void updateWallet(interface::Wallet& wallet, const uint256 &hash, int status, bool showTransaction)
{
qDebug() << "TransactionTablePriv::updateWallet: " + QString::fromStdString(hash.ToString()) + " " + QString::number(status);
@@ -128,17 +126,16 @@ public:
}
if(showTransaction)
{
- LOCK2(cs_main, wallet->cs_wallet);
// Find transaction in wallet
- std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(hash);
- if(mi == wallet->mapWallet.end())
+ interface::WalletTx wtx = wallet.getWalletTx(hash);
+ if(!wtx.tx)
{
qWarning() << "TransactionTablePriv::updateWallet: Warning: Got CT_NEW, but transaction is not in wallet";
break;
}
// Added -- insert at the right position
QList<TransactionRecord> toInsert =
- TransactionRecord::decomposeTransaction(wallet, mi->second);
+ TransactionRecord::decomposeTransaction(wtx);
if(!toInsert.isEmpty()) /* only if something to insert */
{
parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex+toInsert.size()-1);
@@ -179,7 +176,7 @@ public:
return cachedWallet.size();
}
- TransactionRecord *index(int idx)
+ TransactionRecord *index(interface::Wallet& wallet, int idx)
{
if(idx >= 0 && idx < cachedWallet.size())
{
@@ -192,61 +189,42 @@ public:
// If a status update is needed (blocks came in since last check),
// update the status of this transaction from the wallet. Otherwise,
// simply re-use the cached status.
- TRY_LOCK(cs_main, lockMain);
- if(lockMain)
- {
- TRY_LOCK(wallet->cs_wallet, lockWallet);
- if(lockWallet && rec->statusUpdateNeeded())
- {
- std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(rec->hash);
-
- if(mi != wallet->mapWallet.end())
- {
- rec->updateStatus(mi->second);
- }
- }
+ interface::WalletTxStatus wtx;
+ int numBlocks;
+ int64_t adjustedTime;
+ if (wallet.tryGetTxStatus(rec->hash, wtx, numBlocks, adjustedTime) && rec->statusUpdateNeeded(numBlocks)) {
+ rec->updateStatus(wtx, numBlocks, adjustedTime);
}
return rec;
}
return 0;
}
- QString describe(TransactionRecord *rec, int unit)
+ QString describe(interface::Node& node, interface::Wallet& wallet, TransactionRecord *rec, int unit)
{
- {
- LOCK2(cs_main, wallet->cs_wallet);
- std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(rec->hash);
- if(mi != wallet->mapWallet.end())
- {
- return TransactionDesc::toHTML(wallet, mi->second, rec, unit);
- }
- }
- return QString();
+ return TransactionDesc::toHTML(node, wallet, rec, unit);
}
- QString getTxHex(TransactionRecord *rec)
+ QString getTxHex(interface::Wallet& wallet, TransactionRecord *rec)
{
- LOCK2(cs_main, wallet->cs_wallet);
- std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(rec->hash);
- if(mi != wallet->mapWallet.end())
- {
- std::string strHex = EncodeHexTx(*mi->second.tx);
+ auto tx = wallet.getTx(rec->hash);
+ if (tx) {
+ std::string strHex = EncodeHexTx(*tx);
return QString::fromStdString(strHex);
}
return QString();
}
};
-TransactionTableModel::TransactionTableModel(const PlatformStyle *_platformStyle, CWallet* _wallet, WalletModel *parent):
+TransactionTableModel::TransactionTableModel(const PlatformStyle *_platformStyle, WalletModel *parent):
QAbstractTableModel(parent),
- wallet(_wallet),
walletModel(parent),
- priv(new TransactionTablePriv(_wallet, this)),
+ priv(new TransactionTablePriv(this)),
fProcessingQueuedTransactions(false),
platformStyle(_platformStyle)
{
columns << QString() << QString() << tr("Date") << tr("Type") << tr("Label") << BitcoinUnits::getAmountColumnTitle(walletModel->getOptionsModel()->getDisplayUnit());
- priv->refreshWallet();
+ priv->refreshWallet(walletModel->wallet());
connect(walletModel->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
@@ -271,7 +249,7 @@ void TransactionTableModel::updateTransaction(const QString &hash, int status, b
uint256 updated;
updated.SetHex(hash.toStdString());
- priv->updateWallet(updated, status, showTransaction);
+ priv->updateWallet(walletModel->wallet(), updated, status, showTransaction);
}
void TransactionTableModel::updateConfirmations()
@@ -608,7 +586,7 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const
case WatchonlyDecorationRole:
return txWatchonlyDecoration(rec);
case LongDescriptionRole:
- return priv->describe(rec, walletModel->getOptionsModel()->getDisplayUnit());
+ return priv->describe(walletModel->node(), walletModel->wallet(), rec, walletModel->getOptionsModel()->getDisplayUnit());
case AddressRole:
return QString::fromStdString(rec->address);
case LabelRole:
@@ -618,7 +596,7 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const
case TxHashRole:
return rec->getTxHash();
case TxHexRole:
- return priv->getTxHex(rec);
+ return priv->getTxHex(walletModel->wallet(), rec);
case TxPlainTextRole:
{
QString details;
@@ -694,10 +672,10 @@ QVariant TransactionTableModel::headerData(int section, Qt::Orientation orientat
QModelIndex TransactionTableModel::index(int row, int column, const QModelIndex &parent) const
{
Q_UNUSED(parent);
- TransactionRecord *data = priv->index(row);
+ TransactionRecord *data = priv->index(walletModel->wallet(), row);
if(data)
{
- return createIndex(row, column, priv->index(row));
+ return createIndex(row, column, priv->index(walletModel->wallet(), row));
}
return QModelIndex();
}
@@ -735,13 +713,11 @@ private:
static bool fQueueNotifications = false;
static std::vector< TransactionNotification > vQueueNotifications;
-static void NotifyTransactionChanged(TransactionTableModel *ttm, CWallet *wallet, const uint256 &hash, ChangeType status)
+static void NotifyTransactionChanged(TransactionTableModel *ttm, const uint256 &hash, ChangeType status)
{
// Find transaction in wallet
- std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(hash);
// Determine whether to show transaction or not (determine this here so that no relocking is needed in GUI thread)
- bool inWallet = mi != wallet->mapWallet.end();
- bool showTransaction = (inWallet && TransactionRecord::showTransaction(mi->second));
+ bool showTransaction = TransactionRecord::showTransaction();
TransactionNotification notification(hash, status, showTransaction);
@@ -777,13 +753,13 @@ static void ShowProgress(TransactionTableModel *ttm, const std::string &title, i
void TransactionTableModel::subscribeToCoreSignals()
{
// Connect signals to wallet
- wallet->NotifyTransactionChanged.connect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3));
- wallet->ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2));
+ m_handler_transaction_changed = walletModel->wallet().handleTransactionChanged(boost::bind(NotifyTransactionChanged, this, _1, _2));
+ m_handler_show_progress = walletModel->wallet().handleShowProgress(boost::bind(ShowProgress, this, _1, _2));
}
void TransactionTableModel::unsubscribeFromCoreSignals()
{
// Disconnect signals from wallet
- wallet->NotifyTransactionChanged.disconnect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3));
- wallet->ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2));
+ m_handler_transaction_changed->disconnect();
+ m_handler_show_progress->disconnect();
}
diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h
index 781874d160..57566db638 100644
--- a/src/qt/transactiontablemodel.h
+++ b/src/qt/transactiontablemodel.h
@@ -10,13 +10,17 @@
#include <QAbstractTableModel>
#include <QStringList>
+#include <memory>
+
+namespace interface {
+class Handler;
+}
+
class PlatformStyle;
class TransactionRecord;
class TransactionTablePriv;
class WalletModel;
-class CWallet;
-
/** UI model for the transaction table of a wallet.
*/
class TransactionTableModel : public QAbstractTableModel
@@ -24,7 +28,7 @@ class TransactionTableModel : public QAbstractTableModel
Q_OBJECT
public:
- explicit TransactionTableModel(const PlatformStyle *platformStyle, CWallet* wallet, WalletModel *parent = 0);
+ explicit TransactionTableModel(const PlatformStyle *platformStyle, WalletModel *parent = 0);
~TransactionTableModel();
enum ColumnIndex {
@@ -80,8 +84,9 @@ public:
bool processingQueuedTransactions() const { return fProcessingQueuedTransactions; }
private:
- CWallet* wallet;
WalletModel *walletModel;
+ std::unique_ptr<interface::Handler> m_handler_transaction_changed;
+ std::unique_ptr<interface::Handler> m_handler_show_progress;
QStringList columns;
TransactionTablePriv *priv;
bool fProcessingQueuedTransactions;
diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp
index 26391452da..aa6444245a 100644
--- a/src/qt/transactionview.cpp
+++ b/src/qt/transactionview.cpp
@@ -254,7 +254,7 @@ void TransactionView::setModel(WalletModel *_model)
}
// show/hide column Watch-only
- updateWatchOnlyColumn(_model->haveWatchOnly());
+ updateWatchOnlyColumn(_model->wallet().haveWatchOnly());
// Watch-only signal
connect(_model, SIGNAL(notifyWatchonlyChanged(bool)), this, SLOT(updateWatchOnlyColumn(bool)));
@@ -364,7 +364,7 @@ void TransactionView::exportClicked()
// name, column, role
writer.setModel(transactionProxyModel);
writer.addColumn(tr("Confirmed"), 0, TransactionTableModel::ConfirmedRole);
- if (model->haveWatchOnly())
+ if (model->wallet().haveWatchOnly())
writer.addColumn(tr("Watch-only"), TransactionTableModel::Watchonly);
writer.addColumn(tr("Date"), 0, TransactionTableModel::DateRole);
writer.addColumn(tr("Type"), TransactionTableModel::Type, Qt::EditRole);
@@ -393,8 +393,8 @@ void TransactionView::contextualMenu(const QPoint &point)
// check if transaction can be abandoned, disable context menu action in case it doesn't
uint256 hash;
hash.SetHex(selection.at(0).data(TransactionTableModel::TxHashRole).toString().toStdString());
- abandonAction->setEnabled(model->transactionCanBeAbandoned(hash));
- bumpFeeAction->setEnabled(model->transactionCanBeBumped(hash));
+ abandonAction->setEnabled(model->wallet().transactionCanBeAbandoned(hash));
+ bumpFeeAction->setEnabled(model->wallet().transactionCanBeBumped(hash));
if(index.isValid())
{
@@ -414,7 +414,7 @@ void TransactionView::abandonTx()
hash.SetHex(hashQStr.toStdString());
// Abandon the wallet transaction over the walletModel
- model->abandonTransaction(hash);
+ model->wallet().abandonTransaction(hash);
// Update the table
model->getTransactionTableModel()->updateTransaction(hashQStr, CT_UPDATED, false);
diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp
index 65e7775f2b..6114ea0de1 100644
--- a/src/qt/utilitydialog.cpp
+++ b/src/qt/utilitydialog.cpp
@@ -19,6 +19,7 @@
#include <clientversion.h>
#include <init.h>
+#include <interface/node.h>
#include <util.h>
#include <stdio.h>
@@ -31,7 +32,7 @@
#include <QVBoxLayout>
/** "Help message" or "About" dialog box */
-HelpMessageDialog::HelpMessageDialog(QWidget *parent, bool about) :
+HelpMessageDialog::HelpMessageDialog(interface::Node& node, QWidget *parent, bool about) :
QDialog(parent),
ui(new Ui::HelpMessageDialog)
{
@@ -77,7 +78,7 @@ HelpMessageDialog::HelpMessageDialog(QWidget *parent, bool about) :
cursor.insertText(header);
cursor.insertBlock();
- std::string strUsage = HelpMessage(HMM_BITCOIN_QT);
+ std::string strUsage = node.helpMessage(HelpMessageMode::BITCOIN_QT);
const bool showDebug = gArgs.GetBoolArg("-help-debug", false);
strUsage += HelpMessageGroup(tr("UI Options:").toStdString());
if (showDebug) {
diff --git a/src/qt/utilitydialog.h b/src/qt/utilitydialog.h
index d43d9a82c0..e6ad7be5d0 100644
--- a/src/qt/utilitydialog.h
+++ b/src/qt/utilitydialog.h
@@ -10,6 +10,10 @@
class BitcoinGUI;
+namespace interface {
+ class Node;
+}
+
namespace Ui {
class HelpMessageDialog;
}
@@ -20,7 +24,7 @@ class HelpMessageDialog : public QDialog
Q_OBJECT
public:
- explicit HelpMessageDialog(QWidget *parent, bool about);
+ explicit HelpMessageDialog(interface::Node& node, QWidget *parent, bool about);
~HelpMessageDialog();
void printToConsole();
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index 555a21e8c5..d9db437dc5 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -5,29 +5,20 @@
#include <qt/walletmodel.h>
#include <qt/addresstablemodel.h>
-#include <consensus/validation.h>
#include <qt/guiconstants.h>
-#include <qt/guiutil.h>
#include <qt/optionsmodel.h>
#include <qt/paymentserver.h>
#include <qt/recentrequeststablemodel.h>
#include <qt/sendcoinsdialog.h>
#include <qt/transactiontablemodel.h>
-#include <chain.h>
+#include <interface/handler.h>
+#include <interface/node.h>
#include <key_io.h>
-#include <keystore.h>
-#include <validation.h>
-#include <net.h> // for g_connman
-#include <policy/fees.h>
-#include <policy/rbf.h>
-#include <sync.h>
#include <ui_interface.h>
#include <util.h> // for GetBoolArg
#include <wallet/coincontrol.h>
-#include <wallet/feebumper.h>
#include <wallet/wallet.h>
-#include <wallet/walletdb.h> // for BackupWallet
#include <stdint.h>
@@ -37,21 +28,19 @@
#include <QTimer>
-WalletModel::WalletModel(const PlatformStyle *platformStyle, CWallet *_wallet, OptionsModel *_optionsModel, QObject *parent) :
- QObject(parent), wallet(_wallet), optionsModel(_optionsModel), addressTableModel(0),
+WalletModel::WalletModel(std::unique_ptr<interface::Wallet> wallet, interface::Node& node, const PlatformStyle *platformStyle, OptionsModel *_optionsModel, QObject *parent) :
+ QObject(parent), m_wallet(std::move(wallet)), m_node(node), optionsModel(_optionsModel), addressTableModel(0),
transactionTableModel(0),
recentRequestsTableModel(0),
- cachedBalance(0), cachedUnconfirmedBalance(0), cachedImmatureBalance(0),
- cachedWatchOnlyBalance{0}, cachedWatchUnconfBalance{0}, cachedWatchImmatureBalance{0},
cachedEncryptionStatus(Unencrypted),
cachedNumBlocks(0)
{
- fHaveWatchOnly = wallet->HaveWatchOnly();
+ fHaveWatchOnly = m_wallet->haveWatchOnly();
fForceCheckBalanceChanged = false;
- addressTableModel = new AddressTableModel(wallet, this);
- transactionTableModel = new TransactionTableModel(platformStyle, wallet, this);
- recentRequestsTableModel = new RecentRequestsTableModel(wallet, this);
+ addressTableModel = new AddressTableModel(this);
+ transactionTableModel = new TransactionTableModel(platformStyle, this);
+ recentRequestsTableModel = new RecentRequestsTableModel(this);
// This timer will be fired repeatedly to update the balance
pollTimer = new QTimer(this);
@@ -66,46 +55,6 @@ WalletModel::~WalletModel()
unsubscribeFromCoreSignals();
}
-CAmount WalletModel::getBalance(const CCoinControl *coinControl) const
-{
- if (coinControl)
- {
- return wallet->GetAvailableBalance(coinControl);
- }
-
- return wallet->GetBalance();
-}
-
-CAmount WalletModel::getUnconfirmedBalance() const
-{
- return wallet->GetUnconfirmedBalance();
-}
-
-CAmount WalletModel::getImmatureBalance() const
-{
- return wallet->GetImmatureBalance();
-}
-
-bool WalletModel::haveWatchOnly() const
-{
- return fHaveWatchOnly;
-}
-
-CAmount WalletModel::getWatchBalance() const
-{
- return wallet->GetWatchOnlyBalance();
-}
-
-CAmount WalletModel::getWatchUnconfirmedBalance() const
-{
- return wallet->GetUnconfirmedWatchOnlyBalance();
-}
-
-CAmount WalletModel::getWatchImmatureBalance() const
-{
- return wallet->GetImmatureWatchOnlyBalance();
-}
-
void WalletModel::updateStatus()
{
EncryptionStatus newEncryptionStatus = getEncryptionStatus();
@@ -117,55 +66,34 @@ void WalletModel::updateStatus()
void WalletModel::pollBalanceChanged()
{
- // Get required locks upfront. This avoids the GUI from getting stuck on
- // periodical polls if the core is holding the locks for a longer time -
- // for example, during a wallet rescan.
- TRY_LOCK(cs_main, lockMain);
- if(!lockMain)
- return;
- TRY_LOCK(wallet->cs_wallet, lockWallet);
- if(!lockWallet)
+ // Try to get balances and return early if locks can't be acquired. This
+ // avoids the GUI from getting stuck on periodical polls if the core is
+ // holding the locks for a longer time - for example, during a wallet
+ // rescan.
+ interface::WalletBalances new_balances;
+ int numBlocks = -1;
+ if (!m_wallet->tryGetBalances(new_balances, numBlocks)) {
return;
+ }
- if(fForceCheckBalanceChanged || chainActive.Height() != cachedNumBlocks)
+ if(fForceCheckBalanceChanged || m_node.getNumBlocks() != cachedNumBlocks)
{
fForceCheckBalanceChanged = false;
// Balance and number of transactions might have changed
- cachedNumBlocks = chainActive.Height();
+ cachedNumBlocks = m_node.getNumBlocks();
- checkBalanceChanged();
+ checkBalanceChanged(new_balances);
if(transactionTableModel)
transactionTableModel->updateConfirmations();
}
}
-void WalletModel::checkBalanceChanged()
+void WalletModel::checkBalanceChanged(const interface::WalletBalances& new_balances)
{
- CAmount newBalance = getBalance();
- CAmount newUnconfirmedBalance = getUnconfirmedBalance();
- CAmount newImmatureBalance = getImmatureBalance();
- CAmount newWatchOnlyBalance = 0;
- CAmount newWatchUnconfBalance = 0;
- CAmount newWatchImmatureBalance = 0;
- if (haveWatchOnly())
- {
- newWatchOnlyBalance = getWatchBalance();
- newWatchUnconfBalance = getWatchUnconfirmedBalance();
- newWatchImmatureBalance = getWatchImmatureBalance();
- }
-
- if(cachedBalance != newBalance || cachedUnconfirmedBalance != newUnconfirmedBalance || cachedImmatureBalance != newImmatureBalance ||
- cachedWatchOnlyBalance != newWatchOnlyBalance || cachedWatchUnconfBalance != newWatchUnconfBalance || cachedWatchImmatureBalance != newWatchImmatureBalance)
- {
- cachedBalance = newBalance;
- cachedUnconfirmedBalance = newUnconfirmedBalance;
- cachedImmatureBalance = newImmatureBalance;
- cachedWatchOnlyBalance = newWatchOnlyBalance;
- cachedWatchUnconfBalance = newWatchUnconfBalance;
- cachedWatchImmatureBalance = newWatchImmatureBalance;
- Q_EMIT balanceChanged(newBalance, newUnconfirmedBalance, newImmatureBalance,
- newWatchOnlyBalance, newWatchUnconfBalance, newWatchImmatureBalance);
+ if(new_balances.balanceChanged(m_cached_balances)) {
+ m_cached_balances = new_balances;
+ Q_EMIT balanceChanged(new_balances);
}
}
@@ -260,7 +188,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
return DuplicateAddress;
}
- CAmount nBalance = getBalance(&coinControl);
+ CAmount nBalance = m_wallet->getAvailableBalance(coinControl);
if(total > nBalance)
{
@@ -268,22 +196,17 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
}
{
- LOCK2(cs_main, wallet->cs_wallet);
-
- transaction.newPossibleKeyChange(wallet);
-
CAmount nFeeRequired = 0;
int nChangePosRet = -1;
std::string strFailReason;
- CTransactionRef& newTx = transaction.getTransaction();
- CReserveKey *keyChange = transaction.getPossibleKeyChange();
- bool fCreated = wallet->CreateTransaction(vecSend, newTx, *keyChange, nFeeRequired, nChangePosRet, strFailReason, coinControl);
+ auto& newTx = transaction.getWtx();
+ newTx = m_wallet->createTransaction(vecSend, coinControl, true /* sign */, nChangePosRet, nFeeRequired, strFailReason);
transaction.setTransactionFee(nFeeRequired);
- if (fSubtractFeeFromAmount && fCreated)
+ if (fSubtractFeeFromAmount && newTx)
transaction.reassignAmounts(nChangePosRet);
- if(!fCreated)
+ if(!newTx)
{
if(!fSubtractFeeFromAmount && (total + nFeeRequired) > nBalance)
{
@@ -297,7 +220,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
// reject absurdly high fee. (This can never happen because the
// wallet caps the fee at maxTxFee. This merely serves as a
// belt-and-suspenders check)
- if (nFeeRequired > maxTxFee)
+ if (nFeeRequired > m_node.getMaxTxFee())
return AbsurdFee;
}
@@ -309,8 +232,6 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran
QByteArray transaction_array; /* store serialized transaction */
{
- LOCK2(cs_main, wallet->cs_wallet);
-
std::vector<std::pair<std::string, std::string>> vOrderForm;
for (const SendCoinsRecipient &rcp : transaction.getRecipients())
{
@@ -330,14 +251,13 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran
vOrderForm.emplace_back("Message", rcp.message.toStdString());
}
- CTransactionRef& newTx = transaction.getTransaction();
- CReserveKey *keyChange = transaction.getPossibleKeyChange();
- CValidationState state;
- if (!wallet->CommitTransaction(newTx, {} /* mapValue */, std::move(vOrderForm), {} /* fromAccount */, *keyChange, g_connman.get(), state))
- return SendCoinsReturn(TransactionCommitFailed, QString::fromStdString(state.GetRejectReason()));
+ auto& newTx = transaction.getWtx();
+ std::string rejectReason;
+ if (!newTx->commit({} /* mapValue */, std::move(vOrderForm), {} /* fromAccount */, rejectReason))
+ return SendCoinsReturn(TransactionCommitFailed, QString::fromStdString(rejectReason));
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
- ssTx << newTx;
+ ssTx << newTx->get();
transaction_array.append(&(ssTx[0]), ssTx.size());
}
@@ -352,24 +272,22 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran
CTxDestination dest = DecodeDestination(strAddress);
std::string strLabel = rcp.label.toStdString();
{
- LOCK(wallet->cs_wallet);
-
- std::map<CTxDestination, CAddressBookData>::iterator mi = wallet->mapAddressBook.find(dest);
-
// Check if we have a new address or an updated label
- if (mi == wallet->mapAddressBook.end())
+ std::string name;
+ if (!m_wallet->getAddress(dest, &name))
{
- wallet->SetAddressBook(dest, strLabel, "send");
+ m_wallet->setAddressBook(dest, strLabel, "send");
}
- else if (mi->second.name != strLabel)
+ else if (name != strLabel)
{
- wallet->SetAddressBook(dest, strLabel, ""); // "" means don't change purpose
+ m_wallet->setAddressBook(dest, strLabel, ""); // "" means don't change purpose
}
}
}
- Q_EMIT coinsSent(wallet, rcp, transaction_array);
+ Q_EMIT coinsSent(this, rcp, transaction_array);
}
- checkBalanceChanged(); // update balance immediately, otherwise there could be a short noticeable delay until pollBalanceChanged hits
+
+ checkBalanceChanged(m_wallet->getBalances()); // update balance immediately, otherwise there could be a short noticeable delay until pollBalanceChanged hits
return SendCoinsReturn(OK);
}
@@ -396,11 +314,11 @@ RecentRequestsTableModel *WalletModel::getRecentRequestsTableModel()
WalletModel::EncryptionStatus WalletModel::getEncryptionStatus() const
{
- if(!wallet->IsCrypted())
+ if(!m_wallet->isCrypted())
{
return Unencrypted;
}
- else if(wallet->IsLocked())
+ else if(m_wallet->isLocked())
{
return Locked;
}
@@ -415,7 +333,7 @@ bool WalletModel::setWalletEncrypted(bool encrypted, const SecureString &passphr
if(encrypted)
{
// Encrypt
- return wallet->EncryptWallet(passphrase);
+ return m_wallet->encryptWallet(passphrase);
}
else
{
@@ -429,39 +347,29 @@ bool WalletModel::setWalletLocked(bool locked, const SecureString &passPhrase)
if(locked)
{
// Lock
- return wallet->Lock();
+ return m_wallet->lock();
}
else
{
// Unlock
- return wallet->Unlock(passPhrase);
+ return m_wallet->unlock(passPhrase);
}
}
bool WalletModel::changePassphrase(const SecureString &oldPass, const SecureString &newPass)
{
- bool retval;
- {
- LOCK(wallet->cs_wallet);
- wallet->Lock(); // Make sure wallet is locked before attempting pass change
- retval = wallet->ChangeWalletPassphrase(oldPass, newPass);
- }
- return retval;
-}
-
-bool WalletModel::backupWallet(const QString &filename)
-{
- return wallet->BackupWallet(filename.toLocal8Bit().data());
+ m_wallet->lock(); // Make sure wallet is locked before attempting pass change
+ return m_wallet->changeWalletPassphrase(oldPass, newPass);
}
// Handlers for core signals
-static void NotifyKeyStoreStatusChanged(WalletModel *walletmodel, CCryptoKeyStore *wallet)
+static void NotifyKeyStoreStatusChanged(WalletModel *walletmodel)
{
qDebug() << "NotifyKeyStoreStatusChanged";
QMetaObject::invokeMethod(walletmodel, "updateStatus", Qt::QueuedConnection);
}
-static void NotifyAddressBookChanged(WalletModel *walletmodel, CWallet *wallet,
+static void NotifyAddressBookChanged(WalletModel *walletmodel,
const CTxDestination &address, const std::string &label, bool isMine,
const std::string &purpose, ChangeType status)
{
@@ -478,9 +386,8 @@ static void NotifyAddressBookChanged(WalletModel *walletmodel, CWallet *wallet,
Q_ARG(int, status));
}
-static void NotifyTransactionChanged(WalletModel *walletmodel, CWallet *wallet, const uint256 &hash, ChangeType status)
+static void NotifyTransactionChanged(WalletModel *walletmodel, const uint256 &hash, ChangeType status)
{
- Q_UNUSED(wallet);
Q_UNUSED(hash);
Q_UNUSED(status);
QMetaObject::invokeMethod(walletmodel, "updateTransaction", Qt::QueuedConnection);
@@ -503,21 +410,21 @@ static void NotifyWatchonlyChanged(WalletModel *walletmodel, bool fHaveWatchonly
void WalletModel::subscribeToCoreSignals()
{
// Connect signals to wallet
- wallet->NotifyStatusChanged.connect(boost::bind(&NotifyKeyStoreStatusChanged, this, _1));
- wallet->NotifyAddressBookChanged.connect(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5, _6));
- wallet->NotifyTransactionChanged.connect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3));
- wallet->ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2));
- wallet->NotifyWatchonlyChanged.connect(boost::bind(NotifyWatchonlyChanged, this, _1));
+ m_handler_status_changed = m_wallet->handleStatusChanged(boost::bind(&NotifyKeyStoreStatusChanged, this));
+ m_handler_address_book_changed = m_wallet->handleAddressBookChanged(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5));
+ m_handler_transaction_changed = m_wallet->handleTransactionChanged(boost::bind(NotifyTransactionChanged, this, _1, _2));
+ m_handler_show_progress = m_wallet->handleShowProgress(boost::bind(ShowProgress, this, _1, _2));
+ m_handler_watch_only_changed = m_wallet->handleWatchOnlyChanged(boost::bind(NotifyWatchonlyChanged, this, _1));
}
void WalletModel::unsubscribeFromCoreSignals()
{
// Disconnect signals from wallet
- wallet->NotifyStatusChanged.disconnect(boost::bind(&NotifyKeyStoreStatusChanged, this, _1));
- wallet->NotifyAddressBookChanged.disconnect(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5, _6));
- wallet->NotifyTransactionChanged.disconnect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3));
- wallet->ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2));
- wallet->NotifyWatchonlyChanged.disconnect(boost::bind(NotifyWatchonlyChanged, this, _1));
+ m_handler_status_changed->disconnect();
+ m_handler_address_book_changed->disconnect();
+ m_handler_transaction_changed->disconnect();
+ m_handler_show_progress->disconnect();
+ m_handler_watch_only_changed->disconnect();
}
// WalletModel::UnlockContext implementation
@@ -557,80 +464,9 @@ void WalletModel::UnlockContext::CopyFrom(const UnlockContext& rhs)
rhs.relock = false;
}
-bool WalletModel::getPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const
-{
- return wallet->GetPubKey(address, vchPubKeyOut);
-}
-
-bool WalletModel::IsSpendable(const CTxDestination& dest) const
-{
- return IsMine(*wallet, dest) & ISMINE_SPENDABLE;
-}
-
-bool WalletModel::getPrivKey(const CKeyID &address, CKey& vchPrivKeyOut) const
-{
- return wallet->GetKey(address, vchPrivKeyOut);
-}
-
-// returns a list of COutputs from COutPoints
-void WalletModel::getOutputs(const std::vector<COutPoint>& vOutpoints, std::vector<COutput>& vOutputs)
-{
- LOCK2(cs_main, wallet->cs_wallet);
- for (const COutPoint& outpoint : vOutpoints)
- {
- auto it = wallet->mapWallet.find(outpoint.hash);
- if (it == wallet->mapWallet.end()) continue;
- int nDepth = it->second.GetDepthInMainChain();
- if (nDepth < 0) continue;
- COutput out(&it->second, outpoint.n, nDepth, true /* spendable */, true /* solvable */, true /* safe */);
- vOutputs.push_back(out);
- }
-}
-
-bool WalletModel::isSpent(const COutPoint& outpoint) const
-{
- LOCK2(cs_main, wallet->cs_wallet);
- return wallet->IsSpent(outpoint.hash, outpoint.n);
-}
-
-// AvailableCoins + LockedCoins grouped by wallet address (put change in one group with wallet address)
-void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& mapCoins) const
-{
- for (auto& group : wallet->ListCoins()) {
- auto& resultGroup = mapCoins[QString::fromStdString(EncodeDestination(group.first))];
- for (auto& coin : group.second) {
- resultGroup.emplace_back(std::move(coin));
- }
- }
-}
-
-bool WalletModel::isLockedCoin(uint256 hash, unsigned int n) const
-{
- LOCK2(cs_main, wallet->cs_wallet);
- return wallet->IsLockedCoin(hash, n);
-}
-
-void WalletModel::lockCoin(COutPoint& output)
-{
- LOCK2(cs_main, wallet->cs_wallet);
- wallet->LockCoin(output);
-}
-
-void WalletModel::unlockCoin(COutPoint& output)
-{
- LOCK2(cs_main, wallet->cs_wallet);
- wallet->UnlockCoin(output);
-}
-
-void WalletModel::listLockedCoins(std::vector<COutPoint>& vOutpts)
-{
- LOCK2(cs_main, wallet->cs_wallet);
- wallet->ListLockedCoins(vOutpts);
-}
-
void WalletModel::loadReceiveRequests(std::vector<std::string>& vReceiveRequests)
{
- vReceiveRequests = wallet->GetDestValues("rr"); // receive request
+ vReceiveRequests = m_wallet->getDestValues("rr"); // receive request
}
bool WalletModel::saveReceiveRequest(const std::string &sAddress, const int64_t nId, const std::string &sRequest)
@@ -641,27 +477,10 @@ bool WalletModel::saveReceiveRequest(const std::string &sAddress, const int64_t
ss << nId;
std::string key = "rr" + ss.str(); // "rr" prefix = "receive request" in destdata
- LOCK(wallet->cs_wallet);
if (sRequest.empty())
- return wallet->EraseDestData(dest, key);
+ return m_wallet->eraseDestData(dest, key);
else
- return wallet->AddDestData(dest, key, sRequest);
-}
-
-bool WalletModel::transactionCanBeAbandoned(uint256 hash) const
-{
- return wallet->TransactionCanBeAbandoned(hash);
-}
-
-bool WalletModel::abandonTransaction(uint256 hash) const
-{
- LOCK2(cs_main, wallet->cs_wallet);
- return wallet->AbandonTransaction(hash);
-}
-
-bool WalletModel::transactionCanBeBumped(uint256 hash) const
-{
- return feebumper::TransactionCanBeBumped(wallet, hash);
+ return m_wallet->addDestData(dest, key, sRequest);
}
bool WalletModel::bumpFee(uint256 hash)
@@ -672,7 +491,7 @@ bool WalletModel::bumpFee(uint256 hash)
CAmount old_fee;
CAmount new_fee;
CMutableTransaction mtx;
- if (feebumper::CreateTransaction(wallet, hash, coin_control, 0 /* totalFee */, errors, old_fee, new_fee, mtx) != feebumper::Result::OK) {
+ if (!m_wallet->createBumpTransaction(hash, coin_control, 0 /* totalFee */, errors, old_fee, new_fee, mtx)) {
QMessageBox::critical(0, tr("Fee bump error"), tr("Increasing transaction fee failed") + "<br />(" +
(errors.size() ? QString::fromStdString(errors[0]) : "") +")");
return false;
@@ -711,13 +530,13 @@ bool WalletModel::bumpFee(uint256 hash)
}
// sign bumped transaction
- if (!feebumper::SignTransaction(wallet, mtx)) {
+ if (!m_wallet->signBumpTransaction(mtx)) {
QMessageBox::critical(0, tr("Fee bump error"), tr("Can't sign transaction."));
return false;
}
// commit the bumped transaction
uint256 txid;
- if (feebumper::CommitTransaction(wallet, hash, std::move(mtx), errors, txid) != feebumper::Result::OK) {
+ if(!m_wallet->commitBumpTransaction(hash, std::move(mtx), errors, txid)) {
QMessageBox::critical(0, tr("Fee bump error"), tr("Could not commit transaction") + "<br />(" +
QString::fromStdString(errors[0])+")");
return false;
@@ -730,32 +549,12 @@ bool WalletModel::isWalletEnabled()
return !gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET);
}
-bool WalletModel::hdEnabled() const
-{
- return wallet->IsHDEnabled();
-}
-
-OutputType WalletModel::getDefaultAddressType() const
-{
- return wallet->m_default_address_type;
-}
-
-int WalletModel::getDefaultConfirmTarget() const
-{
- return nTxConfirmTarget;
-}
-
QString WalletModel::getWalletName() const
{
- LOCK(wallet->cs_wallet);
- QString walletName = QString::fromStdString(wallet->GetName());
- if (walletName.endsWith(".dat")) {
- walletName.truncate(walletName.size() - 4);
- }
- return walletName;
+ return QString::fromStdString(m_wallet->getWalletName());
}
bool WalletModel::isMultiwallet()
{
- return gArgs.GetArgs("-wallet").size() > 1;
+ return m_node.getWallets().size() > 1;
}
diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h
index ff4b38a804..bc409b10ee 100644
--- a/src/qt/walletmodel.h
+++ b/src/qt/walletmodel.h
@@ -13,6 +13,7 @@
#include <qt/paymentrequestplus.h>
#include <qt/walletmodeltransaction.h>
+#include <interface/wallet.h>
#include <support/allocators/secure.h>
#include <map>
@@ -34,9 +35,12 @@ class CKeyID;
class COutPoint;
class COutput;
class CPubKey;
-class CWallet;
class uint256;
+namespace interface {
+class Node;
+} // namespace interface
+
QT_BEGIN_NAMESPACE
class QTimer;
QT_END_NAMESPACE
@@ -107,7 +111,7 @@ class WalletModel : public QObject
Q_OBJECT
public:
- explicit WalletModel(const PlatformStyle *platformStyle, CWallet *wallet, OptionsModel *optionsModel, QObject *parent = 0);
+ explicit WalletModel(std::unique_ptr<interface::Wallet> wallet, interface::Node& node, const PlatformStyle *platformStyle, OptionsModel *optionsModel, QObject *parent = 0);
~WalletModel();
enum StatusCode // Returned by sendCoins
@@ -136,15 +140,6 @@ public:
TransactionTableModel *getTransactionTableModel();
RecentRequestsTableModel *getRecentRequestsTableModel();
- CWallet *getWallet() const { return wallet; };
-
- CAmount getBalance(const CCoinControl *coinControl = nullptr) const;
- CAmount getUnconfirmedBalance() const;
- CAmount getImmatureBalance() const;
- bool haveWatchOnly() const;
- CAmount getWatchBalance() const;
- CAmount getWatchUnconfirmedBalance() const;
- CAmount getWatchImmatureBalance() const;
EncryptionStatus getEncryptionStatus() const;
// Check address for validity
@@ -173,8 +168,6 @@ public:
// Passphrase only needed when unlocking
bool setWalletLocked(bool locked, const SecureString &passPhrase=SecureString());
bool changePassphrase(const SecureString &oldPass, const SecureString &newPass);
- // Wallet backup
- bool backupWallet(const QString &filename);
// RAI object for unlocking wallet, returned by requestUnlock()
class UnlockContext
@@ -198,40 +191,28 @@ public:
UnlockContext requestUnlock();
- bool getPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const;
- bool IsSpendable(const CTxDestination& dest) const;
- bool getPrivKey(const CKeyID &address, CKey& vchPrivKeyOut) const;
- void getOutputs(const std::vector<COutPoint>& vOutpoints, std::vector<COutput>& vOutputs);
- bool isSpent(const COutPoint& outpoint) const;
- void listCoins(std::map<QString, std::vector<COutput> >& mapCoins) const;
-
- bool isLockedCoin(uint256 hash, unsigned int n) const;
- void lockCoin(COutPoint& output);
- void unlockCoin(COutPoint& output);
- void listLockedCoins(std::vector<COutPoint>& vOutpts);
-
void loadReceiveRequests(std::vector<std::string>& vReceiveRequests);
bool saveReceiveRequest(const std::string &sAddress, const int64_t nId, const std::string &sRequest);
- bool transactionCanBeAbandoned(uint256 hash) const;
- bool abandonTransaction(uint256 hash) const;
-
- bool transactionCanBeBumped(uint256 hash) const;
bool bumpFee(uint256 hash);
static bool isWalletEnabled();
- bool hdEnabled() const;
-
- OutputType getDefaultAddressType() const;
-
- int getDefaultConfirmTarget() const;
+ interface::Node& node() const { return m_node; }
+ interface::Wallet& wallet() const { return *m_wallet; }
QString getWalletName() const;
- static bool isMultiwallet();
+ bool isMultiwallet();
private:
- CWallet *wallet;
+ std::unique_ptr<interface::Wallet> m_wallet;
+ std::unique_ptr<interface::Handler> m_handler_status_changed;
+ std::unique_ptr<interface::Handler> m_handler_address_book_changed;
+ std::unique_ptr<interface::Handler> m_handler_transaction_changed;
+ std::unique_ptr<interface::Handler> m_handler_show_progress;
+ std::unique_ptr<interface::Handler> m_handler_watch_only_changed;
+ interface::Node& m_node;
+
bool fHaveWatchOnly;
bool fForceCheckBalanceChanged;
@@ -244,12 +225,7 @@ private:
RecentRequestsTableModel *recentRequestsTableModel;
// Cache some values to be able to detect changes
- CAmount cachedBalance;
- CAmount cachedUnconfirmedBalance;
- CAmount cachedImmatureBalance;
- CAmount cachedWatchOnlyBalance;
- CAmount cachedWatchUnconfBalance;
- CAmount cachedWatchImmatureBalance;
+ interface::WalletBalances m_cached_balances;
EncryptionStatus cachedEncryptionStatus;
int cachedNumBlocks;
@@ -257,12 +233,11 @@ private:
void subscribeToCoreSignals();
void unsubscribeFromCoreSignals();
- void checkBalanceChanged();
+ void checkBalanceChanged(const interface::WalletBalances& new_balances);
Q_SIGNALS:
// Signal that balance in wallet changed
- void balanceChanged(const CAmount& balance, const CAmount& unconfirmedBalance, const CAmount& immatureBalance,
- const CAmount& watchOnlyBalance, const CAmount& watchUnconfBalance, const CAmount& watchImmatureBalance);
+ void balanceChanged(const interface::WalletBalances& balances);
// Encryption status of wallet changed
void encryptionStatusChanged();
@@ -276,7 +251,7 @@ Q_SIGNALS:
void message(const QString &title, const QString &message, unsigned int style);
// Coins sent: from wallet, to recipient, in (serialized) transaction:
- void coinsSent(CWallet* wallet, SendCoinsRecipient recipient, QByteArray transaction);
+ void coinsSent(WalletModel* wallet, SendCoinsRecipient recipient, QByteArray transaction);
// Show progress dialog e.g. for rescan
void showProgress(const QString &title, int nProgress);
diff --git a/src/qt/walletmodeltransaction.cpp b/src/qt/walletmodeltransaction.cpp
index 4df8a5687e..21bdfe3818 100644
--- a/src/qt/walletmodeltransaction.cpp
+++ b/src/qt/walletmodeltransaction.cpp
@@ -4,12 +4,11 @@
#include <qt/walletmodeltransaction.h>
+#include <interface/node.h>
#include <policy/policy.h>
-#include <wallet/wallet.h>
WalletModelTransaction::WalletModelTransaction(const QList<SendCoinsRecipient> &_recipients) :
recipients(_recipients),
- walletTransaction(0),
fee(0)
{
}
@@ -19,14 +18,14 @@ QList<SendCoinsRecipient> WalletModelTransaction::getRecipients() const
return recipients;
}
-CTransactionRef& WalletModelTransaction::getTransaction()
+std::unique_ptr<interface::PendingWalletTx>& WalletModelTransaction::getWtx()
{
- return walletTransaction;
+ return wtx;
}
unsigned int WalletModelTransaction::getTransactionSize()
{
- return (!walletTransaction ? 0 : ::GetVirtualTransactionSize(*walletTransaction));
+ return wtx ? wtx->getVirtualSize() : 0;
}
CAmount WalletModelTransaction::getTransactionFee() const
@@ -41,6 +40,7 @@ void WalletModelTransaction::setTransactionFee(const CAmount& newFee)
void WalletModelTransaction::reassignAmounts(int nChangePosRet)
{
+ const CTransaction* walletTransaction = &wtx->get();
int i = 0;
for (QList<SendCoinsRecipient>::iterator it = recipients.begin(); it != recipients.end(); ++it)
{
@@ -80,13 +80,3 @@ CAmount WalletModelTransaction::getTotalTransactionAmount() const
}
return totalTransactionAmount;
}
-
-void WalletModelTransaction::newPossibleKeyChange(CWallet *wallet)
-{
- keyChange.reset(new CReserveKey(wallet));
-}
-
-CReserveKey *WalletModelTransaction::getPossibleKeyChange()
-{
- return keyChange.get();
-}
diff --git a/src/qt/walletmodeltransaction.h b/src/qt/walletmodeltransaction.h
index 931e960d18..32fd0d2110 100644
--- a/src/qt/walletmodeltransaction.h
+++ b/src/qt/walletmodeltransaction.h
@@ -7,13 +7,16 @@
#include <qt/walletmodel.h>
+#include <memory>
+
#include <QObject>
class SendCoinsRecipient;
-class CReserveKey;
-class CWallet;
-class CWalletTx;
+namespace interface {
+class Node;
+class PendingWalletTx;
+}
/** Data model for a walletmodel transaction. */
class WalletModelTransaction
@@ -23,7 +26,7 @@ public:
QList<SendCoinsRecipient> getRecipients() const;
- CTransactionRef& getTransaction();
+ std::unique_ptr<interface::PendingWalletTx>& getWtx();
unsigned int getTransactionSize();
void setTransactionFee(const CAmount& newFee);
@@ -31,15 +34,11 @@ public:
CAmount getTotalTransactionAmount() const;
- void newPossibleKeyChange(CWallet *wallet);
- CReserveKey *getPossibleKeyChange();
-
void reassignAmounts(int nChangePosRet); // needed for the subtract-fee-from-amount feature
private:
QList<SendCoinsRecipient> recipients;
- CTransactionRef walletTransaction;
- std::unique_ptr<CReserveKey> keyChange;
+ std::unique_ptr<interface::PendingWalletTx> wtx;
CAmount fee;
};
diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp
index cc4300a7a1..1505557244 100644
--- a/src/qt/walletview.cpp
+++ b/src/qt/walletview.cpp
@@ -19,6 +19,7 @@
#include <qt/transactionview.h>
#include <qt/walletmodel.h>
+#include <interface/node.h>
#include <ui_interface.h>
#include <QAction>
@@ -158,7 +159,7 @@ void WalletView::setWalletModel(WalletModel *_walletModel)
void WalletView::processNewTransaction(const QModelIndex& parent, int start, int /*end*/)
{
// Prevent balloon-spam when initial block download is in progress
- if (!walletModel || !clientModel || clientModel->inInitialBlockDownload())
+ if (!walletModel || !clientModel || clientModel->node().isInitialBlockDownload())
return;
TransactionTableModel *ttm = walletModel->getTransactionTableModel();
@@ -257,7 +258,7 @@ void WalletView::backupWallet()
if (filename.isEmpty())
return;
- if (!walletModel->backupWallet(filename)) {
+ if (!walletModel->wallet().backupWallet(filename.toLocal8Bit().data())) {
Q_EMIT message(tr("Backup Failed"), tr("There was an error trying to save the wallet data to %1.").arg(filename),
CClientUIInterface::MSG_ERROR);
}
diff --git a/src/rest.cpp b/src/rest.cpp
index f47b40343b..5871b554a6 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -24,21 +24,21 @@
static const size_t MAX_GETUTXOS_OUTPOINTS = 15; //allow a max of 15 outpoints to be queried at once
-enum RetFormat {
- RF_UNDEF,
- RF_BINARY,
- RF_HEX,
- RF_JSON,
+enum class RetFormat {
+ UNDEF,
+ BINARY,
+ HEX,
+ JSON,
};
static const struct {
enum RetFormat rf;
const char* name;
} rf_names[] = {
- {RF_UNDEF, ""},
- {RF_BINARY, "bin"},
- {RF_HEX, "hex"},
- {RF_JSON, "json"},
+ {RetFormat::UNDEF, ""},
+ {RetFormat::BINARY, "bin"},
+ {RetFormat::HEX, "hex"},
+ {RetFormat::JSON, "json"},
};
struct CCoin {
@@ -162,20 +162,20 @@ static bool rest_headers(HTTPRequest* req,
}
switch (rf) {
- case RF_BINARY: {
+ case RetFormat::BINARY: {
std::string binaryHeader = ssHeader.str();
req->WriteHeader("Content-Type", "application/octet-stream");
req->WriteReply(HTTP_OK, binaryHeader);
return true;
}
- case RF_HEX: {
+ case RetFormat::HEX: {
std::string strHex = HexStr(ssHeader.begin(), ssHeader.end()) + "\n";
req->WriteHeader("Content-Type", "text/plain");
req->WriteReply(HTTP_OK, strHex);
return true;
}
- case RF_JSON: {
+ case RetFormat::JSON: {
UniValue jsonHeaders(UniValue::VARR);
{
LOCK(cs_main);
@@ -227,21 +227,21 @@ static bool rest_block(HTTPRequest* req,
ssBlock << block;
switch (rf) {
- case RF_BINARY: {
+ case RetFormat::BINARY: {
std::string binaryBlock = ssBlock.str();
req->WriteHeader("Content-Type", "application/octet-stream");
req->WriteReply(HTTP_OK, binaryBlock);
return true;
}
- case RF_HEX: {
+ case RetFormat::HEX: {
std::string strHex = HexStr(ssBlock.begin(), ssBlock.end()) + "\n";
req->WriteHeader("Content-Type", "text/plain");
req->WriteReply(HTTP_OK, strHex);
return true;
}
- case RF_JSON: {
+ case RetFormat::JSON: {
UniValue objBlock;
{
LOCK(cs_main);
@@ -280,7 +280,7 @@ static bool rest_chaininfo(HTTPRequest* req, const std::string& strURIPart)
const RetFormat rf = ParseDataFormat(param, strURIPart);
switch (rf) {
- case RF_JSON: {
+ case RetFormat::JSON: {
JSONRPCRequest jsonRequest;
jsonRequest.params = UniValue(UniValue::VARR);
UniValue chainInfoObject = getblockchaininfo(jsonRequest);
@@ -303,7 +303,7 @@ static bool rest_mempool_info(HTTPRequest* req, const std::string& strURIPart)
const RetFormat rf = ParseDataFormat(param, strURIPart);
switch (rf) {
- case RF_JSON: {
+ case RetFormat::JSON: {
UniValue mempoolInfoObject = mempoolInfoToJSON();
std::string strJSON = mempoolInfoObject.write() + "\n";
@@ -325,7 +325,7 @@ static bool rest_mempool_contents(HTTPRequest* req, const std::string& strURIPar
const RetFormat rf = ParseDataFormat(param, strURIPart);
switch (rf) {
- case RF_JSON: {
+ case RetFormat::JSON: {
UniValue mempoolObject = mempoolToJSON(true);
std::string strJSON = mempoolObject.write() + "\n";
@@ -359,21 +359,21 @@ static bool rest_tx(HTTPRequest* req, const std::string& strURIPart)
ssTx << tx;
switch (rf) {
- case RF_BINARY: {
+ case RetFormat::BINARY: {
std::string binaryTx = ssTx.str();
req->WriteHeader("Content-Type", "application/octet-stream");
req->WriteReply(HTTP_OK, binaryTx);
return true;
}
- case RF_HEX: {
+ case RetFormat::HEX: {
std::string strHex = HexStr(ssTx.begin(), ssTx.end()) + "\n";
req->WriteHeader("Content-Type", "text/plain");
req->WriteReply(HTTP_OK, strHex);
return true;
}
- case RF_JSON: {
+ case RetFormat::JSON: {
UniValue objTx(UniValue::VOBJ);
TxToUniv(*tx, hashBlock, objTx);
std::string strJSON = objTx.write() + "\n";
@@ -440,13 +440,13 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart)
}
switch (rf) {
- case RF_HEX: {
+ case RetFormat::HEX: {
// convert hex to bin, continue then with bin part
std::vector<unsigned char> strRequestV = ParseHex(strRequestMutable);
strRequestMutable.assign(strRequestV.begin(), strRequestV.end());
}
- case RF_BINARY: {
+ case RetFormat::BINARY: {
try {
//deserialize only if user sent a request
if (strRequestMutable.size() > 0)
@@ -466,7 +466,7 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart)
break;
}
- case RF_JSON: {
+ case RetFormat::JSON: {
if (!fInputParsed)
return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request");
break;
@@ -487,33 +487,35 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart)
std::vector<bool> hits;
bitmap.resize((vOutPoints.size() + 7) / 8);
{
- LOCK2(cs_main, mempool.cs);
-
- CCoinsView viewDummy;
- CCoinsViewCache view(&viewDummy);
-
- CCoinsViewCache& viewChain = *pcoinsTip;
- CCoinsViewMemPool viewMempool(&viewChain, mempool);
-
- if (fCheckMemPool)
- view.SetBackend(viewMempool); // switch cache backend to db+mempool in case user likes to query mempool
-
- for (size_t i = 0; i < vOutPoints.size(); i++) {
- bool hit = false;
- Coin coin;
- if (view.GetCoin(vOutPoints[i], coin) && !mempool.isSpent(vOutPoints[i])) {
- hit = true;
- outs.emplace_back(std::move(coin));
+ auto process_utxos = [&vOutPoints, &outs, &hits](const CCoinsView& view, const CTxMemPool& mempool) {
+ for (const COutPoint& vOutPoint : vOutPoints) {
+ Coin coin;
+ bool hit = !mempool.isSpent(vOutPoint) && view.GetCoin(vOutPoint, coin);
+ hits.push_back(hit);
+ if (hit) outs.emplace_back(std::move(coin));
}
+ };
+
+ if (fCheckMemPool) {
+ // use db+mempool as cache backend in case user likes to query mempool
+ LOCK2(cs_main, mempool.cs);
+ CCoinsViewCache& viewChain = *pcoinsTip;
+ CCoinsViewMemPool viewMempool(&viewChain, mempool);
+ process_utxos(viewMempool, mempool);
+ } else {
+ LOCK(cs_main); // no need to lock mempool!
+ process_utxos(*pcoinsTip, CTxMemPool());
+ }
- hits.push_back(hit);
+ for (size_t i = 0; i < hits.size(); ++i) {
+ const bool hit = hits[i];
bitmapStringRepresentation.append(hit ? "1" : "0"); // form a binary string representation (human-readable for json output)
bitmap[i / 8] |= ((uint8_t)hit) << (i % 8);
}
}
switch (rf) {
- case RF_BINARY: {
+ case RetFormat::BINARY: {
// serialize data
// use exact same output as mentioned in Bip64
CDataStream ssGetUTXOResponse(SER_NETWORK, PROTOCOL_VERSION);
@@ -525,7 +527,7 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart)
return true;
}
- case RF_HEX: {
+ case RetFormat::HEX: {
CDataStream ssGetUTXOResponse(SER_NETWORK, PROTOCOL_VERSION);
ssGetUTXOResponse << chainActive.Height() << chainActive.Tip()->GetBlockHash() << bitmap << outs;
std::string strHex = HexStr(ssGetUTXOResponse.begin(), ssGetUTXOResponse.end()) + "\n";
@@ -535,7 +537,7 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart)
return true;
}
- case RF_JSON: {
+ case RetFormat::JSON: {
UniValue objGetUTXOResponse(UniValue::VOBJ);
// pack in some essentials
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 169caddc59..06c68ea27c 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -33,6 +33,7 @@
#include <boost/thread/thread.hpp> // boost::thread::interrupt
+#include <memory>
#include <mutex>
#include <condition_variable>
@@ -1120,20 +1121,20 @@ static UniValue BIP9SoftForkDesc(const Consensus::Params& consensusParams, Conse
UniValue rv(UniValue::VOBJ);
const ThresholdState thresholdState = VersionBitsTipState(consensusParams, id);
switch (thresholdState) {
- case THRESHOLD_DEFINED: rv.pushKV("status", "defined"); break;
- case THRESHOLD_STARTED: rv.pushKV("status", "started"); break;
- case THRESHOLD_LOCKED_IN: rv.pushKV("status", "locked_in"); break;
- case THRESHOLD_ACTIVE: rv.pushKV("status", "active"); break;
- case THRESHOLD_FAILED: rv.pushKV("status", "failed"); break;
+ case ThresholdState::DEFINED: rv.pushKV("status", "defined"); break;
+ case ThresholdState::STARTED: rv.pushKV("status", "started"); break;
+ case ThresholdState::LOCKED_IN: rv.pushKV("status", "locked_in"); break;
+ case ThresholdState::ACTIVE: rv.pushKV("status", "active"); break;
+ case ThresholdState::FAILED: rv.pushKV("status", "failed"); break;
}
- if (THRESHOLD_STARTED == thresholdState)
+ if (ThresholdState::STARTED == thresholdState)
{
rv.pushKV("bit", consensusParams.vDeployments[id].bit);
}
rv.pushKV("startTime", consensusParams.vDeployments[id].nStartTime);
rv.pushKV("timeout", consensusParams.vDeployments[id].nTimeout);
rv.pushKV("since", VersionBitsTipStateSinceHeight(consensusParams, id));
- if (THRESHOLD_STARTED == thresholdState)
+ if (ThresholdState::STARTED == thresholdState)
{
UniValue statsUV(UniValue::VOBJ);
BIP9Stats statsStruct = VersionBitsTipStatistics(consensusParams, id);
@@ -1607,13 +1608,17 @@ UniValue savemempool(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 0) {
throw std::runtime_error(
"savemempool\n"
- "\nDumps the mempool to disk.\n"
+ "\nDumps the mempool to disk. It will fail until the previous dump is fully loaded.\n"
"\nExamples:\n"
+ HelpExampleCli("savemempool", "")
+ HelpExampleRpc("savemempool", "")
);
}
+ if (!g_is_mempool_loaded) {
+ throw JSONRPCError(RPC_MISC_ERROR, "The mempool was not loaded yet");
+ }
+
if (!DumpMempool()) {
throw JSONRPCError(RPC_MISC_ERROR, "Unable to dump mempool to disk");
}
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index e12685da65..01f932dbb4 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -103,6 +103,8 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "signrawtransactionwithkey", 2, "prevtxs" },
{ "signrawtransactionwithwallet", 1, "prevtxs" },
{ "sendrawtransaction", 1, "allowhighfees" },
+ { "testmempoolaccept", 0, "rawtxs" },
+ { "testmempoolaccept", 1, "allowhighfees" },
{ "combinerawtransaction", 0, "txs" },
{ "fundrawtransaction", 1, "options" },
{ "fundrawtransaction", 2, "iswitness" },
diff --git a/src/rpc/client.h b/src/rpc/client.h
index e7cf035d8f..e09e1dedf3 100644
--- a/src/rpc/client.h
+++ b/src/rpc/client.h
@@ -3,8 +3,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#ifndef BITCOIN_RPCCLIENT_H
-#define BITCOIN_RPCCLIENT_H
+#ifndef BITCOIN_RPC_CLIENT_H
+#define BITCOIN_RPC_CLIENT_H
#include <univalue.h>
@@ -19,4 +19,4 @@ UniValue RPCConvertNamedValues(const std::string& strMethod, const std::vector<s
*/
UniValue ParseNonRFCJSONValue(const std::string& strVal);
-#endif // BITCOIN_RPCCLIENT_H
+#endif // BITCOIN_RPC_CLIENT_H
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 0537628763..06882c0dfd 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -532,7 +532,7 @@ UniValue getblocktemplate(const JSONRPCRequest& request)
pblock->nNonce = 0;
// NOTE: If at some point we support pre-segwit miners post-segwit-activation, this needs to take segwit support into consideration
- const bool fPreSegWit = (THRESHOLD_ACTIVE != VersionBitsState(pindexPrev, consensusParams, Consensus::DEPLOYMENT_SEGWIT, versionbitscache));
+ const bool fPreSegWit = (ThresholdState::ACTIVE != VersionBitsState(pindexPrev, consensusParams, Consensus::DEPLOYMENT_SEGWIT, versionbitscache));
UniValue aCaps(UniValue::VARR); aCaps.push_back("proposal");
@@ -593,15 +593,15 @@ UniValue getblocktemplate(const JSONRPCRequest& request)
Consensus::DeploymentPos pos = Consensus::DeploymentPos(j);
ThresholdState state = VersionBitsState(pindexPrev, consensusParams, pos, versionbitscache);
switch (state) {
- case THRESHOLD_DEFINED:
- case THRESHOLD_FAILED:
+ case ThresholdState::DEFINED:
+ case ThresholdState::FAILED:
// Not exposed to GBT at all
break;
- case THRESHOLD_LOCKED_IN:
+ case ThresholdState::LOCKED_IN:
// Ensure bit is set in block version
pblock->nVersion |= VersionBitsMask(consensusParams, pos);
// FALL THROUGH to get vbavailable set...
- case THRESHOLD_STARTED:
+ case ThresholdState::STARTED:
{
const struct VBDeploymentInfo& vbinfo = VersionBitsDeploymentInfo[pos];
vbavailable.pushKV(gbt_vb_name(pos), consensusParams.vDeployments[pos].bit);
@@ -613,7 +613,7 @@ UniValue getblocktemplate(const JSONRPCRequest& request)
}
break;
}
- case THRESHOLD_ACTIVE:
+ case ThresholdState::ACTIVE:
{
// Add to rules only
const struct VBDeploymentInfo& vbinfo = VersionBitsDeploymentInfo[pos];
diff --git a/src/rpc/protocol.h b/src/rpc/protocol.h
index ff63bf4901..4a265735d2 100644
--- a/src/rpc/protocol.h
+++ b/src/rpc/protocol.h
@@ -3,8 +3,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#ifndef BITCOIN_RPCPROTOCOL_H
-#define BITCOIN_RPCPROTOCOL_H
+#ifndef BITCOIN_RPC_PROTOCOL_H
+#define BITCOIN_RPC_PROTOCOL_H
#include <fs.h>
@@ -104,4 +104,4 @@ void DeleteAuthCookie();
/** Parse JSON-RPC batch reply into a vector */
std::vector<UniValue> JSONRPCProcessBatchReply(const UniValue &in, size_t num);
-#endif // BITCOIN_RPCPROTOCOL_H
+#endif // BITCOIN_RPC_PROTOCOL_H
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 20bfd3f355..f0493de3bd 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -1023,18 +1023,18 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
new_request.params.push_back(request.params[1]);
new_request.params.push_back(request.params[3]);
return signrawtransactionwithkey(new_request);
- }
- // Otherwise sign with the wallet which does not take a privkeys parameter
+ } else {
#ifdef ENABLE_WALLET
- else {
+ // Otherwise sign with the wallet which does not take a privkeys parameter
new_request.params.push_back(request.params[0]);
new_request.params.push_back(request.params[1]);
new_request.params.push_back(request.params[3]);
return signrawtransactionwithwallet(new_request);
- }
+#else
+ // If we have made it this far, then wallet is disabled and no private keys were given, so fail here.
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "No private keys available.");
#endif
- // If we have made it this far, then wallet is disabled and no private keys were given, so fail here.
- throw JSONRPCError(RPC_INVALID_PARAMETER, "No private keys available.");
+ }
}
UniValue sendrawtransaction(const JSONRPCRequest& request)
@@ -1134,6 +1134,87 @@ UniValue sendrawtransaction(const JSONRPCRequest& request)
return hashTx.GetHex();
}
+UniValue testmempoolaccept(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
+ throw std::runtime_error(
+ // clang-format off
+ "testmempoolaccept [\"rawtxs\"] ( allowhighfees )\n"
+ "\nReturns if raw transaction (serialized, hex-encoded) would be accepted by mempool.\n"
+ "\nThis checks if the transaction violates the consensus or policy rules.\n"
+ "\nSee sendrawtransaction call.\n"
+ "\nArguments:\n"
+ "1. [\"rawtxs\"] (array, required) An array of hex strings of raw transactions.\n"
+ " Length must be one for now.\n"
+ "2. allowhighfees (boolean, optional, default=false) Allow high fees\n"
+ "\nResult:\n"
+ "[ (array) The result of the mempool acceptance test for each raw transaction in the input array.\n"
+ " Length is exactly one for now.\n"
+ " {\n"
+ " \"txid\" (string) The transaction hash in hex\n"
+ " \"allowed\" (boolean) If the mempool allows this tx to be inserted\n"
+ " \"reject-reason\" (string) Rejection string (only present when 'allowed' is false)\n"
+ " }\n"
+ "]\n"
+ "\nExamples:\n"
+ "\nCreate a transaction\n"
+ + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\" : \\\"mytxid\\\",\\\"vout\\\":0}]\" \"{\\\"myaddress\\\":0.01}\"") +
+ "Sign the transaction, and get back the hex\n"
+ + HelpExampleCli("signrawtransaction", "\"myhex\"") +
+ "\nTest acceptance of the transaction (signed hex)\n"
+ + HelpExampleCli("testmempoolaccept", "\"signedhex\"") +
+ "\nAs a json rpc call\n"
+ + HelpExampleRpc("testmempoolaccept", "[\"signedhex\"]")
+ // clang-format on
+ );
+ }
+
+ ObserveSafeMode();
+
+ RPCTypeCheck(request.params, {UniValue::VARR, UniValue::VBOOL});
+ if (request.params[0].get_array().size() != 1) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Array must contain exactly one raw transaction for now");
+ }
+
+ CMutableTransaction mtx;
+ if (!DecodeHexTx(mtx, request.params[0].get_array()[0].get_str())) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
+ }
+ CTransactionRef tx(MakeTransactionRef(std::move(mtx)));
+ const uint256& tx_hash = tx->GetHash();
+
+ CAmount max_raw_tx_fee = ::maxTxFee;
+ if (!request.params[1].isNull() && request.params[1].get_bool()) {
+ max_raw_tx_fee = 0;
+ }
+
+ UniValue result(UniValue::VARR);
+ UniValue result_0(UniValue::VOBJ);
+ result_0.pushKV("txid", tx_hash.GetHex());
+
+ CValidationState state;
+ bool missing_inputs;
+ bool test_accept_res;
+ {
+ LOCK(cs_main);
+ test_accept_res = AcceptToMemoryPool(mempool, state, std::move(tx), &missing_inputs,
+ nullptr /* plTxnReplaced */, false /* bypass_limits */, max_raw_tx_fee, /* test_accept */ true);
+ }
+ result_0.pushKV("allowed", test_accept_res);
+ if (!test_accept_res) {
+ if (state.IsInvalid()) {
+ result_0.pushKV("reject-reason", strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason()));
+ } else if (missing_inputs) {
+ result_0.pushKV("reject-reason", "missing-inputs");
+ } else {
+ result_0.pushKV("reject-reason", state.GetRejectReason());
+ }
+ }
+
+ result.push_back(std::move(result_0));
+ return result;
+}
+
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
// --------------------- ------------------------ ----------------------- ----------
@@ -1145,6 +1226,7 @@ static const CRPCCommand commands[] =
{ "rawtransactions", "combinerawtransaction", &combinerawtransaction, {"txs"} },
{ "rawtransactions", "signrawtransaction", &signrawtransaction, {"hexstring","prevtxs","privkeys","sighashtype"} }, /* uses wallet if enabled */
{ "rawtransactions", "signrawtransactionwithkey", &signrawtransactionwithkey, {"hexstring","privkeys","prevtxs","sighashtype"} },
+ { "rawtransactions", "testmempoolaccept", &testmempoolaccept, {"rawtxs","allowhighfees"} },
{ "blockchain", "gettxoutproof", &gettxoutproof, {"txids", "blockhash"} },
{ "blockchain", "verifytxoutproof", &verifytxoutproof, {"proof"} },
diff --git a/src/rpc/register.h b/src/rpc/register.h
index 49aee2365f..b689740681 100644
--- a/src/rpc/register.h
+++ b/src/rpc/register.h
@@ -2,8 +2,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#ifndef BITCOIN_RPCREGISTER_H
-#define BITCOIN_RPCREGISTER_H
+#ifndef BITCOIN_RPC_REGISTER_H
+#define BITCOIN_RPC_REGISTER_H
/** These are in one header file to avoid creating tons of single-function
* headers for everything under src/rpc/ */
@@ -29,4 +29,4 @@ static inline void RegisterAllCoreRPCCommands(CRPCTable &t)
RegisterRawTransactionRPCCommands(t);
}
-#endif
+#endif // BITCOIN_RPC_REGISTER_H
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
index 54995ef000..c7c3b1f0d3 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -367,7 +367,11 @@ void JSONRPCRequest::parse(const UniValue& valRequest)
if (!valMethod.isStr())
throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string");
strMethod = valMethod.get_str();
- LogPrint(BCLog::RPC, "ThreadRPCServer method=%s\n", SanitizeString(strMethod));
+ if (fLogIPs)
+ LogPrint(BCLog::RPC, "ThreadRPCServer method=%s user=%s peeraddr=%s\n", SanitizeString(strMethod),
+ this->authUser, this->peerAddr);
+ else
+ LogPrint(BCLog::RPC, "ThreadRPCServer method=%s user=%s\n", SanitizeString(strMethod), this->authUser);
// Parse params
UniValue valParams = find_value(request, "params");
diff --git a/src/rpc/server.h b/src/rpc/server.h
index d25268a8ab..373914885c 100644
--- a/src/rpc/server.h
+++ b/src/rpc/server.h
@@ -3,8 +3,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#ifndef BITCOIN_RPCSERVER_H
-#define BITCOIN_RPCSERVER_H
+#ifndef BITCOIN_RPC_SERVER_H
+#define BITCOIN_RPC_SERVER_H
#include <amount.h>
#include <rpc/protocol.h>
@@ -45,6 +45,7 @@ public:
bool fHelp;
std::string URI;
std::string authUser;
+ std::string peerAddr;
JSONRPCRequest() : id(NullUniValue), params(NullUniValue), fHelp(false) {}
void parse(const UniValue& valRequest);
@@ -205,4 +206,4 @@ std::string JSONRPCExecBatch(const JSONRPCRequest& jreq, const UniValue& vReq);
// Retrieves any serialization flags requested in command line argument
int RPCSerializationFlags();
-#endif // BITCOIN_RPCSERVER_H
+#endif // BITCOIN_RPC_SERVER_H
diff --git a/src/script/bitcoinconsensus.h b/src/script/bitcoinconsensus.h
index bb94c17528..5973808fa5 100644
--- a/src/script/bitcoinconsensus.h
+++ b/src/script/bitcoinconsensus.h
@@ -3,8 +3,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#ifndef BITCOIN_BITCOINCONSENSUS_H
-#define BITCOIN_BITCOINCONSENSUS_H
+#ifndef BITCOIN_SCRIPT_BITCOINCONSENSUS_H
+#define BITCOIN_SCRIPT_BITCOINCONSENSUS_H
#include <stdint.h>
@@ -80,4 +80,4 @@ EXPORT_SYMBOL unsigned int bitcoinconsensus_version();
#undef EXPORT_SYMBOL
-#endif // BITCOIN_BITCOINCONSENSUS_H
+#endif // BITCOIN_SCRIPT_BITCOINCONSENSUS_H
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index ffaba393c0..182f4a3327 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -61,17 +61,17 @@ static inline void popstack(std::vector<valtype>& stack)
}
bool static IsCompressedOrUncompressedPubKey(const valtype &vchPubKey) {
- if (vchPubKey.size() < 33) {
+ if (vchPubKey.size() < CPubKey::COMPRESSED_PUBLIC_KEY_SIZE) {
// Non-canonical public key: too short
return false;
}
if (vchPubKey[0] == 0x04) {
- if (vchPubKey.size() != 65) {
+ if (vchPubKey.size() != CPubKey::PUBLIC_KEY_SIZE) {
// Non-canonical public key: invalid length for uncompressed key
return false;
}
} else if (vchPubKey[0] == 0x02 || vchPubKey[0] == 0x03) {
- if (vchPubKey.size() != 33) {
+ if (vchPubKey.size() != CPubKey::COMPRESSED_PUBLIC_KEY_SIZE) {
// Non-canonical public key: invalid length for compressed key
return false;
}
@@ -83,7 +83,7 @@ bool static IsCompressedOrUncompressedPubKey(const valtype &vchPubKey) {
}
bool static IsCompressedPubKey(const valtype &vchPubKey) {
- if (vchPubKey.size() != 33) {
+ if (vchPubKey.size() != CPubKey::COMPRESSED_PUBLIC_KEY_SIZE) {
// Non-canonical public key: invalid length for compressed key
return false;
}
@@ -219,7 +219,7 @@ bool static CheckPubKeyEncoding(const valtype &vchPubKey, unsigned int flags, co
return set_error(serror, SCRIPT_ERR_PUBKEYTYPE);
}
// Only compressed keys are accepted in segwit
- if ((flags & SCRIPT_VERIFY_WITNESS_PUBKEYTYPE) != 0 && sigversion == SIGVERSION_WITNESS_V0 && !IsCompressedPubKey(vchPubKey)) {
+ if ((flags & SCRIPT_VERIFY_WITNESS_PUBKEYTYPE) != 0 && sigversion == SigVersion::WITNESS_V0 && !IsCompressedPubKey(vchPubKey)) {
return set_error(serror, SCRIPT_ERR_WITNESS_PUBKEYTYPE);
}
return true;
@@ -443,7 +443,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
if (stack.size() < 1)
return set_error(serror, SCRIPT_ERR_UNBALANCED_CONDITIONAL);
valtype& vch = stacktop(-1);
- if (sigversion == SIGVERSION_WITNESS_V0 && (flags & SCRIPT_VERIFY_MINIMALIF)) {
+ if (sigversion == SigVersion::WITNESS_V0 && (flags & SCRIPT_VERIFY_MINIMALIF)) {
if (vch.size() > 1)
return set_error(serror, SCRIPT_ERR_MINIMALIF);
if (vch.size() == 1 && vch[0] != 1)
@@ -890,7 +890,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
CScript scriptCode(pbegincodehash, pend);
// Drop the signature in pre-segwit scripts but not segwit scripts
- if (sigversion == SIGVERSION_BASE) {
+ if (sigversion == SigVersion::BASE) {
scriptCode.FindAndDelete(CScript(vchSig));
}
@@ -954,7 +954,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
for (int k = 0; k < nSigsCount; k++)
{
valtype& vchSig = stacktop(-isig-k);
- if (sigversion == SIGVERSION_BASE) {
+ if (sigversion == SigVersion::BASE) {
scriptCode.FindAndDelete(CScript(vchSig));
}
}
@@ -1182,7 +1182,7 @@ uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsig
{
assert(nIn < txTo.vin.size());
- if (sigversion == SIGVERSION_WITNESS_V0) {
+ if (sigversion == SigVersion::WITNESS_V0) {
uint256 hashPrevouts;
uint256 hashSequence;
uint256 hashOutputs;
@@ -1396,13 +1396,13 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion,
return set_error(serror, SCRIPT_ERR_PUSH_SIZE);
}
- if (!EvalScript(stack, scriptPubKey, flags, checker, SIGVERSION_WITNESS_V0, serror)) {
+ if (!EvalScript(stack, scriptPubKey, flags, checker, SigVersion::WITNESS_V0, serror)) {
return false;
}
// Scripts inside witness implicitly require cleanstack behaviour
if (stack.size() != 1)
- return set_error(serror, SCRIPT_ERR_EVAL_FALSE);
+ return set_error(serror, SCRIPT_ERR_CLEANSTACK);
if (!CastToBool(stack.back()))
return set_error(serror, SCRIPT_ERR_EVAL_FALSE);
return true;
@@ -1423,12 +1423,12 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C
}
std::vector<std::vector<unsigned char> > stack, stackCopy;
- if (!EvalScript(stack, scriptSig, flags, checker, SIGVERSION_BASE, serror))
+ if (!EvalScript(stack, scriptSig, flags, checker, SigVersion::BASE, serror))
// serror is set
return false;
if (flags & SCRIPT_VERIFY_P2SH)
stackCopy = stack;
- if (!EvalScript(stack, scriptPubKey, flags, checker, SIGVERSION_BASE, serror))
+ if (!EvalScript(stack, scriptPubKey, flags, checker, SigVersion::BASE, serror))
// serror is set
return false;
if (stack.empty())
@@ -1474,7 +1474,7 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C
CScript pubKey2(pubKeySerialized.begin(), pubKeySerialized.end());
popstack(stack);
- if (!EvalScript(stack, pubKey2, flags, checker, SIGVERSION_BASE, serror))
+ if (!EvalScript(stack, pubKey2, flags, checker, SigVersion::BASE, serror))
// serror is set
return false;
if (stack.empty())
diff --git a/src/script/interpreter.h b/src/script/interpreter.h
index 4dad6b44c5..bb7750d783 100644
--- a/src/script/interpreter.h
+++ b/src/script/interpreter.h
@@ -123,10 +123,10 @@ struct PrecomputedTransactionData
explicit PrecomputedTransactionData(const CTransaction& tx);
};
-enum SigVersion
+enum class SigVersion
{
- SIGVERSION_BASE = 0,
- SIGVERSION_WITNESS_V0 = 1,
+ BASE = 0,
+ WITNESS_V0 = 1,
};
uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache = nullptr);
diff --git a/src/script/ismine.cpp b/src/script/ismine.cpp
index 35d794b983..05bc5e9bd6 100644
--- a/src/script/ismine.cpp
+++ b/src/script/ismine.cpp
@@ -61,7 +61,7 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool&
break;
case TX_PUBKEY:
keyID = CPubKey(vSolutions[0]).GetID();
- if (sigversion != SIGVERSION_BASE && vSolutions[0].size() != 33) {
+ if (sigversion != SigVersion::BASE && vSolutions[0].size() != 33) {
isInvalid = true;
return ISMINE_NO;
}
@@ -76,14 +76,14 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool&
// This also applies to the P2WSH case.
break;
}
- isminetype ret = ::IsMine(keystore, GetScriptForDestination(CKeyID(uint160(vSolutions[0]))), isInvalid, SIGVERSION_WITNESS_V0);
+ isminetype ret = ::IsMine(keystore, GetScriptForDestination(CKeyID(uint160(vSolutions[0]))), isInvalid, SigVersion::WITNESS_V0);
if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid))
return ret;
break;
}
case TX_PUBKEYHASH:
keyID = CKeyID(uint160(vSolutions[0]));
- if (sigversion != SIGVERSION_BASE) {
+ if (sigversion != SigVersion::BASE) {
CPubKey pubkey;
if (keystore.GetPubKey(keyID, pubkey) && !pubkey.IsCompressed()) {
isInvalid = true;
@@ -114,7 +114,7 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool&
CScriptID scriptID = CScriptID(hash);
CScript subscript;
if (keystore.GetCScript(scriptID, subscript)) {
- isminetype ret = IsMine(keystore, subscript, isInvalid, SIGVERSION_WITNESS_V0);
+ isminetype ret = IsMine(keystore, subscript, isInvalid, SigVersion::WITNESS_V0);
if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid))
return ret;
}
@@ -129,7 +129,7 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool&
// them) enable spend-out-from-under-you attacks, especially
// in shared-wallet situations.
std::vector<valtype> keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1);
- if (sigversion != SIGVERSION_BASE) {
+ if (sigversion != SigVersion::BASE) {
for (size_t i = 0; i < keys.size(); i++) {
if (keys[i].size() != 33) {
isInvalid = true;
diff --git a/src/script/ismine.h b/src/script/ismine.h
index c1338c3a8e..f93a66e35a 100644
--- a/src/script/ismine.h
+++ b/src/script/ismine.h
@@ -31,11 +31,11 @@ typedef uint8_t isminefilter;
/* isInvalid becomes true when the script is found invalid by consensus or policy. This will terminate the recursion
* and return ISMINE_NO immediately, as an invalid script should never be considered as "mine". This is needed as
* different SIGVERSION may have different network rules. Currently the only use of isInvalid is indicate uncompressed
- * keys in SIGVERSION_WITNESS_V0 script, but could also be used in similar cases in the future
+ * keys in SigVersion::WITNESS_V0 script, but could also be used in similar cases in the future
*/
-isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey, bool& isInvalid, SigVersion = SIGVERSION_BASE);
-isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey, SigVersion = SIGVERSION_BASE);
-isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest, bool& isInvalid, SigVersion = SIGVERSION_BASE);
-isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest, SigVersion = SIGVERSION_BASE);
+isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey, bool& isInvalid, SigVersion = SigVersion::BASE);
+isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey, SigVersion = SigVersion::BASE);
+isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest, bool& isInvalid, SigVersion = SigVersion::BASE);
+isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest, SigVersion = SigVersion::BASE);
#endif // BITCOIN_SCRIPT_ISMINE_H
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index baa712dc2d..910bb39ce6 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -6,7 +6,6 @@
#include <script/sign.h>
#include <key.h>
-#include <keystore.h>
#include <policy/policy.h>
#include <primitives/transaction.h>
#include <script/standard.h>
@@ -15,16 +14,16 @@
typedef std::vector<unsigned char> valtype;
-TransactionSignatureCreator::TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : BaseSignatureCreator(keystoreIn), txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn) {}
+TransactionSignatureCreator::TransactionSignatureCreator(const SigningProvider* provider, const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : BaseSignatureCreator(provider), txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn) {}
bool TransactionSignatureCreator::CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& address, const CScript& scriptCode, SigVersion sigversion) const
{
CKey key;
- if (!keystore->GetKey(address, key))
+ if (!m_provider->GetKey(address, key))
return false;
// Signing with uncompressed keys is disabled in witness scripts
- if (sigversion == SIGVERSION_WITNESS_V0 && !key.IsCompressed())
+ if (sigversion == SigVersion::WITNESS_V0 && !key.IsCompressed())
return false;
uint256 hash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion);
@@ -91,12 +90,12 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP
else
{
CPubKey vch;
- creator.KeyStore().GetPubKey(keyID, vch);
+ creator.Provider().GetPubKey(keyID, vch);
ret.push_back(ToByteVector(vch));
}
return true;
case TX_SCRIPTHASH:
- if (creator.KeyStore().GetCScript(uint160(vSolutions[0]), scriptRet)) {
+ if (creator.Provider().GetCScript(uint160(vSolutions[0]), scriptRet)) {
ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end()));
return true;
}
@@ -112,7 +111,7 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP
case TX_WITNESS_V0_SCRIPTHASH:
CRIPEMD160().Write(&vSolutions[0][0], vSolutions[0].size()).Finalize(h160.begin());
- if (creator.KeyStore().GetCScript(h160, scriptRet)) {
+ if (creator.Provider().GetCScript(h160, scriptRet)) {
ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end()));
return true;
}
@@ -142,7 +141,7 @@ bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPu
{
std::vector<valtype> result;
txnouttype whichType;
- bool solved = SignStep(creator, fromPubKey, result, whichType, SIGVERSION_BASE);
+ bool solved = SignStep(creator, fromPubKey, result, whichType, SigVersion::BASE);
bool P2SH = false;
CScript subscript;
sigdata.scriptWitness.stack.clear();
@@ -153,7 +152,7 @@ bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPu
// the final scriptSig is the signatures from that
// and then the serialized subscript:
subscript = CScript(result[0].begin(), result[0].end());
- solved = solved && SignStep(creator, subscript, result, whichType, SIGVERSION_BASE) && whichType != TX_SCRIPTHASH;
+ solved = solved && SignStep(creator, subscript, result, whichType, SigVersion::BASE) && whichType != TX_SCRIPTHASH;
P2SH = true;
}
@@ -162,7 +161,7 @@ bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPu
CScript witnessscript;
witnessscript << OP_DUP << OP_HASH160 << ToByteVector(result[0]) << OP_EQUALVERIFY << OP_CHECKSIG;
txnouttype subType;
- solved = solved && SignStep(creator, witnessscript, result, subType, SIGVERSION_WITNESS_V0);
+ solved = solved && SignStep(creator, witnessscript, result, subType, SigVersion::WITNESS_V0);
sigdata.scriptWitness.stack = result;
result.clear();
}
@@ -170,7 +169,7 @@ bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPu
{
CScript witnessscript(result[0].begin(), result[0].end());
txnouttype subType;
- solved = solved && SignStep(creator, witnessscript, result, subType, SIGVERSION_WITNESS_V0) && subType != TX_SCRIPTHASH && subType != TX_WITNESS_V0_SCRIPTHASH && subType != TX_WITNESS_V0_KEYHASH;
+ solved = solved && SignStep(creator, witnessscript, result, subType, SigVersion::WITNESS_V0) && subType != TX_SCRIPTHASH && subType != TX_WITNESS_V0_SCRIPTHASH && subType != TX_WITNESS_V0_KEYHASH;
result.push_back(std::vector<unsigned char>(witnessscript.begin(), witnessscript.end()));
sigdata.scriptWitness.stack = result;
result.clear();
@@ -206,12 +205,12 @@ void UpdateTransaction(CMutableTransaction& tx, unsigned int nIn, const Signatur
UpdateInput(tx.vin[nIn], data);
}
-bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType)
+bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType)
{
assert(nIn < txTo.vin.size());
CTransaction txToConst(txTo);
- TransactionSignatureCreator creator(&keystore, &txToConst, nIn, amount, nHashType);
+ TransactionSignatureCreator creator(&provider, &txToConst, nIn, amount, nHashType);
SignatureData sigdata;
bool ret = ProduceSignature(creator, fromPubKey, sigdata);
@@ -219,14 +218,14 @@ bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CMutabl
return ret;
}
-bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType)
+bool SignSignature(const SigningProvider &provider, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType)
{
assert(nIn < txTo.vin.size());
CTxIn& txin = txTo.vin[nIn];
assert(txin.prevout.n < txFrom.vout.size());
const CTxOut& txout = txFrom.vout[txin.prevout.n];
- return SignSignature(keystore, txout.scriptPubKey, txTo, nIn, txout.nValue, nHashType);
+ return SignSignature(provider, txout.scriptPubKey, txTo, nIn, txout.nValue, nHashType);
}
static std::vector<valtype> CombineMultisig(const CScript& scriptPubKey, const BaseSignatureChecker& checker,
@@ -294,7 +293,7 @@ struct Stacks
Stacks() {}
explicit Stacks(const std::vector<valtype>& scriptSigStack_) : script(scriptSigStack_), witness() {}
explicit Stacks(const SignatureData& data) : witness(data.scriptWitness.stack) {
- EvalScript(script, data.scriptSig, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker(), SIGVERSION_BASE);
+ EvalScript(script, data.scriptSig, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker(), SigVersion::BASE);
}
SignatureData Output() const {
@@ -370,7 +369,7 @@ static Stacks CombineSignatures(const CScript& scriptPubKey, const BaseSignature
sigs2.witness.pop_back();
sigs2.script = sigs2.witness;
sigs2.witness.clear();
- Stacks result = CombineSignatures(pubKey2, checker, txType2, vSolutions2, sigs1, sigs2, SIGVERSION_WITNESS_V0);
+ Stacks result = CombineSignatures(pubKey2, checker, txType2, vSolutions2, sigs1, sigs2, SigVersion::WITNESS_V0);
result.witness = result.script;
result.script.clear();
result.witness.push_back(valtype(pubKey2.begin(), pubKey2.end()));
@@ -388,7 +387,7 @@ SignatureData CombineSignatures(const CScript& scriptPubKey, const BaseSignature
std::vector<std::vector<unsigned char> > vSolutions;
Solver(scriptPubKey, txType, vSolutions);
- return CombineSignatures(scriptPubKey, checker, txType, vSolutions, Stacks(scriptSig1), Stacks(scriptSig2), SIGVERSION_BASE).Output();
+ return CombineSignatures(scriptPubKey, checker, txType, vSolutions, Stacks(scriptSig1), Stacks(scriptSig2), SigVersion::BASE).Output();
}
namespace {
@@ -427,13 +426,13 @@ bool DummySignatureCreator::CreateSig(std::vector<unsigned char>& vchSig, const
return true;
}
-bool IsSolvable(const CKeyStore& store, const CScript& script)
+bool IsSolvable(const SigningProvider& provider, const CScript& script)
{
// This check is to make sure that the script we created can actually be solved for and signed by us
// if we were to have the private keys. This is just to make sure that the script is valid and that,
// if found in a transaction, we would still accept and relay that transaction. In particular,
// it will reject witness outputs that require signing with an uncompressed public key.
- DummySignatureCreator creator(&store);
+ DummySignatureCreator creator(&provider);
SignatureData sigs;
// Make sure that STANDARD_SCRIPT_VERIFY_FLAGS includes SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, the most
// important property this function is designed to test for.
diff --git a/src/script/sign.h b/src/script/sign.h
index 2c749521cd..c301f0544f 100644
--- a/src/script/sign.h
+++ b/src/script/sign.h
@@ -8,21 +8,32 @@
#include <script/interpreter.h>
+class CKey;
class CKeyID;
-class CKeyStore;
class CScript;
+class CScriptID;
class CTransaction;
struct CMutableTransaction;
+/** An interface to be implemented by keystores that support signing. */
+class SigningProvider
+{
+public:
+ virtual ~SigningProvider() {}
+ virtual bool GetCScript(const CScriptID &scriptid, CScript& script) const =0;
+ virtual bool GetPubKey(const CKeyID &address, CPubKey& pubkey) const =0;
+ virtual bool GetKey(const CKeyID &address, CKey& key) const =0;
+};
+
/** Virtual base class for signature creators. */
class BaseSignatureCreator {
protected:
- const CKeyStore* keystore;
+ const SigningProvider* m_provider;
public:
- explicit BaseSignatureCreator(const CKeyStore* keystoreIn) : keystore(keystoreIn) {}
- const CKeyStore& KeyStore() const { return *keystore; };
+ explicit BaseSignatureCreator(const SigningProvider* provider) : m_provider(provider) {}
+ const SigningProvider& Provider() const { return *m_provider; }
virtual ~BaseSignatureCreator() {}
virtual const BaseSignatureChecker& Checker() const =0;
@@ -39,7 +50,7 @@ class TransactionSignatureCreator : public BaseSignatureCreator {
const TransactionSignatureChecker checker;
public:
- TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn=SIGHASH_ALL);
+ TransactionSignatureCreator(const SigningProvider* provider, const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn=SIGHASH_ALL);
const BaseSignatureChecker& Checker() const override { return checker; }
bool CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override;
};
@@ -48,13 +59,13 @@ class MutableTransactionSignatureCreator : public TransactionSignatureCreator {
CTransaction tx;
public:
- MutableTransactionSignatureCreator(const CKeyStore* keystoreIn, const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : TransactionSignatureCreator(keystoreIn, &tx, nInIn, amountIn, nHashTypeIn), tx(*txToIn) {}
+ MutableTransactionSignatureCreator(const SigningProvider* provider, const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : TransactionSignatureCreator(provider, &tx, nInIn, amountIn, nHashTypeIn), tx(*txToIn) {}
};
/** A signature creator that just produces 72-byte empty signatures. */
class DummySignatureCreator : public BaseSignatureCreator {
public:
- explicit DummySignatureCreator(const CKeyStore* keystoreIn) : BaseSignatureCreator(keystoreIn) {}
+ explicit DummySignatureCreator(const SigningProvider* provider) : BaseSignatureCreator(provider) {}
const BaseSignatureChecker& Checker() const override;
bool CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override;
};
@@ -71,8 +82,8 @@ struct SignatureData {
bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& scriptPubKey, SignatureData& sigdata);
/** Produce a script signature for a transaction. */
-bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType);
-bool SignSignature(const CKeyStore& keystore, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType);
+bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType);
+bool SignSignature(const SigningProvider &provider, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType);
/** Combine two script signatures using a generic signature checker, intelligently, possibly with OP_0 placeholders. */
SignatureData CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, const SignatureData& scriptSig1, const SignatureData& scriptSig2);
@@ -84,8 +95,8 @@ void UpdateInput(CTxIn& input, const SignatureData& data);
/* Check whether we know how to sign for an output like this, assuming we
* have all private keys. While this function does not need private keys, the passed
- * keystore is used to look up public keys and redeemscripts by hash.
+ * provider is used to look up public keys and redeemscripts by hash.
* Solvability is unrelated to whether we consider this output to be ours. */
-bool IsSolvable(const CKeyStore& store, const CScript& script);
+bool IsSolvable(const SigningProvider& provider, const CScript& script);
#endif // BITCOIN_SCRIPT_SIGN_H
diff --git a/src/script/standard.cpp b/src/script/standard.cpp
index cfb3c58588..0b9053d7fc 100644
--- a/src/script/standard.cpp
+++ b/src/script/standard.cpp
@@ -132,7 +132,7 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::v
// Template matching opcodes:
if (opcode2 == OP_PUBKEYS)
{
- while (vch1.size() >= 33 && vch1.size() <= 65)
+ while (CPubKey::ValidSize(vch1))
{
vSolutionsRet.push_back(vch1);
if (!script1.GetOp(pc1, opcode1, vch1))
@@ -146,7 +146,7 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::v
if (opcode2 == OP_PUBKEY)
{
- if (vch1.size() < 33 || vch1.size() > 65)
+ if (!CPubKey::ValidSize(vch1))
break;
vSolutionsRet.push_back(vch1);
}
diff --git a/src/serialize.h b/src/serialize.h
index 91da6b0f80..247e915298 100644
--- a/src/serialize.h
+++ b/src/serialize.h
@@ -59,6 +59,12 @@ inline T* NCONST_PTR(const T* val)
return const_cast<T*>(val);
}
+//! Safely convert odd char pointer types to standard ones.
+inline char* CharCast(char* c) { return c; }
+inline char* CharCast(unsigned char* c) { return (char*)c; }
+inline const char* CharCast(const char* c) { return c; }
+inline const char* CharCast(const unsigned char* c) { return (const char*)c; }
+
/*
* Lowest-level serialization and conversion.
* @note Sizes of these types are verified in the tests
@@ -177,6 +183,8 @@ template<typename Stream> inline void Serialize(Stream& s, int64_t a ) { ser_wri
template<typename Stream> inline void Serialize(Stream& s, uint64_t a) { ser_writedata64(s, a); }
template<typename Stream> inline void Serialize(Stream& s, float a ) { ser_writedata32(s, ser_float_to_uint32(a)); }
template<typename Stream> inline void Serialize(Stream& s, double a ) { ser_writedata64(s, ser_double_to_uint64(a)); }
+template<typename Stream, int N> inline void Serialize(Stream& s, const char (&a)[N]) { s.write(a, N); }
+template<typename Stream, int N> inline void Serialize(Stream& s, const unsigned char (&a)[N]) { s.write(CharCast(a), N); }
template<typename Stream> inline void Unserialize(Stream& s, char& a ) { a = ser_readdata8(s); } // TODO Get rid of bare char
template<typename Stream> inline void Unserialize(Stream& s, int8_t& a ) { a = ser_readdata8(s); }
@@ -189,6 +197,8 @@ template<typename Stream> inline void Unserialize(Stream& s, int64_t& a ) { a =
template<typename Stream> inline void Unserialize(Stream& s, uint64_t& a) { a = ser_readdata64(s); }
template<typename Stream> inline void Unserialize(Stream& s, float& a ) { a = ser_uint32_to_float(ser_readdata32(s)); }
template<typename Stream> inline void Unserialize(Stream& s, double& a ) { a = ser_uint64_to_double(ser_readdata64(s)); }
+template<typename Stream, int N> inline void Unserialize(Stream& s, char (&a)[N]) { s.read(a, N); }
+template<typename Stream, int N> inline void Unserialize(Stream& s, unsigned char (&a)[N]) { s.read(CharCast(a), N); }
template<typename Stream> inline void Serialize(Stream& s, bool a) { char f=a; ser_writedata8(s, f); }
template<typename Stream> inline void Unserialize(Stream& s, bool& a) { char f=ser_readdata8(s); a=f; }
diff --git a/src/support/lockedpool.cpp b/src/support/lockedpool.cpp
index f10fd07c63..51c337ed2f 100644
--- a/src/support/lockedpool.cpp
+++ b/src/support/lockedpool.cpp
@@ -27,6 +27,7 @@
#endif
#include <algorithm>
+#include <memory>
LockedPoolManager* LockedPoolManager::_instance = nullptr;
std::once_flag LockedPoolManager::init_flag;
diff --git a/src/sync.cpp b/src/sync.cpp
index bf3d131e4e..fdf7c44e4c 100644
--- a/src/sync.cpp
+++ b/src/sync.cpp
@@ -4,6 +4,7 @@
#include <sync.h>
+#include <memory>
#include <set>
#include <util.h>
#include <utilstrencodings.h>
diff --git a/src/test/allocator_tests.cpp b/src/test/allocator_tests.cpp
index c177f0bf00..24cd88c7a7 100644
--- a/src/test/allocator_tests.cpp
+++ b/src/test/allocator_tests.cpp
@@ -7,6 +7,8 @@
#include <support/allocators/secure.h>
#include <test/test_bitcoin.h>
+#include <memory>
+
#include <boost/test/unit_test.hpp>
BOOST_FIXTURE_TEST_SUITE(allocator_tests, BasicTestingSetup)
diff --git a/src/test/blockchain_tests.cpp b/src/test/blockchain_tests.cpp
index 55fdd2c071..32b408838c 100644
--- a/src/test/blockchain_tests.cpp
+++ b/src/test/blockchain_tests.cpp
@@ -54,7 +54,7 @@ void TestDifficulty(uint32_t nbits, double expected_difficulty)
RejectDifficultyMismatch(difficulty, expected_difficulty);
}
-BOOST_FIXTURE_TEST_SUITE(blockchain_difficulty_tests, BasicTestingSetup)
+BOOST_FIXTURE_TEST_SUITE(blockchain_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(get_difficulty_for_very_low_target)
{
diff --git a/src/test/compress_tests.cpp b/src/test/compress_tests.cpp
index 3c26013622..127cc154df 100644
--- a/src/test/compress_tests.cpp
+++ b/src/test/compress_tests.cpp
@@ -25,16 +25,16 @@
BOOST_FIXTURE_TEST_SUITE(compress_tests, BasicTestingSetup)
bool static TestEncode(uint64_t in) {
- return in == CTxOutCompressor::DecompressAmount(CTxOutCompressor::CompressAmount(in));
+ return in == DecompressAmount(CompressAmount(in));
}
bool static TestDecode(uint64_t in) {
- return in == CTxOutCompressor::CompressAmount(CTxOutCompressor::DecompressAmount(in));
+ return in == CompressAmount(DecompressAmount(in));
}
bool static TestPair(uint64_t dec, uint64_t enc) {
- return CTxOutCompressor::CompressAmount(dec) == enc &&
- CTxOutCompressor::DecompressAmount(enc) == dec;
+ return CompressAmount(dec) == enc &&
+ DecompressAmount(enc) == dec;
}
BOOST_AUTO_TEST_CASE(compress_amounts)
diff --git a/src/test/data/script_tests.json b/src/test/data/script_tests.json
index ccefe52246..97edc98bf6 100644
--- a/src/test/data/script_tests.json
+++ b/src/test/data/script_tests.json
@@ -2553,22 +2553,22 @@
[["01", "635168", 0.00000001], "", "0 0x20 0xc7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "P2SH,WITNESS", "OK"],
[["02", "635168", 0.00000001], "", "0 0x20 0xc7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "P2SH,WITNESS", "OK"],
[["0100", "635168", 0.00000001], "", "0 0x20 0xc7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "P2SH,WITNESS", "OK"],
-[["", "635168", 0.00000001], "", "0 0x20 0xc7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "P2SH,WITNESS", "EVAL_FALSE"],
-[["00", "635168", 0.00000001], "", "0 0x20 0xc7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "P2SH,WITNESS", "EVAL_FALSE"],
+[["", "635168", 0.00000001], "", "0 0x20 0xc7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "P2SH,WITNESS", "CLEANSTACK"],
+[["00", "635168", 0.00000001], "", "0 0x20 0xc7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "P2SH,WITNESS", "CLEANSTACK"],
[["01", "635168", 0.00000001], "", "0 0x20 0xc7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "P2SH,WITNESS,MINIMALIF", "OK"],
[["02", "635168", 0.00000001], "", "0 0x20 0xc7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "P2SH,WITNESS,MINIMALIF", "MINIMALIF"],
[["0100", "635168", 0.00000001], "", "0 0x20 0xc7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "P2SH,WITNESS,MINIMALIF", "MINIMALIF"],
-[["", "635168", 0.00000001], "", "0 0x20 0xc7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "P2SH,WITNESS,MINIMALIF", "EVAL_FALSE"],
+[["", "635168", 0.00000001], "", "0 0x20 0xc7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "P2SH,WITNESS,MINIMALIF", "CLEANSTACK"],
[["00", "635168", 0.00000001], "", "0 0x20 0xc7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "P2SH,WITNESS,MINIMALIF", "MINIMALIF"],
[["635168", 0.00000001], "", "0 0x20 0xc7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "P2SH,WITNESS", "UNBALANCED_CONDITIONAL"],
[["635168", 0.00000001], "", "0 0x20 0xc7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "P2SH,WITNESS,MINIMALIF", "UNBALANCED_CONDITIONAL"],
["P2WSH NOTIF 1 ENDIF"],
-[["01", "645168", 0.00000001], "", "0 0x20 0xf913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "P2SH,WITNESS", "EVAL_FALSE"],
-[["02", "645168", 0.00000001], "", "0 0x20 0xf913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "P2SH,WITNESS", "EVAL_FALSE"],
-[["0100", "645168", 0.00000001], "", "0 0x20 0xf913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "P2SH,WITNESS", "EVAL_FALSE"],
+[["01", "645168", 0.00000001], "", "0 0x20 0xf913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "P2SH,WITNESS", "CLEANSTACK"],
+[["02", "645168", 0.00000001], "", "0 0x20 0xf913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "P2SH,WITNESS", "CLEANSTACK"],
+[["0100", "645168", 0.00000001], "", "0 0x20 0xf913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "P2SH,WITNESS", "CLEANSTACK"],
[["", "645168", 0.00000001], "", "0 0x20 0xf913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "P2SH,WITNESS", "OK"],
[["00", "645168", 0.00000001], "", "0 0x20 0xf913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "P2SH,WITNESS", "OK"],
-[["01", "645168", 0.00000001], "", "0 0x20 0xf913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "P2SH,WITNESS,MINIMALIF", "EVAL_FALSE"],
+[["01", "645168", 0.00000001], "", "0 0x20 0xf913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "P2SH,WITNESS,MINIMALIF", "CLEANSTACK"],
[["02", "645168", 0.00000001], "", "0 0x20 0xf913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "P2SH,WITNESS,MINIMALIF", "MINIMALIF"],
[["0100", "645168", 0.00000001], "", "0 0x20 0xf913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "P2SH,WITNESS,MINIMALIF", "MINIMALIF"],
[["", "645168", 0.00000001], "", "0 0x20 0xf913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "P2SH,WITNESS,MINIMALIF", "OK"],
@@ -2582,22 +2582,22 @@
[["01", "635168", 0.00000001], "0x22 0x0020c7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "HASH160 0x14 0x9b27ee6d9010c21bf837b334d043be5d150e7ba7 EQUAL", "P2SH,WITNESS", "OK"],
[["02", "635168", 0.00000001], "0x22 0x0020c7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "HASH160 0x14 0x9b27ee6d9010c21bf837b334d043be5d150e7ba7 EQUAL", "P2SH,WITNESS", "OK"],
[["0100", "635168", 0.00000001], "0x22 0x0020c7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "HASH160 0x14 0x9b27ee6d9010c21bf837b334d043be5d150e7ba7 EQUAL", "P2SH,WITNESS", "OK"],
-[["", "635168", 0.00000001], "0x22 0x0020c7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "HASH160 0x14 0x9b27ee6d9010c21bf837b334d043be5d150e7ba7 EQUAL", "P2SH,WITNESS", "EVAL_FALSE"],
-[["00", "635168", 0.00000001], "0x22 0x0020c7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "HASH160 0x14 0x9b27ee6d9010c21bf837b334d043be5d150e7ba7 EQUAL", "P2SH,WITNESS", "EVAL_FALSE"],
+[["", "635168", 0.00000001], "0x22 0x0020c7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "HASH160 0x14 0x9b27ee6d9010c21bf837b334d043be5d150e7ba7 EQUAL", "P2SH,WITNESS", "CLEANSTACK"],
+[["00", "635168", 0.00000001], "0x22 0x0020c7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "HASH160 0x14 0x9b27ee6d9010c21bf837b334d043be5d150e7ba7 EQUAL", "P2SH,WITNESS", "CLEANSTACK"],
[["01", "635168", 0.00000001], "0x22 0x0020c7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "HASH160 0x14 0x9b27ee6d9010c21bf837b334d043be5d150e7ba7 EQUAL", "P2SH,WITNESS,MINIMALIF", "OK"],
[["02", "635168", 0.00000001], "0x22 0x0020c7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "HASH160 0x14 0x9b27ee6d9010c21bf837b334d043be5d150e7ba7 EQUAL", "P2SH,WITNESS,MINIMALIF", "MINIMALIF"],
[["0100", "635168", 0.00000001], "0x22 0x0020c7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "HASH160 0x14 0x9b27ee6d9010c21bf837b334d043be5d150e7ba7 EQUAL", "P2SH,WITNESS,MINIMALIF", "MINIMALIF"],
-[["", "635168", 0.00000001], "0x22 0x0020c7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "HASH160 0x14 0x9b27ee6d9010c21bf837b334d043be5d150e7ba7 EQUAL", "P2SH,WITNESS,MINIMALIF", "EVAL_FALSE"],
+[["", "635168", 0.00000001], "0x22 0x0020c7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "HASH160 0x14 0x9b27ee6d9010c21bf837b334d043be5d150e7ba7 EQUAL", "P2SH,WITNESS,MINIMALIF", "CLEANSTACK"],
[["00", "635168", 0.00000001], "0x22 0x0020c7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "HASH160 0x14 0x9b27ee6d9010c21bf837b334d043be5d150e7ba7 EQUAL", "P2SH,WITNESS,MINIMALIF", "MINIMALIF"],
[["635168", 0.00000001], "0x22 0x0020c7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "HASH160 0x14 0x9b27ee6d9010c21bf837b334d043be5d150e7ba7 EQUAL", "P2SH,WITNESS", "UNBALANCED_CONDITIONAL"],
[["635168", 0.00000001], "0x22 0x0020c7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "HASH160 0x14 0x9b27ee6d9010c21bf837b334d043be5d150e7ba7 EQUAL", "P2SH,WITNESS,MINIMALIF", "UNBALANCED_CONDITIONAL"],
["P2SH-P2WSH NOTIF 1 ENDIF"],
-[["01", "645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS", "EVAL_FALSE"],
-[["02", "645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS", "EVAL_FALSE"],
-[["0100", "645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS", "EVAL_FALSE"],
+[["01", "645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS", "CLEANSTACK"],
+[["02", "645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS", "CLEANSTACK"],
+[["0100", "645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS", "CLEANSTACK"],
[["", "645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS", "OK"],
[["00", "645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS", "OK"],
-[["01", "645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS,MINIMALIF", "EVAL_FALSE"],
+[["01", "645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS,MINIMALIF", "CLEANSTACK"],
[["02", "645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS,MINIMALIF", "MINIMALIF"],
[["0100", "645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS,MINIMALIF", "MINIMALIF"],
[["", "645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS,MINIMALIF", "OK"],
diff --git a/src/test/data/tx_invalid.json b/src/test/data/tx_invalid.json
index f8a1347c31..abb46fe533 100644
--- a/src/test/data/tx_invalid.json
+++ b/src/test/data/tx_invalid.json
@@ -321,7 +321,7 @@
["where the pubkey is obtained through key recovery with sig and the wrong sighash."],
["This is to show that FindAndDelete is applied only to non-segwit scripts"],
["To show that the tests are 'correctly wrong', they should pass by modifying OP_CHECKSIG under interpreter.cpp"],
-["by replacing (sigversion == SIGVERSION_BASE) with (sigversion != SIGVERSION_BASE)"],
+["by replacing (sigversion == SigVersion::BASE) with (sigversion != SigVersion::BASE)"],
["Non-segwit: wrong sighash (without FindAndDelete) = 1ba1fe3bc90c5d1265460e684ce6774e324f0fabdf67619eda729e64e8b6bc08"],
[[["f18783ace138abac5d3a7a5cf08e88fe6912f267ef936452e0c27d090621c169", 7000, "HASH160 0x14 0x0c746489e2d83cdbb5b90b432773342ba809c134 EQUAL", 200000]],
"010000000169c12106097dc2e0526493ef67f21269fe888ef05c7a3a5dacab38e1ac8387f1581b0000b64830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e012103b12a1ec8428fc74166926318c15e17408fea82dbb157575e16a8c365f546248f4aad4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e01ffffffff0101000000000000000000000000", "P2SH,WITNESS"],
@@ -332,7 +332,7 @@
["Script is 2 CHECKMULTISIGVERIFY <sig1> <sig2> DROP"],
["52af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960175"],
["Signature is 0 <sig1> <sig2> 2 <key1> <key2>"],
-["Should pass by replacing (sigversion == SIGVERSION_BASE) with (sigversion != SIGVERSION_BASE) under OP_CHECKMULTISIG"],
+["Should pass by replacing (sigversion == SigVersion::BASE) with (sigversion != SigVersion::BASE) under OP_CHECKMULTISIG"],
["Non-segwit: wrong sighash (without FindAndDelete) = 4bc6a53e8e16ef508c19e38bba08831daba85228b0211f323d4cb0999cf2a5e8"],
[[["9628667ad48219a169b41b020800162287d2c0f713c04157e95c484a8dcb7592", 7000, "HASH160 0x14 0x5748407f5ca5cdca53ba30b79040260770c9ee1b EQUAL", 200000]],
"01000000019275cb8d4a485ce95741c013f7c0d28722160008021bb469a11982d47a662896581b0000fd6f01004830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c039596015221023fd5dd42b44769c5653cbc5947ff30ab8871f240ad0c0e7432aefe84b5b4ff3421039d52178dbde360b83f19cf348deb04fa8360e1bf5634577be8e50fafc2b0e4ef4c9552af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960175ffffffff0101000000000000000000000000", "P2SH,WITNESS"],
diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp
index 6694401a29..35f0463e3e 100644
--- a/src/test/dbwrapper_tests.cpp
+++ b/src/test/dbwrapper_tests.cpp
@@ -7,6 +7,8 @@
#include <random.h>
#include <test/test_bitcoin.h>
+#include <memory>
+
#include <boost/test/unit_test.hpp>
// Test if a string consists entirely of null characters
diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp
index 72d9d3d75c..b593f9633c 100644
--- a/src/test/multisig_tests.cpp
+++ b/src/test/multisig_tests.cpp
@@ -21,7 +21,7 @@ BOOST_FIXTURE_TEST_SUITE(multisig_tests, BasicTestingSetup)
CScript
sign_multisig(CScript scriptPubKey, std::vector<CKey> keys, CTransaction transaction, int whichIn)
{
- uint256 hash = SignatureHash(scriptPubKey, transaction, whichIn, SIGHASH_ALL, 0, SIGVERSION_BASE);
+ uint256 hash = SignatureHash(scriptPubKey, transaction, whichIn, SIGHASH_ALL, 0, SigVersion::BASE);
CScript result;
result << OP_0; // CHECKMULTISIG bug workaround
diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp
index e03234060d..436ae696d1 100644
--- a/src/test/net_tests.cpp
+++ b/src/test/net_tests.cpp
@@ -13,6 +13,8 @@
#include <chainparams.h>
#include <util.h>
+#include <memory>
+
class CAddrManSerializationMock : public CAddrMan
{
public:
@@ -64,7 +66,7 @@ public:
CDataStream AddrmanToStream(CAddrManSerializationMock& _addrman)
{
CDataStream ssPeersIn(SER_DISK, CLIENT_VERSION);
- ssPeersIn << FLATDATA(Params().MessageStart());
+ ssPeersIn << Params().MessageStart();
ssPeersIn << _addrman;
std::string str = ssPeersIn.str();
std::vector<unsigned char> vchData(str.begin(), str.end());
@@ -110,7 +112,7 @@ BOOST_AUTO_TEST_CASE(caddrdb_read)
BOOST_CHECK(addrman1.size() == 0);
try {
unsigned char pchMsgTmp[4];
- ssPeers1 >> FLATDATA(pchMsgTmp);
+ ssPeers1 >> pchMsgTmp;
ssPeers1 >> addrman1;
} catch (const std::exception& e) {
exceptionThrown = true;
@@ -142,7 +144,7 @@ BOOST_AUTO_TEST_CASE(caddrdb_read_corrupted)
BOOST_CHECK(addrman1.size() == 0);
try {
unsigned char pchMsgTmp[4];
- ssPeers1 >> FLATDATA(pchMsgTmp);
+ ssPeers1 >> pchMsgTmp;
ssPeers1 >> addrman1;
} catch (const std::exception& e) {
exceptionThrown = true;
diff --git a/src/test/prevector_tests.cpp b/src/test/prevector_tests.cpp
index 01c3a6cedd..fe6f10d845 100644
--- a/src/test/prevector_tests.cpp
+++ b/src/test/prevector_tests.cpp
@@ -13,7 +13,7 @@
#include <boost/test/unit_test.hpp>
-BOOST_FIXTURE_TEST_SUITE(PrevectorTests, TestingSetup)
+BOOST_FIXTURE_TEST_SUITE(prevector_tests, TestingSetup)
template<unsigned int N, typename T>
class prevector_tester {
diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp
index c7a497f3a7..46a2d13745 100644
--- a/src/test/script_tests.cpp
+++ b/src/test/script_tests.cpp
@@ -267,10 +267,10 @@ struct KeyData
}
};
-enum WitnessMode {
- WITNESS_NONE,
- WITNESS_PKH,
- WITNESS_SH
+enum class WitnessMode {
+ NONE,
+ PKH,
+ SH
};
class TestBuilder
@@ -308,15 +308,15 @@ private:
}
public:
- TestBuilder(const CScript& script_, const std::string& comment_, int flags_, bool P2SH = false, WitnessMode wm = WITNESS_NONE, int witnessversion = 0, CAmount nValue_ = 0) : script(script_), havePush(false), comment(comment_), flags(flags_), scriptError(SCRIPT_ERR_OK), nValue(nValue_)
+ TestBuilder(const CScript& script_, const std::string& comment_, int flags_, bool P2SH = false, WitnessMode wm = WitnessMode::NONE, int witnessversion = 0, CAmount nValue_ = 0) : script(script_), havePush(false), comment(comment_), flags(flags_), scriptError(SCRIPT_ERR_OK), nValue(nValue_)
{
CScript scriptPubKey = script;
- if (wm == WITNESS_PKH) {
+ if (wm == WitnessMode::PKH) {
uint160 hash;
CHash160().Write(&script[1], script.size() - 1).Finalize(hash.begin());
script = CScript() << OP_DUP << OP_HASH160 << ToByteVector(hash) << OP_EQUALVERIFY << OP_CHECKSIG;
scriptPubKey = CScript() << witnessversion << ToByteVector(hash);
- } else if (wm == WITNESS_SH) {
+ } else if (wm == WitnessMode::SH) {
witscript = scriptPubKey;
uint256 hash;
CSHA256().Write(&witscript[0], witscript.size()).Finalize(hash.begin());
@@ -361,7 +361,7 @@ public:
return *this;
}
- TestBuilder& PushSig(const CKey& key, int nHashType = SIGHASH_ALL, unsigned int lenR = 32, unsigned int lenS = 32, SigVersion sigversion = SIGVERSION_BASE, CAmount amount = 0)
+ TestBuilder& PushSig(const CKey& key, int nHashType = SIGHASH_ALL, unsigned int lenR = 32, unsigned int lenS = 32, SigVersion sigversion = SigVersion::BASE, CAmount amount = 0)
{
uint256 hash = SignatureHash(script, spendTx, 0, nHashType, amount, sigversion);
std::vector<unsigned char> vchSig, r, s;
@@ -379,7 +379,7 @@ public:
return *this;
}
- TestBuilder& PushWitSig(const CKey& key, CAmount amount = -1, int nHashType = SIGHASH_ALL, unsigned int lenR = 32, unsigned int lenS = 32, SigVersion sigversion = SIGVERSION_WITNESS_V0)
+ TestBuilder& PushWitSig(const CKey& key, CAmount amount = -1, int nHashType = SIGHASH_ALL, unsigned int lenR = 32, unsigned int lenS = 32, SigVersion sigversion = SigVersion::WITNESS_V0)
{
if (amount == -1)
amount = nValue;
@@ -747,57 +747,57 @@ BOOST_AUTO_TEST_CASE(script_build)
).PushSig(keys.key0).PushRedeem());
tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey0) << OP_CHECKSIG,
- "Basic P2WSH", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false, WITNESS_SH,
+ "Basic P2WSH", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false, WitnessMode::SH,
0, 1).PushWitSig(keys.key0).PushWitRedeem());
tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey0),
- "Basic P2WPKH", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false, WITNESS_PKH,
+ "Basic P2WPKH", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false, WitnessMode::PKH,
0, 1).PushWitSig(keys.key0).Push(keys.pubkey0).AsWit());
tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey0) << OP_CHECKSIG,
- "Basic P2SH(P2WSH)", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true, WITNESS_SH,
+ "Basic P2SH(P2WSH)", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true, WitnessMode::SH,
0, 1).PushWitSig(keys.key0).PushWitRedeem().PushRedeem());
tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey0),
- "Basic P2SH(P2WPKH)", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true, WITNESS_PKH,
+ "Basic P2SH(P2WPKH)", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true, WitnessMode::PKH,
0, 1).PushWitSig(keys.key0).Push(keys.pubkey0).AsWit().PushRedeem());
tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey1) << OP_CHECKSIG,
- "Basic P2WSH with the wrong key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false, WITNESS_SH
+ "Basic P2WSH with the wrong key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false, WitnessMode::SH
).PushWitSig(keys.key0).PushWitRedeem().ScriptError(SCRIPT_ERR_EVAL_FALSE));
tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey1),
- "Basic P2WPKH with the wrong key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false, WITNESS_PKH
+ "Basic P2WPKH with the wrong key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false, WitnessMode::PKH
).PushWitSig(keys.key0).Push(keys.pubkey1).AsWit().ScriptError(SCRIPT_ERR_EVAL_FALSE));
tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey1) << OP_CHECKSIG,
- "Basic P2SH(P2WSH) with the wrong key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true, WITNESS_SH
+ "Basic P2SH(P2WSH) with the wrong key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true, WitnessMode::SH
).PushWitSig(keys.key0).PushWitRedeem().PushRedeem().ScriptError(SCRIPT_ERR_EVAL_FALSE));
tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey1),
- "Basic P2SH(P2WPKH) with the wrong key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true, WITNESS_PKH
+ "Basic P2SH(P2WPKH) with the wrong key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true, WitnessMode::PKH
).PushWitSig(keys.key0).Push(keys.pubkey1).AsWit().PushRedeem().ScriptError(SCRIPT_ERR_EVAL_FALSE));
tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey1) << OP_CHECKSIG,
- "Basic P2WSH with the wrong key but no WITNESS", SCRIPT_VERIFY_P2SH, false, WITNESS_SH
+ "Basic P2WSH with the wrong key but no WITNESS", SCRIPT_VERIFY_P2SH, false, WitnessMode::SH
).PushWitSig(keys.key0).PushWitRedeem());
tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey1),
- "Basic P2WPKH with the wrong key but no WITNESS", SCRIPT_VERIFY_P2SH, false, WITNESS_PKH
+ "Basic P2WPKH with the wrong key but no WITNESS", SCRIPT_VERIFY_P2SH, false, WitnessMode::PKH
).PushWitSig(keys.key0).Push(keys.pubkey1).AsWit());
tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey1) << OP_CHECKSIG,
- "Basic P2SH(P2WSH) with the wrong key but no WITNESS", SCRIPT_VERIFY_P2SH, true, WITNESS_SH
+ "Basic P2SH(P2WSH) with the wrong key but no WITNESS", SCRIPT_VERIFY_P2SH, true, WitnessMode::SH
).PushWitSig(keys.key0).PushWitRedeem().PushRedeem());
tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey1),
- "Basic P2SH(P2WPKH) with the wrong key but no WITNESS", SCRIPT_VERIFY_P2SH, true, WITNESS_PKH
+ "Basic P2SH(P2WPKH) with the wrong key but no WITNESS", SCRIPT_VERIFY_P2SH, true, WitnessMode::PKH
).PushWitSig(keys.key0).Push(keys.pubkey1).AsWit().PushRedeem());
tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey0) << OP_CHECKSIG,
- "Basic P2WSH with wrong value", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false, WITNESS_SH,
+ "Basic P2WSH with wrong value", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false, WitnessMode::SH,
0, 0).PushWitSig(keys.key0, 1).PushWitRedeem().ScriptError(SCRIPT_ERR_EVAL_FALSE));
tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey0),
- "Basic P2WPKH with wrong value", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false, WITNESS_PKH,
+ "Basic P2WPKH with wrong value", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false, WitnessMode::PKH,
0, 0).PushWitSig(keys.key0, 1).Push(keys.pubkey0).AsWit().ScriptError(SCRIPT_ERR_EVAL_FALSE));
tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey0) << OP_CHECKSIG,
- "Basic P2SH(P2WSH) with wrong value", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true, WITNESS_SH,
+ "Basic P2SH(P2WSH) with wrong value", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true, WitnessMode::SH,
0, 0).PushWitSig(keys.key0, 1).PushWitRedeem().PushRedeem().ScriptError(SCRIPT_ERR_EVAL_FALSE));
tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey0),
- "Basic P2SH(P2WPKH) with wrong value", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true, WITNESS_PKH,
+ "Basic P2SH(P2WPKH) with wrong value", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true, WitnessMode::PKH,
0, 0).PushWitSig(keys.key0, 1).Push(keys.pubkey0).AsWit().PushRedeem().ScriptError(SCRIPT_ERR_EVAL_FALSE));
tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey0),
"P2WPKH with future witness version", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH |
- SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM, false, WITNESS_PKH, 1
+ SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM, false, WitnessMode::PKH, 1
).PushWitSig(keys.key0).Push(keys.pubkey0).AsWit().ScriptError(SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM));
{
CScript witscript = CScript() << ToByteVector(keys.pubkey0);
@@ -810,22 +810,22 @@ BOOST_AUTO_TEST_CASE(script_build)
).PushWitSig(keys.key0).Push(keys.pubkey0).AsWit().ScriptError(SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH));
}
tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey0) << OP_CHECKSIG,
- "P2WSH with empty witness", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false, WITNESS_SH
+ "P2WSH with empty witness", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false, WitnessMode::SH
).ScriptError(SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY));
{
CScript witscript = CScript() << ToByteVector(keys.pubkey0) << OP_CHECKSIG;
tests.push_back(TestBuilder(witscript,
- "P2WSH with witness program mismatch", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false, WITNESS_SH
+ "P2WSH with witness program mismatch", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false, WitnessMode::SH
).PushWitSig(keys.key0).Push(witscript).DamagePush(0).AsWit().ScriptError(SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH));
}
tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey0),
- "P2WPKH with witness program mismatch", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false, WITNESS_PKH
+ "P2WPKH with witness program mismatch", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false, WitnessMode::PKH
).PushWitSig(keys.key0).Push(keys.pubkey0).AsWit().Push("0").AsWit().ScriptError(SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH));
tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey0),
- "P2WPKH with non-empty scriptSig", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false, WITNESS_PKH
+ "P2WPKH with non-empty scriptSig", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false, WitnessMode::PKH
).PushWitSig(keys.key0).Push(keys.pubkey0).AsWit().Num(11).ScriptError(SCRIPT_ERR_WITNESS_MALLEATED));
tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey1),
- "P2SH(P2WPKH) with superfluous push in scriptSig", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true, WITNESS_PKH
+ "P2SH(P2WPKH) with superfluous push in scriptSig", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true, WitnessMode::PKH
).PushWitSig(keys.key0).Push(keys.pubkey1).AsWit().Num(11).PushRedeem().ScriptError(SCRIPT_ERR_WITNESS_MALLEATED_P2SH));
tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey0) << OP_CHECKSIG,
"P2PK with witness", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH
@@ -833,95 +833,95 @@ BOOST_AUTO_TEST_CASE(script_build)
// Compressed keys should pass SCRIPT_VERIFY_WITNESS_PUBKEYTYPE
tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey0C) << OP_CHECKSIG,
- "Basic P2WSH with compressed key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, false, WITNESS_SH,
+ "Basic P2WSH with compressed key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, false, WitnessMode::SH,
0, 1).PushWitSig(keys.key0C).PushWitRedeem());
tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey0C),
- "Basic P2WPKH with compressed key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, false, WITNESS_PKH,
+ "Basic P2WPKH with compressed key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, false, WitnessMode::PKH,
0, 1).PushWitSig(keys.key0C).Push(keys.pubkey0C).AsWit());
tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey0C) << OP_CHECKSIG,
- "Basic P2SH(P2WSH) with compressed key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, true, WITNESS_SH,
+ "Basic P2SH(P2WSH) with compressed key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, true, WitnessMode::SH,
0, 1).PushWitSig(keys.key0C).PushWitRedeem().PushRedeem());
tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey0C),
- "Basic P2SH(P2WPKH) with compressed key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, true, WITNESS_PKH,
+ "Basic P2SH(P2WPKH) with compressed key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, true, WitnessMode::PKH,
0, 1).PushWitSig(keys.key0C).Push(keys.pubkey0C).AsWit().PushRedeem());
// Testing uncompressed key in witness with SCRIPT_VERIFY_WITNESS_PUBKEYTYPE
tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey0) << OP_CHECKSIG,
- "Basic P2WSH", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, false, WITNESS_SH,
+ "Basic P2WSH", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, false, WitnessMode::SH,
0, 1).PushWitSig(keys.key0).PushWitRedeem().ScriptError(SCRIPT_ERR_WITNESS_PUBKEYTYPE));
tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey0),
- "Basic P2WPKH", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, false, WITNESS_PKH,
+ "Basic P2WPKH", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, false, WitnessMode::PKH,
0, 1).PushWitSig(keys.key0).Push(keys.pubkey0).AsWit().ScriptError(SCRIPT_ERR_WITNESS_PUBKEYTYPE));
tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey0) << OP_CHECKSIG,
- "Basic P2SH(P2WSH)", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, true, WITNESS_SH,
+ "Basic P2SH(P2WSH)", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, true, WitnessMode::SH,
0, 1).PushWitSig(keys.key0).PushWitRedeem().PushRedeem().ScriptError(SCRIPT_ERR_WITNESS_PUBKEYTYPE));
tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey0),
- "Basic P2SH(P2WPKH)", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, true, WITNESS_PKH,
+ "Basic P2SH(P2WPKH)", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, true, WitnessMode::PKH,
0, 1).PushWitSig(keys.key0).Push(keys.pubkey0).AsWit().PushRedeem().ScriptError(SCRIPT_ERR_WITNESS_PUBKEYTYPE));
// P2WSH 1-of-2 multisig with compressed keys
tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey0C) << OP_2 << OP_CHECKMULTISIG,
- "P2WSH CHECKMULTISIG with compressed keys", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, false, WITNESS_SH,
+ "P2WSH CHECKMULTISIG with compressed keys", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, false, WitnessMode::SH,
0, 1).Push(CScript()).AsWit().PushWitSig(keys.key0C).PushWitRedeem());
tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey0C) << OP_2 << OP_CHECKMULTISIG,
- "P2SH(P2WSH) CHECKMULTISIG with compressed keys", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, true, WITNESS_SH,
+ "P2SH(P2WSH) CHECKMULTISIG with compressed keys", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, true, WitnessMode::SH,
0, 1).Push(CScript()).AsWit().PushWitSig(keys.key0C).PushWitRedeem().PushRedeem());
tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey0C) << OP_2 << OP_CHECKMULTISIG,
- "P2WSH CHECKMULTISIG with compressed keys", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, false, WITNESS_SH,
+ "P2WSH CHECKMULTISIG with compressed keys", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, false, WitnessMode::SH,
0, 1).Push(CScript()).AsWit().PushWitSig(keys.key1C).PushWitRedeem());
tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey0C) << OP_2 << OP_CHECKMULTISIG,
- "P2SH(P2WSH) CHECKMULTISIG with compressed keys", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, true, WITNESS_SH,
+ "P2SH(P2WSH) CHECKMULTISIG with compressed keys", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, true, WitnessMode::SH,
0, 1).Push(CScript()).AsWit().PushWitSig(keys.key1C).PushWitRedeem().PushRedeem());
// P2WSH 1-of-2 multisig with first key uncompressed
tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey0) << OP_2 << OP_CHECKMULTISIG,
- "P2WSH CHECKMULTISIG with first key uncompressed and signing with the first key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false, WITNESS_SH,
+ "P2WSH CHECKMULTISIG with first key uncompressed and signing with the first key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false, WitnessMode::SH,
0, 1).Push(CScript()).AsWit().PushWitSig(keys.key0).PushWitRedeem());
tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey0) << OP_2 << OP_CHECKMULTISIG,
- "P2SH(P2WSH) CHECKMULTISIG first key uncompressed and signing with the first key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true, WITNESS_SH,
+ "P2SH(P2WSH) CHECKMULTISIG first key uncompressed and signing with the first key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true, WitnessMode::SH,
0, 1).Push(CScript()).AsWit().PushWitSig(keys.key0).PushWitRedeem().PushRedeem());
tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey0) << OP_2 << OP_CHECKMULTISIG,
- "P2WSH CHECKMULTISIG with first key uncompressed and signing with the first key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, false, WITNESS_SH,
+ "P2WSH CHECKMULTISIG with first key uncompressed and signing with the first key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, false, WitnessMode::SH,
0, 1).Push(CScript()).AsWit().PushWitSig(keys.key0).PushWitRedeem().ScriptError(SCRIPT_ERR_WITNESS_PUBKEYTYPE));
tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey0) << OP_2 << OP_CHECKMULTISIG,
- "P2SH(P2WSH) CHECKMULTISIG with first key uncompressed and signing with the first key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, true, WITNESS_SH,
+ "P2SH(P2WSH) CHECKMULTISIG with first key uncompressed and signing with the first key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, true, WitnessMode::SH,
0, 1).Push(CScript()).AsWit().PushWitSig(keys.key0).PushWitRedeem().PushRedeem().ScriptError(SCRIPT_ERR_WITNESS_PUBKEYTYPE));
tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey0) << OP_2 << OP_CHECKMULTISIG,
- "P2WSH CHECKMULTISIG with first key uncompressed and signing with the second key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false, WITNESS_SH,
+ "P2WSH CHECKMULTISIG with first key uncompressed and signing with the second key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false, WitnessMode::SH,
0, 1).Push(CScript()).AsWit().PushWitSig(keys.key1C).PushWitRedeem());
tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey0) << OP_2 << OP_CHECKMULTISIG,
- "P2SH(P2WSH) CHECKMULTISIG with first key uncompressed and signing with the second key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true, WITNESS_SH,
+ "P2SH(P2WSH) CHECKMULTISIG with first key uncompressed and signing with the second key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true, WitnessMode::SH,
0, 1).Push(CScript()).AsWit().PushWitSig(keys.key1C).PushWitRedeem().PushRedeem());
tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey0) << OP_2 << OP_CHECKMULTISIG,
- "P2WSH CHECKMULTISIG with first key uncompressed and signing with the second key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, false, WITNESS_SH,
+ "P2WSH CHECKMULTISIG with first key uncompressed and signing with the second key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, false, WitnessMode::SH,
0, 1).Push(CScript()).AsWit().PushWitSig(keys.key1C).PushWitRedeem().ScriptError(SCRIPT_ERR_WITNESS_PUBKEYTYPE));
tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey0) << OP_2 << OP_CHECKMULTISIG,
- "P2SH(P2WSH) CHECKMULTISIG with first key uncompressed and signing with the second key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, true, WITNESS_SH,
+ "P2SH(P2WSH) CHECKMULTISIG with first key uncompressed and signing with the second key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, true, WitnessMode::SH,
0, 1).Push(CScript()).AsWit().PushWitSig(keys.key1C).PushWitRedeem().PushRedeem().ScriptError(SCRIPT_ERR_WITNESS_PUBKEYTYPE));
// P2WSH 1-of-2 multisig with second key uncompressed
tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1) << ToByteVector(keys.pubkey0C) << OP_2 << OP_CHECKMULTISIG,
- "P2WSH CHECKMULTISIG with second key uncompressed and signing with the first key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false, WITNESS_SH,
+ "P2WSH CHECKMULTISIG with second key uncompressed and signing with the first key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false, WitnessMode::SH,
0, 1).Push(CScript()).AsWit().PushWitSig(keys.key0C).PushWitRedeem());
tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1) << ToByteVector(keys.pubkey0C) << OP_2 << OP_CHECKMULTISIG,
- "P2SH(P2WSH) CHECKMULTISIG second key uncompressed and signing with the first key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true, WITNESS_SH,
+ "P2SH(P2WSH) CHECKMULTISIG second key uncompressed and signing with the first key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true, WitnessMode::SH,
0, 1).Push(CScript()).AsWit().PushWitSig(keys.key0C).PushWitRedeem().PushRedeem());
tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1) << ToByteVector(keys.pubkey0C) << OP_2 << OP_CHECKMULTISIG,
- "P2WSH CHECKMULTISIG with second key uncompressed and signing with the first key should pass as the uncompressed key is not used", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, false, WITNESS_SH,
+ "P2WSH CHECKMULTISIG with second key uncompressed and signing with the first key should pass as the uncompressed key is not used", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, false, WitnessMode::SH,
0, 1).Push(CScript()).AsWit().PushWitSig(keys.key0C).PushWitRedeem());
tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1) << ToByteVector(keys.pubkey0C) << OP_2 << OP_CHECKMULTISIG,
- "P2SH(P2WSH) CHECKMULTISIG with second key uncompressed and signing with the first key should pass as the uncompressed key is not used", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, true, WITNESS_SH,
+ "P2SH(P2WSH) CHECKMULTISIG with second key uncompressed and signing with the first key should pass as the uncompressed key is not used", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, true, WitnessMode::SH,
0, 1).Push(CScript()).AsWit().PushWitSig(keys.key0C).PushWitRedeem().PushRedeem());
tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1) << ToByteVector(keys.pubkey0C) << OP_2 << OP_CHECKMULTISIG,
- "P2WSH CHECKMULTISIG with second key uncompressed and signing with the second key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false, WITNESS_SH,
+ "P2WSH CHECKMULTISIG with second key uncompressed and signing with the second key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false, WitnessMode::SH,
0, 1).Push(CScript()).AsWit().PushWitSig(keys.key1).PushWitRedeem());
tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1) << ToByteVector(keys.pubkey0C) << OP_2 << OP_CHECKMULTISIG,
- "P2SH(P2WSH) CHECKMULTISIG with second key uncompressed and signing with the second key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true, WITNESS_SH,
+ "P2SH(P2WSH) CHECKMULTISIG with second key uncompressed and signing with the second key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true, WitnessMode::SH,
0, 1).Push(CScript()).AsWit().PushWitSig(keys.key1).PushWitRedeem().PushRedeem());
tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1) << ToByteVector(keys.pubkey0C) << OP_2 << OP_CHECKMULTISIG,
- "P2WSH CHECKMULTISIG with second key uncompressed and signing with the second key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, false, WITNESS_SH,
+ "P2WSH CHECKMULTISIG with second key uncompressed and signing with the second key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, false, WitnessMode::SH,
0, 1).Push(CScript()).AsWit().PushWitSig(keys.key1).PushWitRedeem().ScriptError(SCRIPT_ERR_WITNESS_PUBKEYTYPE));
tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1) << ToByteVector(keys.pubkey0C) << OP_2 << OP_CHECKMULTISIG,
- "P2SH(P2WSH) CHECKMULTISIG with second key uncompressed and signing with the second key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, true, WITNESS_SH,
+ "P2SH(P2WSH) CHECKMULTISIG with second key uncompressed and signing with the second key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, true, WitnessMode::SH,
0, 1).Push(CScript()).AsWit().PushWitSig(keys.key1).PushWitRedeem().PushRedeem().ScriptError(SCRIPT_ERR_WITNESS_PUBKEYTYPE));
std::set<std::string> tests_set;
@@ -1009,21 +1009,21 @@ BOOST_AUTO_TEST_CASE(script_PushData)
ScriptError err;
std::vector<std::vector<unsigned char> > directStack;
- BOOST_CHECK(EvalScript(directStack, CScript(&direct[0], &direct[sizeof(direct)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SIGVERSION_BASE, &err));
+ BOOST_CHECK(EvalScript(directStack, CScript(&direct[0], &direct[sizeof(direct)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SigVersion::BASE, &err));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
std::vector<std::vector<unsigned char> > pushdata1Stack;
- BOOST_CHECK(EvalScript(pushdata1Stack, CScript(&pushdata1[0], &pushdata1[sizeof(pushdata1)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SIGVERSION_BASE, &err));
+ BOOST_CHECK(EvalScript(pushdata1Stack, CScript(&pushdata1[0], &pushdata1[sizeof(pushdata1)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SigVersion::BASE, &err));
BOOST_CHECK(pushdata1Stack == directStack);
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
std::vector<std::vector<unsigned char> > pushdata2Stack;
- BOOST_CHECK(EvalScript(pushdata2Stack, CScript(&pushdata2[0], &pushdata2[sizeof(pushdata2)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SIGVERSION_BASE, &err));
+ BOOST_CHECK(EvalScript(pushdata2Stack, CScript(&pushdata2[0], &pushdata2[sizeof(pushdata2)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SigVersion::BASE, &err));
BOOST_CHECK(pushdata2Stack == directStack);
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
std::vector<std::vector<unsigned char> > pushdata4Stack;
- BOOST_CHECK(EvalScript(pushdata4Stack, CScript(&pushdata4[0], &pushdata4[sizeof(pushdata4)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SIGVERSION_BASE, &err));
+ BOOST_CHECK(EvalScript(pushdata4Stack, CScript(&pushdata4[0], &pushdata4[sizeof(pushdata4)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SigVersion::BASE, &err));
BOOST_CHECK(pushdata4Stack == directStack);
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
}
@@ -1031,7 +1031,7 @@ BOOST_AUTO_TEST_CASE(script_PushData)
CScript
sign_multisig(CScript scriptPubKey, std::vector<CKey> keys, CTransaction transaction)
{
- uint256 hash = SignatureHash(scriptPubKey, transaction, 0, SIGHASH_ALL, 0, SIGVERSION_BASE);
+ uint256 hash = SignatureHash(scriptPubKey, transaction, 0, SIGHASH_ALL, 0, SigVersion::BASE);
CScript result;
//
@@ -1227,15 +1227,15 @@ BOOST_AUTO_TEST_CASE(script_combineSigs)
// A couple of partially-signed versions:
std::vector<unsigned char> sig1;
- uint256 hash1 = SignatureHash(scriptPubKey, txTo, 0, SIGHASH_ALL, 0, SIGVERSION_BASE);
+ uint256 hash1 = SignatureHash(scriptPubKey, txTo, 0, SIGHASH_ALL, 0, SigVersion::BASE);
BOOST_CHECK(keys[0].Sign(hash1, sig1));
sig1.push_back(SIGHASH_ALL);
std::vector<unsigned char> sig2;
- uint256 hash2 = SignatureHash(scriptPubKey, txTo, 0, SIGHASH_NONE, 0, SIGVERSION_BASE);
+ uint256 hash2 = SignatureHash(scriptPubKey, txTo, 0, SIGHASH_NONE, 0, SigVersion::BASE);
BOOST_CHECK(keys[1].Sign(hash2, sig2));
sig2.push_back(SIGHASH_NONE);
std::vector<unsigned char> sig3;
- uint256 hash3 = SignatureHash(scriptPubKey, txTo, 0, SIGHASH_SINGLE, 0, SIGVERSION_BASE);
+ uint256 hash3 = SignatureHash(scriptPubKey, txTo, 0, SIGHASH_SINGLE, 0, SigVersion::BASE);
BOOST_CHECK(keys[2].Sign(hash3, sig3));
sig3.push_back(SIGHASH_SINGLE);
diff --git a/src/test/serialize_tests.cpp b/src/test/serialize_tests.cpp
index 7a79a77e8b..9b8b7bdc56 100644
--- a/src/test/serialize_tests.cpp
+++ b/src/test/serialize_tests.cpp
@@ -19,11 +19,15 @@ protected:
int intval;
bool boolval;
std::string stringval;
- const char* charstrval;
+ char charstrval[16];
CTransactionRef txval;
public:
CSerializeMethodsTestSingle() = default;
- CSerializeMethodsTestSingle(int intvalin, bool boolvalin, std::string stringvalin, const char* charstrvalin, CTransaction txvalin) : intval(intvalin), boolval(boolvalin), stringval(std::move(stringvalin)), charstrval(charstrvalin), txval(MakeTransactionRef(txvalin)){}
+ CSerializeMethodsTestSingle(int intvalin, bool boolvalin, std::string stringvalin, const char* charstrvalin, CTransaction txvalin) : intval(intvalin), boolval(boolvalin), stringval(std::move(stringvalin)), txval(MakeTransactionRef(txvalin))
+ {
+ memcpy(charstrval, charstrvalin, sizeof(charstrval));
+ }
+
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
@@ -31,7 +35,7 @@ public:
READWRITE(intval);
READWRITE(boolval);
READWRITE(stringval);
- READWRITE(FLATDATA(charstrval));
+ READWRITE(charstrval);
READWRITE(txval);
}
@@ -53,7 +57,7 @@ public:
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
- READWRITE(intval, boolval, stringval, FLATDATA(charstrval), txval);
+ READWRITE(intval, boolval, stringval, charstrval, txval);
}
};
@@ -344,7 +348,7 @@ BOOST_AUTO_TEST_CASE(class_methods)
int intval(100);
bool boolval(true);
std::string stringval("testing");
- const char* charstrval("testing charstr");
+ const char charstrval[16] = "testing charstr";
CMutableTransaction txval;
CSerializeMethodsTestSingle methodtest1(intval, boolval, stringval, charstrval, txval);
CSerializeMethodsTestMany methodtest2(intval, boolval, stringval, charstrval, txval);
@@ -360,7 +364,7 @@ BOOST_AUTO_TEST_CASE(class_methods)
BOOST_CHECK(methodtest2 == methodtest3);
BOOST_CHECK(methodtest3 == methodtest4);
- CDataStream ss2(SER_DISK, PROTOCOL_VERSION, intval, boolval, stringval, FLATDATA(charstrval), txval);
+ CDataStream ss2(SER_DISK, PROTOCOL_VERSION, intval, boolval, stringval, charstrval, txval);
ss2 >> methodtest3;
BOOST_CHECK(methodtest3 == methodtest4);
}
diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp
index 32cd3a50b0..a2bd8998b1 100644
--- a/src/test/sighash_tests.cpp
+++ b/src/test/sighash_tests.cpp
@@ -138,7 +138,7 @@ BOOST_AUTO_TEST_CASE(sighash_test)
uint256 sh, sho;
sho = SignatureHashOld(scriptCode, txTo, nIn, nHashType);
- sh = SignatureHash(scriptCode, txTo, nIn, nHashType, 0, SIGVERSION_BASE);
+ sh = SignatureHash(scriptCode, txTo, nIn, nHashType, 0, SigVersion::BASE);
#if defined(PRINT_SIGHASH_JSON)
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << txTo;
@@ -204,7 +204,7 @@ BOOST_AUTO_TEST_CASE(sighash_from_data)
continue;
}
- sh = SignatureHash(scriptCode, *tx, nIn, nHashType, 0, SIGVERSION_BASE);
+ sh = SignatureHash(scriptCode, *tx, nIn, nHashType, 0, SigVersion::BASE);
BOOST_CHECK_MESSAGE(sh.GetHex() == sigHashHex, strTest);
}
}
diff --git a/src/test/streams_tests.cpp b/src/test/streams_tests.cpp
index 1108dab584..5d057108b1 100644
--- a/src/test/streams_tests.cpp
+++ b/src/test/streams_tests.cpp
@@ -57,16 +57,16 @@ BOOST_AUTO_TEST_CASE(streams_vector_writer)
BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 0, 0, 1, 2}}));
vch.clear();
- CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 0, FLATDATA(bytes));
+ CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 0, bytes);
BOOST_CHECK((vch == std::vector<unsigned char>{{3, 4, 5, 6}}));
- CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 0, FLATDATA(bytes));
+ CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 0, bytes);
BOOST_CHECK((vch == std::vector<unsigned char>{{3, 4, 5, 6}}));
vch.clear();
vch.resize(4, 8);
- CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 2, a, FLATDATA(bytes), b);
+ CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 2, a, bytes, b);
BOOST_CHECK((vch == std::vector<unsigned char>{{8, 8, 1, 3, 4, 5, 6, 2}}));
- CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 2, a, FLATDATA(bytes), b);
+ CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 2, a, bytes, b);
BOOST_CHECK((vch == std::vector<unsigned char>{{8, 8, 1, 3, 4, 5, 6, 2}}));
vch.clear();
}
diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp
index 95c4825b84..9390a93b99 100644
--- a/src/test/test_bitcoin.cpp
+++ b/src/test/test_bitcoin.cpp
@@ -145,9 +145,9 @@ TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransaction>&
for (const CMutableTransaction& tx : txns)
block.vtx.push_back(MakeTransactionRef(tx));
// IncrementExtraNonce creates a valid coinbase and merkleRoot
- unsigned int extraNonce = 0;
{
LOCK(cs_main);
+ unsigned int extraNonce = 0;
IncrementExtraNonce(&block, chainActive.Tip(), extraNonce);
}
diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h
index 944835cccf..8136da3aa9 100644
--- a/src/test/test_bitcoin.h
+++ b/src/test/test_bitcoin.h
@@ -14,6 +14,8 @@
#include <txdb.h>
#include <txmempool.h>
+#include <memory>
+
#include <boost/thread.hpp>
extern uint256 insecure_rand_seed;
diff --git a/src/test/test_bitcoin_fuzzy.cpp b/src/test/test_bitcoin_fuzzy.cpp
index aaba2095e0..69e9804c2f 100644
--- a/src/test/test_bitcoin_fuzzy.cpp
+++ b/src/test/test_bitcoin_fuzzy.cpp
@@ -25,6 +25,7 @@
#include <unistd.h>
#include <algorithm>
+#include <memory>
#include <vector>
enum TEST_ID {
diff --git a/src/test/test_bitcoin_main.cpp b/src/test/test_bitcoin_main.cpp
index 64408e9c5b..e48c685b6b 100644
--- a/src/test/test_bitcoin_main.cpp
+++ b/src/test/test_bitcoin_main.cpp
@@ -6,6 +6,8 @@
#include <net.h>
+#include <memory>
+
#include <boost/test/unit_test.hpp>
std::unique_ptr<CConnman> g_connman;
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index edfb35d155..b222392ee5 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -407,7 +407,7 @@ static CScript PushAll(const std::vector<valtype>& values)
void ReplaceRedeemScript(CScript& script, const CScript& redeemScript)
{
std::vector<valtype> stack;
- EvalScript(stack, script, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker(), SIGVERSION_BASE);
+ EvalScript(stack, script, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker(), SigVersion::BASE);
assert(stack.size() > 0);
stack.back() = std::vector<unsigned char>(redeemScript.begin(), redeemScript.end());
script = PushAll(stack);
diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp
index 1aa54189b6..7087c26774 100644
--- a/src/test/txvalidationcache_tests.cpp
+++ b/src/test/txvalidationcache_tests.cpp
@@ -56,7 +56,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
// Sign:
std::vector<unsigned char> vchSig;
- uint256 hash = SignatureHash(scriptPubKey, spends[i], 0, SIGHASH_ALL, 0, SIGVERSION_BASE);
+ uint256 hash = SignatureHash(scriptPubKey, spends[i], 0, SIGHASH_ALL, 0, SigVersion::BASE);
BOOST_CHECK(coinbaseKey.Sign(hash, vchSig));
vchSig.push_back((unsigned char)SIGHASH_ALL);
spends[i].vin[0].scriptSig << vchSig;
@@ -182,7 +182,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
// Sign, with a non-DER signature
{
std::vector<unsigned char> vchSig;
- uint256 hash = SignatureHash(p2pk_scriptPubKey, spend_tx, 0, SIGHASH_ALL, 0, SIGVERSION_BASE);
+ uint256 hash = SignatureHash(p2pk_scriptPubKey, spend_tx, 0, SIGHASH_ALL, 0, SigVersion::BASE);
BOOST_CHECK(coinbaseKey.Sign(hash, vchSig));
vchSig.push_back((unsigned char) 0); // padding byte makes this non-DER
vchSig.push_back((unsigned char)SIGHASH_ALL);
@@ -256,7 +256,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
// Sign
std::vector<unsigned char> vchSig;
- uint256 hash = SignatureHash(spend_tx.vout[2].scriptPubKey, invalid_with_cltv_tx, 0, SIGHASH_ALL, 0, SIGVERSION_BASE);
+ uint256 hash = SignatureHash(spend_tx.vout[2].scriptPubKey, invalid_with_cltv_tx, 0, SIGHASH_ALL, 0, SigVersion::BASE);
BOOST_CHECK(coinbaseKey.Sign(hash, vchSig));
vchSig.push_back((unsigned char)SIGHASH_ALL);
invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 101;
@@ -284,7 +284,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
// Sign
std::vector<unsigned char> vchSig;
- uint256 hash = SignatureHash(spend_tx.vout[3].scriptPubKey, invalid_with_csv_tx, 0, SIGHASH_ALL, 0, SIGVERSION_BASE);
+ uint256 hash = SignatureHash(spend_tx.vout[3].scriptPubKey, invalid_with_csv_tx, 0, SIGHASH_ALL, 0, SigVersion::BASE);
BOOST_CHECK(coinbaseKey.Sign(hash, vchSig));
vchSig.push_back((unsigned char)SIGHASH_ALL);
invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 101;
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index b6f3cbe2b7..4b44bbadac 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -185,17 +185,11 @@ BOOST_AUTO_TEST_CASE(util_FormatISO8601Time)
BOOST_CHECK_EQUAL(FormatISO8601Time(1317425777), "23:36:17Z");
}
-class TestArgsManager : public ArgsManager
+struct TestArgsManager : public ArgsManager
{
-public:
- std::map<std::string, std::string>& GetMapArgs()
- {
- return mapArgs;
- };
- const std::map<std::string, std::vector<std::string> >& GetMapMultiArgs()
- {
- return mapMultiArgs;
- };
+ std::map<std::string, std::string>& GetMapArgs() { return mapArgs; }
+ const std::map<std::string, std::vector<std::string> >& GetMapMultiArgs() { return mapMultiArgs; }
+ const std::unordered_set<std::string>& GetNegatedArgs() { return m_negated_args; }
};
BOOST_AUTO_TEST_CASE(util_ParseParameters)
@@ -223,6 +217,54 @@ BOOST_AUTO_TEST_CASE(util_ParseParameters)
BOOST_CHECK(testArgs.GetArgs("-ccc").size() == 2);
}
+BOOST_AUTO_TEST_CASE(util_GetBoolArg)
+{
+ TestArgsManager testArgs;
+ const char *argv_test[] = {
+ "ignored", "-a", "-nob", "-c=0", "-d=1", "-e=false", "-f=true"};
+ testArgs.ParseParameters(7, (char**)argv_test);
+
+ // Each letter should be set.
+ for (char opt : "abcdef")
+ BOOST_CHECK(testArgs.IsArgSet({'-', opt}) || !opt);
+
+ // Nothing else should be in the map
+ BOOST_CHECK(testArgs.GetMapArgs().size() == 6 &&
+ testArgs.GetMapMultiArgs().size() == 6);
+
+ // The -no prefix should get stripped on the way in.
+ BOOST_CHECK(!testArgs.IsArgSet("-nob"));
+
+ // The -b option is flagged as negated, and nothing else is
+ BOOST_CHECK(testArgs.IsArgNegated("-b"));
+ BOOST_CHECK(testArgs.GetNegatedArgs().size() == 1);
+ BOOST_CHECK(!testArgs.IsArgNegated("-a"));
+
+ // Check expected values.
+ BOOST_CHECK(testArgs.GetBoolArg("-a", false) == true);
+ BOOST_CHECK(testArgs.GetBoolArg("-b", true) == false);
+ BOOST_CHECK(testArgs.GetBoolArg("-c", true) == false);
+ BOOST_CHECK(testArgs.GetBoolArg("-d", false) == true);
+ BOOST_CHECK(testArgs.GetBoolArg("-e", true) == false);
+ BOOST_CHECK(testArgs.GetBoolArg("-f", true) == false);
+}
+
+BOOST_AUTO_TEST_CASE(util_GetBoolArgEdgeCases)
+{
+ // Test some awful edge cases that hopefully no user will ever exercise.
+ TestArgsManager testArgs;
+ const char *argv_test[] = {"ignored", "-nofoo", "-foo", "-nobar=0"};
+ testArgs.ParseParameters(4, (char**)argv_test);
+
+ // This was passed twice, second one overrides the negative setting.
+ BOOST_CHECK(!testArgs.IsArgNegated("-foo"));
+ BOOST_CHECK(testArgs.GetBoolArg("-foo", false) == true);
+
+ // A double negative is a positive.
+ BOOST_CHECK(testArgs.IsArgNegated("-bar"));
+ BOOST_CHECK(testArgs.GetBoolArg("-bar", false) == true);
+}
+
BOOST_AUTO_TEST_CASE(util_GetArg)
{
TestArgsManager testArgs;
@@ -704,9 +746,8 @@ static constexpr char ExitCommand = 'X';
static void TestOtherProcess(fs::path dirname, std::string lockname, int fd)
{
char ch;
- int rv;
while (true) {
- rv = read(fd, &ch, 1); // Wait for command
+ int rv = read(fd, &ch, 1); // Wait for command
assert(rv == 1);
switch(ch) {
case LockCommand:
diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp
index 5d6f781404..92ef58e517 100644
--- a/src/test/versionbits_tests.cpp
+++ b/src/test/versionbits_tests.cpp
@@ -101,8 +101,8 @@ public:
VersionBitsTester& TestDefined() {
for (int i = 0; i < CHECKERS; i++) {
if (InsecureRandBits(i) == 0) {
- BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_DEFINED, strprintf("Test %i for DEFINED", num));
- BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_ACTIVE, strprintf("Test %i for ACTIVE (always active)", num));
+ BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::DEFINED, strprintf("Test %i for DEFINED", num));
+ BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::ACTIVE, strprintf("Test %i for ACTIVE (always active)", num));
}
}
num++;
@@ -112,8 +112,8 @@ public:
VersionBitsTester& TestStarted() {
for (int i = 0; i < CHECKERS; i++) {
if (InsecureRandBits(i) == 0) {
- BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_STARTED, strprintf("Test %i for STARTED", num));
- BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_ACTIVE, strprintf("Test %i for ACTIVE (always active)", num));
+ BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::STARTED, strprintf("Test %i for STARTED", num));
+ BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::ACTIVE, strprintf("Test %i for ACTIVE (always active)", num));
}
}
num++;
@@ -123,8 +123,8 @@ public:
VersionBitsTester& TestLockedIn() {
for (int i = 0; i < CHECKERS; i++) {
if (InsecureRandBits(i) == 0) {
- BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_LOCKED_IN, strprintf("Test %i for LOCKED_IN", num));
- BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_ACTIVE, strprintf("Test %i for ACTIVE (always active)", num));
+ BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::LOCKED_IN, strprintf("Test %i for LOCKED_IN", num));
+ BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::ACTIVE, strprintf("Test %i for ACTIVE (always active)", num));
}
}
num++;
@@ -134,8 +134,8 @@ public:
VersionBitsTester& TestActive() {
for (int i = 0; i < CHECKERS; i++) {
if (InsecureRandBits(i) == 0) {
- BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_ACTIVE, strprintf("Test %i for ACTIVE", num));
- BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_ACTIVE, strprintf("Test %i for ACTIVE (always active)", num));
+ BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::ACTIVE, strprintf("Test %i for ACTIVE", num));
+ BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::ACTIVE, strprintf("Test %i for ACTIVE (always active)", num));
}
}
num++;
@@ -145,8 +145,8 @@ public:
VersionBitsTester& TestFailed() {
for (int i = 0; i < CHECKERS; i++) {
if (InsecureRandBits(i) == 0) {
- BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_FAILED, strprintf("Test %i for FAILED", num));
- BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_ACTIVE, strprintf("Test %i for ACTIVE (always active)", num));
+ BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::FAILED, strprintf("Test %i for FAILED", num));
+ BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::ACTIVE, strprintf("Test %i for ACTIVE (always active)", num));
}
}
num++;
diff --git a/src/txdb.cpp b/src/txdb.cpp
index 91d6c98430..8550a7e889 100644
--- a/src/txdb.cpp
+++ b/src/txdb.cpp
@@ -147,7 +147,7 @@ size_t CCoinsViewDB::EstimateSize() const
return db.EstimateSize(DB_COIN, (char)(DB_COIN+1));
}
-CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe) : CDBWrapper(GetDataDir() / "blocks" / "index", nCacheSize, fMemory, fWipe) {
+CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe) : CDBWrapper(gArgs.IsArgSet("-blocksdir") ? GetDataDir() / "blocks" / "index" : GetBlocksDir() / "index", nCacheSize, fMemory, fWipe) {
}
bool CBlockTreeDB::ReadBlockFileInfo(int nFile, CBlockFileInfo &info) {
diff --git a/src/txdb.h b/src/txdb.h
index 2fc69e563b..ad76b3257d 100644
--- a/src/txdb.h
+++ b/src/txdb.h
@@ -11,6 +11,7 @@
#include <chain.h>
#include <map>
+#include <memory>
#include <string>
#include <utility>
#include <vector>
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index d1edde284f..d03429ca81 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -339,7 +339,7 @@ CTxMemPool::CTxMemPool(CBlockPolicyEstimator* estimator) :
nCheckFrequency = 0;
}
-bool CTxMemPool::isSpent(const COutPoint& outpoint)
+bool CTxMemPool::isSpent(const COutPoint& outpoint) const
{
LOCK(cs);
return mapNextTx.count(outpoint);
@@ -447,7 +447,7 @@ void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
// Also assumes that if an entry is in setDescendants already, then all
// in-mempool descendants of it are already in setDescendants as well, so that we
// can save time by not iterating over those entries.
-void CTxMemPool::CalculateDescendants(txiter entryit, setEntries &setDescendants)
+void CTxMemPool::CalculateDescendants(txiter entryit, setEntries& setDescendants) const
{
setEntries stage;
if (setDescendants.count(entryit) == 0) {
diff --git a/src/txmempool.h b/src/txmempool.h
index 08a3421015..a1cde6f779 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -550,7 +550,7 @@ public:
void _clear(); //lock free
bool CompareDepthAndScore(const uint256& hasha, const uint256& hashb);
void queryHashes(std::vector<uint256>& vtxid);
- bool isSpent(const COutPoint& outpoint);
+ bool isSpent(const COutPoint& outpoint) const;
unsigned int GetTransactionsUpdated() const;
void AddTransactionsUpdated(unsigned int n);
/**
@@ -600,7 +600,7 @@ public:
/** Populate setDescendants with all in-mempool descendants of hash.
* Assumes that setDescendants includes all in-mempool descendants of anything
* already in it. */
- void CalculateDescendants(txiter it, setEntries &setDescendants);
+ void CalculateDescendants(txiter it, setEntries& setDescendants) const;
/** The minimum fee to get into the mempool, which may itself not be enough
* for larger-sized transactions.
diff --git a/src/util.cpp b/src/util.cpp
index 842d4f6e19..8b36c3e5f7 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -71,8 +71,6 @@
#include <malloc.h>
#endif
-#include <boost/algorithm/string/case_conv.hpp> // for to_lower()
-#include <boost/algorithm/string/predicate.hpp> // for startswith() and endswith()
#include <boost/interprocess/sync/file_lock.hpp>
#include <boost/program_options/detail/config_file.hpp>
#include <boost/thread.hpp>
@@ -433,7 +431,23 @@ bool DirIsWritable(const fs::path& directory)
return true;
}
-/** Interpret string as boolean, for argument parsing */
+/**
+ * Interpret a string argument as a boolean.
+ *
+ * The definition of atoi() requires that non-numeric string values like "foo",
+ * return 0. This means that if a user unintentionally supplies a non-integer
+ * argument here, the return value is always false. This means that -foo=false
+ * does what the user probably expects, but -foo=true is well defined but does
+ * not do what they probably expected.
+ *
+ * The return value of atoi() is undefined when given input not representable as
+ * an int. On most systems this means string value between "-2147483648" and
+ * "2147483647" are well defined (this method will return true). Setting
+ * -txindex=2147483648 on most systems, however, is probably undefined.
+ *
+ * For a more extensive discussion of this topic (and a wide range of opinions
+ * on the Right Way to change this code), see PR12713.
+ */
static bool InterpretBool(const std::string& strValue)
{
if (strValue.empty())
@@ -441,13 +455,30 @@ static bool InterpretBool(const std::string& strValue)
return (atoi(strValue) != 0);
}
-/** Turn -noX into -X=0 */
-static void InterpretNegativeSetting(std::string& strKey, std::string& strValue)
+/**
+ * Interpret -nofoo as if the user supplied -foo=0.
+ *
+ * This method also tracks when the -no form was supplied, and treats "-foo" as
+ * a negated option when this happens. This can be later checked using the
+ * IsArgNegated() method. One use case for this is to have a way to disable
+ * options that are not normally boolean (e.g. using -nodebuglogfile to request
+ * that debug log output is not sent to any file at all).
+ */
+void ArgsManager::InterpretNegatedOption(std::string& key, std::string& val)
{
- if (strKey.length()>3 && strKey[0]=='-' && strKey[1]=='n' && strKey[2]=='o')
- {
- strKey = "-" + strKey.substr(3);
- strValue = InterpretBool(strValue) ? "0" : "1";
+ if (key.substr(0, 3) == "-no") {
+ bool bool_val = InterpretBool(val);
+ if (!bool_val ) {
+ // Double negatives like -nofoo=0 are supported (but discouraged)
+ LogPrintf("Warning: parsed potentially confusing double-negative %s=%s\n", key, val);
+ }
+ key.erase(1, 2);
+ m_negated_args.insert(key);
+ val = bool_val ? "0" : "1";
+ } else {
+ // In an invocation like "bitcoind -nofoo -foo" we want to unmark -foo
+ // as negated when we see the second option.
+ m_negated_args.erase(key);
}
}
@@ -456,34 +487,34 @@ void ArgsManager::ParseParameters(int argc, const char* const argv[])
LOCK(cs_args);
mapArgs.clear();
mapMultiArgs.clear();
-
- for (int i = 1; i < argc; i++)
- {
- std::string str(argv[i]);
- std::string strValue;
- size_t is_index = str.find('=');
- if (is_index != std::string::npos)
- {
- strValue = str.substr(is_index+1);
- str = str.substr(0, is_index);
+ m_negated_args.clear();
+
+ for (int i = 1; i < argc; i++) {
+ std::string key(argv[i]);
+ std::string val;
+ size_t is_index = key.find('=');
+ if (is_index != std::string::npos) {
+ val = key.substr(is_index + 1);
+ key.erase(is_index);
}
#ifdef WIN32
- boost::to_lower(str);
- if (boost::algorithm::starts_with(str, "/"))
- str = "-" + str.substr(1);
+ std::transform(key.begin(), key.end(), key.begin(), ::tolower);
+ if (key[0] == '/')
+ key[0] = '-';
#endif
- if (str[0] != '-')
+ if (key[0] != '-')
break;
- // Interpret --foo as -foo.
- // If both --foo and -foo are set, the last takes effect.
- if (str.length() > 1 && str[1] == '-')
- str = str.substr(1);
- InterpretNegativeSetting(str, strValue);
+ // Transform --foo to -foo
+ if (key.length() > 1 && key[1] == '-')
+ key.erase(0, 1);
- mapArgs[str] = strValue;
- mapMultiArgs[str].push_back(strValue);
+ // Transform -nofoo to -foo=0
+ InterpretNegatedOption(key, val);
+
+ mapArgs[key] = val;
+ mapMultiArgs[key].push_back(val);
}
}
@@ -501,6 +532,12 @@ bool ArgsManager::IsArgSet(const std::string& strArg) const
return mapArgs.count(strArg);
}
+bool ArgsManager::IsArgNegated(const std::string& strArg) const
+{
+ LOCK(cs_args);
+ return m_negated_args.find(strArg) != m_negated_args.end();
+}
+
std::string ArgsManager::GetArg(const std::string& strArg, const std::string& strDefault) const
{
LOCK(cs_args);
@@ -548,7 +585,10 @@ void ArgsManager::ForceSetArg(const std::string& strArg, const std::string& strV
mapMultiArgs[strArg] = {strValue};
}
-
+bool HelpRequested(const ArgsManager& args)
+{
+ return args.IsArgSet("-?") || args.IsArgSet("-h") || args.IsArgSet("-help");
+}
static const int screenWidth = 79;
static const int optIndent = 2;
@@ -614,10 +654,41 @@ fs::path GetDefaultDataDir()
#endif
}
+static fs::path g_blocks_path_cached;
+static fs::path g_blocks_path_cache_net_specific;
static fs::path pathCached;
static fs::path pathCachedNetSpecific;
static CCriticalSection csPathCached;
+const fs::path &GetBlocksDir(bool fNetSpecific)
+{
+
+ LOCK(csPathCached);
+
+ fs::path &path = fNetSpecific ? g_blocks_path_cache_net_specific : g_blocks_path_cached;
+
+ // This can be called during exceptions by LogPrintf(), so we cache the
+ // value so we don't have to do memory allocations after that.
+ if (!path.empty())
+ return path;
+
+ if (gArgs.IsArgSet("-blocksdir")) {
+ path = fs::system_complete(gArgs.GetArg("-blocksdir", ""));
+ if (!fs::is_directory(path)) {
+ path = "";
+ return path;
+ }
+ } else {
+ path = GetDataDir(false);
+ }
+ if (fNetSpecific)
+ path /= BaseParams().DataDir();
+
+ path /= "blocks";
+ fs::create_directories(path);
+ return path;
+}
+
const fs::path &GetDataDir(bool fNetSpecific)
{
@@ -656,6 +727,8 @@ void ClearDatadirCache()
pathCached = fs::path();
pathCachedNetSpecific = fs::path();
+ g_blocks_path_cached = fs::path();
+ g_blocks_path_cache_net_specific = fs::path();
}
fs::path GetConfigFile(const std::string& confPath)
@@ -679,7 +752,7 @@ void ArgsManager::ReadConfigFile(const std::string& confPath)
// Don't overwrite existing settings so command line settings override bitcoin.conf
std::string strKey = std::string("-") + it->string_key;
std::string strValue = it->value[0];
- InterpretNegativeSetting(strKey, strValue);
+ InterpretNegatedOption(strKey, strValue);
if (mapArgs.count(strKey) == 0)
mapArgs[strKey] = strValue;
mapMultiArgs[strKey].push_back(strValue);
diff --git a/src/util.h b/src/util.h
index a9f64a0566..f625a61d8b 100644
--- a/src/util.h
+++ b/src/util.h
@@ -23,8 +23,10 @@
#include <atomic>
#include <exception>
#include <map>
+#include <memory>
#include <stdint.h>
#include <string>
+#include <unordered_set>
#include <vector>
#include <boost/signals2/signal.hpp>
@@ -183,6 +185,7 @@ void ReleaseDirectoryLocks();
bool TryCreateDirectories(const fs::path& p);
fs::path GetDefaultDataDir();
+const fs::path &GetBlocksDir(bool fNetSpecific = true);
const fs::path &GetDataDir(bool fNetSpecific = true);
void ClearDatadirCache();
fs::path GetConfigFile(const std::string& confPath);
@@ -223,6 +226,8 @@ protected:
mutable CCriticalSection cs_args;
std::map<std::string, std::string> mapArgs;
std::map<std::string, std::vector<std::string>> mapMultiArgs;
+ std::unordered_set<std::string> m_negated_args;
+
public:
void ParseParameters(int argc, const char*const argv[]);
void ReadConfigFile(const std::string& confPath);
@@ -244,6 +249,15 @@ public:
bool IsArgSet(const std::string& strArg) const;
/**
+ * Return true if the argument was originally passed as a negated option,
+ * i.e. -nofoo.
+ *
+ * @param strArg Argument to get (e.g. "-foo")
+ * @return true if the argument was passed negated
+ */
+ bool IsArgNegated(const std::string& strArg) const;
+
+ /**
* Return string argument or default value
*
* @param strArg Argument to get (e.g. "-foo")
@@ -291,11 +305,21 @@ public:
// Forces an arg setting. Called by SoftSetArg() if the arg hasn't already
// been set. Also called directly in testing.
void ForceSetArg(const std::string& strArg, const std::string& strValue);
+
+private:
+
+ // Munge -nofoo into -foo=0 and track the value as negated.
+ void InterpretNegatedOption(std::string &key, std::string &val);
};
extern ArgsManager gArgs;
/**
+ * @return true if help has been requested via a command-line arg
+ */
+bool HelpRequested(const ArgsManager& args);
+
+/**
* Format a string to be used as group of options in help messages
*
* @param message Group name (e.g. "RPC server options:")
diff --git a/src/validation.cpp b/src/validation.cpp
index 614876ea49..e881b45bd2 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -227,6 +227,7 @@ CAmount maxTxFee = DEFAULT_TRANSACTION_MAXFEE;
CBlockPolicyEstimator feeEstimator;
CTxMemPool mempool(&feeEstimator);
+std::atomic_bool g_is_mempool_loaded{false};
/** Constant stuff for coinbase transactions we create: */
CScript COINBASE_FLAGS;
@@ -280,11 +281,11 @@ std::unique_ptr<CCoinsViewDB> pcoinsdbview;
std::unique_ptr<CCoinsViewCache> pcoinsTip;
std::unique_ptr<CBlockTreeDB> pblocktree;
-enum FlushStateMode {
- FLUSH_STATE_NONE,
- FLUSH_STATE_IF_NEEDED,
- FLUSH_STATE_PERIODIC,
- FLUSH_STATE_ALWAYS
+enum class FlushStateMode {
+ NONE,
+ IF_NEEDED,
+ PERIODIC,
+ ALWAYS
};
// See definition for documentation
@@ -506,7 +507,7 @@ void UpdateMempoolForReorg(DisconnectedBlockTransactions &disconnectpool, bool f
// Used to avoid mempool polluting consensus critical paths if CCoinsViewMempool
// were somehow broken and returning the wrong scriptPubKeys
-static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &view, CTxMemPool& pool,
+static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& view, const CTxMemPool& pool,
unsigned int flags, bool cacheSigStore, PrecomputedTransactionData& txdata) {
AssertLockHeld(cs_main);
@@ -542,7 +543,7 @@ static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, CValidationSt
static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool& pool, CValidationState& state, const CTransactionRef& ptx,
bool* pfMissingInputs, int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced,
- bool bypass_limits, const CAmount& nAbsurdFee, std::vector<COutPoint>& coins_to_uncache)
+ bool bypass_limits, const CAmount& nAbsurdFee, std::vector<COutPoint>& coins_to_uncache, bool test_accept)
{
const CTransaction& tx = *ptx;
const uint256 hash = tx.GetHash();
@@ -916,8 +917,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
// invalid blocks (using TestBlockValidity), however allowing such
// transactions into the mempool can be exploited as a DoS attack.
unsigned int currentBlockScriptVerifyFlags = GetBlockScriptFlags(chainActive.Tip(), Params().GetConsensus());
- if (!CheckInputsFromMempoolAndCache(tx, state, view, pool, currentBlockScriptVerifyFlags, true, txdata))
- {
+ if (!CheckInputsFromMempoolAndCache(tx, state, view, pool, currentBlockScriptVerifyFlags, true, txdata)) {
// If we're using promiscuousmempoolflags, we may hit this normally
// Check if current block has some flags that scriptVerifyFlags
// does not before printing an ominous warning
@@ -934,6 +934,11 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
}
}
+ if (test_accept) {
+ // Tx was accepted, but not added
+ return true;
+ }
+
// Remove conflicting transactions from the mempool
for (const CTxMemPool::txiter it : allConflicting)
{
@@ -973,26 +978,26 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
/** (try to) add transaction to memory pool with a specified acceptance time **/
static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx,
bool* pfMissingInputs, int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced,
- bool bypass_limits, const CAmount nAbsurdFee)
+ bool bypass_limits, const CAmount nAbsurdFee, bool test_accept)
{
std::vector<COutPoint> coins_to_uncache;
- bool res = AcceptToMemoryPoolWorker(chainparams, pool, state, tx, pfMissingInputs, nAcceptTime, plTxnReplaced, bypass_limits, nAbsurdFee, coins_to_uncache);
+ bool res = AcceptToMemoryPoolWorker(chainparams, pool, state, tx, pfMissingInputs, nAcceptTime, plTxnReplaced, bypass_limits, nAbsurdFee, coins_to_uncache, test_accept);
if (!res) {
for (const COutPoint& hashTx : coins_to_uncache)
pcoinsTip->Uncache(hashTx);
}
// After we've (potentially) uncached entries, ensure our coins cache is still within its size limits
CValidationState stateDummy;
- FlushStateToDisk(chainparams, stateDummy, FLUSH_STATE_PERIODIC);
+ FlushStateToDisk(chainparams, stateDummy, FlushStateMode::PERIODIC);
return res;
}
bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx,
bool* pfMissingInputs, std::list<CTransactionRef>* plTxnReplaced,
- bool bypass_limits, const CAmount nAbsurdFee)
+ bool bypass_limits, const CAmount nAbsurdFee, bool test_accept)
{
const CChainParams& chainparams = Params();
- return AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, pfMissingInputs, GetTime(), plTxnReplaced, bypass_limits, nAbsurdFee);
+ return AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, pfMissingInputs, GetTime(), plTxnReplaced, bypass_limits, nAbsurdFee, test_accept);
}
/**
@@ -1077,7 +1082,7 @@ static bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMes
// Write index header
unsigned int nSize = GetSerializeSize(fileout, block);
- fileout << FLATDATA(messageStart) << nSize;
+ fileout << messageStart << nSize;
// Write block
long fileOutPos = ftell(fileout.Get());
@@ -1441,7 +1446,7 @@ bool UndoWriteToDisk(const CBlockUndo& blockundo, CDiskBlockPos& pos, const uint
// Write index header
unsigned int nSize = GetSerializeSize(fileout, blockundo);
- fileout << FLATDATA(messageStart) << nSize;
+ fileout << messageStart << nSize;
// Write undo data
long fileOutPos = ftell(fileout.Get());
@@ -1684,7 +1689,7 @@ int32_t ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Para
for (int i = 0; i < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; i++) {
ThresholdState state = VersionBitsState(pindexPrev, params, static_cast<Consensus::DeploymentPos>(i), versionbitscache);
- if (state == THRESHOLD_LOCKED_IN || state == THRESHOLD_STARTED) {
+ if (state == ThresholdState::LOCKED_IN || state == ThresholdState::STARTED) {
nVersion |= VersionBitsMask(params, static_cast<Consensus::DeploymentPos>(i));
}
}
@@ -1740,7 +1745,7 @@ static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consens
}
// Start enforcing BIP68 (sequence locks) and BIP112 (CHECKSEQUENCEVERIFY) using versionbits logic.
- if (VersionBitsState(pindex->pprev, consensusparams, Consensus::DEPLOYMENT_CSV, versionbitscache) == THRESHOLD_ACTIVE) {
+ if (VersionBitsState(pindex->pprev, consensusparams, Consensus::DEPLOYMENT_CSV, versionbitscache) == ThresholdState::ACTIVE) {
flags |= SCRIPT_VERIFY_CHECKSEQUENCEVERIFY;
}
@@ -1926,7 +1931,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
// Start enforcing BIP68 (sequence locks) and BIP112 (CHECKSEQUENCEVERIFY) using versionbits logic.
int nLockTimeFlags = 0;
- if (VersionBitsState(pindex->pprev, chainparams.GetConsensus(), Consensus::DEPLOYMENT_CSV, versionbitscache) == THRESHOLD_ACTIVE) {
+ if (VersionBitsState(pindex->pprev, chainparams.GetConsensus(), Consensus::DEPLOYMENT_CSV, versionbitscache) == ThresholdState::ACTIVE) {
nLockTimeFlags |= LOCKTIME_VERIFY_SEQUENCE;
}
@@ -2096,19 +2101,19 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &
int64_t cacheSize = pcoinsTip->DynamicMemoryUsage();
int64_t nTotalSpace = nCoinCacheUsage + std::max<int64_t>(nMempoolSizeMax - nMempoolUsage, 0);
// The cache is large and we're within 10% and 10 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 - MAX_BLOCK_COINSDB_USAGE * 1024 * 1024);
+ bool fCacheLarge = mode == FlushStateMode::PERIODIC && cacheSize > 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;
+ bool fCacheCritical = mode == FlushStateMode::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.
- bool fPeriodicWrite = mode == FLUSH_STATE_PERIODIC && nNow > nLastWrite + (int64_t)DATABASE_WRITE_INTERVAL * 1000000;
+ bool fPeriodicWrite = mode == FlushStateMode::PERIODIC && nNow > nLastWrite + (int64_t)DATABASE_WRITE_INTERVAL * 1000000;
// It's been very long since we flushed the cache. Do this infrequently, to optimize cache usage.
- bool fPeriodicFlush = mode == FLUSH_STATE_PERIODIC && nNow > nLastFlush + (int64_t)DATABASE_FLUSH_INTERVAL * 1000000;
+ bool fPeriodicFlush = mode == FlushStateMode::PERIODIC && nNow > nLastFlush + (int64_t)DATABASE_FLUSH_INTERVAL * 1000000;
// Combine all conditions that result in a full cache flush.
- fDoFullFlush = (mode == FLUSH_STATE_ALWAYS) || fCacheLarge || fCacheCritical || fPeriodicFlush || fFlushForPrune;
+ fDoFullFlush = (mode == FlushStateMode::ALWAYS) || fCacheLarge || fCacheCritical || fPeriodicFlush || fFlushForPrune;
// Write blocks and block index to disk.
if (fDoFullFlush || fPeriodicWrite) {
// Depend on nMinDiskSpace to ensure we can write block index
- if (!CheckDiskSpace(0))
+ if (!CheckDiskSpace(0, true))
return state.Error("out of disk space");
// First make sure all block and undo data is flushed to disk.
FlushBlockFile();
@@ -2150,7 +2155,7 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &
nLastFlush = nNow;
}
}
- if (fDoFullFlush || ((mode == FLUSH_STATE_ALWAYS || mode == FLUSH_STATE_PERIODIC) && nNow > nLastSetChain + (int64_t)DATABASE_WRITE_INTERVAL * 1000000)) {
+ if (fDoFullFlush || ((mode == FlushStateMode::ALWAYS || mode == FlushStateMode::PERIODIC) && nNow > nLastSetChain + (int64_t)DATABASE_WRITE_INTERVAL * 1000000)) {
// Update best block in wallet (so we can detect restored wallets).
GetMainSignals().SetBestChain(chainActive.GetLocator());
nLastSetChain = nNow;
@@ -2164,14 +2169,14 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &
void FlushStateToDisk() {
CValidationState state;
const CChainParams& chainparams = Params();
- FlushStateToDisk(chainparams, state, FLUSH_STATE_ALWAYS);
+ FlushStateToDisk(chainparams, state, FlushStateMode::ALWAYS);
}
void PruneAndFlush() {
CValidationState state;
fCheckForPruning = true;
const CChainParams& chainparams = Params();
- FlushStateToDisk(chainparams, state, FLUSH_STATE_NONE);
+ FlushStateToDisk(chainparams, state, FlushStateMode::NONE);
}
static void DoWarning(const std::string& strWarning)
@@ -2199,9 +2204,9 @@ void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams& chainPar
for (int bit = 0; bit < VERSIONBITS_NUM_BITS; bit++) {
WarningBitsConditionChecker checker(bit);
ThresholdState state = checker.GetStateFor(pindex, chainParams.GetConsensus(), warningcache[bit]);
- if (state == THRESHOLD_ACTIVE || state == THRESHOLD_LOCKED_IN) {
+ if (state == ThresholdState::ACTIVE || state == ThresholdState::LOCKED_IN) {
const std::string strWarning = strprintf(_("Warning: unknown new rules activated (versionbit %i)"), bit);
- if (state == THRESHOLD_ACTIVE) {
+ if (state == ThresholdState::ACTIVE) {
DoWarning(strWarning);
} else {
warningMessages.push_back(strWarning);
@@ -2267,7 +2272,7 @@ bool CChainState::DisconnectTip(CValidationState& state, const CChainParams& cha
}
LogPrint(BCLog::BENCH, "- Disconnect block: %.2fms\n", (GetTimeMicros() - nStart) * MILLI);
// Write the chain state to disk, if necessary.
- if (!FlushStateToDisk(chainparams, state, FLUSH_STATE_IF_NEEDED))
+ if (!FlushStateToDisk(chainparams, state, FlushStateMode::IF_NEEDED))
return false;
if (disconnectpool) {
@@ -2405,7 +2410,7 @@ bool CChainState::ConnectTip(CValidationState& state, const CChainParams& chainp
int64_t nTime4 = GetTimeMicros(); nTimeFlush += nTime4 - nTime3;
LogPrint(BCLog::BENCH, " - Flush: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime4 - nTime3) * MILLI, nTimeFlush * MICRO, nTimeFlush * MILLI / nBlocksTotal);
// Write the chain state to disk, if necessary.
- if (!FlushStateToDisk(chainparams, state, FLUSH_STATE_IF_NEEDED))
+ if (!FlushStateToDisk(chainparams, state, FlushStateMode::IF_NEEDED))
return false;
int64_t nTime5 = GetTimeMicros(); nTimeChainState += nTime5 - nTime4;
LogPrint(BCLog::BENCH, " - Writing chainstate: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime5 - nTime4) * MILLI, nTimeChainState * MICRO, nTimeChainState * MILLI / nBlocksTotal);
@@ -2682,7 +2687,7 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams&
CheckBlockIndex(chainparams.GetConsensus());
// Write changes periodically to disk, after relay.
- if (!FlushStateToDisk(chainparams, state, FLUSH_STATE_PERIODIC)) {
+ if (!FlushStateToDisk(chainparams, state, FlushStateMode::PERIODIC)) {
return false;
}
@@ -2953,7 +2958,7 @@ static bool FindBlockPos(CDiskBlockPos &pos, unsigned int nAddSize, unsigned int
if (nNewChunks > nOldChunks) {
if (fPruneMode)
fCheckForPruning = true;
- if (CheckDiskSpace(nNewChunks * BLOCKFILE_CHUNK_SIZE - pos.nPos)) {
+ if (CheckDiskSpace(nNewChunks * BLOCKFILE_CHUNK_SIZE - pos.nPos, true)) {
FILE *file = OpenBlockFile(pos);
if (file) {
LogPrintf("Pre-allocating up to position 0x%x in blk%05u.dat\n", nNewChunks * BLOCKFILE_CHUNK_SIZE, pos.nFile);
@@ -2986,7 +2991,7 @@ static bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos,
if (nNewChunks > nOldChunks) {
if (fPruneMode)
fCheckForPruning = true;
- if (CheckDiskSpace(nNewChunks * UNDOFILE_CHUNK_SIZE - pos.nPos)) {
+ if (CheckDiskSpace(nNewChunks * UNDOFILE_CHUNK_SIZE - pos.nPos, true)) {
FILE *file = OpenUndoFile(pos);
if (file) {
LogPrintf("Pre-allocating up to position 0x%x in rev%05u.dat\n", nNewChunks * UNDOFILE_CHUNK_SIZE, pos.nFile);
@@ -3076,7 +3081,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P
bool IsWitnessEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& params)
{
LOCK(cs_main);
- return (VersionBitsState(pindexPrev, params, Consensus::DEPLOYMENT_SEGWIT, versionbitscache) == THRESHOLD_ACTIVE);
+ return (VersionBitsState(pindexPrev, params, Consensus::DEPLOYMENT_SEGWIT, versionbitscache) == ThresholdState::ACTIVE);
}
// Compute at which vout of the block's coinbase transaction the witness
@@ -3195,7 +3200,7 @@ static bool ContextualCheckBlock(const CBlock& block, CValidationState& state, c
// Start enforcing BIP113 (Median Time Past) using versionbits logic.
int nLockTimeFlags = 0;
- if (VersionBitsState(pindexPrev, consensusParams, Consensus::DEPLOYMENT_CSV, versionbitscache) == THRESHOLD_ACTIVE) {
+ if (VersionBitsState(pindexPrev, consensusParams, Consensus::DEPLOYMENT_CSV, versionbitscache) == ThresholdState::ACTIVE) {
nLockTimeFlags |= LOCKTIME_MEDIAN_TIME_PAST;
}
@@ -3223,13 +3228,13 @@ static bool ContextualCheckBlock(const CBlock& block, CValidationState& state, c
// Validation for witness commitments.
// * We compute the witness hash (which is the hash including witnesses) of all the block's transactions, except the
// coinbase (where 0x0000....0000 is used instead).
- // * The coinbase scriptWitness is a stack of a single 32-byte vector, containing a witness nonce (unconstrained).
+ // * The coinbase scriptWitness is a stack of a single 32-byte vector, containing a witness reserved value (unconstrained).
// * We build a merkle tree with all those witness hashes as leaves (similar to the hashMerkleRoot in the block header).
// * There must be at least one output whose scriptPubKey is a single 36-byte push, the first 4 bytes of which are
- // {0xaa, 0x21, 0xa9, 0xed}, and the following 32 bytes are SHA256^2(witness root, witness nonce). In case there are
+ // {0xaa, 0x21, 0xa9, 0xed}, and the following 32 bytes are SHA256^2(witness root, witness reserved value). In case there are
// multiple, the last one is used.
bool fHaveWitness = false;
- if (VersionBitsState(pindexPrev, consensusParams, Consensus::DEPLOYMENT_SEGWIT, versionbitscache) == THRESHOLD_ACTIVE) {
+ if (VersionBitsState(pindexPrev, consensusParams, Consensus::DEPLOYMENT_SEGWIT, versionbitscache) == ThresholdState::ACTIVE) {
int commitpos = GetWitnessCommitmentIndex(block);
if (commitpos != -1) {
bool malleated = false;
@@ -3238,7 +3243,7 @@ static bool ContextualCheckBlock(const CBlock& block, CValidationState& state, c
// already does not permit it, it is impossible to trigger in the
// witness tree.
if (block.vtx[0]->vin[0].scriptWitness.stack.size() != 1 || block.vtx[0]->vin[0].scriptWitness.stack[0].size() != 32) {
- return state.DoS(100, false, REJECT_INVALID, "bad-witness-nonce-size", true, strprintf("%s : invalid witness nonce size", __func__));
+ return state.DoS(100, false, REJECT_INVALID, "bad-witness-nonce-size", true, strprintf("%s : invalid witness reserved value size", __func__));
}
CHash256().Write(hashWitness.begin(), 32).Write(&block.vtx[0]->vin[0].scriptWitness.stack[0][0], 32).Finalize(hashWitness.begin());
if (memcmp(hashWitness.begin(), &block.vtx[0]->vout[commitpos].scriptPubKey[6], 32)) {
@@ -3257,7 +3262,7 @@ static bool ContextualCheckBlock(const CBlock& block, CValidationState& state, c
}
}
- // After the coinbase witness nonce and commitment are verified,
+ // After the coinbase witness reserved value and commitment are verified,
// we can check if the block weight passes (before we've checked the
// coinbase witness, it would be possible for the weight to be too
// large by filling up the coinbase witness, which doesn't change
@@ -3443,7 +3448,7 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CVali
}
if (fCheckForPruning)
- FlushStateToDisk(chainparams, state, FLUSH_STATE_NONE); // we just allocated more disk space for block files
+ FlushStateToDisk(chainparams, state, FlushStateMode::NONE); // we just allocated more disk space for block files
CheckBlockIndex(chainparams.GetConsensus());
@@ -3596,7 +3601,7 @@ void PruneBlockFilesManual(int nManualPruneHeight)
{
CValidationState state;
const CChainParams& chainparams = Params();
- FlushStateToDisk(chainparams, state, FLUSH_STATE_NONE, nManualPruneHeight);
+ FlushStateToDisk(chainparams, state, FlushStateMode::NONE, nManualPruneHeight);
}
/**
@@ -3661,9 +3666,9 @@ static void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfte
nLastBlockWeCanPrune, count);
}
-bool CheckDiskSpace(uint64_t nAdditionalBytes)
+bool CheckDiskSpace(uint64_t nAdditionalBytes, bool blocks_dir)
{
- uint64_t nFreeBytesAvailable = fs::space(GetDataDir()).available;
+ uint64_t nFreeBytesAvailable = fs::space(blocks_dir ? GetBlocksDir() : GetDataDir()).available;
// Check for nMinDiskSpace bytes (currently 50MB)
if (nFreeBytesAvailable < nMinDiskSpace + nAdditionalBytes)
@@ -3706,7 +3711,7 @@ static FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly) {
fs::path GetBlockPosFilename(const CDiskBlockPos &pos, const char *prefix)
{
- return GetDataDir() / "blocks" / strprintf("%s%05u.dat", prefix, pos.nFile);
+ return GetBlocksDir() / strprintf("%s%05u.dat", prefix, pos.nFile);
}
CBlockIndex * CChainState::InsertBlockIndex(const uint256& hash)
@@ -4094,7 +4099,7 @@ bool CChainState::RewindBlockIndex(const CChainParams& params)
return error("RewindBlockIndex: unable to disconnect block at height %i", pindex->nHeight);
}
// Occasionally flush state to disk.
- if (!FlushStateToDisk(params, state, FLUSH_STATE_PERIODIC))
+ if (!FlushStateToDisk(params, state, FlushStateMode::PERIODIC))
return false;
}
@@ -4160,7 +4165,7 @@ bool RewindBlockIndex(const CChainParams& params) {
// and skip it here, we're about to -reindex-chainstate anyway, so
// it'll get called a bunch real soon.
CValidationState state;
- if (!FlushStateToDisk(params, state, FLUSH_STATE_ALWAYS)) {
+ if (!FlushStateToDisk(params, state, FlushStateMode::ALWAYS)) {
return false;
}
}
@@ -4283,7 +4288,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB
unsigned char buf[CMessageHeader::MESSAGE_START_SIZE];
blkdat.FindByte(chainparams.MessageStart()[0]);
nRewind = blkdat.GetPos()+1;
- blkdat >> FLATDATA(buf);
+ blkdat >> buf;
if (memcmp(buf, chainparams.MessageStart(), CMessageHeader::MESSAGE_START_SIZE))
continue;
// read size
@@ -4639,7 +4644,8 @@ bool LoadMempool(void)
if (nTime + nExpiryTimeout > nNow) {
LOCK(cs_main);
AcceptToMemoryPoolWithTime(chainparams, mempool, state, tx, nullptr /* pfMissingInputs */, nTime,
- nullptr /* plTxnReplaced */, false /* bypass_limits */, 0 /* nAbsurdFee */);
+ nullptr /* plTxnReplaced */, false /* bypass_limits */, 0 /* nAbsurdFee */,
+ false /* test_accept */);
if (state.IsValid()) {
++count;
} else {
diff --git a/src/validation.h b/src/validation.h
index e780f453b2..dad6858b1e 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -22,6 +22,7 @@
#include <algorithm>
#include <exception>
#include <map>
+#include <memory>
#include <set>
#include <stdint.h>
#include <string>
@@ -158,6 +159,7 @@ extern CScript COINBASE_FLAGS;
extern CCriticalSection cs_main;
extern CBlockPolicyEstimator feeEstimator;
extern CTxMemPool mempool;
+extern std::atomic_bool g_is_mempool_loaded;
typedef std::unordered_map<uint256, CBlockIndex*, BlockHasher> BlockMap;
extern BlockMap& mapBlockIndex;
extern uint64_t nLastBlockTx;
@@ -254,7 +256,7 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons
bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& block, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex=nullptr, CBlockHeader *first_invalid=nullptr);
/** Check whether enough disk space is available for an incoming block */
-bool CheckDiskSpace(uint64_t nAdditionalBytes = 0);
+bool CheckDiskSpace(uint64_t nAdditionalBytes = 0, bool blocks_dir = false);
/** Open a block file (blk?????.dat) */
FILE* OpenBlockFile(const CDiskBlockPos &pos, bool fReadOnly = false);
/** Translation to a filesystem path */
@@ -307,7 +309,7 @@ void PruneBlockFilesManual(int nManualPruneHeight);
* plTxnReplaced will be appended to with all transactions replaced from mempool **/
bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx,
bool* pfMissingInputs, std::list<CTransactionRef>* plTxnReplaced,
- bool bypass_limits, const CAmount nAbsurdFee);
+ bool bypass_limits, const CAmount nAbsurdFee, bool test_accept=false);
/** Convert CValidationState to a human-readable message for logging */
std::string FormatStateMessage(const CValidationState &state);
@@ -411,7 +413,7 @@ bool IsWitnessEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& pa
/** When there are blocks in the active chain with missing data, rewind the chainstate and remove them from the block index */
bool RewindBlockIndex(const CChainParams& params);
-/** Update uncommitted block structures (currently: only the witness nonce). This is safe for submitted blocks. */
+/** Update uncommitted block structures (currently: only the witness reserved value). This is safe for submitted blocks. */
void UpdateUncommittedBlockStructures(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams);
/** Produce the necessary coinbase commitment for a block (modifies the hash, don't call for mined blocks). */
diff --git a/src/versionbits.cpp b/src/versionbits.cpp
index d2ee49db20..e3ec078173 100644
--- a/src/versionbits.cpp
+++ b/src/versionbits.cpp
@@ -29,7 +29,7 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex*
// Check if this deployment is always active.
if (nTimeStart == Consensus::BIP9Deployment::ALWAYS_ACTIVE) {
- return THRESHOLD_ACTIVE;
+ return ThresholdState::ACTIVE;
}
// A block's state is always the same as that of the first of its period, so it is computed based on a pindexPrev whose height equals a multiple of nPeriod - 1.
@@ -42,12 +42,12 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex*
while (cache.count(pindexPrev) == 0) {
if (pindexPrev == nullptr) {
// The genesis block is by definition defined.
- cache[pindexPrev] = THRESHOLD_DEFINED;
+ cache[pindexPrev] = ThresholdState::DEFINED;
break;
}
if (pindexPrev->GetMedianTimePast() < nTimeStart) {
// Optimization: don't recompute down further, as we know every earlier block will be before the start time
- cache[pindexPrev] = THRESHOLD_DEFINED;
+ cache[pindexPrev] = ThresholdState::DEFINED;
break;
}
vToCompute.push_back(pindexPrev);
@@ -65,17 +65,17 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex*
vToCompute.pop_back();
switch (state) {
- case THRESHOLD_DEFINED: {
+ case ThresholdState::DEFINED: {
if (pindexPrev->GetMedianTimePast() >= nTimeTimeout) {
- stateNext = THRESHOLD_FAILED;
+ stateNext = ThresholdState::FAILED;
} else if (pindexPrev->GetMedianTimePast() >= nTimeStart) {
- stateNext = THRESHOLD_STARTED;
+ stateNext = ThresholdState::STARTED;
}
break;
}
- case THRESHOLD_STARTED: {
+ case ThresholdState::STARTED: {
if (pindexPrev->GetMedianTimePast() >= nTimeTimeout) {
- stateNext = THRESHOLD_FAILED;
+ stateNext = ThresholdState::FAILED;
break;
}
// We need to count
@@ -88,17 +88,17 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex*
pindexCount = pindexCount->pprev;
}
if (count >= nThreshold) {
- stateNext = THRESHOLD_LOCKED_IN;
+ stateNext = ThresholdState::LOCKED_IN;
}
break;
}
- case THRESHOLD_LOCKED_IN: {
+ case ThresholdState::LOCKED_IN: {
// Always progresses into ACTIVE.
- stateNext = THRESHOLD_ACTIVE;
+ stateNext = ThresholdState::ACTIVE;
break;
}
- case THRESHOLD_FAILED:
- case THRESHOLD_ACTIVE: {
+ case ThresholdState::FAILED:
+ case ThresholdState::ACTIVE: {
// Nothing happens, these are terminal states.
break;
}
@@ -149,7 +149,7 @@ int AbstractThresholdConditionChecker::GetStateSinceHeightFor(const CBlockIndex*
const ThresholdState initialState = GetStateFor(pindexPrev, params, cache);
// BIP 9 about state DEFINED: "The genesis block is by definition in this state for each deployment."
- if (initialState == THRESHOLD_DEFINED) {
+ if (initialState == ThresholdState::DEFINED) {
return 0;
}
diff --git a/src/versionbits.h b/src/versionbits.h
index 1600dc8c93..8962a65057 100644
--- a/src/versionbits.h
+++ b/src/versionbits.h
@@ -2,8 +2,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#ifndef BITCOIN_CONSENSUS_VERSIONBITS
-#define BITCOIN_CONSENSUS_VERSIONBITS
+#ifndef BITCOIN_VERSIONBITS_H
+#define BITCOIN_VERSIONBITS_H
#include <chain.h>
#include <map>
@@ -17,12 +17,12 @@ static const int32_t VERSIONBITS_TOP_MASK = 0xE0000000UL;
/** Total bits available for versionbits */
static const int32_t VERSIONBITS_NUM_BITS = 29;
-enum ThresholdState {
- THRESHOLD_DEFINED,
- THRESHOLD_STARTED,
- THRESHOLD_LOCKED_IN,
- THRESHOLD_ACTIVE,
- THRESHOLD_FAILED,
+enum class ThresholdState {
+ DEFINED,
+ STARTED,
+ LOCKED_IN,
+ ACTIVE,
+ FAILED,
};
// A map that gives the state for blocks whose height is a multiple of Period().
@@ -77,4 +77,4 @@ BIP9Stats VersionBitsStatistics(const CBlockIndex* pindexPrev, const Consensus::
int VersionBitsStateSinceHeight(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache);
uint32_t VersionBitsMask(const Consensus::Params& params, Consensus::DeploymentPos pos);
-#endif
+#endif // BITCOIN_VERSIONBITS_H
diff --git a/src/wallet/coinselection.h b/src/wallet/coinselection.h
index 4d1a43bc17..2b185879c6 100644
--- a/src/wallet/coinselection.h
+++ b/src/wallet/coinselection.h
@@ -2,8 +2,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#ifndef BITCOIN_COINSELECTION_H
-#define BITCOIN_COINSELECTION_H
+#ifndef BITCOIN_WALLET_COINSELECTION_H
+#define BITCOIN_WALLET_COINSELECTION_H
#include <amount.h>
#include <primitives/transaction.h>
@@ -51,4 +51,4 @@ bool SelectCoinsBnB(std::vector<CInputCoin>& utxo_pool, const CAmount& target_va
// Original coin selection algorithm as a fallback
bool KnapsackSolver(const CAmount& nTargetValue, std::vector<CInputCoin>& vCoins, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet);
-#endif // BITCOIN_COINSELECTION_H
+#endif // BITCOIN_WALLET_COINSELECTION_H
diff --git a/src/wallet/crypter.h b/src/wallet/crypter.h
index f3ae7144b4..fdeb4cfee0 100644
--- a/src/wallet/crypter.h
+++ b/src/wallet/crypter.h
@@ -67,7 +67,7 @@ public:
typedef std::vector<unsigned char, secure_allocator<unsigned char> > CKeyingMaterial;
-namespace wallet_crypto
+namespace crypto_tests
{
class TestCrypter;
}
@@ -75,7 +75,7 @@ namespace wallet_crypto
/** Encryption/decryption context with key information */
class CCrypter
{
-friend class wallet_crypto::TestCrypter; // for test access to chKey/chIV
+friend class crypto_tests::TestCrypter; // for test access to chKey/chIV
private:
std::vector<unsigned char, secure_allocator<unsigned char>> vchKey;
std::vector<unsigned char, secure_allocator<unsigned char>> vchIV;
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index ebe7b48da0..553cae4d02 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -235,13 +235,13 @@ CDBEnv::VerifyResult CDBEnv::Verify(const std::string& strFile, recoverFunc_type
Db db(dbenv.get(), 0);
int result = db.verify(strFile.c_str(), nullptr, nullptr, 0);
if (result == 0)
- return VERIFY_OK;
+ return VerifyResult::VERIFY_OK;
else if (recoverFunc == nullptr)
- return RECOVER_FAIL;
+ return VerifyResult::RECOVER_FAIL;
// Try to recover:
bool fRecovered = (*recoverFunc)(fs::path(strPath) / strFile, out_backup_filename);
- return (fRecovered ? RECOVER_OK : RECOVER_FAIL);
+ return (fRecovered ? VerifyResult::RECOVER_OK : VerifyResult::RECOVER_FAIL);
}
bool CDB::Recover(const fs::path& file_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& newFilename)
@@ -347,7 +347,7 @@ bool CDB::VerifyDatabaseFile(const fs::path& file_path, std::string& warningStr,
{
std::string backup_filename;
CDBEnv::VerifyResult r = env->Verify(walletFile, recoverFunc, backup_filename);
- if (r == CDBEnv::RECOVER_OK)
+ if (r == CDBEnv::VerifyResult::RECOVER_OK)
{
warningStr = strprintf(_("Warning: Wallet file corrupt, data salvaged!"
" Original %s saved as %s in %s; if"
@@ -355,7 +355,7 @@ bool CDB::VerifyDatabaseFile(const fs::path& file_path, std::string& warningStr,
" restore from a backup."),
walletFile, backup_filename, walletDir);
}
- if (r == CDBEnv::RECOVER_FAIL)
+ if (r == CDBEnv::VerifyResult::RECOVER_FAIL)
{
errorStr = strprintf(_("%s corrupt, salvage failed"), walletFile);
return false;
diff --git a/src/wallet/db.h b/src/wallet/db.h
index b1ce451534..49a9f3f082 100644
--- a/src/wallet/db.h
+++ b/src/wallet/db.h
@@ -16,6 +16,7 @@
#include <atomic>
#include <map>
+#include <memory>
#include <string>
#include <vector>
@@ -53,7 +54,7 @@ public:
* This must be called BEFORE strFile is opened.
* Returns true if strFile is OK.
*/
- enum VerifyResult { VERIFY_OK,
+ enum class VerifyResult { VERIFY_OK,
RECOVER_OK,
RECOVER_FAIL };
typedef bool (*recoverFunc_type)(const fs::path& file_path, std::string& out_backup_filename);
diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp
index 61481e01b6..c860eede05 100644
--- a/src/wallet/init.cpp
+++ b/src/wallet/init.cpp
@@ -3,18 +3,54 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <wallet/init.h>
-
#include <chainparams.h>
+#include <init.h>
#include <net.h>
#include <util.h>
#include <utilmoneystr.h>
#include <validation.h>
+#include <walletinitinterface.h>
#include <wallet/rpcwallet.h>
#include <wallet/wallet.h>
#include <wallet/walletutil.h>
-std::string GetWalletHelpString(bool showDebug)
+class WalletInit : public WalletInitInterface {
+public:
+
+ //! Return the wallets help message.
+ std::string GetHelpString(bool showDebug) override;
+
+ //! Wallets parameter interaction
+ bool ParameterInteraction() override;
+
+ //! Register wallet RPCs.
+ void RegisterRPC(CRPCTable &tableRPC) override;
+
+ //! Responsible for reading and validating the -wallet arguments and verifying the wallet database.
+ // This function will perform salvage on the wallet if requested, as long as only one wallet is
+ // being loaded (WalletParameterInteraction forbids -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet).
+ bool Verify() override;
+
+ //! Load wallet databases.
+ bool Open() override;
+
+ //! Complete startup of wallets.
+ void Start(CScheduler& scheduler) override;
+
+ //! Flush all wallets in preparation for shutdown.
+ void Flush() override;
+
+ //! Stop all wallets. Wallets will be flushed first.
+ void Stop() override;
+
+ //! Close all wallets.
+ void Close() override;
+};
+
+static WalletInit g_wallet_init;
+WalletInitInterface* const g_wallet_init_interface = &g_wallet_init;
+
+std::string WalletInit::GetHelpString(bool showDebug)
{
std::string strUsage = HelpMessageGroup(_("Wallet options:"));
strUsage += HelpMessageOpt("-addresstype", strprintf("What type of addresses to use (\"legacy\", \"p2sh-segwit\", or \"bech32\", default: \"%s\")", FormatOutputType(DEFAULT_ADDRESS_TYPE)));
@@ -56,7 +92,7 @@ std::string GetWalletHelpString(bool showDebug)
return strUsage;
}
-bool WalletParameterInteraction()
+bool WalletInit::ParameterInteraction()
{
if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
for (const std::string& wallet : gArgs.GetArgs("-wallet")) {
@@ -184,7 +220,7 @@ bool WalletParameterInteraction()
return true;
}
-void RegisterWalletRPC(CRPCTable &t)
+void WalletInit::RegisterRPC(CRPCTable &t)
{
if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
return;
@@ -193,7 +229,7 @@ void RegisterWalletRPC(CRPCTable &t)
RegisterWalletRPCCommands(t);
}
-bool VerifyWallets()
+bool WalletInit::Verify()
{
if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
return true;
@@ -268,7 +304,7 @@ bool VerifyWallets()
return true;
}
-bool OpenWallets()
+bool WalletInit::Open()
{
if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
LogPrintf("Wallet disabled!\n");
@@ -286,25 +322,29 @@ bool OpenWallets()
return true;
}
-void StartWallets(CScheduler& scheduler) {
+void WalletInit::Start(CScheduler& scheduler)
+{
for (CWalletRef pwallet : vpwallets) {
pwallet->postInitProcess(scheduler);
}
}
-void FlushWallets() {
+void WalletInit::Flush()
+{
for (CWalletRef pwallet : vpwallets) {
pwallet->Flush(false);
}
}
-void StopWallets() {
+void WalletInit::Stop()
+{
for (CWalletRef pwallet : vpwallets) {
pwallet->Flush(true);
}
}
-void CloseWallets() {
+void WalletInit::Close()
+{
for (CWalletRef pwallet : vpwallets) {
delete pwallet;
}
diff --git a/src/wallet/init.h b/src/wallet/init.h
deleted file mode 100644
index 0b3ee2dda2..0000000000
--- a/src/wallet/init.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2017 The Bitcoin Core developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-#ifndef BITCOIN_WALLET_INIT_H
-#define BITCOIN_WALLET_INIT_H
-
-#include <string>
-
-class CRPCTable;
-class CScheduler;
-
-//! Return the wallets help message.
-std::string GetWalletHelpString(bool showDebug);
-
-//! Wallets parameter interaction
-bool WalletParameterInteraction();
-
-//! Register wallet RPCs.
-void RegisterWalletRPC(CRPCTable &tableRPC);
-
-//! Responsible for reading and validating the -wallet arguments and verifying the wallet database.
-// This function will perform salvage on the wallet if requested, as long as only one wallet is
-// being loaded (WalletParameterInteraction forbids -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet).
-bool VerifyWallets();
-
-//! Load wallet databases.
-bool OpenWallets();
-
-//! Complete startup of wallets.
-void StartWallets(CScheduler& scheduler);
-
-//! Flush all wallets in preparation for shutdown.
-void FlushWallets();
-
-//! Stop all wallets. Wallets will be flushed first.
-void StopWallets();
-
-//! Close all wallets.
-void CloseWallets();
-
-#endif // BITCOIN_WALLET_INIT_H
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index 1721bc6df6..a3594aa692 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -6,7 +6,6 @@
#include <key_io.h>
#include <rpc/safemode.h>
#include <rpc/server.h>
-#include <wallet/init.h>
#include <validation.h>
#include <script/script.h>
#include <script/standard.h>
@@ -100,6 +99,7 @@ UniValue importprivkey(const JSONRPCRequest& request)
throw std::runtime_error(
"importprivkey \"privkey\" ( \"label\" ) ( rescan )\n"
"\nAdds a private key (as returned by dumpprivkey) to your wallet. Requires a new wallet backup.\n"
+ "Hint: use importmulti to import more than one private key.\n"
"\nArguments:\n"
"1. \"privkey\" (string, required) The private key (see dumpprivkey)\n"
"2. \"label\" (string, optional, default=\"\") An optional label\n"
@@ -406,7 +406,7 @@ UniValue removeprunedfunds(const JSONRPCRequest& request)
vHash.push_back(hash);
std::vector<uint256> vHashOut;
- if (pwallet->ZapSelectTx(vHash, vHashOut) != DB_LOAD_OK) {
+ if (pwallet->ZapSelectTx(vHash, vHashOut) != DBErrors::LOAD_OK) {
throw JSONRPCError(RPC_WALLET_ERROR, "Could not properly delete the transaction.");
}
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 365dedfceb..c34b166a41 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -113,9 +113,9 @@ void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry)
if (confirms <= 0) {
LOCK(mempool.cs);
RBFTransactionState rbfState = IsRBFOptIn(*wtx.tx, mempool);
- if (rbfState == RBF_TRANSACTIONSTATE_UNKNOWN)
+ if (rbfState == RBFTransactionState::UNKNOWN)
rbfStatus = "unknown";
- else if (rbfState == RBF_TRANSACTIONSTATE_REPLACEABLE_BIP125)
+ else if (rbfState == RBFTransactionState::REPLACEABLE_BIP125)
rbfStatus = "yes";
}
entry.pushKV("bip125-replaceable", rbfStatus);
diff --git a/src/wallet/test/accounting_tests.cpp b/src/wallet/test/accounting_tests.cpp
index aae328d81f..cc6e491f53 100644
--- a/src/wallet/test/accounting_tests.cpp
+++ b/src/wallet/test/accounting_tests.cpp
@@ -18,7 +18,7 @@ GetResults(CWallet& wallet, std::map<CAmount, CAccountingEntry>& results)
std::list<CAccountingEntry> aes;
results.clear();
- BOOST_CHECK(wallet.ReorderTransactions() == DB_LOAD_OK);
+ BOOST_CHECK(wallet.ReorderTransactions() == DBErrors::LOAD_OK);
wallet.ListAccountCreditDebit("", aes);
for (CAccountingEntry& ae : aes)
{
diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp
index 6c36e2e965..184a8a3f1f 100644
--- a/src/wallet/test/coinselector_tests.cpp
+++ b/src/wallet/test/coinselector_tests.cpp
@@ -14,7 +14,7 @@
#include <boost/test/unit_test.hpp>
#include <random>
-BOOST_FIXTURE_TEST_SUITE(coin_selection_tests, WalletTestingSetup)
+BOOST_FIXTURE_TEST_SUITE(coinselector_tests, WalletTestingSetup)
// how many times to run all the tests to have a chance to catch errors that only show up with particular random shuffles
#define RUN_TESTS 100
diff --git a/src/wallet/test/crypto_tests.cpp b/src/wallet/test/crypto_tests.cpp
index 89b2c4e796..d8c0cdf0f9 100644
--- a/src/wallet/test/crypto_tests.cpp
+++ b/src/wallet/test/crypto_tests.cpp
@@ -10,7 +10,7 @@
#include <boost/test/unit_test.hpp>
-BOOST_FIXTURE_TEST_SUITE(wallet_crypto, BasicTestingSetup)
+BOOST_FIXTURE_TEST_SUITE(crypto_tests, BasicTestingSetup)
class TestCrypter
{
diff --git a/src/wallet/test/wallet_test_fixture.h b/src/wallet/test/wallet_test_fixture.h
index 663836a955..b328b3543b 100644
--- a/src/wallet/test/wallet_test_fixture.h
+++ b/src/wallet/test/wallet_test_fixture.h
@@ -2,13 +2,15 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#ifndef BITCOIN_WALLET_TEST_FIXTURE_H
-#define BITCOIN_WALLET_TEST_FIXTURE_H
+#ifndef BITCOIN_WALLET_TEST_WALLET_TEST_FIXTURE_H
+#define BITCOIN_WALLET_TEST_WALLET_TEST_FIXTURE_H
#include <test/test_bitcoin.h>
#include <wallet/wallet.h>
+#include <memory>
+
/** Testing setup and teardown for wallet.
*/
struct WalletTestingSetup: public TestingSetup {
@@ -18,5 +20,4 @@ struct WalletTestingSetup: public TestingSetup {
CWallet m_wallet;
};
-#endif
-
+#endif // BITCOIN_WALLET_TEST_WALLET_TEST_FIXTURE_H
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 808f8b8838..e93e0f1966 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -4,6 +4,7 @@
#include <wallet/wallet.h>
+#include <memory>
#include <set>
#include <stdint.h>
#include <utility>
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index a80875e7ed..8dac547abb 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -12,7 +12,6 @@
#include <consensus/consensus.h>
#include <consensus/validation.h>
#include <fs.h>
-#include <wallet/init.h>
#include <key.h>
#include <key_io.h>
#include <keystore.h>
@@ -727,11 +726,11 @@ DBErrors CWallet::ReorderTransactions()
if (pwtx)
{
if (!walletdb.WriteTx(*pwtx))
- return DB_LOAD_FAIL;
+ return DBErrors::LOAD_FAIL;
}
else
if (!walletdb.WriteAccountingEntry(pacentry->nEntryNo, *pacentry))
- return DB_LOAD_FAIL;
+ return DBErrors::LOAD_FAIL;
}
else
{
@@ -751,16 +750,16 @@ DBErrors CWallet::ReorderTransactions()
if (pwtx)
{
if (!walletdb.WriteTx(*pwtx))
- return DB_LOAD_FAIL;
+ return DBErrors::LOAD_FAIL;
}
else
if (!walletdb.WriteAccountingEntry(pacentry->nEntryNo, *pacentry))
- return DB_LOAD_FAIL;
+ return DBErrors::LOAD_FAIL;
}
}
walletdb.WriteOrderPosNext(nOrderPosNext);
- return DB_LOAD_OK;
+ return DBErrors::LOAD_OK;
}
int64_t CWallet::IncOrderPosNext(CWalletDB *pwalletdb)
@@ -878,7 +877,7 @@ bool CWallet::MarkReplaced(const uint256& originalHash, const uint256& newHash)
bool success = true;
if (!walletdb.WriteTx(wtx)) {
- LogPrintf("%s: Updating walletdb tx %s failed", __func__, wtx.GetHash().ToString());
+ LogPrintf("%s: Updating walletdb tx %s failed\n", __func__, wtx.GetHash().ToString());
success = false;
}
@@ -3146,7 +3145,7 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
fFirstRunRet = false;
DBErrors nLoadWalletRet = CWalletDB(*dbw,"cr+").LoadWallet(this);
- if (nLoadWalletRet == DB_NEED_REWRITE)
+ if (nLoadWalletRet == DBErrors::NEED_REWRITE)
{
if (dbw->Rewrite("\x04pool"))
{
@@ -3162,12 +3161,12 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
// This wallet is in its first run if all of these are empty
fFirstRunRet = mapKeys.empty() && mapCryptedKeys.empty() && mapWatchKeys.empty() && setWatchOnly.empty() && mapScripts.empty();
- if (nLoadWalletRet != DB_LOAD_OK)
+ if (nLoadWalletRet != DBErrors::LOAD_OK)
return nLoadWalletRet;
uiInterface.LoadWallet(this);
- return DB_LOAD_OK;
+ return DBErrors::LOAD_OK;
}
DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut)
@@ -3177,7 +3176,7 @@ DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256
for (uint256 hash : vHashOut)
mapWallet.erase(hash);
- if (nZapSelectTxRet == DB_NEED_REWRITE)
+ if (nZapSelectTxRet == DBErrors::NEED_REWRITE)
{
if (dbw->Rewrite("\x04pool"))
{
@@ -3190,19 +3189,19 @@ DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256
}
}
- if (nZapSelectTxRet != DB_LOAD_OK)
+ if (nZapSelectTxRet != DBErrors::LOAD_OK)
return nZapSelectTxRet;
MarkDirty();
- return DB_LOAD_OK;
+ return DBErrors::LOAD_OK;
}
DBErrors CWallet::ZapWalletTx(std::vector<CWalletTx>& vWtx)
{
DBErrors nZapWalletTxRet = CWalletDB(*dbw,"cr+").ZapWalletTx(vWtx);
- if (nZapWalletTxRet == DB_NEED_REWRITE)
+ if (nZapWalletTxRet == DBErrors::NEED_REWRITE)
{
if (dbw->Rewrite("\x04pool"))
{
@@ -3216,10 +3215,10 @@ DBErrors CWallet::ZapWalletTx(std::vector<CWalletTx>& vWtx)
}
}
- if (nZapWalletTxRet != DB_LOAD_OK)
+ if (nZapWalletTxRet != DBErrors::LOAD_OK)
return nZapWalletTxRet;
- return DB_LOAD_OK;
+ return DBErrors::LOAD_OK;
}
@@ -3931,7 +3930,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path&
std::unique_ptr<CWallet> tempWallet = MakeUnique<CWallet>(name, CWalletDBWrapper::Create(path));
DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx);
- if (nZapWalletRet != DB_LOAD_OK) {
+ if (nZapWalletRet != DBErrors::LOAD_OK) {
InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile));
return nullptr;
}
@@ -3943,23 +3942,23 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path&
bool fFirstRun = true;
CWallet *walletInstance = new CWallet(name, CWalletDBWrapper::Create(path));
DBErrors nLoadWalletRet = walletInstance->LoadWallet(fFirstRun);
- if (nLoadWalletRet != DB_LOAD_OK)
+ if (nLoadWalletRet != DBErrors::LOAD_OK)
{
- if (nLoadWalletRet == DB_CORRUPT) {
+ if (nLoadWalletRet == DBErrors::CORRUPT) {
InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile));
return nullptr;
}
- else if (nLoadWalletRet == DB_NONCRITICAL_ERROR)
+ else if (nLoadWalletRet == DBErrors::NONCRITICAL_ERROR)
{
InitWarning(strprintf(_("Error reading %s! All keys read correctly, but transaction data"
" or address book entries might be missing or incorrect."),
walletFile));
}
- else if (nLoadWalletRet == DB_TOO_NEW) {
+ else if (nLoadWalletRet == DBErrors::TOO_NEW) {
InitError(strprintf(_("Error loading %s: Wallet requires newer version of %s"), walletFile, _(PACKAGE_NAME)));
return nullptr;
}
- else if (nLoadWalletRet == DB_NEED_REWRITE)
+ else if (nLoadWalletRet == DBErrors::NEED_REWRITE)
{
InitError(strprintf(_("Wallet needed to be rewritten: restart %s to complete"), _(PACKAGE_NAME)));
return nullptr;
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 3ef5bfbb65..22a0ac2924 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -24,6 +24,7 @@
#include <algorithm>
#include <atomic>
#include <map>
+#include <memory>
#include <set>
#include <stdexcept>
#include <stdint.h>
@@ -660,22 +661,22 @@ class CWallet final : public CCryptoKeyStore, public CValidationInterface
{
private:
static std::atomic<bool> fFlushScheduled;
- std::atomic<bool> fAbortRescan;
- std::atomic<bool> fScanningWallet; //controlled by WalletRescanReserver
+ std::atomic<bool> fAbortRescan{false};
+ std::atomic<bool> fScanningWallet{false}; // controlled by WalletRescanReserver
std::mutex mutexScanning;
friend class WalletRescanReserver;
- CWalletDB *pwalletdbEncryption;
+ CWalletDB *pwalletdbEncryption = nullptr;
//! the current wallet version: clients below this version are not able to load the wallet
- int nWalletVersion;
+ int nWalletVersion = FEATURE_BASE;
//! the maximum wallet format version: memory-only variable that specifies to what version this wallet may be upgraded
- int nWalletMaxVersion;
+ int nWalletMaxVersion = FEATURE_BASE;
- int64_t nNextResend;
- int64_t nLastResend;
- bool fBroadcastTransactions;
+ int64_t nNextResend = 0;
+ int64_t nLastResend = 0;
+ bool fBroadcastTransactions = false;
/**
* Used to keep track of spent outpoints, and
@@ -704,10 +705,10 @@ private:
std::set<int64_t> setInternalKeyPool;
std::set<int64_t> setExternalKeyPool;
- int64_t m_max_keypool_index;
+ int64_t m_max_keypool_index = 0;
std::map<CKeyID, int64_t> m_pool_key_to_index;
- int64_t nTimeFirstKey;
+ int64_t nTimeFirstKey = 0;
/**
* Private version of AddWatchOnly method which does not accept a
@@ -740,7 +741,7 @@ private:
*
* Protected by cs_main (see BlockUntilSyncedToCurrentChain)
*/
- const CBlockIndex* m_last_block_processed;
+ const CBlockIndex* m_last_block_processed = nullptr;
public:
/*
@@ -779,12 +780,11 @@ public:
typedef std::map<unsigned int, CMasterKey> MasterKeyMap;
MasterKeyMap mapMasterKeys;
- unsigned int nMasterKeyMaxID;
+ unsigned int nMasterKeyMaxID = 0;
/** Construct wallet with specified name and database implementation. */
CWallet(std::string name, std::unique_ptr<CWalletDBWrapper> dbw) : m_name(std::move(name)), dbw(std::move(dbw))
{
- SetNull();
}
~CWallet()
@@ -793,24 +793,6 @@ public:
pwalletdbEncryption = nullptr;
}
- void SetNull()
- {
- nWalletVersion = FEATURE_BASE;
- nWalletMaxVersion = FEATURE_BASE;
- nMasterKeyMaxID = 0;
- pwalletdbEncryption = nullptr;
- nOrderPosNext = 0;
- nAccountingEntryNumber = 0;
- nNextResend = 0;
- nLastResend = 0;
- m_max_keypool_index = 0;
- nTimeFirstKey = 0;
- fBroadcastTransactions = false;
- nRelockTime = 0;
- fAbortRescan = false;
- fScanningWallet = false;
- }
-
std::map<uint256, CWalletTx> mapWallet;
std::list<CAccountingEntry> laccentries;
@@ -818,8 +800,8 @@ public:
typedef std::multimap<int64_t, TxPair > TxItems;
TxItems wtxOrdered;
- int64_t nOrderPosNext;
- uint64_t nAccountingEntryNumber;
+ int64_t nOrderPosNext = 0;
+ uint64_t nAccountingEntryNumber = 0;
std::map<uint256, int> mapRequestCount;
std::map<CTxDestination, CAddressBookData> mapAddressBook;
@@ -912,7 +894,7 @@ public:
bool LoadWatchOnly(const CScript &dest);
//! Holds a timestamp at which point the wallet is scheduled (externally) to be relocked. Caller must arrange for actual relocking to occur via Lock().
- int64_t nRelockTime;
+ int64_t nRelockTime = 0;
bool Unlock(const SecureString& strWalletPassphrase);
bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase);
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index 7f5f3b84b2..77cdfe7dd0 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -522,7 +522,7 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
{
CWalletScanState wss;
bool fNoncriticalErrors = false;
- DBErrors result = DB_LOAD_OK;
+ DBErrors result = DBErrors::LOAD_OK;
LOCK(pwallet->cs_wallet);
try {
@@ -530,7 +530,7 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
if (batch.Read((std::string)"minversion", nMinVersion))
{
if (nMinVersion > CLIENT_VERSION)
- return DB_TOO_NEW;
+ return DBErrors::TOO_NEW;
pwallet->LoadMinVersion(nMinVersion);
}
@@ -539,7 +539,7 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
if (!pcursor)
{
LogPrintf("Error getting wallet database cursor\n");
- return DB_CORRUPT;
+ return DBErrors::CORRUPT;
}
while (true)
@@ -553,7 +553,7 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
else if (ret != 0)
{
LogPrintf("Error reading next record from wallet database\n");
- return DB_CORRUPT;
+ return DBErrors::CORRUPT;
}
// Try to be tolerant of single corrupt records:
@@ -563,7 +563,7 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
// losing keys is considered a catastrophic error, anything else
// we assume the user can live with:
if (IsKeyType(strType) || strType == "defaultkey")
- result = DB_CORRUPT;
+ result = DBErrors::CORRUPT;
else
{
// Leave other errors alone, if we try to fix them we might make things worse.
@@ -582,15 +582,15 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
throw;
}
catch (...) {
- result = DB_CORRUPT;
+ result = DBErrors::CORRUPT;
}
- if (fNoncriticalErrors && result == DB_LOAD_OK)
- result = DB_NONCRITICAL_ERROR;
+ if (fNoncriticalErrors && result == DBErrors::LOAD_OK)
+ result = DBErrors::NONCRITICAL_ERROR;
// Any wallet corruption at all: skip any rewriting or
// upgrading, we don't want to make it worse.
- if (result != DB_LOAD_OK)
+ if (result != DBErrors::LOAD_OK)
return result;
LogPrintf("nFileVersion = %d\n", wss.nFileVersion);
@@ -607,7 +607,7 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
// Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc:
if (wss.fIsEncrypted && (wss.nFileVersion == 40000 || wss.nFileVersion == 50000))
- return DB_NEED_REWRITE;
+ return DBErrors::NEED_REWRITE;
if (wss.nFileVersion < CLIENT_VERSION) // Update
WriteVersion(CLIENT_VERSION);
@@ -626,14 +626,14 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
DBErrors CWalletDB::FindWalletTx(std::vector<uint256>& vTxHash, std::vector<CWalletTx>& vWtx)
{
- DBErrors result = DB_LOAD_OK;
+ DBErrors result = DBErrors::LOAD_OK;
try {
int nMinVersion = 0;
if (batch.Read((std::string)"minversion", nMinVersion))
{
if (nMinVersion > CLIENT_VERSION)
- return DB_TOO_NEW;
+ return DBErrors::TOO_NEW;
}
// Get cursor
@@ -641,7 +641,7 @@ DBErrors CWalletDB::FindWalletTx(std::vector<uint256>& vTxHash, std::vector<CWal
if (!pcursor)
{
LogPrintf("Error getting wallet database cursor\n");
- return DB_CORRUPT;
+ return DBErrors::CORRUPT;
}
while (true)
@@ -655,7 +655,7 @@ DBErrors CWalletDB::FindWalletTx(std::vector<uint256>& vTxHash, std::vector<CWal
else if (ret != 0)
{
LogPrintf("Error reading next record from wallet database\n");
- return DB_CORRUPT;
+ return DBErrors::CORRUPT;
}
std::string strType;
@@ -677,7 +677,7 @@ DBErrors CWalletDB::FindWalletTx(std::vector<uint256>& vTxHash, std::vector<CWal
throw;
}
catch (...) {
- result = DB_CORRUPT;
+ result = DBErrors::CORRUPT;
}
return result;
@@ -689,7 +689,7 @@ DBErrors CWalletDB::ZapSelectTx(std::vector<uint256>& vTxHashIn, std::vector<uin
std::vector<uint256> vTxHash;
std::vector<CWalletTx> vWtx;
DBErrors err = FindWalletTx(vTxHash, vWtx);
- if (err != DB_LOAD_OK) {
+ if (err != DBErrors::LOAD_OK) {
return err;
}
@@ -716,9 +716,9 @@ DBErrors CWalletDB::ZapSelectTx(std::vector<uint256>& vTxHashIn, std::vector<uin
}
if (delerror) {
- return DB_CORRUPT;
+ return DBErrors::CORRUPT;
}
- return DB_LOAD_OK;
+ return DBErrors::LOAD_OK;
}
DBErrors CWalletDB::ZapWalletTx(std::vector<CWalletTx>& vWtx)
@@ -726,16 +726,16 @@ DBErrors CWalletDB::ZapWalletTx(std::vector<CWalletTx>& vWtx)
// build list of wallet TXs
std::vector<uint256> vTxHash;
DBErrors err = FindWalletTx(vTxHash, vWtx);
- if (err != DB_LOAD_OK)
+ if (err != DBErrors::LOAD_OK)
return err;
// erase each wallet TX
for (uint256& hash : vTxHash) {
if (!EraseTx(hash))
- return DB_CORRUPT;
+ return DBErrors::CORRUPT;
}
- return DB_LOAD_OK;
+ return DBErrors::LOAD_OK;
}
void MaybeCompactWalletDB()
diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h
index 7d754c7284..606b7dace7 100644
--- a/src/wallet/walletdb.h
+++ b/src/wallet/walletdb.h
@@ -46,14 +46,14 @@ class uint160;
class uint256;
/** Error statuses for the wallet database */
-enum DBErrors
+enum class DBErrors
{
- DB_LOAD_OK,
- DB_CORRUPT,
- DB_NONCRITICAL_ERROR,
- DB_TOO_NEW,
- DB_LOAD_FAIL,
- DB_NEED_REWRITE
+ LOAD_OK,
+ CORRUPT,
+ NONCRITICAL_ERROR,
+ TOO_NEW,
+ LOAD_FAIL,
+ NEED_REWRITE
};
/* simple HD chain data model */
diff --git a/src/wallet/walletutil.h b/src/wallet/walletutil.h
index 50ff736402..f12acacd00 100644
--- a/src/wallet/walletutil.h
+++ b/src/wallet/walletutil.h
@@ -2,8 +2,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#ifndef BITCOIN_WALLET_UTIL_H
-#define BITCOIN_WALLET_UTIL_H
+#ifndef BITCOIN_WALLET_WALLETUTIL_H
+#define BITCOIN_WALLET_WALLETUTIL_H
#include <chainparamsbase.h>
#include <util.h>
@@ -11,4 +11,4 @@
//! Get the path of the wallet directory.
fs::path GetWalletDir();
-#endif // BITCOIN_WALLET_UTIL_H
+#endif // BITCOIN_WALLET_WALLETUTIL_H
diff --git a/src/walletinitinterface.h b/src/walletinitinterface.h
new file mode 100644
index 0000000000..c7eee37ce5
--- /dev/null
+++ b/src/walletinitinterface.h
@@ -0,0 +1,37 @@
+// 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_WALLETINITINTERFACE_H
+#define BITCOIN_WALLETINITINTERFACE_H
+
+#include <string>
+
+class CScheduler;
+class CRPCTable;
+
+class WalletInitInterface {
+public:
+ /** Get wallet help string */
+ virtual std::string GetHelpString(bool showDebug) = 0;
+ /** Check wallet parameter interaction */
+ virtual bool ParameterInteraction() = 0;
+ /** Register wallet RPC*/
+ virtual void RegisterRPC(CRPCTable &) = 0;
+ /** Verify wallets */
+ virtual bool Verify() = 0;
+ /** Open wallets*/
+ virtual bool Open() = 0;
+ /** Start wallets*/
+ virtual void Start(CScheduler& scheduler) = 0;
+ /** Flush Wallets*/
+ virtual void Flush() = 0;
+ /** Stop Wallets*/
+ virtual void Stop() = 0;
+ /** Close wallets */
+ virtual void Close() = 0;
+
+ virtual ~WalletInitInterface() {}
+};
+
+#endif // BITCOIN_WALLETINITINTERFACE_H
diff --git a/test/functional/README.md b/test/functional/README.md
index 662b4b44d5..21050cc2fa 100644
--- a/test/functional/README.md
+++ b/test/functional/README.md
@@ -89,52 +89,6 @@ thread.)
- Can be used to write tests where specific P2P protocol behavior is tested.
Examples tests are `p2p_unrequested_blocks.py`, `p2p_compactblocks.py`.
-#### Comptool
-
-- Comptool is a Testing framework for writing tests that compare the block/tx acceptance
-behavior of a bitcoind against 1 or more other bitcoind instances. It should not be used
-to write static tests with known outcomes, since that type of test is easier to write and
-maintain using the standard BitcoinTestFramework.
-
-- Set the `num_nodes` variable (defined in `ComparisonTestFramework`) to start up
-1 or more nodes. If using 1 node, then `--testbinary` can be used as a command line
-option to change the bitcoind binary used by the test. If using 2 or more nodes,
-then `--refbinary` can be optionally used to change the bitcoind that will be used
-on nodes 2 and up.
-
-- Implement a (generator) function called `get_tests()` which yields `TestInstance`s.
-Each `TestInstance` consists of:
- - A list of `[object, outcome, hash]` entries
- * `object` is a `CBlock`, `CTransaction`, or
- `CBlockHeader`. `CBlock`'s and `CTransaction`'s are tested for
- acceptance. `CBlockHeader`s can be used so that the test runner can deliver
- complete headers-chains when requested from the bitcoind, to allow writing
- tests where blocks can be delivered out of order but still processed by
- headers-first bitcoind's.
- * `outcome` is `True`, `False`, or `None`. If `True`
- or `False`, the tip is compared with the expected tip -- either the
- block passed in, or the hash specified as the optional 3rd entry. If
- `None` is specified, then the test will compare all the bitcoind's
- being tested to see if they all agree on what the best tip is.
- * `hash` is the block hash of the tip to compare against. Optional to
- specify; if left out then the hash of the block passed in will be used as
- the expected tip. This allows for specifying an expected tip while testing
- the handling of either invalid blocks or blocks delivered out of order,
- which complete a longer chain.
- - `sync_every_block`: `True/False`. If `False`, then all blocks
- are inv'ed together, and the test runner waits until the node receives the
- last one, and tests only the last block for tip acceptance using the
- outcome and specified tip. If `True`, then each block is tested in
- sequence and synced (this is slower when processing many blocks).
- - `sync_every_transaction`: `True/False`. Analogous to
- `sync_every_block`, except if the outcome on the last tx is "None",
- then the contents of the entire mempool are compared across all bitcoind
- connections. If `True` or `False`, then only the last tx's
- acceptance is tested against the given outcome.
-
-- For examples of tests written in this framework, see
- `p2p_invalid_block.py` and `feature_block.py`.
-
### test-framework modules
#### [test_framework/authproxy.py](test_framework/authproxy.py)
@@ -149,15 +103,9 @@ Generally useful functions.
#### [test_framework/mininode.py](test_framework/mininode.py)
Basic code to support P2P connectivity to a bitcoind.
-#### [test_framework/comptool.py](test_framework/comptool.py)
-Framework for comparison-tool style, P2P tests.
-
#### [test_framework/script.py](test_framework/script.py)
Utilities for manipulating transaction scripts (originally from python-bitcoinlib)
-#### [test_framework/blockstore.py](test_framework/blockstore.py)
-Implements disk-backed block and tx storage.
-
#### [test_framework/key.py](test_framework/key.py)
Wrapper around OpenSSL EC_Key (originally from python-bitcoinlib)
diff --git a/test/functional/feature_bip9_softforks.py b/test/functional/feature_bip9_softforks.py
deleted file mode 100755
index ac6176e976..0000000000
--- a/test/functional/feature_bip9_softforks.py
+++ /dev/null
@@ -1,283 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (c) 2015-2017 The Bitcoin Core developers
-# Distributed under the MIT software license, see the accompanying
-# file COPYING or http://www.opensource.org/licenses/mit-license.php.
-"""Test BIP 9 soft forks.
-
-Connect to a single node.
-regtest lock-in with 108/144 block signalling
-activation after a further 144 blocks
-mine 2 block and save coinbases for later use
-mine 141 blocks to transition from DEFINED to STARTED
-mine 100 blocks signalling readiness and 44 not in order to fail to change state this period
-mine 108 blocks signalling readiness and 36 blocks not signalling readiness (STARTED->LOCKED_IN)
-mine a further 143 blocks (LOCKED_IN)
-test that enforcement has not triggered (which triggers ACTIVE)
-test that enforcement has triggered
-"""
-from io import BytesIO
-import shutil
-import time
-import itertools
-
-from test_framework.test_framework import ComparisonTestFramework
-from test_framework.util import *
-from test_framework.mininode import CTransaction, network_thread_start
-from test_framework.blocktools import create_coinbase, create_block
-from test_framework.comptool import TestInstance, TestManager
-from test_framework.script import CScript, OP_1NEGATE, OP_CHECKSEQUENCEVERIFY, OP_DROP
-
-class BIP9SoftForksTest(ComparisonTestFramework):
- def set_test_params(self):
- self.num_nodes = 1
- self.extra_args = [['-whitelist=127.0.0.1']]
- self.setup_clean_chain = True
-
- def run_test(self):
- self.test = TestManager(self, self.options.tmpdir)
- self.test.add_all_connections(self.nodes)
- network_thread_start()
- self.test.run()
-
- def create_transaction(self, node, coinbase, to_address, amount):
- from_txid = node.getblock(coinbase)['tx'][0]
- inputs = [{ "txid" : from_txid, "vout" : 0}]
- outputs = { to_address : amount }
- rawtx = node.createrawtransaction(inputs, outputs)
- tx = CTransaction()
- f = BytesIO(hex_str_to_bytes(rawtx))
- tx.deserialize(f)
- tx.nVersion = 2
- return tx
-
- def sign_transaction(self, node, tx):
- signresult = node.signrawtransactionwithwallet(bytes_to_hex_str(tx.serialize()))
- tx = CTransaction()
- f = BytesIO(hex_str_to_bytes(signresult['hex']))
- tx.deserialize(f)
- return tx
-
- def generate_blocks(self, number, version, test_blocks = []):
- for i in range(number):
- block = create_block(self.tip, create_coinbase(self.height), self.last_block_time + 1)
- block.nVersion = version
- block.rehash()
- block.solve()
- test_blocks.append([block, True])
- self.last_block_time += 1
- self.tip = block.sha256
- self.height += 1
- return test_blocks
-
- def get_bip9_status(self, key):
- info = self.nodes[0].getblockchaininfo()
- return info['bip9_softforks'][key]
-
- def test_BIP(self, bipName, activated_version, invalidate, invalidatePostSignature, bitno):
- assert_equal(self.get_bip9_status(bipName)['status'], 'defined')
- assert_equal(self.get_bip9_status(bipName)['since'], 0)
-
- # generate some coins for later
- self.coinbase_blocks = self.nodes[0].generate(2)
- self.height = 3 # height of the next block to build
- self.tip = int("0x" + self.nodes[0].getbestblockhash(), 0)
- self.nodeaddress = self.nodes[0].getnewaddress()
- self.last_block_time = int(time.time())
-
- assert_equal(self.get_bip9_status(bipName)['status'], 'defined')
- assert_equal(self.get_bip9_status(bipName)['since'], 0)
- tmpl = self.nodes[0].getblocktemplate({})
- assert(bipName not in tmpl['rules'])
- assert(bipName not in tmpl['vbavailable'])
- assert_equal(tmpl['vbrequired'], 0)
- assert_equal(tmpl['version'], 0x20000000)
-
- # Test 1
- # Advance from DEFINED to STARTED
- test_blocks = self.generate_blocks(141, 4)
- yield TestInstance(test_blocks, sync_every_block=False)
-
- assert_equal(self.get_bip9_status(bipName)['status'], 'started')
- assert_equal(self.get_bip9_status(bipName)['since'], 144)
- assert_equal(self.get_bip9_status(bipName)['statistics']['elapsed'], 0)
- assert_equal(self.get_bip9_status(bipName)['statistics']['count'], 0)
- tmpl = self.nodes[0].getblocktemplate({})
- assert(bipName not in tmpl['rules'])
- assert_equal(tmpl['vbavailable'][bipName], bitno)
- assert_equal(tmpl['vbrequired'], 0)
- assert(tmpl['version'] & activated_version)
-
- # Test 1-A
- # check stats after max number of "signalling not" blocks such that LOCKED_IN still possible this period
- test_blocks = self.generate_blocks(36, 4, test_blocks) # 0x00000004 (signalling not)
- test_blocks = self.generate_blocks(10, activated_version) # 0x20000001 (signalling ready)
- yield TestInstance(test_blocks, sync_every_block=False)
-
- assert_equal(self.get_bip9_status(bipName)['statistics']['elapsed'], 46)
- assert_equal(self.get_bip9_status(bipName)['statistics']['count'], 10)
- assert_equal(self.get_bip9_status(bipName)['statistics']['possible'], True)
-
- # Test 1-B
- # check stats after one additional "signalling not" block -- LOCKED_IN no longer possible this period
- test_blocks = self.generate_blocks(1, 4, test_blocks) # 0x00000004 (signalling not)
- yield TestInstance(test_blocks, sync_every_block=False)
-
- assert_equal(self.get_bip9_status(bipName)['statistics']['elapsed'], 47)
- assert_equal(self.get_bip9_status(bipName)['statistics']['count'], 10)
- assert_equal(self.get_bip9_status(bipName)['statistics']['possible'], False)
-
- # Test 1-C
- # finish period with "ready" blocks, but soft fork will still fail to advance to LOCKED_IN
- test_blocks = self.generate_blocks(97, activated_version) # 0x20000001 (signalling ready)
- yield TestInstance(test_blocks, sync_every_block=False)
-
- assert_equal(self.get_bip9_status(bipName)['statistics']['elapsed'], 0)
- assert_equal(self.get_bip9_status(bipName)['statistics']['count'], 0)
- assert_equal(self.get_bip9_status(bipName)['statistics']['possible'], True)
- assert_equal(self.get_bip9_status(bipName)['status'], 'started')
-
- # Test 2
- # Fail to achieve LOCKED_IN 100 out of 144 signal bit 1
- # using a variety of bits to simulate multiple parallel softforks
- test_blocks = self.generate_blocks(50, activated_version) # 0x20000001 (signalling ready)
- test_blocks = self.generate_blocks(20, 4, test_blocks) # 0x00000004 (signalling not)
- test_blocks = self.generate_blocks(50, activated_version, test_blocks) # 0x20000101 (signalling ready)
- test_blocks = self.generate_blocks(24, 4, test_blocks) # 0x20010000 (signalling not)
- yield TestInstance(test_blocks, sync_every_block=False)
-
- assert_equal(self.get_bip9_status(bipName)['status'], 'started')
- assert_equal(self.get_bip9_status(bipName)['since'], 144)
- assert_equal(self.get_bip9_status(bipName)['statistics']['elapsed'], 0)
- assert_equal(self.get_bip9_status(bipName)['statistics']['count'], 0)
- tmpl = self.nodes[0].getblocktemplate({})
- assert(bipName not in tmpl['rules'])
- assert_equal(tmpl['vbavailable'][bipName], bitno)
- assert_equal(tmpl['vbrequired'], 0)
- assert(tmpl['version'] & activated_version)
-
- # Test 3
- # 108 out of 144 signal bit 1 to achieve LOCKED_IN
- # using a variety of bits to simulate multiple parallel softforks
- test_blocks = self.generate_blocks(57, activated_version) # 0x20000001 (signalling ready)
- test_blocks = self.generate_blocks(26, 4, test_blocks) # 0x00000004 (signalling not)
- test_blocks = self.generate_blocks(50, activated_version, test_blocks) # 0x20000101 (signalling ready)
- test_blocks = self.generate_blocks(10, 4, test_blocks) # 0x20010000 (signalling not)
- yield TestInstance(test_blocks, sync_every_block=False)
-
- # check counting stats and "possible" flag before last block of this period achieves LOCKED_IN...
- assert_equal(self.get_bip9_status(bipName)['statistics']['elapsed'], 143)
- assert_equal(self.get_bip9_status(bipName)['statistics']['count'], 107)
- assert_equal(self.get_bip9_status(bipName)['statistics']['possible'], True)
- assert_equal(self.get_bip9_status(bipName)['status'], 'started')
-
- # ...continue with Test 3
- test_blocks = self.generate_blocks(1, activated_version) # 0x20000001 (signalling ready)
- yield TestInstance(test_blocks, sync_every_block=False)
-
- assert_equal(self.get_bip9_status(bipName)['status'], 'locked_in')
- assert_equal(self.get_bip9_status(bipName)['since'], 576)
- tmpl = self.nodes[0].getblocktemplate({})
- assert(bipName not in tmpl['rules'])
-
- # Test 4
- # 143 more version 536870913 blocks (waiting period-1)
- test_blocks = self.generate_blocks(143, 4)
- yield TestInstance(test_blocks, sync_every_block=False)
-
- assert_equal(self.get_bip9_status(bipName)['status'], 'locked_in')
- assert_equal(self.get_bip9_status(bipName)['since'], 576)
- tmpl = self.nodes[0].getblocktemplate({})
- assert(bipName not in tmpl['rules'])
-
- # Test 5
- # Check that the new rule is enforced
- spendtx = self.create_transaction(self.nodes[0],
- self.coinbase_blocks[0], self.nodeaddress, 1.0)
- invalidate(spendtx)
- spendtx = self.sign_transaction(self.nodes[0], spendtx)
- spendtx.rehash()
- invalidatePostSignature(spendtx)
- spendtx.rehash()
- block = create_block(self.tip, create_coinbase(self.height), self.last_block_time + 1)
- block.nVersion = activated_version
- block.vtx.append(spendtx)
- block.hashMerkleRoot = block.calc_merkle_root()
- block.rehash()
- block.solve()
-
- self.last_block_time += 1
- self.tip = block.sha256
- self.height += 1
- yield TestInstance([[block, True]])
-
- assert_equal(self.get_bip9_status(bipName)['status'], 'active')
- assert_equal(self.get_bip9_status(bipName)['since'], 720)
- tmpl = self.nodes[0].getblocktemplate({})
- assert(bipName in tmpl['rules'])
- assert(bipName not in tmpl['vbavailable'])
- assert_equal(tmpl['vbrequired'], 0)
- assert(not (tmpl['version'] & (1 << bitno)))
-
- # Test 6
- # Check that the new sequence lock rules are enforced
- spendtx = self.create_transaction(self.nodes[0],
- self.coinbase_blocks[1], self.nodeaddress, 1.0)
- invalidate(spendtx)
- spendtx = self.sign_transaction(self.nodes[0], spendtx)
- spendtx.rehash()
- invalidatePostSignature(spendtx)
- spendtx.rehash()
-
- block = create_block(self.tip, create_coinbase(self.height), self.last_block_time + 1)
- block.nVersion = 5
- block.vtx.append(spendtx)
- block.hashMerkleRoot = block.calc_merkle_root()
- block.rehash()
- block.solve()
- self.last_block_time += 1
- yield TestInstance([[block, False]])
-
- # Restart all
- self.test.clear_all_connections()
- self.stop_nodes()
- self.nodes = []
- shutil.rmtree(get_datadir_path(self.options.tmpdir, 0))
- self.setup_chain()
- self.setup_network()
- self.test.add_all_connections(self.nodes)
- network_thread_start()
- self.test.p2p_connections[0].wait_for_verack()
-
- def get_tests(self):
- for test in itertools.chain(
- self.test_BIP('csv', 0x20000001, self.sequence_lock_invalidate, self.donothing, 0),
- self.test_BIP('csv', 0x20000001, self.mtp_invalidate, self.donothing, 0),
- self.test_BIP('csv', 0x20000001, self.donothing, self.csv_invalidate, 0)
- ):
- yield test
-
- def donothing(self, tx):
- return
-
- def csv_invalidate(self, tx):
- """Modify the signature in vin 0 of the tx to fail CSV
- Prepends -1 CSV DROP in the scriptSig itself.
- """
- tx.vin[0].scriptSig = CScript([OP_1NEGATE, OP_CHECKSEQUENCEVERIFY, OP_DROP] +
- list(CScript(tx.vin[0].scriptSig)))
-
- def sequence_lock_invalidate(self, tx):
- """Modify the nSequence to make it fails once sequence lock rule is
- activated (high timespan).
- """
- tx.vin[0].nSequence = 0x00FFFFFF
- tx.nLockTime = 0
-
- def mtp_invalidate(self, tx):
- """Modify the nLockTime to make it fails once MTP rule is activated."""
- # Disable Sequence lock, Activate nLockTime
- tx.vin[0].nSequence = 0x90FFFFFF
- tx.nLockTime = self.last_block_time
-
-if __name__ == '__main__':
- BIP9SoftForksTest().main()
diff --git a/test/functional/feature_block.py b/test/functional/feature_block.py
index fe9bbda14b..181c7f3369 100755
--- a/test/functional/feature_block.py
+++ b/test/functional/feature_block.py
@@ -2,36 +2,59 @@
# Copyright (c) 2015-2017 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
-"""Test block processing.
-
-This reimplements tests from the bitcoinj/FullBlockTestGenerator used
-by the pull-tester.
-
-We use the testing framework in which we expect a particular answer from
-each test.
-"""
-
-from test_framework.test_framework import ComparisonTestFramework
-from test_framework.util import *
-from test_framework.comptool import TestManager, TestInstance, RejectResult
-from test_framework.blocktools import *
+"""Test block processing."""
+import copy
+import struct
import time
+
+from test_framework.blocktools import create_block, create_coinbase, create_transaction, get_legacy_sigopcount_block
from test_framework.key import CECKey
-from test_framework.script import *
-from test_framework.mininode import network_thread_start
-import struct
+from test_framework.messages import (
+ CBlock,
+ COIN,
+ COutPoint,
+ CTransaction,
+ CTxIn,
+ CTxOut,
+ MAX_BLOCK_BASE_SIZE,
+ uint256_from_compact,
+ uint256_from_str,
+)
+from test_framework.mininode import P2PDataStore, network_thread_start, network_thread_join
+from test_framework.script import (
+ CScript,
+ MAX_SCRIPT_ELEMENT_SIZE,
+ OP_2DUP,
+ OP_CHECKMULTISIG,
+ OP_CHECKMULTISIGVERIFY,
+ OP_CHECKSIG,
+ OP_CHECKSIGVERIFY,
+ OP_ELSE,
+ OP_ENDIF,
+ OP_EQUAL,
+ OP_FALSE,
+ OP_HASH160,
+ OP_IF,
+ OP_INVALIDOPCODE,
+ OP_RETURN,
+ OP_TRUE,
+ SIGHASH_ALL,
+ SignatureHash,
+ hash160,
+)
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_equal
+
+MAX_BLOCK_SIGOPS = 20000
class PreviousSpendableOutput():
- def __init__(self, tx = CTransaction(), n = -1):
+ def __init__(self, tx=CTransaction(), n=-1):
self.tx = tx
self.n = n # the output we're spending
# Use this class for tests that require behavior other than normal "mininode" behavior.
# For now, it is used to serialize a bloated varint (b64).
class CBrokenBlock(CBlock):
- def __init__(self, header=None):
- super(CBrokenBlock, self).__init__(header)
-
def initialize(self, base_block):
self.vtx = copy.deepcopy(base_block.vtx)
self.hashMerkleRoot = self.calc_merkle_root()
@@ -48,381 +71,272 @@ class CBrokenBlock(CBlock):
return r
def normal_serialize(self):
- r = b""
- r += super(CBrokenBlock, self).serialize()
- return r
+ return super().serialize()
-class FullBlockTest(ComparisonTestFramework):
- # Can either run this test as 1 node with expected answers, or two and compare them.
- # Change the "outcome" variable from each TestInstance object to only do the comparison.
+class FullBlockTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.setup_clean_chain = True
+ self.extra_args = [[]]
+
+ def run_test(self):
+ node = self.nodes[0] # convenience reference to the node
+
+ # reconnect_p2p() expects the network thread to be running
+ network_thread_start()
+
+ self.reconnect_p2p()
+
self.block_heights = {}
self.coinbase_key = CECKey()
self.coinbase_key.set_secretbytes(b"horsebattery")
self.coinbase_pubkey = self.coinbase_key.get_pubkey()
self.tip = None
self.blocks = {}
-
- def add_options(self, parser):
- super().add_options(parser)
- parser.add_option("--runbarelyexpensive", dest="runbarelyexpensive", default=True)
-
- def run_test(self):
- self.test = TestManager(self, self.options.tmpdir)
- self.test.add_all_connections(self.nodes)
- network_thread_start()
- self.test.run()
-
- def add_transactions_to_block(self, block, tx_list):
- [ tx.rehash() for tx in tx_list ]
- block.vtx.extend(tx_list)
-
- # this is a little handier to use than the version in blocktools.py
- def create_tx(self, spend_tx, n, value, script=CScript([OP_TRUE])):
- tx = create_transaction(spend_tx, n, b"", value, script)
- return tx
-
- # sign a transaction, using the key we know about
- # this signs input 0 in tx, which is assumed to be spending output n in spend_tx
- def sign_tx(self, tx, spend_tx, n):
- scriptPubKey = bytearray(spend_tx.vout[n].scriptPubKey)
- if (scriptPubKey[0] == OP_TRUE): # an anyone-can-spend
- tx.vin[0].scriptSig = CScript()
- return
- (sighash, err) = SignatureHash(spend_tx.vout[n].scriptPubKey, tx, 0, SIGHASH_ALL)
- tx.vin[0].scriptSig = CScript([self.coinbase_key.sign(sighash) + bytes(bytearray([SIGHASH_ALL]))])
-
- def create_and_sign_transaction(self, spend_tx, n, value, script=CScript([OP_TRUE])):
- tx = self.create_tx(spend_tx, n, value, script)
- self.sign_tx(tx, spend_tx, n)
- tx.rehash()
- return tx
-
- def next_block(self, number, spend=None, additional_coinbase_value=0, script=CScript([OP_TRUE]), solve=True):
- if self.tip == None:
- base_block_hash = self.genesis_hash
- block_time = int(time.time())+1
- else:
- base_block_hash = self.tip.sha256
- block_time = self.tip.nTime + 1
- # First create the coinbase
- height = self.block_heights[base_block_hash] + 1
- coinbase = create_coinbase(height, self.coinbase_pubkey)
- coinbase.vout[0].nValue += additional_coinbase_value
- coinbase.rehash()
- if spend == None:
- block = create_block(base_block_hash, coinbase, block_time)
- else:
- coinbase.vout[0].nValue += spend.tx.vout[spend.n].nValue - 1 # all but one satoshi to fees
- coinbase.rehash()
- block = create_block(base_block_hash, coinbase, block_time)
- tx = create_transaction(spend.tx, spend.n, b"", 1, script) # spend 1 satoshi
- self.sign_tx(tx, spend.tx, spend.n)
- self.add_transactions_to_block(block, [tx])
- block.hashMerkleRoot = block.calc_merkle_root()
- if solve:
- block.solve()
- self.tip = block
- self.block_heights[block.sha256] = height
- assert number not in self.blocks
- self.blocks[number] = block
- return block
-
- def get_tests(self):
self.genesis_hash = int(self.nodes[0].getbestblockhash(), 16)
self.block_heights[self.genesis_hash] = 0
- spendable_outputs = []
-
- # save the current tip so it can be spent by a later block
- def save_spendable_output():
- spendable_outputs.append(self.tip)
-
- # get an output that we previously marked as spendable
- def get_spendable_output():
- return PreviousSpendableOutput(spendable_outputs.pop(0).vtx[0], 0)
-
- # returns a test case that asserts that the current tip was accepted
- def accepted():
- return TestInstance([[self.tip, True]])
-
- # returns a test case that asserts that the current tip was rejected
- def rejected(reject = None):
- if reject is None:
- return TestInstance([[self.tip, False]])
- else:
- return TestInstance([[self.tip, reject]])
-
- # move the tip back to a previous block
- def tip(number):
- self.tip = self.blocks[number]
-
- # adds transactions to the block and updates state
- def update_block(block_number, new_transactions):
- block = self.blocks[block_number]
- self.add_transactions_to_block(block, new_transactions)
- old_sha256 = block.sha256
- block.hashMerkleRoot = block.calc_merkle_root()
- block.solve()
- # Update the internal state just like in next_block
- self.tip = block
- if block.sha256 != old_sha256:
- self.block_heights[block.sha256] = self.block_heights[old_sha256]
- del self.block_heights[old_sha256]
- self.blocks[block_number] = block
- return block
-
- # shorthand for functions
- block = self.next_block
- create_tx = self.create_tx
- create_and_sign_tx = self.create_and_sign_transaction
-
- # these must be updated if consensus changes
- MAX_BLOCK_SIGOPS = 20000
-
+ self.spendable_outputs = []
# Create a new block
- block(0)
- save_spendable_output()
- yield accepted()
-
+ b0 = self.next_block(0)
+ self.save_spendable_output()
+ self.sync_blocks([b0])
- # Now we need that block to mature so we can spend the coinbase.
- test = TestInstance(sync_every_block=False)
+ # Allow the block to mature
+ blocks = []
for i in range(99):
- block(5000 + i)
- test.blocks_and_transactions.append([self.tip, True])
- save_spendable_output()
- yield test
+ blocks.append(self.next_block(5000 + i))
+ self.save_spendable_output()
+ self.sync_blocks(blocks)
# collect spendable outputs now to avoid cluttering the code later on
out = []
for i in range(33):
- out.append(get_spendable_output())
+ out.append(self.get_spendable_output())
# Start by building a couple of blocks on top (which output is spent is
# in parentheses):
# genesis -> b1 (0) -> b2 (1)
- block(1, spend=out[0])
- save_spendable_output()
- yield accepted()
+ b1 = self.next_block(1, spend=out[0])
+ self.save_spendable_output()
- block(2, spend=out[1])
- yield accepted()
- save_spendable_output()
+ b2 = self.next_block(2, spend=out[1])
+ self.save_spendable_output()
- # so fork like this:
+ self.sync_blocks([b1, b2])
+
+ # Fork like this:
#
# genesis -> b1 (0) -> b2 (1)
# \-> b3 (1)
#
# Nothing should happen at this point. We saw b2 first so it takes priority.
- tip(1)
- b3 = block(3, spend=out[1])
+ self.log.info("Don't reorg to a chain of the same length")
+ self.move_tip(1)
+ b3 = self.next_block(3, spend=out[1])
txout_b3 = PreviousSpendableOutput(b3.vtx[1], 0)
- yield rejected()
-
+ self.sync_blocks([b3], False)
# Now we add another block to make the alternative chain longer.
#
# genesis -> b1 (0) -> b2 (1)
# \-> b3 (1) -> b4 (2)
- block(4, spend=out[2])
- yield accepted()
-
+ self.log.info("Reorg to a longer chain")
+ b4 = self.next_block(4, spend=out[2])
+ self.sync_blocks([b4])
# ... and back to the first chain.
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
# \-> b3 (1) -> b4 (2)
- tip(2)
- block(5, spend=out[2])
- save_spendable_output()
- yield rejected()
+ self.move_tip(2)
+ b5 = self.next_block(5, spend=out[2])
+ self.save_spendable_output()
+ self.sync_blocks([b5], False)
- block(6, spend=out[3])
- yield accepted()
+ self.log.info("Reorg back to the original chain")
+ b6 = self.next_block(6, spend=out[3])
+ self.sync_blocks([b6], True)
# Try to create a fork that double-spends
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
# \-> b7 (2) -> b8 (4)
# \-> b3 (1) -> b4 (2)
- tip(5)
- block(7, spend=out[2])
- yield rejected()
+ self.log.info("Reject a chain with a double spend, even if it is longer")
+ self.move_tip(5)
+ b7 = self.next_block(7, spend=out[2])
+ self.sync_blocks([b7], False)
- block(8, spend=out[4])
- yield rejected()
+ b8 = self.next_block(8, spend=out[4])
+ self.sync_blocks([b8], False, reconnect=True)
# Try to create a block that has too much fee
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
# \-> b9 (4)
# \-> b3 (1) -> b4 (2)
- tip(6)
- block(9, spend=out[4], additional_coinbase_value=1)
- yield rejected(RejectResult(16, b'bad-cb-amount'))
+ self.log.info("Reject a block where the miner creates too much coinbase reward")
+ self.move_tip(6)
+ b9 = self.next_block(9, spend=out[4], additional_coinbase_value=1)
+ self.sync_blocks([b9], False, 16, b'bad-cb-amount', reconnect=True)
# Create a fork that ends in a block with too much fee (the one that causes the reorg)
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
# \-> b10 (3) -> b11 (4)
# \-> b3 (1) -> b4 (2)
- tip(5)
- block(10, spend=out[3])
- yield rejected()
-
- block(11, spend=out[4], additional_coinbase_value=1)
- yield rejected(RejectResult(16, b'bad-cb-amount'))
+ self.log.info("Reject a chain where the miner creates too much coinbase reward, even if the chain is longer")
+ self.move_tip(5)
+ b10 = self.next_block(10, spend=out[3])
+ self.sync_blocks([b10], False)
+ b11 = self.next_block(11, spend=out[4], additional_coinbase_value=1)
+ self.sync_blocks([b11], False, 16, b'bad-cb-amount', reconnect=True)
# Try again, but with a valid fork first
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
# \-> b12 (3) -> b13 (4) -> b14 (5)
- # (b12 added last)
# \-> b3 (1) -> b4 (2)
- tip(5)
- b12 = block(12, spend=out[3])
- save_spendable_output()
- b13 = block(13, spend=out[4])
- # Deliver the block header for b12, and the block b13.
- # b13 should be accepted but the tip won't advance until b12 is delivered.
- yield TestInstance([[CBlockHeader(b12), None], [b13, False]])
-
- save_spendable_output()
- # b14 is invalid, but the node won't know that until it tries to connect
- # Tip still can't advance because b12 is missing
- block(14, spend=out[5], additional_coinbase_value=1)
- yield rejected()
-
- yield TestInstance([[b12, True, b13.sha256]]) # New tip should be b13.
+ self.log.info("Reject a chain where the miner creates too much coinbase reward, even if the chain is longer (on a forked chain)")
+ self.move_tip(5)
+ b12 = self.next_block(12, spend=out[3])
+ self.save_spendable_output()
+ b13 = self.next_block(13, spend=out[4])
+ self.save_spendable_output()
+ b14 = self.next_block(14, spend=out[5], additional_coinbase_value=1)
+ self.sync_blocks([b12, b13, b14], False, 16, b'bad-cb-amount', reconnect=True)
+
+ # New tip should be b13.
+ assert_equal(node.getbestblockhash(), b13.hash)
# Add a block with MAX_BLOCK_SIGOPS and one with one more sigop
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
# \-> b12 (3) -> b13 (4) -> b15 (5) -> b16 (6)
# \-> b3 (1) -> b4 (2)
-
- # Test that a block with a lot of checksigs is okay
+ self.log.info("Accept a block with lots of checksigs")
lots_of_checksigs = CScript([OP_CHECKSIG] * (MAX_BLOCK_SIGOPS - 1))
- tip(13)
- block(15, spend=out[5], script=lots_of_checksigs)
- yield accepted()
- save_spendable_output()
+ self.move_tip(13)
+ b15 = self.next_block(15, spend=out[5], script=lots_of_checksigs)
+ self.save_spendable_output()
+ self.sync_blocks([b15], True)
-
- # Test that a block with too many checksigs is rejected
+ self.log.info("Reject a block with too many checksigs")
too_many_checksigs = CScript([OP_CHECKSIG] * (MAX_BLOCK_SIGOPS))
- block(16, spend=out[6], script=too_many_checksigs)
- yield rejected(RejectResult(16, b'bad-blk-sigops'))
-
+ b16 = self.next_block(16, spend=out[6], script=too_many_checksigs)
+ self.sync_blocks([b16], False, 16, b'bad-blk-sigops', reconnect=True)
# Attempt to spend a transaction created on a different fork
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
# \-> b12 (3) -> b13 (4) -> b15 (5) -> b17 (b3.vtx[1])
# \-> b3 (1) -> b4 (2)
- tip(15)
- block(17, spend=txout_b3)
- yield rejected(RejectResult(16, b'bad-txns-inputs-missingorspent'))
+ self.log.info("Reject a block with a spend from a re-org'ed out tx")
+ self.move_tip(15)
+ b17 = self.next_block(17, spend=txout_b3)
+ self.sync_blocks([b17], False, 16, b'bad-txns-inputs-missingorspent', reconnect=True)
# Attempt to spend a transaction created on a different fork (on a fork this time)
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
# \-> b12 (3) -> b13 (4) -> b15 (5)
# \-> b18 (b3.vtx[1]) -> b19 (6)
# \-> b3 (1) -> b4 (2)
- tip(13)
- block(18, spend=txout_b3)
- yield rejected()
+ self.log.info("Reject a block with a spend from a re-org'ed out tx (on a forked chain)")
+ self.move_tip(13)
+ b18 = self.next_block(18, spend=txout_b3)
+ self.sync_blocks([b18], False)
- block(19, spend=out[6])
- yield rejected()
+ b19 = self.next_block(19, spend=out[6])
+ self.sync_blocks([b19], False, 16, b'bad-txns-inputs-missingorspent', reconnect=True)
# Attempt to spend a coinbase at depth too low
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
# \-> b12 (3) -> b13 (4) -> b15 (5) -> b20 (7)
# \-> b3 (1) -> b4 (2)
- tip(15)
- block(20, spend=out[7])
- yield rejected(RejectResult(16, b'bad-txns-premature-spend-of-coinbase'))
+ self.log.info("Reject a block spending an immature coinbase.")
+ self.move_tip(15)
+ b20 = self.next_block(20, spend=out[7])
+ self.sync_blocks([b20], False, 16, b'bad-txns-premature-spend-of-coinbase')
# Attempt to spend a coinbase at depth too low (on a fork this time)
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
# \-> b12 (3) -> b13 (4) -> b15 (5)
# \-> b21 (6) -> b22 (5)
# \-> b3 (1) -> b4 (2)
- tip(13)
- block(21, spend=out[6])
- yield rejected()
+ self.log.info("Reject a block spending an immature coinbase (on a forked chain)")
+ self.move_tip(13)
+ b21 = self.next_block(21, spend=out[6])
+ self.sync_blocks([b21], False)
- block(22, spend=out[5])
- yield rejected()
+ b22 = self.next_block(22, spend=out[5])
+ self.sync_blocks([b22], False, 16, b'bad-txns-premature-spend-of-coinbase')
# Create a block on either side of MAX_BLOCK_BASE_SIZE and make sure its accepted/rejected
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
# \-> b12 (3) -> b13 (4) -> b15 (5) -> b23 (6)
# \-> b24 (6) -> b25 (7)
# \-> b3 (1) -> b4 (2)
- tip(15)
- b23 = block(23, spend=out[6])
+ self.log.info("Accept a block of size MAX_BLOCK_BASE_SIZE")
+ self.move_tip(15)
+ b23 = self.next_block(23, spend=out[6])
tx = CTransaction()
script_length = MAX_BLOCK_BASE_SIZE - len(b23.serialize()) - 69
script_output = CScript([b'\x00' * script_length])
tx.vout.append(CTxOut(0, script_output))
tx.vin.append(CTxIn(COutPoint(b23.vtx[1].sha256, 0)))
- b23 = update_block(23, [tx])
+ b23 = self.update_block(23, [tx])
# Make sure the math above worked out to produce a max-sized block
assert_equal(len(b23.serialize()), MAX_BLOCK_BASE_SIZE)
- yield accepted()
- save_spendable_output()
+ self.sync_blocks([b23], True)
+ self.save_spendable_output()
- # Make the next block one byte bigger and check that it fails
- tip(15)
- b24 = block(24, spend=out[6])
+ self.log.info("Reject a block of size MAX_BLOCK_BASE_SIZE + 1")
+ self.move_tip(15)
+ b24 = self.next_block(24, spend=out[6])
script_length = MAX_BLOCK_BASE_SIZE - len(b24.serialize()) - 69
- script_output = CScript([b'\x00' * (script_length+1)])
+ script_output = CScript([b'\x00' * (script_length + 1)])
tx.vout = [CTxOut(0, script_output)]
- b24 = update_block(24, [tx])
- assert_equal(len(b24.serialize()), MAX_BLOCK_BASE_SIZE+1)
- yield rejected(RejectResult(16, b'bad-blk-length'))
+ b24 = self.update_block(24, [tx])
+ assert_equal(len(b24.serialize()), MAX_BLOCK_BASE_SIZE + 1)
+ self.sync_blocks([b24], False, 16, b'bad-blk-length', reconnect=True)
- block(25, spend=out[7])
- yield rejected()
+ b25 = self.next_block(25, spend=out[7])
+ self.sync_blocks([b25], False)
# Create blocks with a coinbase input script size out of range
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
# \-> b12 (3) -> b13 (4) -> b15 (5) -> b23 (6) -> b30 (7)
# \-> ... (6) -> ... (7)
# \-> b3 (1) -> b4 (2)
- tip(15)
- b26 = block(26, spend=out[6])
+ self.log.info("Reject a block with coinbase input script size out of range")
+ self.move_tip(15)
+ b26 = self.next_block(26, spend=out[6])
b26.vtx[0].vin[0].scriptSig = b'\x00'
b26.vtx[0].rehash()
# update_block causes the merkle root to get updated, even with no new
# transactions, and updates the required state.
- b26 = update_block(26, [])
- yield rejected(RejectResult(16, b'bad-cb-length'))
+ b26 = self.update_block(26, [])
+ self.sync_blocks([b26], False, 16, b'bad-cb-length', reconnect=True)
# Extend the b26 chain to make sure bitcoind isn't accepting b26
- block(27, spend=out[7])
- yield rejected(False)
+ b27 = self.next_block(27, spend=out[7])
+ self.sync_blocks([b27], False)
# Now try a too-large-coinbase script
- tip(15)
- b28 = block(28, spend=out[6])
+ self.move_tip(15)
+ b28 = self.next_block(28, spend=out[6])
b28.vtx[0].vin[0].scriptSig = b'\x00' * 101
b28.vtx[0].rehash()
- b28 = update_block(28, [])
- yield rejected(RejectResult(16, b'bad-cb-length'))
+ b28 = self.update_block(28, [])
+ self.sync_blocks([b28], False, 16, b'bad-cb-length', reconnect=True)
# Extend the b28 chain to make sure bitcoind isn't accepting b28
- block(29, spend=out[7])
- yield rejected(False)
+ b29 = self.next_block(29, spend=out[7])
+ self.sync_blocks([b29], False)
# b30 has a max-sized coinbase scriptSig.
- tip(23)
- b30 = block(30)
+ self.move_tip(23)
+ b30 = self.next_block(30)
b30.vtx[0].vin[0].scriptSig = b'\x00' * 100
b30.vtx[0].rehash()
- b30 = update_block(30, [])
- yield accepted()
- save_spendable_output()
+ b30 = self.update_block(30, [])
+ self.sync_blocks([b30], True)
+ self.save_spendable_output()
# b31 - b35 - check sigops of OP_CHECKMULTISIG / OP_CHECKMULTISIGVERIFY / OP_CHECKSIGVERIFY
#
@@ -433,42 +347,45 @@ class FullBlockTest(ComparisonTestFramework):
#
# MULTISIG: each op code counts as 20 sigops. To create the edge case, pack another 19 sigops at the end.
- lots_of_multisigs = CScript([OP_CHECKMULTISIG] * ((MAX_BLOCK_SIGOPS-1) // 20) + [OP_CHECKSIG] * 19)
- b31 = block(31, spend=out[8], script=lots_of_multisigs)
+ self.log.info("Accept a block with the max number of OP_CHECKMULTISIG sigops")
+ lots_of_multisigs = CScript([OP_CHECKMULTISIG] * ((MAX_BLOCK_SIGOPS - 1) // 20) + [OP_CHECKSIG] * 19)
+ b31 = self.next_block(31, spend=out[8], script=lots_of_multisigs)
assert_equal(get_legacy_sigopcount_block(b31), MAX_BLOCK_SIGOPS)
- yield accepted()
- save_spendable_output()
+ self.sync_blocks([b31], True)
+ self.save_spendable_output()
# this goes over the limit because the coinbase has one sigop
+ self.log.info("Reject a block with too many OP_CHECKMULTISIG sigops")
too_many_multisigs = CScript([OP_CHECKMULTISIG] * (MAX_BLOCK_SIGOPS // 20))
- b32 = block(32, spend=out[9], script=too_many_multisigs)
+ b32 = self.next_block(32, spend=out[9], script=too_many_multisigs)
assert_equal(get_legacy_sigopcount_block(b32), MAX_BLOCK_SIGOPS + 1)
- yield rejected(RejectResult(16, b'bad-blk-sigops'))
-
+ self.sync_blocks([b32], False, 16, b'bad-blk-sigops', reconnect=True)
# CHECKMULTISIGVERIFY
- tip(31)
- lots_of_multisigs = CScript([OP_CHECKMULTISIGVERIFY] * ((MAX_BLOCK_SIGOPS-1) // 20) + [OP_CHECKSIG] * 19)
- block(33, spend=out[9], script=lots_of_multisigs)
- yield accepted()
- save_spendable_output()
-
+ self.log.info("Accept a block with the max number of OP_CHECKMULTISIGVERIFY sigops")
+ self.move_tip(31)
+ lots_of_multisigs = CScript([OP_CHECKMULTISIGVERIFY] * ((MAX_BLOCK_SIGOPS - 1) // 20) + [OP_CHECKSIG] * 19)
+ b33 = self.next_block(33, spend=out[9], script=lots_of_multisigs)
+ self.sync_blocks([b33], True)
+ self.save_spendable_output()
+
+ self.log.info("Reject a block with too many OP_CHECKMULTISIGVERIFY sigops")
too_many_multisigs = CScript([OP_CHECKMULTISIGVERIFY] * (MAX_BLOCK_SIGOPS // 20))
- block(34, spend=out[10], script=too_many_multisigs)
- yield rejected(RejectResult(16, b'bad-blk-sigops'))
-
+ b34 = self.next_block(34, spend=out[10], script=too_many_multisigs)
+ self.sync_blocks([b34], False, 16, b'bad-blk-sigops', reconnect=True)
# CHECKSIGVERIFY
- tip(33)
+ self.log.info("Accept a block with the max number of OP_CHECKSIGVERIFY sigops")
+ self.move_tip(33)
lots_of_checksigs = CScript([OP_CHECKSIGVERIFY] * (MAX_BLOCK_SIGOPS - 1))
- b35 = block(35, spend=out[10], script=lots_of_checksigs)
- yield accepted()
- save_spendable_output()
+ b35 = self.next_block(35, spend=out[10], script=lots_of_checksigs)
+ self.sync_blocks([b35], True)
+ self.save_spendable_output()
+ self.log.info("Reject a block with too many OP_CHECKSIGVERIFY sigops")
too_many_checksigs = CScript([OP_CHECKSIGVERIFY] * (MAX_BLOCK_SIGOPS))
- block(36, spend=out[11], script=too_many_checksigs)
- yield rejected(RejectResult(16, b'bad-blk-sigops'))
-
+ b36 = self.next_block(36, spend=out[11], script=too_many_checksigs)
+ self.sync_blocks([b36], False, 16, b'bad-blk-sigops', reconnect=True)
# Check spending of a transaction in a block which failed to connect
#
@@ -479,17 +396,18 @@ class FullBlockTest(ComparisonTestFramework):
#
# save 37's spendable output, but then double-spend out11 to invalidate the block
- tip(35)
- b37 = block(37, spend=out[11])
+ self.log.info("Reject a block spending transaction from a block which failed to connect")
+ self.move_tip(35)
+ b37 = self.next_block(37, spend=out[11])
txout_b37 = PreviousSpendableOutput(b37.vtx[1], 0)
- tx = create_and_sign_tx(out[11].tx, out[11].n, 0)
- b37 = update_block(37, [tx])
- yield rejected(RejectResult(16, b'bad-txns-inputs-missingorspent'))
+ tx = self.create_and_sign_transaction(out[11].tx, out[11].n, 0)
+ b37 = self.update_block(37, [tx])
+ self.sync_blocks([b37], False, 16, b'bad-txns-inputs-missingorspent', reconnect=True)
# attempt to spend b37's first non-coinbase tx, at which point b37 was still considered valid
- tip(35)
- block(38, spend=txout_b37)
- yield rejected(RejectResult(16, b'bad-txns-inputs-missingorspent'))
+ self.move_tip(35)
+ b38 = self.next_block(38, spend=txout_b37)
+ self.sync_blocks([b38], False, 16, b'bad-txns-inputs-missingorspent', reconnect=True)
# Check P2SH SigOp counting
#
@@ -502,45 +420,45 @@ class FullBlockTest(ComparisonTestFramework):
# redeem_script = COINBASE_PUBKEY, (OP_2DUP+OP_CHECKSIGVERIFY) * 5, OP_CHECKSIG
# p2sh_script = OP_HASH160, ripemd160(sha256(script)), OP_EQUAL
#
- tip(35)
- b39 = block(39)
+ self.log.info("Check P2SH SIGOPS are correctly counted")
+ self.move_tip(35)
+ b39 = self.next_block(39)
b39_outputs = 0
b39_sigops_per_output = 6
# Build the redeem script, hash it, use hash to create the p2sh script
- redeem_script = CScript([self.coinbase_pubkey] + [OP_2DUP, OP_CHECKSIGVERIFY]*5 + [OP_CHECKSIG])
+ redeem_script = CScript([self.coinbase_pubkey] + [OP_2DUP, OP_CHECKSIGVERIFY] * 5 + [OP_CHECKSIG])
redeem_script_hash = hash160(redeem_script)
p2sh_script = CScript([OP_HASH160, redeem_script_hash, OP_EQUAL])
# Create a transaction that spends one satoshi to the p2sh_script, the rest to OP_TRUE
# This must be signed because it is spending a coinbase
spend = out[11]
- tx = create_tx(spend.tx, spend.n, 1, p2sh_script)
+ tx = self.create_tx(spend.tx, spend.n, 1, p2sh_script)
tx.vout.append(CTxOut(spend.tx.vout[spend.n].nValue - 1, CScript([OP_TRUE])))
self.sign_tx(tx, spend.tx, spend.n)
tx.rehash()
- b39 = update_block(39, [tx])
+ b39 = self.update_block(39, [tx])
b39_outputs += 1
# Until block is full, add tx's with 1 satoshi to p2sh_script, the rest to OP_TRUE
tx_new = None
tx_last = tx
- total_size=len(b39.serialize())
+ total_size = len(b39.serialize())
while(total_size < MAX_BLOCK_BASE_SIZE):
- tx_new = create_tx(tx_last, 1, 1, p2sh_script)
+ tx_new = self.create_tx(tx_last, 1, 1, p2sh_script)
tx_new.vout.append(CTxOut(tx_last.vout[1].nValue - 1, CScript([OP_TRUE])))
tx_new.rehash()
total_size += len(tx_new.serialize())
if total_size >= MAX_BLOCK_BASE_SIZE:
break
- b39.vtx.append(tx_new) # add tx to block
+ b39.vtx.append(tx_new) # add tx to block
tx_last = tx_new
b39_outputs += 1
- b39 = update_block(39, [])
- yield accepted()
- save_spendable_output()
-
+ b39 = self.update_block(39, [])
+ self.sync_blocks([b39], True)
+ self.save_spendable_output()
# Test sigops in P2SH redeem scripts
#
@@ -549,15 +467,16 @@ class FullBlockTest(ComparisonTestFramework):
#
# b41 does the same, less one, so it has the maximum sigops permitted.
#
- tip(39)
- b40 = block(40, spend=out[12])
+ self.log.info("Reject a block with too many P2SH sigops")
+ self.move_tip(39)
+ b40 = self.next_block(40, spend=out[12])
sigops = get_legacy_sigopcount_block(b40)
numTxes = (MAX_BLOCK_SIGOPS - sigops) // b39_sigops_per_output
assert_equal(numTxes <= b39_outputs, True)
lastOutpoint = COutPoint(b40.vtx[1].sha256, 0)
new_txs = []
- for i in range(1, numTxes+1):
+ for i in range(1, numTxes + 1):
tx = CTransaction()
tx.vout.append(CTxOut(1, CScript([OP_TRUE])))
tx.vin.append(CTxIn(lastOutpoint, b''))
@@ -579,35 +498,34 @@ class FullBlockTest(ComparisonTestFramework):
tx.vout.append(CTxOut(1, CScript([OP_CHECKSIG] * b40_sigops_to_fill)))
tx.rehash()
new_txs.append(tx)
- update_block(40, new_txs)
- yield rejected(RejectResult(16, b'bad-blk-sigops'))
+ self.update_block(40, new_txs)
+ self.sync_blocks([b40], False, 16, b'bad-blk-sigops', reconnect=True)
# same as b40, but one less sigop
- tip(39)
- block(41, spend=None)
- update_block(41, b40.vtx[1:-1])
+ self.log.info("Accept a block with the max number of P2SH sigops")
+ self.move_tip(39)
+ b41 = self.next_block(41, spend=None)
+ self.update_block(41, b40.vtx[1:-1])
b41_sigops_to_fill = b40_sigops_to_fill - 1
tx = CTransaction()
tx.vin.append(CTxIn(lastOutpoint, b''))
tx.vout.append(CTxOut(1, CScript([OP_CHECKSIG] * b41_sigops_to_fill)))
tx.rehash()
- update_block(41, [tx])
- yield accepted()
+ self.update_block(41, [tx])
+ self.sync_blocks([b41], True)
# Fork off of b39 to create a constant base again
#
# b23 (6) -> b30 (7) -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13)
# \-> b41 (12)
#
- tip(39)
- block(42, spend=out[12])
- yield rejected()
- save_spendable_output()
-
- block(43, spend=out[13])
- yield accepted()
- save_spendable_output()
+ self.move_tip(39)
+ b42 = self.next_block(42, spend=out[12])
+ self.save_spendable_output()
+ b43 = self.next_block(43, spend=out[13])
+ self.save_spendable_output()
+ self.sync_blocks([b42, b43], True)
# Test a number of really invalid scenarios
#
@@ -616,6 +534,7 @@ class FullBlockTest(ComparisonTestFramework):
# The next few blocks are going to be created "by hand" since they'll do funky things, such as having
# the first transaction be non-coinbase, etc. The purpose of b44 is to make sure this works.
+ self.log.info("Build block 44 manually")
height = self.block_heights[self.tip.sha256] + 1
coinbase = create_coinbase(height, self.coinbase_pubkey)
b44 = CBlock()
@@ -628,10 +547,10 @@ class FullBlockTest(ComparisonTestFramework):
self.tip = b44
self.block_heights[b44.sha256] = height
self.blocks[44] = b44
- yield accepted()
+ self.sync_blocks([b44], True)
- # A block with a non-coinbase as the first tx
- non_coinbase = create_tx(out[15].tx, out[15].n, 1)
+ self.log.info("Reject a block with a non-coinbase as the first tx")
+ non_coinbase = self.create_tx(out[15].tx, out[15].n, 1)
b45 = CBlock()
b45.nTime = self.tip.nTime + 1
b45.hashPrevBlock = self.tip.sha256
@@ -640,104 +559,102 @@ class FullBlockTest(ComparisonTestFramework):
b45.hashMerkleRoot = b45.calc_merkle_root()
b45.calc_sha256()
b45.solve()
- self.block_heights[b45.sha256] = self.block_heights[self.tip.sha256]+1
+ self.block_heights[b45.sha256] = self.block_heights[self.tip.sha256] + 1
self.tip = b45
self.blocks[45] = b45
- yield rejected(RejectResult(16, b'bad-cb-missing'))
+ self.sync_blocks([b45], False, 16, b'bad-cb-missing', reconnect=True)
- # A block with no txns
- tip(44)
+ self.log.info("Reject a block with no transactions")
+ self.move_tip(44)
b46 = CBlock()
- b46.nTime = b44.nTime+1
+ b46.nTime = b44.nTime + 1
b46.hashPrevBlock = b44.sha256
b46.nBits = 0x207fffff
b46.vtx = []
b46.hashMerkleRoot = 0
b46.solve()
- self.block_heights[b46.sha256] = self.block_heights[b44.sha256]+1
+ self.block_heights[b46.sha256] = self.block_heights[b44.sha256] + 1
self.tip = b46
assert 46 not in self.blocks
self.blocks[46] = b46
- s = ser_uint256(b46.hashMerkleRoot)
- yield rejected(RejectResult(16, b'bad-blk-length'))
+ self.sync_blocks([b46], False, 16, b'bad-blk-length', reconnect=True)
- # A block with invalid work
- tip(44)
- b47 = block(47, solve=False)
+ self.log.info("Reject a block with invalid work")
+ self.move_tip(44)
+ b47 = self.next_block(47, solve=False)
target = uint256_from_compact(b47.nBits)
- while b47.sha256 < target: #changed > to <
+ while b47.sha256 < target:
b47.nNonce += 1
b47.rehash()
- yield rejected(RejectResult(16, b'high-hash'))
+ self.sync_blocks([b47], False, request_block=False)
- # A block with timestamp > 2 hrs in the future
- tip(44)
- b48 = block(48, solve=False)
+ self.log.info("Reject a block with a timestamp >2 hours in the future")
+ self.move_tip(44)
+ b48 = self.next_block(48, solve=False)
b48.nTime = int(time.time()) + 60 * 60 * 3
b48.solve()
- yield rejected(RejectResult(16, b'time-too-new'))
+ self.sync_blocks([b48], False, request_block=False)
- # A block with an invalid merkle hash
- tip(44)
- b49 = block(49)
+ self.log.info("Reject a block with invalid merkle hash")
+ self.move_tip(44)
+ b49 = self.next_block(49)
b49.hashMerkleRoot += 1
b49.solve()
- yield rejected(RejectResult(16, b'bad-txnmrklroot'))
+ self.sync_blocks([b49], False, 16, b'bad-txnmrklroot', reconnect=True)
- # A block with an incorrect POW limit
- tip(44)
- b50 = block(50)
+ self.log.info("Reject a block with incorrect POW limit")
+ self.move_tip(44)
+ b50 = self.next_block(50)
b50.nBits = b50.nBits - 1
b50.solve()
- yield rejected(RejectResult(16, b'bad-diffbits'))
+ self.sync_blocks([b50], False, request_block=False, reconnect=True)
- # A block with two coinbase txns
- tip(44)
- b51 = block(51)
+ self.log.info("Reject a block with two coinbase transactions")
+ self.move_tip(44)
+ b51 = self.next_block(51)
cb2 = create_coinbase(51, self.coinbase_pubkey)
- b51 = update_block(51, [cb2])
- yield rejected(RejectResult(16, b'bad-cb-multiple'))
+ b51 = self.update_block(51, [cb2])
+ self.sync_blocks([b51], False, 16, b'bad-cb-multiple', reconnect=True)
- # A block w/ duplicate txns
+ self.log.info("Reject a block with duplicate transactions")
# Note: txns have to be in the right position in the merkle tree to trigger this error
- tip(44)
- b52 = block(52, spend=out[15])
- tx = create_tx(b52.vtx[1], 0, 1)
- b52 = update_block(52, [tx, tx])
- yield rejected(RejectResult(16, b'bad-txns-duplicate'))
+ self.move_tip(44)
+ b52 = self.next_block(52, spend=out[15])
+ tx = self.create_tx(b52.vtx[1], 0, 1)
+ b52 = self.update_block(52, [tx, tx])
+ self.sync_blocks([b52], False, 16, b'bad-txns-duplicate', reconnect=True)
# Test block timestamps
# -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15)
# \-> b54 (15)
#
- tip(43)
- block(53, spend=out[14])
- yield rejected() # rejected since b44 is at same height
- save_spendable_output()
+ self.move_tip(43)
+ b53 = self.next_block(53, spend=out[14])
+ self.sync_blocks([b53], False)
+ self.save_spendable_output()
- # invalid timestamp (b35 is 5 blocks back, so its time is MedianTimePast)
- b54 = block(54, spend=out[15])
+ self.log.info("Reject a block with timestamp before MedianTimePast")
+ b54 = self.next_block(54, spend=out[15])
b54.nTime = b35.nTime - 1
b54.solve()
- yield rejected(RejectResult(16, b'time-too-old'))
+ self.sync_blocks([b54], False, request_block=False)
# valid timestamp
- tip(53)
- b55 = block(55, spend=out[15])
+ self.move_tip(53)
+ b55 = self.next_block(55, spend=out[15])
b55.nTime = b35.nTime
- update_block(55, [])
- yield accepted()
- save_spendable_output()
+ self.update_block(55, [])
+ self.sync_blocks([b55], True)
+ self.save_spendable_output()
-
- # Test CVE-2012-2459
+ # Test Merkle tree malleability
#
# -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57p2 (16)
# \-> b57 (16)
# \-> b56p2 (16)
# \-> b56 (16)
#
- # Merkle tree malleability (CVE-2012-2459): repeating sequences of transactions in a block without
+ # Merkle tree malleability (CVE-2012-2459): repeating sequences of transactions in a block without
# affecting the merkle root of a block, while still invalidating it.
# See: src/consensus/merkle.h
#
@@ -758,46 +675,48 @@ class FullBlockTest(ComparisonTestFramework):
# that the error was caught early, avoiding a DOS vulnerability.)
# b57 - a good block with 2 txs, don't submit until end
- tip(55)
- b57 = block(57)
- tx = create_and_sign_tx(out[16].tx, out[16].n, 1)
- tx1 = create_tx(tx, 0, 1)
- b57 = update_block(57, [tx, tx1])
+ self.move_tip(55)
+ b57 = self.next_block(57)
+ tx = self.create_and_sign_transaction(out[16].tx, out[16].n, 1)
+ tx1 = self.create_tx(tx, 0, 1)
+ b57 = self.update_block(57, [tx, tx1])
# b56 - copy b57, add a duplicate tx
- tip(55)
+ self.log.info("Reject a block with a duplicate transaction in the Merkle Tree (but with a valid Merkle Root)")
+ self.move_tip(55)
b56 = copy.deepcopy(b57)
self.blocks[56] = b56
- assert_equal(len(b56.vtx),3)
- b56 = update_block(56, [tx1])
+ assert_equal(len(b56.vtx), 3)
+ b56 = self.update_block(56, [tx1])
assert_equal(b56.hash, b57.hash)
- yield rejected(RejectResult(16, b'bad-txns-duplicate'))
+ self.sync_blocks([b56], False, 16, b'bad-txns-duplicate', reconnect=True)
# b57p2 - a good block with 6 tx'es, don't submit until end
- tip(55)
- b57p2 = block("57p2")
- tx = create_and_sign_tx(out[16].tx, out[16].n, 1)
- tx1 = create_tx(tx, 0, 1)
- tx2 = create_tx(tx1, 0, 1)
- tx3 = create_tx(tx2, 0, 1)
- tx4 = create_tx(tx3, 0, 1)
- b57p2 = update_block("57p2", [tx, tx1, tx2, tx3, tx4])
+ self.move_tip(55)
+ b57p2 = self.next_block("57p2")
+ tx = self.create_and_sign_transaction(out[16].tx, out[16].n, 1)
+ tx1 = self.create_tx(tx, 0, 1)
+ tx2 = self.create_tx(tx1, 0, 1)
+ tx3 = self.create_tx(tx2, 0, 1)
+ tx4 = self.create_tx(tx3, 0, 1)
+ b57p2 = self.update_block("57p2", [tx, tx1, tx2, tx3, tx4])
# b56p2 - copy b57p2, duplicate two non-consecutive tx's
- tip(55)
+ self.log.info("Reject a block with two duplicate transactions in the Merkle Tree (but with a valid Merkle Root)")
+ self.move_tip(55)
b56p2 = copy.deepcopy(b57p2)
self.blocks["b56p2"] = b56p2
assert_equal(b56p2.hash, b57p2.hash)
- assert_equal(len(b56p2.vtx),6)
- b56p2 = update_block("b56p2", [tx3, tx4])
- yield rejected(RejectResult(16, b'bad-txns-duplicate'))
+ assert_equal(len(b56p2.vtx), 6)
+ b56p2 = self.update_block("b56p2", [tx3, tx4])
+ self.sync_blocks([b56p2], False, 16, b'bad-txns-duplicate', reconnect=True)
- tip("57p2")
- yield accepted()
+ self.move_tip("57p2")
+ self.sync_blocks([b57p2], True)
- tip(57)
- yield rejected() #rejected because 57p2 seen first
- save_spendable_output()
+ self.move_tip(57)
+ self.sync_blocks([b57], False) # The tip is not updated because 57p2 seen first
+ self.save_spendable_output()
# Test a few invalid tx types
#
@@ -806,28 +725,30 @@ class FullBlockTest(ComparisonTestFramework):
#
# tx with prevout.n out of range
- tip(57)
- b58 = block(58, spend=out[17])
+ self.log.info("Reject a block with a transaction with prevout.n out of range")
+ self.move_tip(57)
+ b58 = self.next_block(58, spend=out[17])
tx = CTransaction()
assert(len(out[17].tx.vout) < 42)
tx.vin.append(CTxIn(COutPoint(out[17].tx.sha256, 42), CScript([OP_TRUE]), 0xffffffff))
tx.vout.append(CTxOut(0, b""))
tx.calc_sha256()
- b58 = update_block(58, [tx])
- yield rejected(RejectResult(16, b'bad-txns-inputs-missingorspent'))
+ b58 = self.update_block(58, [tx])
+ self.sync_blocks([b58], False, 16, b'bad-txns-inputs-missingorspent', reconnect=True)
- # tx with output value > input value out of range
- tip(57)
- b59 = block(59)
- tx = create_and_sign_tx(out[17].tx, out[17].n, 51*COIN)
- b59 = update_block(59, [tx])
- yield rejected(RejectResult(16, b'bad-txns-in-belowout'))
+ # tx with output value > input value
+ self.log.info("Reject a block with a transaction with outputs > inputs")
+ self.move_tip(57)
+ b59 = self.next_block(59)
+ tx = self.create_and_sign_transaction(out[17].tx, out[17].n, 51 * COIN)
+ b59 = self.update_block(59, [tx])
+ self.sync_blocks([b59], False, 16, b'bad-txns-in-belowout', reconnect=True)
# reset to good chain
- tip(57)
- b60 = block(60, spend=out[17])
- yield accepted()
- save_spendable_output()
+ self.move_tip(57)
+ b60 = self.next_block(60, spend=out[17])
+ self.sync_blocks([b60], True)
+ self.save_spendable_output()
# Test BIP30
#
@@ -838,46 +759,46 @@ class FullBlockTest(ComparisonTestFramework):
# not-fully-spent transaction in the same chain. To test, make identical coinbases;
# the second one should be rejected.
#
- tip(60)
- b61 = block(61, spend=out[18])
- b61.vtx[0].vin[0].scriptSig = b60.vtx[0].vin[0].scriptSig #equalize the coinbases
+ self.log.info("Reject a block with a transaction with a duplicate hash of a previous transaction (BIP30)")
+ self.move_tip(60)
+ b61 = self.next_block(61, spend=out[18])
+ b61.vtx[0].vin[0].scriptSig = b60.vtx[0].vin[0].scriptSig # Equalize the coinbases
b61.vtx[0].rehash()
- b61 = update_block(61, [])
+ b61 = self.update_block(61, [])
assert_equal(b60.vtx[0].serialize(), b61.vtx[0].serialize())
- yield rejected(RejectResult(16, b'bad-txns-BIP30'))
-
+ self.sync_blocks([b61], False, 16, b'bad-txns-BIP30', reconnect=True)
# Test tx.isFinal is properly rejected (not an exhaustive tx.isFinal test, that should be in data-driven transaction tests)
#
# -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17)
# \-> b62 (18)
#
- tip(60)
- b62 = block(62)
+ self.log.info("Reject a block with a transaction with a nonfinal locktime")
+ self.move_tip(60)
+ b62 = self.next_block(62)
tx = CTransaction()
- tx.nLockTime = 0xffffffff #this locktime is non-final
+ tx.nLockTime = 0xffffffff # this locktime is non-final
assert(out[18].n < len(out[18].tx.vout))
- tx.vin.append(CTxIn(COutPoint(out[18].tx.sha256, out[18].n))) # don't set nSequence
+ tx.vin.append(CTxIn(COutPoint(out[18].tx.sha256, out[18].n))) # don't set nSequence
tx.vout.append(CTxOut(0, CScript([OP_TRUE])))
assert(tx.vin[0].nSequence < 0xffffffff)
tx.calc_sha256()
- b62 = update_block(62, [tx])
- yield rejected(RejectResult(16, b'bad-txns-nonfinal'))
-
+ b62 = self.update_block(62, [tx])
+ self.sync_blocks([b62], False, 16, b'bad-txns-nonfinal')
# Test a non-final coinbase is also rejected
#
# -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17)
# \-> b63 (-)
#
- tip(60)
- b63 = block(63)
+ self.log.info("Reject a block with a coinbase transaction with a nonfinal locktime")
+ self.move_tip(60)
+ b63 = self.next_block(63)
b63.vtx[0].nLockTime = 0xffffffff
b63.vtx[0].vin[0].nSequence = 0xDEADBEEF
b63.vtx[0].rehash()
- b63 = update_block(63, [])
- yield rejected(RejectResult(16, b'bad-txns-nonfinal'))
-
+ b63 = self.update_block(63, [])
+ self.sync_blocks([b63], False, 16, b'bad-txns-nonfinal')
# This checks that a block with a bloated VARINT between the block_header and the array of tx such that
# the block is > MAX_BLOCK_BASE_SIZE with the bloated varint, but <= MAX_BLOCK_BASE_SIZE without the bloated varint,
@@ -893,8 +814,9 @@ class FullBlockTest(ComparisonTestFramework):
# b64a is a bloated block (non-canonical varint)
# b64 is a good block (same as b64 but w/ canonical varint)
#
- tip(60)
- regular_block = block("64a", spend=out[18])
+ self.log.info("Accept a valid block even if a bloated version of the block has previously been sent")
+ self.move_tip(60)
+ regular_block = self.next_block("64a", spend=out[18])
# make it a "broken_block," with non-canonical serialization
b64a = CBrokenBlock(regular_block)
@@ -908,45 +830,51 @@ class FullBlockTest(ComparisonTestFramework):
script_output = CScript([b'\x00' * script_length])
tx.vout.append(CTxOut(0, script_output))
tx.vin.append(CTxIn(COutPoint(b64a.vtx[1].sha256, 0)))
- b64a = update_block("64a", [tx])
+ b64a = self.update_block("64a", [tx])
assert_equal(len(b64a.serialize()), MAX_BLOCK_BASE_SIZE + 8)
- yield TestInstance([[self.tip, None]])
+ self.sync_blocks([b64a], False, 1, b'error parsing message')
- # comptool workaround: to make sure b64 is delivered, manually erase b64a from blockstore
- self.test.block_store.erase(b64a.sha256)
+ # bitcoind doesn't disconnect us for sending a bloated block, but if we subsequently
+ # resend the header message, it won't send us the getdata message again. Just
+ # disconnect and reconnect and then call sync_blocks.
+ # TODO: improve this test to be less dependent on P2P DOS behaviour.
+ node.disconnect_p2ps()
+ self.reconnect_p2p()
- tip(60)
+ self.move_tip(60)
b64 = CBlock(b64a)
b64.vtx = copy.deepcopy(b64a.vtx)
assert_equal(b64.hash, b64a.hash)
assert_equal(len(b64.serialize()), MAX_BLOCK_BASE_SIZE)
self.blocks[64] = b64
- update_block(64, [])
- yield accepted()
- save_spendable_output()
+ b64 = self.update_block(64, [])
+ self.sync_blocks([b64], True)
+ self.save_spendable_output()
# Spend an output created in the block itself
#
# -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19)
#
- tip(64)
- block(65)
- tx1 = create_and_sign_tx(out[19].tx, out[19].n, out[19].tx.vout[0].nValue)
- tx2 = create_and_sign_tx(tx1, 0, 0)
- update_block(65, [tx1, tx2])
- yield accepted()
- save_spendable_output()
+ self.log.info("Accept a block with a transaction spending an output created in the same block")
+ self.move_tip(64)
+ b65 = self.next_block(65)
+ tx1 = self.create_and_sign_transaction(out[19].tx, out[19].n, out[19].tx.vout[0].nValue)
+ tx2 = self.create_and_sign_transaction(tx1, 0, 0)
+ b65 = self.update_block(65, [tx1, tx2])
+ self.sync_blocks([b65], True)
+ self.save_spendable_output()
# Attempt to spend an output created later in the same block
#
# -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19)
# \-> b66 (20)
- tip(65)
- block(66)
- tx1 = create_and_sign_tx(out[20].tx, out[20].n, out[20].tx.vout[0].nValue)
- tx2 = create_and_sign_tx(tx1, 0, 1)
- update_block(66, [tx2, tx1])
- yield rejected(RejectResult(16, b'bad-txns-inputs-missingorspent'))
+ self.log.info("Reject a block with a transaction spending an output created later in the same block")
+ self.move_tip(65)
+ b66 = self.next_block(66)
+ tx1 = self.create_and_sign_transaction(out[20].tx, out[20].n, out[20].tx.vout[0].nValue)
+ tx2 = self.create_and_sign_transaction(tx1, 0, 1)
+ b66 = self.update_block(66, [tx2, tx1])
+ self.sync_blocks([b66], False, 16, b'bad-txns-inputs-missingorspent', reconnect=True)
# Attempt to double-spend a transaction created in a block
#
@@ -954,13 +882,14 @@ class FullBlockTest(ComparisonTestFramework):
# \-> b67 (20)
#
#
- tip(65)
- block(67)
- tx1 = create_and_sign_tx(out[20].tx, out[20].n, out[20].tx.vout[0].nValue)
- tx2 = create_and_sign_tx(tx1, 0, 1)
- tx3 = create_and_sign_tx(tx1, 0, 2)
- update_block(67, [tx1, tx2, tx3])
- yield rejected(RejectResult(16, b'bad-txns-inputs-missingorspent'))
+ self.log.info("Reject a block with a transaction double spending a transaction creted in the same block")
+ self.move_tip(65)
+ b67 = self.next_block(67)
+ tx1 = self.create_and_sign_transaction(out[20].tx, out[20].n, out[20].tx.vout[0].nValue)
+ tx2 = self.create_and_sign_transaction(tx1, 0, 1)
+ tx3 = self.create_and_sign_transaction(tx1, 0, 2)
+ b67 = self.update_block(67, [tx1, tx2, tx3])
+ self.sync_blocks([b67], False, 16, b'bad-txns-inputs-missingorspent', reconnect=True)
# More tests of block subsidy
#
@@ -974,34 +903,36 @@ class FullBlockTest(ComparisonTestFramework):
# b69 - coinbase with extra 10 satoshis, and a tx that gives a 10 satoshi fee
# this succeeds
#
- tip(65)
- block(68, additional_coinbase_value=10)
- tx = create_and_sign_tx(out[20].tx, out[20].n, out[20].tx.vout[0].nValue-9)
- update_block(68, [tx])
- yield rejected(RejectResult(16, b'bad-cb-amount'))
-
- tip(65)
- b69 = block(69, additional_coinbase_value=10)
- tx = create_and_sign_tx(out[20].tx, out[20].n, out[20].tx.vout[0].nValue-10)
- update_block(69, [tx])
- yield accepted()
- save_spendable_output()
+ self.log.info("Reject a block trying to claim too much subsidy in the coinbase transaction")
+ self.move_tip(65)
+ b68 = self.next_block(68, additional_coinbase_value=10)
+ tx = self.create_and_sign_transaction(out[20].tx, out[20].n, out[20].tx.vout[0].nValue - 9)
+ b68 = self.update_block(68, [tx])
+ self.sync_blocks([b68], False, 16, b'bad-cb-amount', reconnect=True)
+
+ self.log.info("Accept a block claiming the correct subsidy in the coinbase transaction")
+ self.move_tip(65)
+ b69 = self.next_block(69, additional_coinbase_value=10)
+ tx = self.create_and_sign_transaction(out[20].tx, out[20].n, out[20].tx.vout[0].nValue - 10)
+ self.update_block(69, [tx])
+ self.sync_blocks([b69], True)
+ self.save_spendable_output()
# Test spending the outpoint of a non-existent transaction
#
# -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20)
# \-> b70 (21)
#
- tip(69)
- block(70, spend=out[21])
+ self.log.info("Reject a block containing a transaction spending from a non-existent input")
+ self.move_tip(69)
+ b70 = self.next_block(70, spend=out[21])
bogus_tx = CTransaction()
bogus_tx.sha256 = uint256_from_str(b"23c70ed7c0506e9178fc1a987f40a33946d4ad4c962b5ae3a52546da53af0c5c")
tx = CTransaction()
tx.vin.append(CTxIn(COutPoint(bogus_tx.sha256, 0), b"", 0xffffffff))
tx.vout.append(CTxOut(1, b""))
- update_block(70, [tx])
- yield rejected(RejectResult(16, b'bad-txns-inputs-missingorspent'))
-
+ b70 = self.update_block(70, [tx])
+ self.sync_blocks([b70], False, 16, b'bad-txns-inputs-missingorspent', reconnect=True)
# Test accepting an invalid block which has the same hash as a valid one (via merkle tree tricks)
#
@@ -1009,13 +940,13 @@ class FullBlockTest(ComparisonTestFramework):
# \-> b71 (21)
#
# b72 is a good block.
- # b71 is a copy of 72, but re-adds one of its transactions. However, it has the same hash as b71.
- #
- tip(69)
- b72 = block(72)
- tx1 = create_and_sign_tx(out[21].tx, out[21].n, 2)
- tx2 = create_and_sign_tx(tx1, 0, 1)
- b72 = update_block(72, [tx1, tx2]) # now tip is 72
+ # b71 is a copy of 72, but re-adds one of its transactions. However, it has the same hash as b72.
+ self.log.info("Reject a block containing a duplicate transaction but with the same Merkle root (Merkle tree malleability")
+ self.move_tip(69)
+ b72 = self.next_block(72)
+ tx1 = self.create_and_sign_transaction(out[21].tx, out[21].n, 2)
+ tx2 = self.create_and_sign_transaction(tx1, 0, 1)
+ b72 = self.update_block(72, [tx1, tx2]) # now tip is 72
b71 = copy.deepcopy(b72)
b71.vtx.append(tx2) # add duplicate tx2
self.block_heights[b71.sha256] = self.block_heights[b69.sha256] + 1 # b71 builds off b69
@@ -1025,12 +956,12 @@ class FullBlockTest(ComparisonTestFramework):
assert_equal(len(b72.vtx), 3)
assert_equal(b72.sha256, b71.sha256)
- tip(71)
- yield rejected(RejectResult(16, b'bad-txns-duplicate'))
- tip(72)
- yield accepted()
- save_spendable_output()
+ self.move_tip(71)
+ self.sync_blocks([b71], False, 16, b'bad-txns-duplicate', reconnect=True)
+ self.move_tip(72)
+ self.sync_blocks([b72], True)
+ self.save_spendable_output()
# Test some invalid scripts and MAX_BLOCK_SIGOPS
#
@@ -1048,23 +979,23 @@ class FullBlockTest(ComparisonTestFramework):
# bytearray[20,000-20,003]: 521 (max_script_element_size+1, in little-endian format)
# bytearray[20,004-20,525]: unread data (script_element)
# bytearray[20,526] : OP_CHECKSIG (this puts us over the limit)
- #
- tip(72)
- b73 = block(73)
+ self.log.info("Reject a block containing too many sigops after a large script element")
+ self.move_tip(72)
+ b73 = self.next_block(73)
size = MAX_BLOCK_SIGOPS - 1 + MAX_SCRIPT_ELEMENT_SIZE + 1 + 5 + 1
a = bytearray([OP_CHECKSIG] * size)
- a[MAX_BLOCK_SIGOPS - 1] = int("4e",16) # OP_PUSHDATA4
+ a[MAX_BLOCK_SIGOPS - 1] = int("4e", 16) # OP_PUSHDATA4
element_size = MAX_SCRIPT_ELEMENT_SIZE + 1
a[MAX_BLOCK_SIGOPS] = element_size % 256
- a[MAX_BLOCK_SIGOPS+1] = element_size // 256
- a[MAX_BLOCK_SIGOPS+2] = 0
- a[MAX_BLOCK_SIGOPS+3] = 0
+ a[MAX_BLOCK_SIGOPS + 1] = element_size // 256
+ a[MAX_BLOCK_SIGOPS + 2] = 0
+ a[MAX_BLOCK_SIGOPS + 3] = 0
- tx = create_and_sign_tx(out[22].tx, 0, 1, CScript(a))
- b73 = update_block(73, [tx])
- assert_equal(get_legacy_sigopcount_block(b73), MAX_BLOCK_SIGOPS+1)
- yield rejected(RejectResult(16, b'bad-blk-sigops'))
+ tx = self.create_and_sign_transaction(out[22].tx, 0, 1, CScript(a))
+ b73 = self.update_block(73, [tx])
+ assert_equal(get_legacy_sigopcount_block(b73), MAX_BLOCK_SIGOPS + 1)
+ self.sync_blocks([b73], False, 16, b'bad-blk-sigops', reconnect=True)
# b74/75 - if we push an invalid script element, all prevous sigops are counted,
# but sigops after the element are not counted.
@@ -1076,45 +1007,44 @@ class FullBlockTest(ComparisonTestFramework):
#
# b74 fails because we put MAX_BLOCK_SIGOPS+1 before the element
# b75 succeeds because we put MAX_BLOCK_SIGOPS before the element
- #
- #
- tip(72)
- b74 = block(74)
- size = MAX_BLOCK_SIGOPS - 1 + MAX_SCRIPT_ELEMENT_SIZE + 42 # total = 20,561
+ self.log.info("Check sigops are counted correctly after an invalid script element")
+ self.move_tip(72)
+ b74 = self.next_block(74)
+ size = MAX_BLOCK_SIGOPS - 1 + MAX_SCRIPT_ELEMENT_SIZE + 42 # total = 20,561
a = bytearray([OP_CHECKSIG] * size)
a[MAX_BLOCK_SIGOPS] = 0x4e
- a[MAX_BLOCK_SIGOPS+1] = 0xfe
- a[MAX_BLOCK_SIGOPS+2] = 0xff
- a[MAX_BLOCK_SIGOPS+3] = 0xff
- a[MAX_BLOCK_SIGOPS+4] = 0xff
- tx = create_and_sign_tx(out[22].tx, 0, 1, CScript(a))
- b74 = update_block(74, [tx])
- yield rejected(RejectResult(16, b'bad-blk-sigops'))
-
- tip(72)
- b75 = block(75)
+ a[MAX_BLOCK_SIGOPS + 1] = 0xfe
+ a[MAX_BLOCK_SIGOPS + 2] = 0xff
+ a[MAX_BLOCK_SIGOPS + 3] = 0xff
+ a[MAX_BLOCK_SIGOPS + 4] = 0xff
+ tx = self.create_and_sign_transaction(out[22].tx, 0, 1, CScript(a))
+ b74 = self.update_block(74, [tx])
+ self.sync_blocks([b74], False, 16, b'bad-blk-sigops', reconnect=True)
+
+ self.move_tip(72)
+ b75 = self.next_block(75)
size = MAX_BLOCK_SIGOPS - 1 + MAX_SCRIPT_ELEMENT_SIZE + 42
a = bytearray([OP_CHECKSIG] * size)
- a[MAX_BLOCK_SIGOPS-1] = 0x4e
+ a[MAX_BLOCK_SIGOPS - 1] = 0x4e
a[MAX_BLOCK_SIGOPS] = 0xff
- a[MAX_BLOCK_SIGOPS+1] = 0xff
- a[MAX_BLOCK_SIGOPS+2] = 0xff
- a[MAX_BLOCK_SIGOPS+3] = 0xff
- tx = create_and_sign_tx(out[22].tx, 0, 1, CScript(a))
- b75 = update_block(75, [tx])
- yield accepted()
- save_spendable_output()
+ a[MAX_BLOCK_SIGOPS + 1] = 0xff
+ a[MAX_BLOCK_SIGOPS + 2] = 0xff
+ a[MAX_BLOCK_SIGOPS + 3] = 0xff
+ tx = self.create_and_sign_transaction(out[22].tx, 0, 1, CScript(a))
+ b75 = self.update_block(75, [tx])
+ self.sync_blocks([b75], True)
+ self.save_spendable_output()
# Check that if we push an element filled with CHECKSIGs, they are not counted
- tip(75)
- b76 = block(76)
+ self.move_tip(75)
+ b76 = self.next_block(76)
size = MAX_BLOCK_SIGOPS - 1 + MAX_SCRIPT_ELEMENT_SIZE + 1 + 5
a = bytearray([OP_CHECKSIG] * size)
- a[MAX_BLOCK_SIGOPS-1] = 0x4e # PUSHDATA4, but leave the following bytes as just checksigs
- tx = create_and_sign_tx(out[23].tx, 0, 1, CScript(a))
- b76 = update_block(76, [tx])
- yield accepted()
- save_spendable_output()
+ a[MAX_BLOCK_SIGOPS - 1] = 0x4e # PUSHDATA4, but leave the following bytes as just checksigs
+ tx = self.create_and_sign_transaction(out[23].tx, 0, 1, CScript(a))
+ b76 = self.update_block(76, [tx])
+ self.sync_blocks([b76], True)
+ self.save_spendable_output()
# Test transaction resurrection
#
@@ -1133,39 +1063,39 @@ class FullBlockTest(ComparisonTestFramework):
# To get around this issue, we construct transactions which are not signed and which
# spend to OP_TRUE. If the standard-ness rules change, this test would need to be
# updated. (Perhaps to spend to a P2SH OP_TRUE script)
- #
- tip(76)
- block(77)
- tx77 = create_and_sign_tx(out[24].tx, out[24].n, 10*COIN)
- update_block(77, [tx77])
- yield accepted()
- save_spendable_output()
-
- block(78)
- tx78 = create_tx(tx77, 0, 9*COIN)
- update_block(78, [tx78])
- yield accepted()
-
- block(79)
- tx79 = create_tx(tx78, 0, 8*COIN)
- update_block(79, [tx79])
- yield accepted()
+ self.log.info("Test transaction resurrection during a re-org")
+ self.move_tip(76)
+ b77 = self.next_block(77)
+ tx77 = self.create_and_sign_transaction(out[24].tx, out[24].n, 10 * COIN)
+ b77 = self.update_block(77, [tx77])
+ self.sync_blocks([b77], True)
+ self.save_spendable_output()
+
+ b78 = self.next_block(78)
+ tx78 = self.create_tx(tx77, 0, 9 * COIN)
+ b78 = self.update_block(78, [tx78])
+ self.sync_blocks([b78], True)
+
+ b79 = self.next_block(79)
+ tx79 = self.create_tx(tx78, 0, 8 * COIN)
+ b79 = self.update_block(79, [tx79])
+ self.sync_blocks([b79], True)
# mempool should be empty
assert_equal(len(self.nodes[0].getrawmempool()), 0)
- tip(77)
- block(80, spend=out[25])
- yield rejected()
- save_spendable_output()
+ self.move_tip(77)
+ b80 = self.next_block(80, spend=out[25])
+ self.sync_blocks([b80], False, request_block=False)
+ self.save_spendable_output()
- block(81, spend=out[26])
- yield rejected() # other chain is same length
- save_spendable_output()
+ b81 = self.next_block(81, spend=out[26])
+ self.sync_blocks([b81], False, request_block=False) # other chain is same length
+ self.save_spendable_output()
- block(82, spend=out[27])
- yield accepted() # now this chain is longer, triggers re-org
- save_spendable_output()
+ b82 = self.next_block(82, spend=out[27])
+ self.sync_blocks([b82], True) # now this chain is longer, triggers re-org
+ self.save_spendable_output()
# now check that tx78 and tx79 have been put back into the peer's mempool
mempool = self.nodes[0].getrawmempool()
@@ -1173,33 +1103,32 @@ class FullBlockTest(ComparisonTestFramework):
assert(tx78.hash in mempool)
assert(tx79.hash in mempool)
-
# Test invalid opcodes in dead execution paths.
#
# -> b81 (26) -> b82 (27) -> b83 (28)
#
- block(83)
+ self.log.info("Accept a block with invalid opcodes in dead execution paths")
+ b83 = self.next_block(83)
op_codes = [OP_IF, OP_INVALIDOPCODE, OP_ELSE, OP_TRUE, OP_ENDIF]
script = CScript(op_codes)
- tx1 = create_and_sign_tx(out[28].tx, out[28].n, out[28].tx.vout[0].nValue, script)
+ tx1 = self.create_and_sign_transaction(out[28].tx, out[28].n, out[28].tx.vout[0].nValue, script)
- tx2 = create_and_sign_tx(tx1, 0, 0, CScript([OP_TRUE]))
+ tx2 = self.create_and_sign_transaction(tx1, 0, 0, CScript([OP_TRUE]))
tx2.vin[0].scriptSig = CScript([OP_FALSE])
tx2.rehash()
- update_block(83, [tx1, tx2])
- yield accepted()
- save_spendable_output()
-
+ b83 = self.update_block(83, [tx1, tx2])
+ self.sync_blocks([b83], True)
+ self.save_spendable_output()
# Reorg on/off blocks that have OP_RETURN in them (and try to spend them)
#
# -> b81 (26) -> b82 (27) -> b83 (28) -> b84 (29) -> b87 (30) -> b88 (31)
# \-> b85 (29) -> b86 (30) \-> b89a (32)
#
- #
- block(84)
- tx1 = create_tx(out[29].tx, out[29].n, 0, CScript([OP_RETURN]))
+ self.log.info("Test re-orging blocks with OP_RETURN in them")
+ b84 = self.next_block(84)
+ tx1 = self.create_tx(out[29].tx, out[29].n, 0, CScript([OP_RETURN]))
tx1.vout.append(CTxOut(0, CScript([OP_TRUE])))
tx1.vout.append(CTxOut(0, CScript([OP_TRUE])))
tx1.vout.append(CTxOut(0, CScript([OP_TRUE])))
@@ -1207,87 +1136,186 @@ class FullBlockTest(ComparisonTestFramework):
tx1.calc_sha256()
self.sign_tx(tx1, out[29].tx, out[29].n)
tx1.rehash()
- tx2 = create_tx(tx1, 1, 0, CScript([OP_RETURN]))
+ tx2 = self.create_tx(tx1, 1, 0, CScript([OP_RETURN]))
tx2.vout.append(CTxOut(0, CScript([OP_RETURN])))
- tx3 = create_tx(tx1, 2, 0, CScript([OP_RETURN]))
+ tx3 = self.create_tx(tx1, 2, 0, CScript([OP_RETURN]))
tx3.vout.append(CTxOut(0, CScript([OP_TRUE])))
- tx4 = create_tx(tx1, 3, 0, CScript([OP_TRUE]))
+ tx4 = self.create_tx(tx1, 3, 0, CScript([OP_TRUE]))
tx4.vout.append(CTxOut(0, CScript([OP_RETURN])))
- tx5 = create_tx(tx1, 4, 0, CScript([OP_RETURN]))
+ tx5 = self.create_tx(tx1, 4, 0, CScript([OP_RETURN]))
- update_block(84, [tx1,tx2,tx3,tx4,tx5])
- yield accepted()
- save_spendable_output()
+ b84 = self.update_block(84, [tx1, tx2, tx3, tx4, tx5])
+ self.sync_blocks([b84], True)
+ self.save_spendable_output()
- tip(83)
- block(85, spend=out[29])
- yield rejected()
+ self.move_tip(83)
+ b85 = self.next_block(85, spend=out[29])
+ self.sync_blocks([b85], False) # other chain is same length
- block(86, spend=out[30])
- yield accepted()
+ b86 = self.next_block(86, spend=out[30])
+ self.sync_blocks([b86], True)
- tip(84)
- block(87, spend=out[30])
- yield rejected()
- save_spendable_output()
+ self.move_tip(84)
+ b87 = self.next_block(87, spend=out[30])
+ self.sync_blocks([b87], False) # other chain is same length
+ self.save_spendable_output()
- block(88, spend=out[31])
- yield accepted()
- save_spendable_output()
+ b88 = self.next_block(88, spend=out[31])
+ self.sync_blocks([b88], True)
+ self.save_spendable_output()
# trying to spend the OP_RETURN output is rejected
- block("89a", spend=out[32])
- tx = create_tx(tx1, 0, 0, CScript([OP_TRUE]))
- update_block("89a", [tx])
- yield rejected()
-
-
- # Test re-org of a week's worth of blocks (1088 blocks)
- # This test takes a minute or two and can be accomplished in memory
- #
- if self.options.runbarelyexpensive:
- tip(88)
- LARGE_REORG_SIZE = 1088
- test1 = TestInstance(sync_every_block=False)
- spend=out[32]
- for i in range(89, LARGE_REORG_SIZE + 89):
- b = block(i, spend)
- tx = CTransaction()
- script_length = MAX_BLOCK_BASE_SIZE - len(b.serialize()) - 69
- script_output = CScript([b'\x00' * script_length])
- tx.vout.append(CTxOut(0, script_output))
- tx.vin.append(CTxIn(COutPoint(b.vtx[1].sha256, 0)))
- b = update_block(i, [tx])
- assert_equal(len(b.serialize()), MAX_BLOCK_BASE_SIZE)
- test1.blocks_and_transactions.append([self.tip, True])
- save_spendable_output()
- spend = get_spendable_output()
-
- yield test1
- chain1_tip = i
-
- # now create alt chain of same length
- tip(88)
- test2 = TestInstance(sync_every_block=False)
- for i in range(89, LARGE_REORG_SIZE + 89):
- block("alt"+str(i))
- test2.blocks_and_transactions.append([self.tip, False])
- yield test2
-
- # extend alt chain to trigger re-org
- block("alt" + str(chain1_tip + 1))
- yield accepted()
-
- # ... and re-org back to the first chain
- tip(chain1_tip)
- block(chain1_tip + 1)
- yield rejected()
- block(chain1_tip + 2)
- yield accepted()
-
- chain1_tip += 2
+ b89a = self.next_block("89a", spend=out[32])
+ tx = self.create_tx(tx1, 0, 0, CScript([OP_TRUE]))
+ b89a = self.update_block("89a", [tx])
+ self.sync_blocks([b89a], False, 16, b'bad-txns-inputs-missingorspent', reconnect=True)
+
+ self.log.info("Test a re-org of one week's worth of blocks (1088 blocks)")
+
+ self.move_tip(88)
+ LARGE_REORG_SIZE = 1088
+ blocks = []
+ spend = out[32]
+ for i in range(89, LARGE_REORG_SIZE + 89):
+ b = self.next_block(i, spend)
+ tx = CTransaction()
+ script_length = MAX_BLOCK_BASE_SIZE - len(b.serialize()) - 69
+ script_output = CScript([b'\x00' * script_length])
+ tx.vout.append(CTxOut(0, script_output))
+ tx.vin.append(CTxIn(COutPoint(b.vtx[1].sha256, 0)))
+ b = self.update_block(i, [tx])
+ assert_equal(len(b.serialize()), MAX_BLOCK_BASE_SIZE)
+ blocks.append(b)
+ self.save_spendable_output()
+ spend = self.get_spendable_output()
+
+ self.sync_blocks(blocks, True, timeout=180)
+ chain1_tip = i
+
+ # now create alt chain of same length
+ self.move_tip(88)
+ blocks2 = []
+ for i in range(89, LARGE_REORG_SIZE + 89):
+ blocks2.append(self.next_block("alt" + str(i)))
+ self.sync_blocks(blocks2, False, request_block=False)
+
+ # extend alt chain to trigger re-org
+ block = self.next_block("alt" + str(chain1_tip + 1))
+ self.sync_blocks([block], True, timeout=180)
+
+ # ... and re-org back to the first chain
+ self.move_tip(chain1_tip)
+ block = self.next_block(chain1_tip + 1)
+ self.sync_blocks([block], False, request_block=False)
+ block = self.next_block(chain1_tip + 2)
+ self.sync_blocks([block], True, timeout=180)
+
+ # Helper methods
+ ################
+
+ def add_transactions_to_block(self, block, tx_list):
+ [tx.rehash() for tx in tx_list]
+ block.vtx.extend(tx_list)
+
+ # this is a little handier to use than the version in blocktools.py
+ def create_tx(self, spend_tx, n, value, script=CScript([OP_TRUE])):
+ return create_transaction(spend_tx, n, b"", value, script)
+
+ # sign a transaction, using the key we know about
+ # this signs input 0 in tx, which is assumed to be spending output n in spend_tx
+ def sign_tx(self, tx, spend_tx, n):
+ scriptPubKey = bytearray(spend_tx.vout[n].scriptPubKey)
+ if (scriptPubKey[0] == OP_TRUE): # an anyone-can-spend
+ tx.vin[0].scriptSig = CScript()
+ return
+ (sighash, err) = SignatureHash(spend_tx.vout[n].scriptPubKey, tx, 0, SIGHASH_ALL)
+ tx.vin[0].scriptSig = CScript([self.coinbase_key.sign(sighash) + bytes(bytearray([SIGHASH_ALL]))])
+
+ def create_and_sign_transaction(self, spend_tx, n, value, script=CScript([OP_TRUE])):
+ tx = self.create_tx(spend_tx, n, value, script)
+ self.sign_tx(tx, spend_tx, n)
+ tx.rehash()
+ return tx
+
+ def next_block(self, number, spend=None, additional_coinbase_value=0, script=CScript([OP_TRUE]), solve=True):
+ if self.tip is None:
+ base_block_hash = self.genesis_hash
+ block_time = int(time.time()) + 1
+ else:
+ base_block_hash = self.tip.sha256
+ block_time = self.tip.nTime + 1
+ # First create the coinbase
+ height = self.block_heights[base_block_hash] + 1
+ coinbase = create_coinbase(height, self.coinbase_pubkey)
+ coinbase.vout[0].nValue += additional_coinbase_value
+ coinbase.rehash()
+ if spend is None:
+ block = create_block(base_block_hash, coinbase, block_time)
+ else:
+ coinbase.vout[0].nValue += spend.tx.vout[spend.n].nValue - 1 # all but one satoshi to fees
+ coinbase.rehash()
+ block = create_block(base_block_hash, coinbase, block_time)
+ tx = create_transaction(spend.tx, spend.n, b"", 1, script) # spend 1 satoshi
+ self.sign_tx(tx, spend.tx, spend.n)
+ self.add_transactions_to_block(block, [tx])
+ block.hashMerkleRoot = block.calc_merkle_root()
+ if solve:
+ block.solve()
+ self.tip = block
+ self.block_heights[block.sha256] = height
+ assert number not in self.blocks
+ self.blocks[number] = block
+ return block
+
+ # save the current tip so it can be spent by a later block
+ def save_spendable_output(self):
+ self.log.debug("saving spendable output %s" % self.tip.vtx[0])
+ self.spendable_outputs.append(self.tip)
+
+ # get an output that we previously marked as spendable
+ def get_spendable_output(self):
+ self.log.debug("getting spendable output %s" % self.spendable_outputs[0].vtx[0])
+ return PreviousSpendableOutput(self.spendable_outputs.pop(0).vtx[0], 0)
+
+ # move the tip back to a previous block
+ def move_tip(self, number):
+ self.tip = self.blocks[number]
+
+ # adds transactions to the block and updates state
+ def update_block(self, block_number, new_transactions):
+ block = self.blocks[block_number]
+ self.add_transactions_to_block(block, new_transactions)
+ old_sha256 = block.sha256
+ block.hashMerkleRoot = block.calc_merkle_root()
+ block.solve()
+ # Update the internal state just like in next_block
+ self.tip = block
+ if block.sha256 != old_sha256:
+ self.block_heights[block.sha256] = self.block_heights[old_sha256]
+ del self.block_heights[old_sha256]
+ self.blocks[block_number] = block
+ return block
+
+ def reconnect_p2p(self):
+ """Add a P2P connection to the node.
+
+ The node gets disconnected several times in this test. This helper
+ method reconnects the p2p and restarts the network thread."""
+
+ network_thread_join()
+ self.nodes[0].disconnect_p2ps()
+ self.nodes[0].add_p2p_connection(P2PDataStore())
+ network_thread_start()
+ self.nodes[0].p2p.wait_for_verack()
+
+ def sync_blocks(self, blocks, success=True, reject_code=None, reject_reason=None, request_block=True, reconnect=False, timeout=60):
+ """Sends blocks to test node. Syncs and verifies that tip has advanced to most recent block.
+ Call with success = False if the tip shouldn't advance to the most recent block."""
+ self.nodes[0].p2p.send_blocks_and_test(blocks, self.nodes[0], success=success, reject_code=reject_code, reject_reason=reject_reason, request_block=request_block, timeout=timeout)
+ if reconnect:
+ self.reconnect_p2p()
if __name__ == '__main__':
FullBlockTest().main()
diff --git a/test/functional/feature_blocksdir.py b/test/functional/feature_blocksdir.py
new file mode 100755
index 0000000000..56f91651a8
--- /dev/null
+++ b/test/functional/feature_blocksdir.py
@@ -0,0 +1,36 @@
+#!/usr/bin/env python3
+# Copyright (c) 2018 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Test the blocksdir option.
+"""
+
+import os
+import shutil
+
+from test_framework.test_framework import BitcoinTestFramework, initialize_datadir
+
+
+class BlocksdirTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.num_nodes = 1
+
+ def run_test(self):
+ self.stop_node(0)
+ shutil.rmtree(self.nodes[0].datadir)
+ initialize_datadir(self.options.tmpdir, 0)
+ self.log.info("Starting with non exiting blocksdir ...")
+ blocksdir_path = os.path.join(self.options.tmpdir, 'blocksdir')
+ self.nodes[0].assert_start_raises_init_error(["-blocksdir=" + blocksdir_path], 'Error: Specified blocks directory "{}" does not exist.'.format(blocksdir_path))
+ os.mkdir(blocksdir_path)
+ self.log.info("Starting with exiting blocksdir ...")
+ self.start_node(0, ["-blocksdir=" + blocksdir_path])
+ self.log.info("mining blocks..")
+ self.nodes[0].generate(10)
+ assert os.path.isfile(os.path.join(blocksdir_path, "regtest", "blocks", "blk00000.dat"))
+ assert os.path.isdir(os.path.join(self.nodes[0].datadir, "regtest", "blocks", "index"))
+
+
+if __name__ == '__main__':
+ BlocksdirTest().main()
diff --git a/test/functional/feature_config_args.py b/test/functional/feature_config_args.py
index 6b1e473aa2..a1d22191af 100755
--- a/test/functional/feature_config_args.py
+++ b/test/functional/feature_config_args.py
@@ -5,7 +5,6 @@
"""Test various command line arguments and configuration file parameters."""
import os
-import re
from test_framework.test_framework import BitcoinTestFramework
@@ -26,13 +25,13 @@ class ConfArgsTest(BitcoinTestFramework):
# Check that using -datadir argument on non-existent directory fails
self.nodes[0].datadir = new_data_dir
- self.nodes[0].assert_start_raises_init_error(['-datadir=' + new_data_dir], 'Error: Specified data directory "' + re.escape(new_data_dir) + '" does not exist.')
+ self.nodes[0].assert_start_raises_init_error(['-datadir=' + new_data_dir], 'Error: Specified data directory "' + new_data_dir + '" does not exist.')
# Check that using non-existent datadir in conf file fails
conf_file = os.path.join(default_data_dir, "bitcoin.conf")
with open(conf_file, 'a', encoding='utf8') as f:
f.write("datadir=" + new_data_dir + "\n")
- self.nodes[0].assert_start_raises_init_error(['-conf=' + conf_file], 'Error reading configuration file: specified data directory "' + re.escape(new_data_dir) + '" does not exist.')
+ self.nodes[0].assert_start_raises_init_error(['-conf=' + conf_file], 'Error reading configuration file: specified data directory "' + new_data_dir + '" does not exist.')
# Create the directory and ensure the config file now works
os.mkdir(new_data_dir)
diff --git a/test/functional/feature_csv_activation.py b/test/functional/feature_csv_activation.py
index 8b5e5681e4..37d60aad61 100755
--- a/test/functional/feature_csv_activation.py
+++ b/test/functional/feature_csv_activation.py
@@ -42,98 +42,131 @@ bip112txs_vary_OP_CSV - 16 txs with nSequence = 10 evaluated against varying {re
bip112txs_vary_OP_CSV_9 - 16 txs with nSequence = 9 evaluated against varying {relative_locktimes of 10} OP_CSV OP_DROP
bip112tx_special - test negative argument to OP_CSV
"""
-
-from test_framework.test_framework import ComparisonTestFramework
-from test_framework.util import *
-from test_framework.mininode import ToHex, CTransaction, network_thread_start
-from test_framework.blocktools import create_coinbase, create_block
-from test_framework.comptool import TestInstance, TestManager
-from test_framework.script import *
+from decimal import Decimal
+from itertools import product
from io import BytesIO
import time
-base_relative_locktime = 10
-seq_disable_flag = 1<<31
-seq_random_high_bit = 1<<25
-seq_type_flag = 1<<22
-seq_random_low_bit = 1<<18
-
-# b31,b25,b22,b18 represent the 31st, 25th, 22nd and 18th bits respectively in the nSequence field
-# relative_locktimes[b31][b25][b22][b18] is a base_relative_locktime with the indicated bits set if their indices are 1
-relative_locktimes = []
-for b31 in range(2):
- b25times = []
- for b25 in range(2):
- b22times = []
- for b22 in range(2):
- b18times = []
- for b18 in range(2):
- rlt = base_relative_locktime
- if (b31):
- rlt = rlt | seq_disable_flag
- if (b25):
- rlt = rlt | seq_random_high_bit
- if (b22):
- rlt = rlt | seq_type_flag
- if (b18):
- rlt = rlt | seq_random_low_bit
- b18times.append(rlt)
- b22times.append(b18times)
- b25times.append(b22times)
- relative_locktimes.append(b25times)
-
-def all_rlt_txs(txarray):
+from test_framework.blocktools import create_coinbase, create_block
+from test_framework.messages import ToHex, CTransaction
+from test_framework.mininode import network_thread_start, P2PDataStore
+from test_framework.script import (
+ CScript,
+ OP_CHECKSEQUENCEVERIFY,
+ OP_DROP,
+)
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+ get_bip9_status,
+ hex_str_to_bytes,
+)
+
+BASE_RELATIVE_LOCKTIME = 10
+SEQ_DISABLE_FLAG = 1 << 31
+SEQ_RANDOM_HIGH_BIT = 1 << 25
+SEQ_TYPE_FLAG = 1 << 22
+SEQ_RANDOM_LOW_BIT = 1 << 18
+
+def relative_locktime(sdf, srhb, stf, srlb):
+ """Returns a locktime with certain bits set."""
+
+ locktime = BASE_RELATIVE_LOCKTIME
+ if sdf:
+ locktime |= SEQ_DISABLE_FLAG
+ if srhb:
+ locktime |= SEQ_RANDOM_HIGH_BIT
+ if stf:
+ locktime |= SEQ_TYPE_FLAG
+ if srlb:
+ locktime |= SEQ_RANDOM_LOW_BIT
+ return locktime
+
+def all_rlt_txs(txs):
+ return [tx['tx'] for tx in txs]
+
+def create_transaction(node, txid, to_address, amount):
+ inputs = [{"txid": txid, "vout": 0}]
+ outputs = {to_address: amount}
+ rawtx = node.createrawtransaction(inputs, outputs)
+ tx = CTransaction()
+ f = BytesIO(hex_str_to_bytes(rawtx))
+ tx.deserialize(f)
+ return tx
+
+def sign_transaction(node, unsignedtx):
+ rawtx = ToHex(unsignedtx)
+ signresult = node.signrawtransactionwithwallet(rawtx)
+ tx = CTransaction()
+ f = BytesIO(hex_str_to_bytes(signresult['hex']))
+ tx.deserialize(f)
+ return tx
+
+def create_bip112special(node, input, txversion, address):
+ tx = create_transaction(node, input, address, Decimal("49.98"))
+ tx.nVersion = txversion
+ signtx = sign_transaction(node, tx)
+ signtx.vin[0].scriptSig = CScript([-1, OP_CHECKSEQUENCEVERIFY, OP_DROP] + list(CScript(signtx.vin[0].scriptSig)))
+ return signtx
+
+def send_generic_input_tx(node, coinbases, address):
+ amount = Decimal("49.99")
+ return node.sendrawtransaction(ToHex(sign_transaction(node, create_transaction(node, node.getblock(coinbases.pop())['tx'][0], address, amount))))
+
+def create_bip68txs(node, bip68inputs, txversion, address, locktime_delta=0):
+ """Returns a list of bip68 transactions with different bits set."""
txs = []
- for b31 in range(2):
- for b25 in range(2):
- for b22 in range(2):
- for b18 in range(2):
- txs.append(txarray[b31][b25][b22][b18])
+ assert(len(bip68inputs) >= 16)
+ for i, (sdf, srhb, stf, srlb) in enumerate(product(*[[True, False]] * 4)):
+ locktime = relative_locktime(sdf, srhb, stf, srlb)
+ tx = create_transaction(node, bip68inputs[i], address, Decimal("49.98"))
+ tx.nVersion = txversion
+ tx.vin[0].nSequence = locktime + locktime_delta
+ tx = sign_transaction(node, tx)
+ tx.rehash()
+ txs.append({'tx': tx, 'sdf': sdf, 'stf': stf})
+
return txs
-class BIP68_112_113Test(ComparisonTestFramework):
+def create_bip112txs(node, bip112inputs, varyOP_CSV, txversion, address, locktime_delta=0):
+ """Returns a list of bip68 transactions with different bits set."""
+ txs = []
+ assert(len(bip112inputs) >= 16)
+ for i, (sdf, srhb, stf, srlb) in enumerate(product(*[[True, False]] * 4)):
+ locktime = relative_locktime(sdf, srhb, stf, srlb)
+ tx = create_transaction(node, bip112inputs[i], address, Decimal("49.98"))
+ if (varyOP_CSV): # if varying OP_CSV, nSequence is fixed
+ tx.vin[0].nSequence = BASE_RELATIVE_LOCKTIME + locktime_delta
+ else: # vary nSequence instead, OP_CSV is fixed
+ tx.vin[0].nSequence = locktime + locktime_delta
+ tx.nVersion = txversion
+ signtx = sign_transaction(node, tx)
+ if (varyOP_CSV):
+ signtx.vin[0].scriptSig = CScript([locktime, OP_CHECKSEQUENCEVERIFY, OP_DROP] + list(CScript(signtx.vin[0].scriptSig)))
+ else:
+ signtx.vin[0].scriptSig = CScript([BASE_RELATIVE_LOCKTIME, OP_CHECKSEQUENCEVERIFY, OP_DROP] + list(CScript(signtx.vin[0].scriptSig)))
+ tx.rehash()
+ txs.append({'tx': signtx, 'sdf': sdf, 'stf': stf})
+ return txs
+
+class BIP68_112_113Test(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.setup_clean_chain = True
self.extra_args = [['-whitelist=127.0.0.1', '-blockversion=4', '-addresstype=legacy']]
- def run_test(self):
- test = TestManager(self, self.options.tmpdir)
- test.add_all_connections(self.nodes)
- network_thread_start()
- test.run()
-
- def send_generic_input_tx(self, node, coinbases):
- amount = Decimal("49.99")
- return node.sendrawtransaction(ToHex(self.sign_transaction(node, self.create_transaction(node, node.getblock(coinbases.pop())['tx'][0], self.nodeaddress, amount))))
-
- def create_transaction(self, node, txid, to_address, amount):
- inputs = [{ "txid" : txid, "vout" : 0}]
- outputs = { to_address : amount }
- rawtx = node.createrawtransaction(inputs, outputs)
- tx = CTransaction()
- f = BytesIO(hex_str_to_bytes(rawtx))
- tx.deserialize(f)
- return tx
-
- def sign_transaction(self, node, unsignedtx):
- rawtx = ToHex(unsignedtx)
- signresult = node.signrawtransactionwithwallet(rawtx)
- tx = CTransaction()
- f = BytesIO(hex_str_to_bytes(signresult['hex']))
- tx.deserialize(f)
- return tx
-
- def generate_blocks(self, number, version, test_blocks = []):
+ def generate_blocks(self, number, version, test_blocks=None):
+ if test_blocks is None:
+ test_blocks = []
for i in range(number):
block = self.create_test_block([], version)
- test_blocks.append([block, True])
+ test_blocks.append(block)
self.last_block_time += 600
self.tip = block.sha256
self.tipheight += 1
return test_blocks
- def create_test_block(self, txs, version = 536870912):
+ def create_test_block(self, txs, version=536870912):
block = create_block(self.tip, create_coinbase(self.tipheight + 1), self.last_block_time + 600)
block.nVersion = version
block.vtx.extend(txs)
@@ -142,184 +175,148 @@ class BIP68_112_113Test(ComparisonTestFramework):
block.solve()
return block
- def create_bip68txs(self, bip68inputs, txversion, locktime_delta = 0):
- txs = []
- assert(len(bip68inputs) >= 16)
- i = 0
- for b31 in range(2):
- b25txs = []
- for b25 in range(2):
- b22txs = []
- for b22 in range(2):
- b18txs = []
- for b18 in range(2):
- tx = self.create_transaction(self.nodes[0], bip68inputs[i], self.nodeaddress, Decimal("49.98"))
- i += 1
- tx.nVersion = txversion
- tx.vin[0].nSequence = relative_locktimes[b31][b25][b22][b18] + locktime_delta
- b18txs.append(self.sign_transaction(self.nodes[0], tx))
- b22txs.append(b18txs)
- b25txs.append(b22txs)
- txs.append(b25txs)
- return txs
-
- def create_bip112special(self, input, txversion):
- tx = self.create_transaction(self.nodes[0], input, self.nodeaddress, Decimal("49.98"))
- tx.nVersion = txversion
- signtx = self.sign_transaction(self.nodes[0], tx)
- signtx.vin[0].scriptSig = CScript([-1, OP_CHECKSEQUENCEVERIFY, OP_DROP] + list(CScript(signtx.vin[0].scriptSig)))
- return signtx
-
- def create_bip112txs(self, bip112inputs, varyOP_CSV, txversion, locktime_delta = 0):
- txs = []
- assert(len(bip112inputs) >= 16)
- i = 0
- for b31 in range(2):
- b25txs = []
- for b25 in range(2):
- b22txs = []
- for b22 in range(2):
- b18txs = []
- for b18 in range(2):
- tx = self.create_transaction(self.nodes[0], bip112inputs[i], self.nodeaddress, Decimal("49.98"))
- i += 1
- if (varyOP_CSV): # if varying OP_CSV, nSequence is fixed
- tx.vin[0].nSequence = base_relative_locktime + locktime_delta
- else: # vary nSequence instead, OP_CSV is fixed
- tx.vin[0].nSequence = relative_locktimes[b31][b25][b22][b18] + locktime_delta
- tx.nVersion = txversion
- signtx = self.sign_transaction(self.nodes[0], tx)
- if (varyOP_CSV):
- signtx.vin[0].scriptSig = CScript([relative_locktimes[b31][b25][b22][b18], OP_CHECKSEQUENCEVERIFY, OP_DROP] + list(CScript(signtx.vin[0].scriptSig)))
- else:
- signtx.vin[0].scriptSig = CScript([base_relative_locktime, OP_CHECKSEQUENCEVERIFY, OP_DROP] + list(CScript(signtx.vin[0].scriptSig)))
- b18txs.append(signtx)
- b22txs.append(b18txs)
- b25txs.append(b22txs)
- txs.append(b25txs)
- return txs
-
- def get_tests(self):
- long_past_time = int(time.time()) - 600 * 1000 # enough to build up to 1000 blocks 10 minutes apart without worrying about getting into the future
- self.nodes[0].setmocktime(long_past_time - 100) # enough so that the generated blocks will still all be before long_past_time
- self.coinbase_blocks = self.nodes[0].generate(1 + 16 + 2*32 + 1) # 82 blocks generated for inputs
- self.nodes[0].setmocktime(0) # set time back to present so yielded blocks aren't in the future as we advance last_block_time
- self.tipheight = 82 # height of the next block to build
+ def sync_blocks(self, blocks, success=True, reject_code=None, reject_reason=None, request_block=True):
+ """Sends blocks to test node. Syncs and verifies that tip has advanced to most recent block.
+
+ Call with success = False if the tip shouldn't advance to the most recent block."""
+ self.nodes[0].p2p.send_blocks_and_test(blocks, self.nodes[0], success=success, reject_code=reject_code, reject_reason=reject_reason, request_block=request_block)
+
+ def run_test(self):
+ self.nodes[0].add_p2p_connection(P2PDataStore())
+ network_thread_start()
+ self.nodes[0].p2p.wait_for_verack()
+
+ self.log.info("Generate blocks in the past for coinbase outputs.")
+ long_past_time = int(time.time()) - 600 * 1000 # enough to build up to 1000 blocks 10 minutes apart without worrying about getting into the future
+ self.nodes[0].setmocktime(long_past_time - 100) # enough so that the generated blocks will still all be before long_past_time
+ self.coinbase_blocks = self.nodes[0].generate(1 + 16 + 2 * 32 + 1) # 82 blocks generated for inputs
+ self.nodes[0].setmocktime(0) # set time back to present so yielded blocks aren't in the future as we advance last_block_time
+ self.tipheight = 82 # height of the next block to build
self.last_block_time = long_past_time
- self.tip = int("0x" + self.nodes[0].getbestblockhash(), 0)
+ self.tip = int(self.nodes[0].getbestblockhash(), 16)
self.nodeaddress = self.nodes[0].getnewaddress()
+ self.log.info("Test that the csv softfork is DEFINED")
assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'defined')
test_blocks = self.generate_blocks(61, 4)
- yield TestInstance(test_blocks, sync_every_block=False) # 1
- # Advanced from DEFINED to STARTED, height = 143
+ self.sync_blocks(test_blocks)
+
+ self.log.info("Advance from DEFINED to STARTED, height = 143")
assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'started')
- # Fail to achieve LOCKED_IN 100 out of 144 signal bit 0
- # using a variety of bits to simulate multiple parallel softforks
- test_blocks = self.generate_blocks(50, 536870913) # 0x20000001 (signalling ready)
- test_blocks = self.generate_blocks(20, 4, test_blocks) # 0x00000004 (signalling not)
- test_blocks = self.generate_blocks(50, 536871169, test_blocks) # 0x20000101 (signalling ready)
- test_blocks = self.generate_blocks(24, 536936448, test_blocks) # 0x20010000 (signalling not)
- yield TestInstance(test_blocks, sync_every_block=False) # 2
- # Failed to advance past STARTED, height = 287
+ self.log.info("Fail to achieve LOCKED_IN")
+ # 100 out of 144 signal bit 0. Use a variety of bits to simulate multiple parallel softforks
+
+ test_blocks = self.generate_blocks(50, 536870913) # 0x20000001 (signalling ready)
+ test_blocks = self.generate_blocks(20, 4, test_blocks) # 0x00000004 (signalling not)
+ test_blocks = self.generate_blocks(50, 536871169, test_blocks) # 0x20000101 (signalling ready)
+ test_blocks = self.generate_blocks(24, 536936448, test_blocks) # 0x20010000 (signalling not)
+ self.sync_blocks(test_blocks)
+
+ self.log.info("Failed to advance past STARTED, height = 287")
assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'started')
+ self.log.info("Generate blocks to achieve LOCK-IN")
# 108 out of 144 signal bit 0 to achieve lock-in
# using a variety of bits to simulate multiple parallel softforks
- test_blocks = self.generate_blocks(58, 536870913) # 0x20000001 (signalling ready)
- test_blocks = self.generate_blocks(26, 4, test_blocks) # 0x00000004 (signalling not)
- test_blocks = self.generate_blocks(50, 536871169, test_blocks) # 0x20000101 (signalling ready)
- test_blocks = self.generate_blocks(10, 536936448, test_blocks) # 0x20010000 (signalling not)
- yield TestInstance(test_blocks, sync_every_block=False) # 3
- # Advanced from STARTED to LOCKED_IN, height = 431
+ test_blocks = self.generate_blocks(58, 536870913) # 0x20000001 (signalling ready)
+ test_blocks = self.generate_blocks(26, 4, test_blocks) # 0x00000004 (signalling not)
+ test_blocks = self.generate_blocks(50, 536871169, test_blocks) # 0x20000101 (signalling ready)
+ test_blocks = self.generate_blocks(10, 536936448, test_blocks) # 0x20010000 (signalling not)
+ self.sync_blocks(test_blocks)
+
+ self.log.info("Advanced from STARTED to LOCKED_IN, height = 431")
assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'locked_in')
- # 140 more version 4 blocks
+ # Generate 140 more version 4 blocks
test_blocks = self.generate_blocks(140, 4)
- yield TestInstance(test_blocks, sync_every_block=False) # 4
+ self.sync_blocks(test_blocks)
- ### Inputs at height = 572
+ # Inputs at height = 572
+ #
# Put inputs for all tests in the chain at height 572 (tip now = 571) (time increases by 600s per block)
# Note we reuse inputs for v1 and v2 txs so must test these separately
# 16 normal inputs
bip68inputs = []
for i in range(16):
- bip68inputs.append(self.send_generic_input_tx(self.nodes[0], self.coinbase_blocks))
+ bip68inputs.append(send_generic_input_tx(self.nodes[0], self.coinbase_blocks, self.nodeaddress))
+
# 2 sets of 16 inputs with 10 OP_CSV OP_DROP (actually will be prepended to spending scriptSig)
bip112basicinputs = []
for j in range(2):
inputs = []
for i in range(16):
- inputs.append(self.send_generic_input_tx(self.nodes[0], self.coinbase_blocks))
+ inputs.append(send_generic_input_tx(self.nodes[0], self.coinbase_blocks, self.nodeaddress))
bip112basicinputs.append(inputs)
+
# 2 sets of 16 varied inputs with (relative_lock_time) OP_CSV OP_DROP (actually will be prepended to spending scriptSig)
bip112diverseinputs = []
for j in range(2):
inputs = []
for i in range(16):
- inputs.append(self.send_generic_input_tx(self.nodes[0], self.coinbase_blocks))
+ inputs.append(send_generic_input_tx(self.nodes[0], self.coinbase_blocks, self.nodeaddress))
bip112diverseinputs.append(inputs)
+
# 1 special input with -1 OP_CSV OP_DROP (actually will be prepended to spending scriptSig)
- bip112specialinput = self.send_generic_input_tx(self.nodes[0], self.coinbase_blocks)
+ bip112specialinput = send_generic_input_tx(self.nodes[0], self.coinbase_blocks, self.nodeaddress)
+
# 1 normal input
- bip113input = self.send_generic_input_tx(self.nodes[0], self.coinbase_blocks)
+ bip113input = send_generic_input_tx(self.nodes[0], self.coinbase_blocks, self.nodeaddress)
self.nodes[0].setmocktime(self.last_block_time + 600)
- inputblockhash = self.nodes[0].generate(1)[0] # 1 block generated for inputs to be in chain at height 572
+ inputblockhash = self.nodes[0].generate(1)[0] # 1 block generated for inputs to be in chain at height 572
self.nodes[0].setmocktime(0)
- self.tip = int("0x" + inputblockhash, 0)
+ self.tip = int(inputblockhash, 16)
self.tipheight += 1
self.last_block_time += 600
- assert_equal(len(self.nodes[0].getblock(inputblockhash,True)["tx"]), 82+1)
+ assert_equal(len(self.nodes[0].getblock(inputblockhash, True)["tx"]), 82 + 1)
# 2 more version 4 blocks
test_blocks = self.generate_blocks(2, 4)
- yield TestInstance(test_blocks, sync_every_block=False) # 5
- # Not yet advanced to ACTIVE, height = 574 (will activate for block 576, not 575)
+ self.sync_blocks(test_blocks)
+
+ self.log.info("Not yet advanced to ACTIVE, height = 574 (will activate for block 576, not 575)")
assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'locked_in')
# Test both version 1 and version 2 transactions for all tests
# BIP113 test transaction will be modified before each use to put in appropriate block time
- bip113tx_v1 = self.create_transaction(self.nodes[0], bip113input, self.nodeaddress, Decimal("49.98"))
+ bip113tx_v1 = create_transaction(self.nodes[0], bip113input, self.nodeaddress, Decimal("49.98"))
bip113tx_v1.vin[0].nSequence = 0xFFFFFFFE
bip113tx_v1.nVersion = 1
- bip113tx_v2 = self.create_transaction(self.nodes[0], bip113input, self.nodeaddress, Decimal("49.98"))
+ bip113tx_v2 = create_transaction(self.nodes[0], bip113input, self.nodeaddress, Decimal("49.98"))
bip113tx_v2.vin[0].nSequence = 0xFFFFFFFE
bip113tx_v2.nVersion = 2
# For BIP68 test all 16 relative sequence locktimes
- bip68txs_v1 = self.create_bip68txs(bip68inputs, 1)
- bip68txs_v2 = self.create_bip68txs(bip68inputs, 2)
+ bip68txs_v1 = create_bip68txs(self.nodes[0], bip68inputs, 1, self.nodeaddress)
+ bip68txs_v2 = create_bip68txs(self.nodes[0], bip68inputs, 2, self.nodeaddress)
# For BIP112 test:
# 16 relative sequence locktimes of 10 against 10 OP_CSV OP_DROP inputs
- bip112txs_vary_nSequence_v1 = self.create_bip112txs(bip112basicinputs[0], False, 1)
- bip112txs_vary_nSequence_v2 = self.create_bip112txs(bip112basicinputs[0], False, 2)
+ bip112txs_vary_nSequence_v1 = create_bip112txs(self.nodes[0], bip112basicinputs[0], False, 1, self.nodeaddress)
+ bip112txs_vary_nSequence_v2 = create_bip112txs(self.nodes[0], bip112basicinputs[0], False, 2, self.nodeaddress)
# 16 relative sequence locktimes of 9 against 10 OP_CSV OP_DROP inputs
- bip112txs_vary_nSequence_9_v1 = self.create_bip112txs(bip112basicinputs[1], False, 1, -1)
- bip112txs_vary_nSequence_9_v2 = self.create_bip112txs(bip112basicinputs[1], False, 2, -1)
+ bip112txs_vary_nSequence_9_v1 = create_bip112txs(self.nodes[0], bip112basicinputs[1], False, 1, self.nodeaddress, -1)
+ bip112txs_vary_nSequence_9_v2 = create_bip112txs(self.nodes[0], bip112basicinputs[1], False, 2, self.nodeaddress, -1)
# sequence lock time of 10 against 16 (relative_lock_time) OP_CSV OP_DROP inputs
- bip112txs_vary_OP_CSV_v1 = self.create_bip112txs(bip112diverseinputs[0], True, 1)
- bip112txs_vary_OP_CSV_v2 = self.create_bip112txs(bip112diverseinputs[0], True, 2)
+ bip112txs_vary_OP_CSV_v1 = create_bip112txs(self.nodes[0], bip112diverseinputs[0], True, 1, self.nodeaddress)
+ bip112txs_vary_OP_CSV_v2 = create_bip112txs(self.nodes[0], bip112diverseinputs[0], True, 2, self.nodeaddress)
# sequence lock time of 9 against 16 (relative_lock_time) OP_CSV OP_DROP inputs
- bip112txs_vary_OP_CSV_9_v1 = self.create_bip112txs(bip112diverseinputs[1], True, 1, -1)
- bip112txs_vary_OP_CSV_9_v2 = self.create_bip112txs(bip112diverseinputs[1], True, 2, -1)
+ bip112txs_vary_OP_CSV_9_v1 = create_bip112txs(self.nodes[0], bip112diverseinputs[1], True, 1, self.nodeaddress, -1)
+ bip112txs_vary_OP_CSV_9_v2 = create_bip112txs(self.nodes[0], bip112diverseinputs[1], True, 2, self.nodeaddress, -1)
# -1 OP_CSV OP_DROP input
- bip112tx_special_v1 = self.create_bip112special(bip112specialinput, 1)
- bip112tx_special_v2 = self.create_bip112special(bip112specialinput, 2)
+ bip112tx_special_v1 = create_bip112special(self.nodes[0], bip112specialinput, 1, self.nodeaddress)
+ bip112tx_special_v2 = create_bip112special(self.nodes[0], bip112specialinput, 2, self.nodeaddress)
+
+ self.log.info("TESTING")
+ self.log.info("Pre-Soft Fork Tests. All txs should pass.")
+ self.log.info("Test version 1 txs")
- ### TESTING ###
- ##################################
- ### Before Soft Forks Activate ###
- ##################################
- # All txs should pass
- ### Version 1 txs ###
success_txs = []
# add BIP113 tx and -1 CSV tx
- bip113tx_v1.nLockTime = self.last_block_time - 600 * 5 # = MTP of prior block (not <) but < time put on current block
- bip113signed1 = self.sign_transaction(self.nodes[0], bip113tx_v1)
+ bip113tx_v1.nLockTime = self.last_block_time - 600 * 5 # = MTP of prior block (not <) but < time put on current block
+ bip113signed1 = sign_transaction(self.nodes[0], bip113tx_v1)
success_txs.append(bip113signed1)
success_txs.append(bip112tx_special_v1)
# add BIP 68 txs
@@ -330,14 +327,15 @@ class BIP68_112_113Test(ComparisonTestFramework):
# try BIP 112 with seq=9 txs
success_txs.extend(all_rlt_txs(bip112txs_vary_nSequence_9_v1))
success_txs.extend(all_rlt_txs(bip112txs_vary_OP_CSV_9_v1))
- yield TestInstance([[self.create_test_block(success_txs), True]]) # 6
+ self.sync_blocks([self.create_test_block(success_txs)])
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
- ### Version 2 txs ###
+ self.log.info("Test version 2 txs")
+
success_txs = []
# add BIP113 tx and -1 CSV tx
- bip113tx_v2.nLockTime = self.last_block_time - 600 * 5 # = MTP of prior block (not <) but < time put on current block
- bip113signed2 = self.sign_transaction(self.nodes[0], bip113tx_v2)
+ bip113tx_v2.nLockTime = self.last_block_time - 600 * 5 # = MTP of prior block (not <) but < time put on current block
+ bip113signed2 = sign_transaction(self.nodes[0], bip113tx_v2)
success_txs.append(bip113signed2)
success_txs.append(bip112tx_special_v2)
# add BIP 68 txs
@@ -348,187 +346,149 @@ class BIP68_112_113Test(ComparisonTestFramework):
# try BIP 112 with seq=9 txs
success_txs.extend(all_rlt_txs(bip112txs_vary_nSequence_9_v2))
success_txs.extend(all_rlt_txs(bip112txs_vary_OP_CSV_9_v2))
- yield TestInstance([[self.create_test_block(success_txs), True]]) # 7
+ self.sync_blocks([self.create_test_block(success_txs)])
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
-
# 1 more version 4 block to get us to height 575 so the fork should now be active for the next block
test_blocks = self.generate_blocks(1, 4)
- yield TestInstance(test_blocks, sync_every_block=False) # 8
+ self.sync_blocks(test_blocks)
assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'active')
+ self.log.info("Post-Soft Fork Tests.")
- #################################
- ### After Soft Forks Activate ###
- #################################
- ### BIP 113 ###
+ self.log.info("BIP 113 tests")
# BIP 113 tests should now fail regardless of version number if nLockTime isn't satisfied by new rules
- bip113tx_v1.nLockTime = self.last_block_time - 600 * 5 # = MTP of prior block (not <) but < time put on current block
- bip113signed1 = self.sign_transaction(self.nodes[0], bip113tx_v1)
- bip113tx_v2.nLockTime = self.last_block_time - 600 * 5 # = MTP of prior block (not <) but < time put on current block
- bip113signed2 = self.sign_transaction(self.nodes[0], bip113tx_v2)
+ bip113tx_v1.nLockTime = self.last_block_time - 600 * 5 # = MTP of prior block (not <) but < time put on current block
+ bip113signed1 = sign_transaction(self.nodes[0], bip113tx_v1)
+ bip113tx_v2.nLockTime = self.last_block_time - 600 * 5 # = MTP of prior block (not <) but < time put on current block
+ bip113signed2 = sign_transaction(self.nodes[0], bip113tx_v2)
for bip113tx in [bip113signed1, bip113signed2]:
- yield TestInstance([[self.create_test_block([bip113tx]), False]]) # 9,10
+ self.sync_blocks([self.create_test_block([bip113tx])], success=False)
# BIP 113 tests should now pass if the locktime is < MTP
- bip113tx_v1.nLockTime = self.last_block_time - 600 * 5 - 1 # < MTP of prior block
- bip113signed1 = self.sign_transaction(self.nodes[0], bip113tx_v1)
- bip113tx_v2.nLockTime = self.last_block_time - 600 * 5 - 1 # < MTP of prior block
- bip113signed2 = self.sign_transaction(self.nodes[0], bip113tx_v2)
+ bip113tx_v1.nLockTime = self.last_block_time - 600 * 5 - 1 # < MTP of prior block
+ bip113signed1 = sign_transaction(self.nodes[0], bip113tx_v1)
+ bip113tx_v2.nLockTime = self.last_block_time - 600 * 5 - 1 # < MTP of prior block
+ bip113signed2 = sign_transaction(self.nodes[0], bip113tx_v2)
for bip113tx in [bip113signed1, bip113signed2]:
- yield TestInstance([[self.create_test_block([bip113tx]), True]]) # 11,12
+ self.sync_blocks([self.create_test_block([bip113tx])])
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
# Next block height = 580 after 4 blocks of random version
test_blocks = self.generate_blocks(4, 1234)
- yield TestInstance(test_blocks, sync_every_block=False) # 13
+ self.sync_blocks(test_blocks)
+
+ self.log.info("BIP 68 tests")
+ self.log.info("Test version 1 txs - all should still pass")
- ### BIP 68 ###
- ### Version 1 txs ###
- # All still pass
success_txs = []
success_txs.extend(all_rlt_txs(bip68txs_v1))
- yield TestInstance([[self.create_test_block(success_txs), True]]) # 14
+ self.sync_blocks([self.create_test_block(success_txs)])
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
- ### Version 2 txs ###
- bip68success_txs = []
+ self.log.info("Test version 2 txs")
+
# All txs with SEQUENCE_LOCKTIME_DISABLE_FLAG set pass
- for b25 in range(2):
- for b22 in range(2):
- for b18 in range(2):
- bip68success_txs.append(bip68txs_v2[1][b25][b22][b18])
- yield TestInstance([[self.create_test_block(bip68success_txs), True]]) # 15
+ bip68success_txs = [tx['tx'] for tx in bip68txs_v2 if tx['sdf']]
+ self.sync_blocks([self.create_test_block(bip68success_txs)])
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
+
# All txs without flag fail as we are at delta height = 8 < 10 and delta time = 8 * 600 < 10 * 512
- bip68timetxs = []
- for b25 in range(2):
- for b18 in range(2):
- bip68timetxs.append(bip68txs_v2[0][b25][1][b18])
+ bip68timetxs = [tx['tx'] for tx in bip68txs_v2 if not tx['sdf'] and tx['stf']]
for tx in bip68timetxs:
- yield TestInstance([[self.create_test_block([tx]), False]]) # 16 - 19
- bip68heighttxs = []
- for b25 in range(2):
- for b18 in range(2):
- bip68heighttxs.append(bip68txs_v2[0][b25][0][b18])
+ self.sync_blocks([self.create_test_block([tx])], success=False)
+
+ bip68heighttxs = [tx['tx'] for tx in bip68txs_v2 if not tx['sdf'] and not tx['stf']]
for tx in bip68heighttxs:
- yield TestInstance([[self.create_test_block([tx]), False]]) # 20 - 23
+ self.sync_blocks([self.create_test_block([tx])], success=False)
# Advance one block to 581
test_blocks = self.generate_blocks(1, 1234)
- yield TestInstance(test_blocks, sync_every_block=False) # 24
+ self.sync_blocks(test_blocks)
# Height txs should fail and time txs should now pass 9 * 600 > 10 * 512
bip68success_txs.extend(bip68timetxs)
- yield TestInstance([[self.create_test_block(bip68success_txs), True]]) # 25
+ self.sync_blocks([self.create_test_block(bip68success_txs)])
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
for tx in bip68heighttxs:
- yield TestInstance([[self.create_test_block([tx]), False]]) # 26 - 29
+ self.sync_blocks([self.create_test_block([tx])], success=False)
# Advance one block to 582
test_blocks = self.generate_blocks(1, 1234)
- yield TestInstance(test_blocks, sync_every_block=False) # 30
+ self.sync_blocks(test_blocks)
# All BIP 68 txs should pass
bip68success_txs.extend(bip68heighttxs)
- yield TestInstance([[self.create_test_block(bip68success_txs), True]]) # 31
+ self.sync_blocks([self.create_test_block(bip68success_txs)])
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
+ self.log.info("BIP 112 tests")
+ self.log.info("Test version 1 txs")
- ### BIP 112 ###
- ### Version 1 txs ###
# -1 OP_CSV tx should fail
- yield TestInstance([[self.create_test_block([bip112tx_special_v1]), False]]) #32
+ self.sync_blocks([self.create_test_block([bip112tx_special_v1])], success=False)
# If SEQUENCE_LOCKTIME_DISABLE_FLAG is set in argument to OP_CSV, version 1 txs should still pass
- success_txs = []
- for b25 in range(2):
- for b22 in range(2):
- for b18 in range(2):
- success_txs.append(bip112txs_vary_OP_CSV_v1[1][b25][b22][b18])
- success_txs.append(bip112txs_vary_OP_CSV_9_v1[1][b25][b22][b18])
- yield TestInstance([[self.create_test_block(success_txs), True]]) # 33
+
+ success_txs = [tx['tx'] for tx in bip112txs_vary_OP_CSV_v1 if tx['sdf']]
+ success_txs += [tx['tx'] for tx in bip112txs_vary_OP_CSV_9_v1 if tx['sdf']]
+ self.sync_blocks([self.create_test_block(success_txs)])
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
# If SEQUENCE_LOCKTIME_DISABLE_FLAG is unset in argument to OP_CSV, version 1 txs should now fail
- fail_txs = []
- fail_txs.extend(all_rlt_txs(bip112txs_vary_nSequence_v1))
- fail_txs.extend(all_rlt_txs(bip112txs_vary_nSequence_9_v1))
- for b25 in range(2):
- for b22 in range(2):
- for b18 in range(2):
- fail_txs.append(bip112txs_vary_OP_CSV_v1[0][b25][b22][b18])
- fail_txs.append(bip112txs_vary_OP_CSV_9_v1[0][b25][b22][b18])
-
+ fail_txs = all_rlt_txs(bip112txs_vary_nSequence_v1)
+ fail_txs += all_rlt_txs(bip112txs_vary_nSequence_9_v1)
+ fail_txs += [tx['tx'] for tx in bip112txs_vary_OP_CSV_9_v1 if not tx['sdf']]
+ fail_txs += [tx['tx'] for tx in bip112txs_vary_OP_CSV_9_v1 if not tx['sdf']]
for tx in fail_txs:
- yield TestInstance([[self.create_test_block([tx]), False]]) # 34 - 81
+ self.sync_blocks([self.create_test_block([tx])], success=False)
+
+ self.log.info("Test version 2 txs")
- ### Version 2 txs ###
# -1 OP_CSV tx should fail
- yield TestInstance([[self.create_test_block([bip112tx_special_v2]), False]]) #82
+ self.sync_blocks([self.create_test_block([bip112tx_special_v2])], success=False)
# If SEQUENCE_LOCKTIME_DISABLE_FLAG is set in argument to OP_CSV, version 2 txs should pass (all sequence locks are met)
- success_txs = []
- for b25 in range(2):
- for b22 in range(2):
- for b18 in range(2):
- success_txs.append(bip112txs_vary_OP_CSV_v2[1][b25][b22][b18]) # 8/16 of vary_OP_CSV
- success_txs.append(bip112txs_vary_OP_CSV_9_v2[1][b25][b22][b18]) # 8/16 of vary_OP_CSV_9
+ success_txs = [tx['tx'] for tx in bip112txs_vary_OP_CSV_v2 if tx['sdf']]
+ success_txs += [tx['tx'] for tx in bip112txs_vary_OP_CSV_9_v2 if tx['sdf']]
- yield TestInstance([[self.create_test_block(success_txs), True]]) # 83
+ self.sync_blocks([self.create_test_block(success_txs)])
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
- ## SEQUENCE_LOCKTIME_DISABLE_FLAG is unset in argument to OP_CSV for all remaining txs ##
- # All txs with nSequence 9 should fail either due to earlier mismatch or failing the CSV check
- fail_txs = []
- fail_txs.extend(all_rlt_txs(bip112txs_vary_nSequence_9_v2)) # 16/16 of vary_nSequence_9
- for b25 in range(2):
- for b22 in range(2):
- for b18 in range(2):
- fail_txs.append(bip112txs_vary_OP_CSV_9_v2[0][b25][b22][b18]) # 16/16 of vary_OP_CSV_9
+ # SEQUENCE_LOCKTIME_DISABLE_FLAG is unset in argument to OP_CSV for all remaining txs ##
+ # All txs with nSequence 9 should fail either due to earlier mismatch or failing the CSV check
+ fail_txs = all_rlt_txs(bip112txs_vary_nSequence_9_v2)
+ fail_txs += [tx['tx'] for tx in bip112txs_vary_OP_CSV_9_v2 if not tx['sdf']]
for tx in fail_txs:
- yield TestInstance([[self.create_test_block([tx]), False]]) # 84 - 107
+ self.sync_blocks([self.create_test_block([tx])], success=False)
# If SEQUENCE_LOCKTIME_DISABLE_FLAG is set in nSequence, tx should fail
- fail_txs = []
- for b25 in range(2):
- for b22 in range(2):
- for b18 in range(2):
- fail_txs.append(bip112txs_vary_nSequence_v2[1][b25][b22][b18]) # 8/16 of vary_nSequence
+ fail_txs = [tx['tx'] for tx in bip112txs_vary_nSequence_v2 if tx['sdf']]
for tx in fail_txs:
- yield TestInstance([[self.create_test_block([tx]), False]]) # 108-115
+ self.sync_blocks([self.create_test_block([tx])], success=False)
# If sequencelock types mismatch, tx should fail
- fail_txs = []
- for b25 in range(2):
- for b18 in range(2):
- fail_txs.append(bip112txs_vary_nSequence_v2[0][b25][1][b18]) # 12/16 of vary_nSequence
- fail_txs.append(bip112txs_vary_OP_CSV_v2[0][b25][1][b18]) # 12/16 of vary_OP_CSV
+ fail_txs = [tx['tx'] for tx in bip112txs_vary_nSequence_v2 if not tx['sdf'] and tx['stf']]
+ fail_txs += [tx['tx'] for tx in bip112txs_vary_OP_CSV_v2 if not tx['sdf'] and tx['stf']]
for tx in fail_txs:
- yield TestInstance([[self.create_test_block([tx]), False]]) # 116-123
+ self.sync_blocks([self.create_test_block([tx])], success=False)
# Remaining txs should pass, just test masking works properly
- success_txs = []
- for b25 in range(2):
- for b18 in range(2):
- success_txs.append(bip112txs_vary_nSequence_v2[0][b25][0][b18]) # 16/16 of vary_nSequence
- success_txs.append(bip112txs_vary_OP_CSV_v2[0][b25][0][b18]) # 16/16 of vary_OP_CSV
- yield TestInstance([[self.create_test_block(success_txs), True]]) # 124
+ success_txs = [tx['tx'] for tx in bip112txs_vary_nSequence_v2 if not tx['sdf'] and not tx['stf']]
+ success_txs += [tx['tx'] for tx in bip112txs_vary_OP_CSV_v2 if not tx['sdf'] and not tx['stf']]
+ self.sync_blocks([self.create_test_block(success_txs)])
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
# Additional test, of checking that comparison of two time types works properly
time_txs = []
- for b25 in range(2):
- for b18 in range(2):
- tx = bip112txs_vary_OP_CSV_v2[0][b25][1][b18]
- tx.vin[0].nSequence = base_relative_locktime | seq_type_flag
- signtx = self.sign_transaction(self.nodes[0], tx)
- time_txs.append(signtx)
- yield TestInstance([[self.create_test_block(time_txs), True]]) # 125
- self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
+ for tx in [tx['tx'] for tx in bip112txs_vary_OP_CSV_v2 if not tx['sdf'] and tx['stf']]:
+ tx.vin[0].nSequence = BASE_RELATIVE_LOCKTIME | SEQ_TYPE_FLAG
+ signtx = sign_transaction(self.nodes[0], tx)
+ time_txs.append(signtx)
- ### Missing aspects of test
- ## Testing empty stack fails
+ self.sync_blocks([self.create_test_block(time_txs)])
+ self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
+ # TODO: Test empty stack fails
if __name__ == '__main__':
BIP68_112_113Test().main()
diff --git a/test/functional/feature_fee_estimation.py b/test/functional/feature_fee_estimation.py
index 8e97df2361..32a6bd5d59 100755
--- a/test/functional/feature_fee_estimation.py
+++ b/test/functional/feature_fee_estimation.py
@@ -133,12 +133,12 @@ class EstimateFeeTest(BitcoinTestFramework):
which we will use to generate our transactions.
"""
self.add_nodes(3, extra_args=[["-maxorphantx=1000", "-whitelist=127.0.0.1"],
- ["-maxorphantx=1000"],
- ["-maxorphantx=1000"]])
+ ["-blockmaxweight=68000", "-maxorphantx=1000"],
+ ["-blockmaxweight=32000", "-maxorphantx=1000"]])
# Use node0 to mine blocks for input splitting
# Node1 mines small blocks but that are bigger than the expected transaction rate.
- # NOTE: the CreateNewBlock code starts counting block size at 1,000 bytes,
- # (17k is room enough for 110 or so transactions)
+ # NOTE: the CreateNewBlock code starts counting block weight at 4,000 weight,
+ # (68k weight is room enough for 120 or so transactions)
# Node2 is a stingy miner, that
# produces too small blocks (room for only 55 or so transactions)
diff --git a/test/functional/feature_help.py b/test/functional/feature_help.py
new file mode 100755
index 0000000000..1e62d7a409
--- /dev/null
+++ b/test/functional/feature_help.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python3
+# Copyright (c) 2018 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Verify that starting bitcoin with -h works as expected."""
+import subprocess
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_equal
+
+class HelpTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.num_nodes = 1
+
+ def setup_network(self):
+ self.add_nodes(self.num_nodes)
+ # Don't start the node
+
+ def run_test(self):
+ self.log.info("Start bitcoin with -h for help text")
+ self.nodes[0].start(extra_args=['-h'], stderr=subprocess.PIPE, stdout=subprocess.PIPE)
+ # Node should exit immediately and output help to stdout.
+ ret_code = self.nodes[0].process.wait(timeout=1)
+ assert_equal(ret_code, 0)
+ output = self.nodes[0].process.stdout.read()
+ assert b'Options' in output
+ self.log.info("Help text received: {} (...)".format(output[0:60]))
+ self.nodes[0].running = False
+
+ self.log.info("Start bitcoin with -version for version information")
+ self.nodes[0].start(extra_args=['-version'], stderr=subprocess.PIPE, stdout=subprocess.PIPE)
+ # Node should exit immediately and output version to stdout.
+ ret_code = self.nodes[0].process.wait(timeout=1)
+ assert_equal(ret_code, 0)
+ output = self.nodes[0].process.stdout.read()
+ assert b'version' in output
+ self.log.info("Version text received: {} (...)".format(output[0:60]))
+ self.nodes[0].running = False
+
+if __name__ == '__main__':
+ HelpTest().main()
diff --git a/test/functional/feature_logging.py b/test/functional/feature_logging.py
index a4ebc7cca3..3c7aecf10a 100755
--- a/test/functional/feature_logging.py
+++ b/test/functional/feature_logging.py
@@ -7,6 +7,8 @@
import os
from test_framework.test_framework import BitcoinTestFramework
+from test_framework.test_node import ErrorMatch
+
class LoggingTest(BitcoinTestFramework):
def set_test_params(self):
@@ -31,7 +33,7 @@ class LoggingTest(BitcoinTestFramework):
invalidname = os.path.join("foo", "foo.log")
self.stop_node(0)
exp_stderr = "Error: Could not open debug log file \S+$"
- self.nodes[0].assert_start_raises_init_error(["-debuglogfile=%s" % (invalidname)], exp_stderr)
+ self.nodes[0].assert_start_raises_init_error(["-debuglogfile=%s" % (invalidname)], exp_stderr, match=ErrorMatch.FULL_REGEX)
assert not os.path.isfile(os.path.join(invdir, "foo.log"))
# check that invalid log (relative) works after path exists
@@ -44,7 +46,7 @@ class LoggingTest(BitcoinTestFramework):
self.stop_node(0)
invdir = os.path.join(self.options.tmpdir, "foo")
invalidname = os.path.join(invdir, "foo.log")
- self.nodes[0].assert_start_raises_init_error(["-debuglogfile=%s" % invalidname], exp_stderr)
+ self.nodes[0].assert_start_raises_init_error(["-debuglogfile=%s" % invalidname], exp_stderr, match=ErrorMatch.FULL_REGEX)
assert not os.path.isfile(os.path.join(invdir, "foo.log"))
# check that invalid log (absolute) works after path exists
diff --git a/test/functional/feature_pruning.py b/test/functional/feature_pruning.py
index 227ae6cb36..3adde8dd73 100755
--- a/test/functional/feature_pruning.py
+++ b/test/functional/feature_pruning.py
@@ -124,7 +124,7 @@ class PruneTest(BitcoinTestFramework):
# Reboot node 1 to clear its mempool (hopefully make the invalidate faster)
# Lower the block max size so we don't keep mining all our big mempool transactions (from disconnected blocks)
self.stop_node(1)
- self.start_node(1, extra_args=["-maxreceivebuffer=20000","-checkblocks=5", "-disablesafemode"])
+ self.start_node(1, extra_args=["-maxreceivebuffer=20000","-blockmaxweight=20000", "-checkblocks=5", "-disablesafemode"])
height = self.nodes[1].getblockcount()
self.log.info("Current block height: %d" % height)
@@ -147,7 +147,7 @@ class PruneTest(BitcoinTestFramework):
# Reboot node1 to clear those giant tx's from mempool
self.stop_node(1)
- self.start_node(1, extra_args=["-maxreceivebuffer=20000","-checkblocks=5", "-disablesafemode"])
+ self.start_node(1, extra_args=["-maxreceivebuffer=20000","-blockmaxweight=20000", "-checkblocks=5", "-disablesafemode"])
self.log.info("Generating new longer chain of 300 more blocks")
self.nodes[1].generate(300)
diff --git a/test/functional/feature_uacomment.py b/test/functional/feature_uacomment.py
index c73bdcfbb8..80bd7ff29f 100755
--- a/test/functional/feature_uacomment.py
+++ b/test/functional/feature_uacomment.py
@@ -7,6 +7,7 @@
import re
from test_framework.test_framework import BitcoinTestFramework
+from test_framework.test_node import ErrorMatch
from test_framework.util import assert_equal
@@ -27,12 +28,12 @@ class UacommentTest(BitcoinTestFramework):
self.log.info("test -uacomment max length")
self.stop_node(0)
expected = "Error: Total length of network version string \([0-9]+\) exceeds maximum length \(256\). Reduce the number or size of uacomments."
- self.nodes[0].assert_start_raises_init_error(["-uacomment=" + 'a' * 256], expected)
+ self.nodes[0].assert_start_raises_init_error(["-uacomment=" + 'a' * 256], expected, match=ErrorMatch.FULL_REGEX)
self.log.info("test -uacomment unsafe characters")
for unsafe_char in ['/', ':', '(', ')']:
expected = "Error: User Agent comment \(" + re.escape(unsafe_char) + "\) contains unsafe characters."
- self.nodes[0].assert_start_raises_init_error(["-uacomment=" + unsafe_char], expected)
+ self.nodes[0].assert_start_raises_init_error(["-uacomment=" + unsafe_char], expected, match=ErrorMatch.FULL_REGEX)
if __name__ == '__main__':
diff --git a/test/functional/interface_rest.py b/test/functional/interface_rest.py
index c6cb4c54cd..2ee33aa869 100755
--- a/test/functional/interface_rest.py
+++ b/test/functional/interface_rest.py
@@ -4,326 +4,297 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test the REST API."""
-from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import *
-from struct import *
+import binascii
+from decimal import Decimal
+from enum import Enum
from io import BytesIO
-from codecs import encode
+import json
+from struct import pack, unpack
import http.client
import urllib.parse
-def deser_uint256(f):
- r = 0
- for i in range(8):
- t = unpack(b"<I", f.read(4))[0]
- r += t << (i * 32)
- return r
-
-#allows simple http get calls
-def http_get_call(host, port, path, response_object = 0):
- conn = http.client.HTTPConnection(host, port)
- conn.request('GET', path)
-
- if response_object:
- return conn.getresponse()
-
- return conn.getresponse().read().decode('utf-8')
-
-#allows simple http post calls with a request body
-def http_post_call(host, port, path, requestdata = '', response_object = 0):
- conn = http.client.HTTPConnection(host, port)
- conn.request('POST', path, requestdata)
-
- if response_object:
- return conn.getresponse()
-
- return conn.getresponse().read()
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+ assert_greater_than,
+ assert_greater_than_or_equal,
+ hex_str_to_bytes,
+)
+
+class ReqType(Enum):
+ JSON = 1
+ BIN = 2
+ HEX = 3
+
+class RetType(Enum):
+ OBJ = 1
+ BYTES = 2
+ JSON = 3
+
+def filter_output_indices_by_value(vouts, value):
+ for vout in vouts:
+ if vout['value'] == value:
+ yield vout['n']
class RESTTest (BitcoinTestFramework):
- FORMAT_SEPARATOR = "."
-
def set_test_params(self):
self.setup_clean_chain = True
- self.num_nodes = 3
- self.extra_args = [["-rest"]] * self.num_nodes
-
- def setup_network(self, split=False):
- super().setup_network()
- connect_nodes_bi(self.nodes, 0, 2)
+ self.num_nodes = 2
+ self.extra_args = [["-rest"], []]
+
+ def test_rest_request(self, uri, http_method='GET', req_type=ReqType.JSON, body='', status=200, ret_type=RetType.JSON):
+ rest_uri = '/rest' + uri
+ if req_type == ReqType.JSON:
+ rest_uri += '.json'
+ elif req_type == ReqType.BIN:
+ rest_uri += '.bin'
+ elif req_type == ReqType.HEX:
+ rest_uri += '.hex'
+
+ conn = http.client.HTTPConnection(self.url.hostname, self.url.port)
+ self.log.debug('%s %s %s', http_method, rest_uri, body)
+ if http_method == 'GET':
+ conn.request('GET', rest_uri)
+ elif http_method == 'POST':
+ conn.request('POST', rest_uri, body)
+ resp = conn.getresponse()
+
+ assert_equal(resp.status, status)
+
+ if ret_type == RetType.OBJ:
+ return resp
+ elif ret_type == RetType.BYTES:
+ return resp.read()
+ elif ret_type == RetType.JSON:
+ return json.loads(resp.read().decode('utf-8'), parse_float=Decimal)
def run_test(self):
- url = urllib.parse.urlparse(self.nodes[0].url)
- self.log.info("Mining blocks...")
+ self.url = urllib.parse.urlparse(self.nodes[0].url)
+ self.log.info("Mine blocks and send Bitcoin to node 1")
+
+ # Random address so node1's balance doesn't increase
+ not_related_address = "2MxqoHEdNQTyYeX1mHcbrrpzgojbosTpCvJ"
self.nodes[0].generate(1)
self.sync_all()
- self.nodes[2].generate(100)
+ self.nodes[1].generatetoaddress(100, not_related_address)
self.sync_all()
assert_equal(self.nodes[0].getbalance(), 50)
txid = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 0.1)
self.sync_all()
- self.nodes[2].generate(1)
+ self.nodes[1].generatetoaddress(1, not_related_address)
self.sync_all()
bb_hash = self.nodes[0].getbestblockhash()
- assert_equal(self.nodes[1].getbalance(), Decimal("0.1")) #balance now should be 0.1 on node 1
+ assert_equal(self.nodes[1].getbalance(), Decimal("0.1"))
+
+ self.log.info("Load the transaction using the /tx URI")
- # load the latest 0.1 tx over the REST API
- json_string = http_get_call(url.hostname, url.port, '/rest/tx/'+txid+self.FORMAT_SEPARATOR+"json")
- json_obj = json.loads(json_string)
- vintx = json_obj['vin'][0]['txid'] # get the vin to later check for utxo (should be spent by then)
+ json_obj = self.test_rest_request("/tx/{}".format(txid))
+ spent = (json_obj['vin'][0]['txid'], json_obj['vin'][0]['vout']) # get the vin to later check for utxo (should be spent by then)
# get n of 0.1 outpoint
- n = 0
- for vout in json_obj['vout']:
- if vout['value'] == 0.1:
- n = vout['n']
+ n, = filter_output_indices_by_value(json_obj['vout'], Decimal('0.1'))
+ spending = (txid, n)
+ self.log.info("Query an unspent TXO using the /getutxos URI")
- #######################################
- # GETUTXOS: query an unspent outpoint #
- #######################################
- json_request = '/checkmempool/'+txid+'-'+str(n)
- json_string = http_get_call(url.hostname, url.port, '/rest/getutxos'+json_request+self.FORMAT_SEPARATOR+'json')
- json_obj = json.loads(json_string)
+ json_obj = self.test_rest_request("/getutxos/{}-{}".format(*spending))
- #check chainTip response
+ # Check chainTip response
assert_equal(json_obj['chaintipHash'], bb_hash)
- #make sure there is one utxo
+ # Make sure there is one utxo
assert_equal(len(json_obj['utxos']), 1)
- assert_equal(json_obj['utxos'][0]['value'], 0.1)
+ assert_equal(json_obj['utxos'][0]['value'], Decimal('0.1'))
+ self.log.info("Query a spent TXO using the /getutxos URI")
- #################################################
- # GETUTXOS: now query an already spent outpoint #
- #################################################
- json_request = '/checkmempool/'+vintx+'-0'
- json_string = http_get_call(url.hostname, url.port, '/rest/getutxos'+json_request+self.FORMAT_SEPARATOR+'json')
- json_obj = json.loads(json_string)
+ json_obj = self.test_rest_request("/getutxos/{}-{}".format(*spent))
- #check chainTip response
+ # Check chainTip response
assert_equal(json_obj['chaintipHash'], bb_hash)
- #make sure there is no utxo in the response because this oupoint has been spent
+ # Make sure there is no utxo in the response because this outpoint has been spent
assert_equal(len(json_obj['utxos']), 0)
- #check bitmap
+ # Check bitmap
assert_equal(json_obj['bitmap'], "0")
+ self.log.info("Query two TXOs using the /getutxos URI")
+
+ json_obj = self.test_rest_request("/getutxos/{}-{}/{}-{}".format(*(spending + spent)))
- ##################################################
- # GETUTXOS: now check both with the same request #
- ##################################################
- json_request = '/checkmempool/'+txid+'-'+str(n)+'/'+vintx+'-0'
- json_string = http_get_call(url.hostname, url.port, '/rest/getutxos'+json_request+self.FORMAT_SEPARATOR+'json')
- json_obj = json.loads(json_string)
assert_equal(len(json_obj['utxos']), 1)
assert_equal(json_obj['bitmap'], "10")
- #test binary response
- bb_hash = self.nodes[0].getbestblockhash()
-
- binaryRequest = b'\x01\x02'
- binaryRequest += hex_str_to_bytes(txid)
- binaryRequest += pack("i", n)
- binaryRequest += hex_str_to_bytes(vintx)
- binaryRequest += pack("i", 0)
+ self.log.info("Query the TXOs using the /getutxos URI with a binary response")
- bin_response = http_post_call(url.hostname, url.port, '/rest/getutxos'+self.FORMAT_SEPARATOR+'bin', binaryRequest)
- output = BytesIO()
- output.write(bin_response)
- output.seek(0)
- chainHeight = unpack("i", output.read(4))[0]
- hashFromBinResponse = hex(deser_uint256(output))[2:].zfill(64)
+ bin_request = b'\x01\x02'
+ for txid, n in [spending, spent]:
+ bin_request += hex_str_to_bytes(txid)
+ bin_request += pack("i", n)
- assert_equal(bb_hash, hashFromBinResponse) #check if getutxo's chaintip during calculation was fine
- assert_equal(chainHeight, 102) #chain height must be 102
+ bin_response = self.test_rest_request("/getutxos", http_method='POST', req_type=ReqType.BIN, body=bin_request, ret_type=RetType.BYTES)
+ output = BytesIO(bin_response)
+ chain_height, = unpack("i", output.read(4))
+ response_hash = binascii.hexlify(output.read(32)[::-1]).decode('ascii')
+ assert_equal(bb_hash, response_hash) # check if getutxo's chaintip during calculation was fine
+ assert_equal(chain_height, 102) # chain height must be 102
- ############################
- # GETUTXOS: mempool checks #
- ############################
+ self.log.info("Test the /getutxos URI with and without /checkmempool")
+ # Create a transaction, check that it's found with /checkmempool, but
+ # not found without. Then confirm the transaction and check that it's
+ # found with or without /checkmempool.
# do a tx and don't sync
txid = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 0.1)
- json_string = http_get_call(url.hostname, url.port, '/rest/tx/'+txid+self.FORMAT_SEPARATOR+"json")
- json_obj = json.loads(json_string)
- vintx = json_obj['vin'][0]['txid'] # get the vin to later check for utxo (should be spent by then)
+ json_obj = self.test_rest_request("/tx/{}".format(txid))
+ # get the spent output to later check for utxo (should be spent by then)
+ spent = (json_obj['vin'][0]['txid'], json_obj['vin'][0]['vout'])
# get n of 0.1 outpoint
- n = 0
- for vout in json_obj['vout']:
- if vout['value'] == 0.1:
- n = vout['n']
-
- json_request = '/'+txid+'-'+str(n)
- json_string = http_get_call(url.hostname, url.port, '/rest/getutxos'+json_request+self.FORMAT_SEPARATOR+'json')
- json_obj = json.loads(json_string)
- assert_equal(len(json_obj['utxos']), 0) #there should be an outpoint because it has just added to the mempool
-
- json_request = '/checkmempool/'+txid+'-'+str(n)
- json_string = http_get_call(url.hostname, url.port, '/rest/getutxos'+json_request+self.FORMAT_SEPARATOR+'json')
- json_obj = json.loads(json_string)
- assert_equal(len(json_obj['utxos']), 1) #there should be an outpoint because it has just added to the mempool
-
- #do some invalid requests
- json_request = '{"checkmempool'
- response = http_post_call(url.hostname, url.port, '/rest/getutxos'+self.FORMAT_SEPARATOR+'json', json_request, True)
- assert_equal(response.status, 400) #must be a 400 because we send an invalid json request
-
- json_request = '{"checkmempool'
- response = http_post_call(url.hostname, url.port, '/rest/getutxos'+self.FORMAT_SEPARATOR+'bin', json_request, True)
- assert_equal(response.status, 400) #must be a 400 because we send an invalid bin request
-
- response = http_post_call(url.hostname, url.port, '/rest/getutxos/checkmempool'+self.FORMAT_SEPARATOR+'bin', '', True)
- assert_equal(response.status, 400) #must be a 400 because we send an invalid bin request
-
- #test limits
- json_request = '/checkmempool/'
- for x in range(0, 20):
- json_request += txid+'-'+str(n)+'/'
- json_request = json_request.rstrip("/")
- response = http_post_call(url.hostname, url.port, '/rest/getutxos'+json_request+self.FORMAT_SEPARATOR+'json', '', True)
- assert_equal(response.status, 400) #must be a 400 because we exceeding the limits
-
- json_request = '/checkmempool/'
- for x in range(0, 15):
- json_request += txid+'-'+str(n)+'/'
- json_request = json_request.rstrip("/")
- response = http_post_call(url.hostname, url.port, '/rest/getutxos'+json_request+self.FORMAT_SEPARATOR+'json', '', True)
- assert_equal(response.status, 200) #must be a 200 because we are within the limits
-
- self.nodes[0].generate(1) #generate block to not affect upcoming tests
+ n, = filter_output_indices_by_value(json_obj['vout'], Decimal('0.1'))
+ spending = (txid, n)
+
+ json_obj = self.test_rest_request("/getutxos/{}-{}".format(*spending))
+ assert_equal(len(json_obj['utxos']), 0)
+
+ json_obj = self.test_rest_request("/getutxos/checkmempool/{}-{}".format(*spending))
+ assert_equal(len(json_obj['utxos']), 1)
+
+ json_obj = self.test_rest_request("/getutxos/{}-{}".format(*spent))
+ assert_equal(len(json_obj['utxos']), 1)
+
+ json_obj = self.test_rest_request("/getutxos/checkmempool/{}-{}".format(*spent))
+ assert_equal(len(json_obj['utxos']), 0)
+
+ self.nodes[0].generate(1)
self.sync_all()
- ################
- # /rest/block/ #
- ################
+ json_obj = self.test_rest_request("/getutxos/{}-{}".format(*spending))
+ assert_equal(len(json_obj['utxos']), 1)
+
+ json_obj = self.test_rest_request("/getutxos/checkmempool/{}-{}".format(*spending))
+ assert_equal(len(json_obj['utxos']), 1)
+
+ # Do some invalid requests
+ self.test_rest_request("/getutxos", http_method='POST', req_type=ReqType.JSON, body='{"checkmempool', status=400, ret_type=RetType.OBJ)
+ self.test_rest_request("/getutxos", http_method='POST', req_type=ReqType.BIN, body='{"checkmempool', status=400, ret_type=RetType.OBJ)
+ self.test_rest_request("/getutxos/checkmempool", http_method='POST', req_type=ReqType.JSON, status=400, ret_type=RetType.OBJ)
- # check binary format
- response = http_get_call(url.hostname, url.port, '/rest/block/'+bb_hash+self.FORMAT_SEPARATOR+"bin", True)
- assert_equal(response.status, 200)
+ # Test limits
+ long_uri = '/'.join(["{}-{}".format(txid, n) for n in range(20)])
+ self.test_rest_request("/getutxos/checkmempool/{}".format(long_uri), http_method='POST', status=400, ret_type=RetType.OBJ)
+
+ long_uri = '/'.join(['{}-{}'.format(txid, n) for n in range(15)])
+ self.test_rest_request("/getutxos/checkmempool/{}".format(long_uri), http_method='POST', status=200)
+
+ self.nodes[0].generate(1) # generate block to not affect upcoming tests
+ self.sync_all()
+
+ self.log.info("Test the /block and /headers URIs")
+ bb_hash = self.nodes[0].getbestblockhash()
+
+ # Check binary format
+ response = self.test_rest_request("/block/{}".format(bb_hash), req_type=ReqType.BIN, ret_type=RetType.OBJ)
assert_greater_than(int(response.getheader('content-length')), 80)
- response_str = response.read()
+ response_bytes = response.read()
- # compare with block header
- response_header = http_get_call(url.hostname, url.port, '/rest/headers/1/'+bb_hash+self.FORMAT_SEPARATOR+"bin", True)
- assert_equal(response_header.status, 200)
+ # Compare with block header
+ response_header = self.test_rest_request("/headers/1/{}".format(bb_hash), req_type=ReqType.BIN, ret_type=RetType.OBJ)
assert_equal(int(response_header.getheader('content-length')), 80)
- response_header_str = response_header.read()
- assert_equal(response_str[0:80], response_header_str)
+ response_header_bytes = response_header.read()
+ assert_equal(response_bytes[:80], response_header_bytes)
- # check block hex format
- response_hex = http_get_call(url.hostname, url.port, '/rest/block/'+bb_hash+self.FORMAT_SEPARATOR+"hex", True)
- assert_equal(response_hex.status, 200)
+ # Check block hex format
+ response_hex = self.test_rest_request("/block/{}".format(bb_hash), req_type=ReqType.HEX, ret_type=RetType.OBJ)
assert_greater_than(int(response_hex.getheader('content-length')), 160)
- response_hex_str = response_hex.read()
- assert_equal(encode(response_str, "hex_codec")[0:160], response_hex_str[0:160])
+ response_hex_bytes = response_hex.read().strip(b'\n')
+ assert_equal(binascii.hexlify(response_bytes), response_hex_bytes)
- # compare with hex block header
- response_header_hex = http_get_call(url.hostname, url.port, '/rest/headers/1/'+bb_hash+self.FORMAT_SEPARATOR+"hex", True)
- assert_equal(response_header_hex.status, 200)
+ # Compare with hex block header
+ response_header_hex = self.test_rest_request("/headers/1/{}".format(bb_hash), req_type=ReqType.HEX, ret_type=RetType.OBJ)
assert_greater_than(int(response_header_hex.getheader('content-length')), 160)
- response_header_hex_str = response_header_hex.read()
- assert_equal(response_hex_str[0:160], response_header_hex_str[0:160])
- assert_equal(encode(response_header_str, "hex_codec")[0:160], response_header_hex_str[0:160])
+ response_header_hex_bytes = response_header_hex.read(160)
+ assert_equal(binascii.hexlify(response_bytes[:80]), response_header_hex_bytes)
- # check json format
- block_json_string = http_get_call(url.hostname, url.port, '/rest/block/'+bb_hash+self.FORMAT_SEPARATOR+'json')
- block_json_obj = json.loads(block_json_string)
+ # Check json format
+ block_json_obj = self.test_rest_request("/block/{}".format(bb_hash))
assert_equal(block_json_obj['hash'], bb_hash)
- # compare with json block header
- response_header_json = http_get_call(url.hostname, url.port, '/rest/headers/1/'+bb_hash+self.FORMAT_SEPARATOR+"json", True)
- assert_equal(response_header_json.status, 200)
- response_header_json_str = response_header_json.read().decode('utf-8')
- json_obj = json.loads(response_header_json_str, parse_float=Decimal)
- assert_equal(len(json_obj), 1) #ensure that there is one header in the json response
- assert_equal(json_obj[0]['hash'], bb_hash) #request/response hash should be the same
+ # Compare with json block header
+ json_obj = self.test_rest_request("/headers/1/{}".format(bb_hash))
+ assert_equal(len(json_obj), 1) # ensure that there is one header in the json response
+ assert_equal(json_obj[0]['hash'], bb_hash) # request/response hash should be the same
- #compare with normal RPC block response
+ # Compare with normal RPC block response
rpc_block_json = self.nodes[0].getblock(bb_hash)
- assert_equal(json_obj[0]['hash'], rpc_block_json['hash'])
- assert_equal(json_obj[0]['confirmations'], rpc_block_json['confirmations'])
- assert_equal(json_obj[0]['height'], rpc_block_json['height'])
- assert_equal(json_obj[0]['version'], rpc_block_json['version'])
- assert_equal(json_obj[0]['merkleroot'], rpc_block_json['merkleroot'])
- assert_equal(json_obj[0]['time'], rpc_block_json['time'])
- assert_equal(json_obj[0]['nonce'], rpc_block_json['nonce'])
- assert_equal(json_obj[0]['bits'], rpc_block_json['bits'])
- assert_equal(json_obj[0]['difficulty'], rpc_block_json['difficulty'])
- assert_equal(json_obj[0]['chainwork'], rpc_block_json['chainwork'])
- assert_equal(json_obj[0]['previousblockhash'], rpc_block_json['previousblockhash'])
-
- #see if we can get 5 headers in one response
+ for key in ['hash', 'confirmations', 'height', 'version', 'merkleroot', 'time', 'nonce', 'bits', 'difficulty', 'chainwork', 'previousblockhash']:
+ assert_equal(json_obj[0][key], rpc_block_json[key])
+
+ # See if we can get 5 headers in one response
self.nodes[1].generate(5)
self.sync_all()
- response_header_json = http_get_call(url.hostname, url.port, '/rest/headers/5/'+bb_hash+self.FORMAT_SEPARATOR+"json", True)
- assert_equal(response_header_json.status, 200)
- response_header_json_str = response_header_json.read().decode('utf-8')
- json_obj = json.loads(response_header_json_str)
- assert_equal(len(json_obj), 5) #now we should have 5 header objects
+ json_obj = self.test_rest_request("/headers/5/{}".format(bb_hash))
+ assert_equal(len(json_obj), 5) # now we should have 5 header objects
+
+ self.log.info("Test the /tx URI")
- # do tx test
tx_hash = block_json_obj['tx'][0]['txid']
- json_string = http_get_call(url.hostname, url.port, '/rest/tx/'+tx_hash+self.FORMAT_SEPARATOR+"json")
- json_obj = json.loads(json_string)
+ json_obj = self.test_rest_request("/tx/{}".format(tx_hash))
assert_equal(json_obj['txid'], tx_hash)
- # check hex format response
- hex_string = http_get_call(url.hostname, url.port, '/rest/tx/'+tx_hash+self.FORMAT_SEPARATOR+"hex", True)
- assert_equal(hex_string.status, 200)
- assert_greater_than(int(response.getheader('content-length')), 10)
+ # Check hex format response
+ hex_response = self.test_rest_request("/tx/{}".format(tx_hash), req_type=ReqType.HEX, ret_type=RetType.OBJ)
+ assert_greater_than_or_equal(int(hex_response.getheader('content-length')),
+ json_obj['size']*2)
+ self.log.info("Test tx inclusion in the /mempool and /block URIs")
- # check block tx details
- # let's make 3 tx and mine them on node 1
+ # Make 3 tx and mine them on node 1
txs = []
- txs.append(self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 11))
- txs.append(self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 11))
- txs.append(self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 11))
+ txs.append(self.nodes[0].sendtoaddress(not_related_address, 11))
+ txs.append(self.nodes[0].sendtoaddress(not_related_address, 11))
+ txs.append(self.nodes[0].sendtoaddress(not_related_address, 11))
self.sync_all()
- # check that there are exactly 3 transactions in the TX memory pool before generating the block
- json_string = http_get_call(url.hostname, url.port, '/rest/mempool/info'+self.FORMAT_SEPARATOR+'json')
- json_obj = json.loads(json_string)
+ # Check that there are exactly 3 transactions in the TX memory pool before generating the block
+ json_obj = self.test_rest_request("/mempool/info")
assert_equal(json_obj['size'], 3)
# the size of the memory pool should be greater than 3x ~100 bytes
assert_greater_than(json_obj['bytes'], 300)
- # check that there are our submitted transactions in the TX memory pool
- json_string = http_get_call(url.hostname, url.port, '/rest/mempool/contents'+self.FORMAT_SEPARATOR+'json')
- json_obj = json.loads(json_string)
+ # Check that there are our submitted transactions in the TX memory pool
+ json_obj = self.test_rest_request("/mempool/contents")
for i, tx in enumerate(txs):
- assert_equal(tx in json_obj, True)
- assert_equal(json_obj[tx]['spentby'], txs[i+1:i+2])
- assert_equal(json_obj[tx]['depends'], txs[i-1:i])
+ assert tx in json_obj
+ assert_equal(json_obj[tx]['spentby'], txs[i + 1:i + 2])
+ assert_equal(json_obj[tx]['depends'], txs[i - 1:i])
- # now mine the transactions
+ # Now mine the transactions
newblockhash = self.nodes[1].generate(1)
self.sync_all()
- #check if the 3 tx show up in the new block
- json_string = http_get_call(url.hostname, url.port, '/rest/block/'+newblockhash[0]+self.FORMAT_SEPARATOR+'json')
- json_obj = json.loads(json_string)
- for tx in json_obj['tx']:
- if not 'coinbase' in tx['vin'][0]: #exclude coinbase
- assert_equal(tx['txid'] in txs, True)
+ # Check if the 3 tx show up in the new block
+ json_obj = self.test_rest_request("/block/{}".format(newblockhash[0]))
+ non_coinbase_txs = {tx['txid'] for tx in json_obj['tx']
+ if 'coinbase' not in tx['vin'][0]}
+ assert_equal(non_coinbase_txs, set(txs))
- #check the same but without tx details
- json_string = http_get_call(url.hostname, url.port, '/rest/block/notxdetails/'+newblockhash[0]+self.FORMAT_SEPARATOR+'json')
- json_obj = json.loads(json_string)
+ # Check the same but without tx details
+ json_obj = self.test_rest_request("/block/notxdetails/{}".format(newblockhash[0]))
for tx in txs:
- assert_equal(tx in json_obj['tx'], True)
+ assert tx in json_obj['tx']
+
+ self.log.info("Test the /chaininfo URI")
- #test rest bestblock
bb_hash = self.nodes[0].getbestblockhash()
- json_string = http_get_call(url.hostname, url.port, '/rest/chaininfo.json')
- json_obj = json.loads(json_string)
+ json_obj = self.test_rest_request("/chaininfo")
assert_equal(json_obj['bestblockhash'], bb_hash)
if __name__ == '__main__':
- RESTTest ().main ()
+ RESTTest().main()
diff --git a/test/functional/mempool_accept.py b/test/functional/mempool_accept.py
new file mode 100755
index 0000000000..7cdb24c6a5
--- /dev/null
+++ b/test/functional/mempool_accept.py
@@ -0,0 +1,293 @@
+#!/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 mempool acceptance of raw transactions."""
+
+from io import BytesIO
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.messages import (
+ BIP125_SEQUENCE_NUMBER,
+ COIN,
+ COutPoint,
+ CTransaction,
+ CTxOut,
+ MAX_BLOCK_BASE_SIZE,
+)
+from test_framework.script import (
+ hash160,
+ CScript,
+ OP_0,
+ OP_EQUAL,
+ OP_HASH160,
+ OP_RETURN,
+)
+from test_framework.util import (
+ assert_equal,
+ assert_raises_rpc_error,
+ bytes_to_hex_str,
+ hex_str_to_bytes,
+ wait_until,
+)
+
+
+class MempoolAcceptanceTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 1
+ self.extra_args = [[
+ '-checkmempool',
+ '-txindex',
+ '-reindex', # Need reindex for txindex
+ '-acceptnonstdtxn=0', # Try to mimic main-net
+ ]] * self.num_nodes
+
+ def check_mempool_result(self, result_expected, *args, **kwargs):
+ """Wrapper to check result of testmempoolaccept on node_0's mempool"""
+ result_test = self.nodes[0].testmempoolaccept(*args, **kwargs)
+ assert_equal(result_expected, result_test)
+ assert_equal(self.nodes[0].getmempoolinfo()['size'], self.mempool_size) # Must not change mempool state
+
+ def run_test(self):
+ node = self.nodes[0]
+
+ self.log.info('Start with empty mempool, and 200 blocks')
+ self.mempool_size = 0
+ wait_until(lambda: node.getblockcount() == 200)
+ assert_equal(node.getmempoolinfo()['size'], self.mempool_size)
+
+ self.log.info('Should not accept garbage to testmempoolaccept')
+ assert_raises_rpc_error(-3, 'Expected type array, got string', lambda: node.testmempoolaccept(rawtxs='ff00baar'))
+ assert_raises_rpc_error(-8, 'Array must contain exactly one raw transaction for now', lambda: node.testmempoolaccept(rawtxs=['ff00baar', 'ff22']))
+ assert_raises_rpc_error(-22, 'TX decode failed', lambda: node.testmempoolaccept(rawtxs=['ff00baar']))
+
+ self.log.info('A transaction already in the blockchain')
+ coin = node.listunspent()[0] # Pick a random coin(base) to spend
+ raw_tx_in_block = node.signrawtransactionwithwallet(node.createrawtransaction(
+ inputs=[{'txid': coin['txid'], 'vout': coin['vout']}],
+ outputs=[{node.getnewaddress(): 0.3}, {node.getnewaddress(): 49}],
+ ))['hex']
+ txid_in_block = node.sendrawtransaction(hexstring=raw_tx_in_block, allowhighfees=True)
+ node.generate(1)
+ self.check_mempool_result(
+ result_expected=[{'txid': txid_in_block, 'allowed': False, 'reject-reason': '18: txn-already-known'}],
+ rawtxs=[raw_tx_in_block],
+ )
+
+ self.log.info('A transaction not in the mempool')
+ fee = 0.00000700
+ raw_tx_0 = node.signrawtransactionwithwallet(node.createrawtransaction(
+ inputs=[{"txid": txid_in_block, "vout": 0, "sequence": BIP125_SEQUENCE_NUMBER}], # RBF is used later
+ outputs=[{node.getnewaddress(): 0.3 - fee}],
+ ))['hex']
+ tx = CTransaction()
+ tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0)))
+ txid_0 = tx.rehash()
+ self.check_mempool_result(
+ result_expected=[{'txid': txid_0, 'allowed': True}],
+ rawtxs=[raw_tx_0],
+ )
+
+ self.log.info('A transaction in the mempool')
+ node.sendrawtransaction(hexstring=raw_tx_0)
+ self.mempool_size = 1
+ self.check_mempool_result(
+ result_expected=[{'txid': txid_0, 'allowed': False, 'reject-reason': '18: txn-already-in-mempool'}],
+ rawtxs=[raw_tx_0],
+ )
+
+ self.log.info('A transaction that replaces a mempool transaction')
+ tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0)))
+ tx.vout[0].nValue -= int(fee * COIN) # Double the fee
+ tx.vin[0].nSequence = BIP125_SEQUENCE_NUMBER + 1 # Now, opt out of RBF
+ raw_tx_0 = node.signrawtransactionwithwallet(bytes_to_hex_str(tx.serialize()))['hex']
+ tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0)))
+ txid_0 = tx.rehash()
+ self.check_mempool_result(
+ result_expected=[{'txid': txid_0, 'allowed': True}],
+ rawtxs=[raw_tx_0],
+ )
+
+ self.log.info('A transaction that conflicts with an unconfirmed tx')
+ # Send the transaction that replaces the mempool transaction and opts out of replaceability
+ node.sendrawtransaction(hexstring=bytes_to_hex_str(tx.serialize()), allowhighfees=True)
+ # take original raw_tx_0
+ tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0)))
+ tx.vout[0].nValue -= int(4 * fee * COIN) # Set more fee
+ # skip re-signing the tx
+ self.check_mempool_result(
+ result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '18: txn-mempool-conflict'}],
+ rawtxs=[bytes_to_hex_str(tx.serialize())],
+ allowhighfees=True,
+ )
+
+ self.log.info('A transaction with missing inputs, that never existed')
+ tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0)))
+ tx.vin[0].prevout = COutPoint(hash=int('ff' * 32, 16), n=14)
+ # skip re-signing the tx
+ self.check_mempool_result(
+ result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'missing-inputs'}],
+ rawtxs=[bytes_to_hex_str(tx.serialize())],
+ )
+
+ self.log.info('A transaction with missing inputs, that existed once in the past')
+ tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0)))
+ tx.vin[0].prevout.n = 1 # Set vout to 1, to spend the other outpoint (49 coins) of the in-chain-tx we want to double spend
+ raw_tx_1 = node.signrawtransactionwithwallet(bytes_to_hex_str(tx.serialize()))['hex']
+ txid_1 = node.sendrawtransaction(hexstring=raw_tx_1, allowhighfees=True)
+ # Now spend both to "clearly hide" the outputs, ie. remove the coins from the utxo set by spending them
+ raw_tx_spend_both = node.signrawtransactionwithwallet(node.createrawtransaction(
+ inputs=[
+ {'txid': txid_0, 'vout': 0},
+ {'txid': txid_1, 'vout': 0},
+ ],
+ outputs=[{node.getnewaddress(): 0.1}]
+ ))['hex']
+ txid_spend_both = node.sendrawtransaction(hexstring=raw_tx_spend_both, allowhighfees=True)
+ node.generate(1)
+ self.mempool_size = 0
+ # Now see if we can add the coins back to the utxo set by sending the exact txs again
+ self.check_mempool_result(
+ result_expected=[{'txid': txid_0, 'allowed': False, 'reject-reason': 'missing-inputs'}],
+ rawtxs=[raw_tx_0],
+ )
+ self.check_mempool_result(
+ result_expected=[{'txid': txid_1, 'allowed': False, 'reject-reason': 'missing-inputs'}],
+ rawtxs=[raw_tx_1],
+ )
+
+ self.log.info('Create a signed "reference" tx for later use')
+ raw_tx_reference = node.signrawtransactionwithwallet(node.createrawtransaction(
+ inputs=[{'txid': txid_spend_both, 'vout': 0}],
+ outputs=[{node.getnewaddress(): 0.05}],
+ ))['hex']
+ tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
+ # Reference tx should be valid on itself
+ self.check_mempool_result(
+ result_expected=[{'txid': tx.rehash(), 'allowed': True}],
+ rawtxs=[bytes_to_hex_str(tx.serialize())],
+ )
+
+ self.log.info('A transaction with no outputs')
+ tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
+ tx.vout = []
+ # Skip re-signing the transaction for context independent checks from now on
+ # tx.deserialize(BytesIO(hex_str_to_bytes(node.signrawtransactionwithwallet(bytes_to_hex_str(tx.serialize()))['hex'])))
+ self.check_mempool_result(
+ result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '16: bad-txns-vout-empty'}],
+ rawtxs=[bytes_to_hex_str(tx.serialize())],
+ )
+
+ self.log.info('A really large transaction')
+ tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
+ tx.vin = [tx.vin[0]] * (MAX_BLOCK_BASE_SIZE // len(tx.vin[0].serialize()))
+ self.check_mempool_result(
+ result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '16: bad-txns-oversize'}],
+ rawtxs=[bytes_to_hex_str(tx.serialize())],
+ )
+
+ self.log.info('A transaction with negative output value')
+ tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
+ tx.vout[0].nValue *= -1
+ self.check_mempool_result(
+ result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '16: bad-txns-vout-negative'}],
+ rawtxs=[bytes_to_hex_str(tx.serialize())],
+ )
+
+ self.log.info('A transaction with too large output value')
+ tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
+ tx.vout[0].nValue = 21000000 * COIN + 1
+ self.check_mempool_result(
+ result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '16: bad-txns-vout-toolarge'}],
+ rawtxs=[bytes_to_hex_str(tx.serialize())],
+ )
+
+ self.log.info('A transaction with too large sum of output values')
+ tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
+ tx.vout = [tx.vout[0]] * 2
+ tx.vout[0].nValue = 21000000 * COIN
+ self.check_mempool_result(
+ result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '16: bad-txns-txouttotal-toolarge'}],
+ rawtxs=[bytes_to_hex_str(tx.serialize())],
+ )
+
+ self.log.info('A transaction with duplicate inputs')
+ tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
+ tx.vin = [tx.vin[0]] * 2
+ self.check_mempool_result(
+ result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '16: bad-txns-inputs-duplicate'}],
+ rawtxs=[bytes_to_hex_str(tx.serialize())],
+ )
+
+ self.log.info('A coinbase transaction')
+ # Pick the input of the first tx we signed, so it has to be a coinbase tx
+ raw_tx_coinbase_spent = node.getrawtransaction(txid=node.decoderawtransaction(hexstring=raw_tx_in_block)['vin'][0]['txid'])
+ tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_coinbase_spent)))
+ self.check_mempool_result(
+ result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '16: coinbase'}],
+ rawtxs=[bytes_to_hex_str(tx.serialize())],
+ )
+
+ self.log.info('Some nonstandard transactions')
+ tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
+ tx.nVersion = 3 # A version currently non-standard
+ self.check_mempool_result(
+ result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: version'}],
+ rawtxs=[bytes_to_hex_str(tx.serialize())],
+ )
+ tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
+ tx.vout[0].scriptPubKey = CScript([OP_0]) # Some non-standard script
+ self.check_mempool_result(
+ result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: scriptpubkey'}],
+ rawtxs=[bytes_to_hex_str(tx.serialize())],
+ )
+ tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
+ tx.vin[0].scriptSig = CScript([OP_HASH160]) # Some not-pushonly scriptSig
+ self.check_mempool_result(
+ result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: scriptsig-not-pushonly'}],
+ rawtxs=[bytes_to_hex_str(tx.serialize())],
+ )
+ tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
+ output_p2sh_burn = CTxOut(nValue=540, scriptPubKey=CScript([OP_HASH160, hash160(b'burn'), OP_EQUAL]))
+ num_scripts = 100000 // len(output_p2sh_burn.serialize()) # Use enough outputs to make the tx too large for our policy
+ tx.vout = [output_p2sh_burn] * num_scripts
+ self.check_mempool_result(
+ result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: tx-size'}],
+ rawtxs=[bytes_to_hex_str(tx.serialize())],
+ )
+ tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
+ tx.vout[0] = output_p2sh_burn
+ tx.vout[0].nValue -= 1 # Make output smaller, such that it is dust for our policy
+ self.check_mempool_result(
+ result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: dust'}],
+ rawtxs=[bytes_to_hex_str(tx.serialize())],
+ )
+ tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
+ tx.vout[0].scriptPubKey = CScript([OP_RETURN, b'\xff'])
+ tx.vout = [tx.vout[0]] * 2
+ self.check_mempool_result(
+ result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: multi-op-return'}],
+ rawtxs=[bytes_to_hex_str(tx.serialize())],
+ )
+
+ self.log.info('A timelocked transaction')
+ tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
+ tx.vin[0].nSequence -= 1 # Should be non-max, so locktime is not ignored
+ tx.nLockTime = node.getblockcount() + 1
+ self.check_mempool_result(
+ result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: non-final'}],
+ rawtxs=[bytes_to_hex_str(tx.serialize())],
+ )
+
+ self.log.info('A transaction that is locked by BIP68 sequence logic')
+ tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
+ tx.vin[0].nSequence = 2 # We could include it in the second block mined from now, but not the very next one
+ # Can skip re-signing the tx because of early rejection
+ self.check_mempool_result(
+ result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: non-BIP68-final'}],
+ rawtxs=[bytes_to_hex_str(tx.serialize())],
+ allowhighfees=True,
+ )
+
+
+if __name__ == '__main__':
+ MempoolAcceptanceTest().main()
diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py
index 8b226c2e9d..5546bf6b29 100755
--- a/test/functional/p2p_segwit.py
+++ b/test/functional/p2p_segwit.py
@@ -425,7 +425,7 @@ class SegWitTest(BitcoinTestFramework):
assert(self.nodes[0].getbestblockhash() == block.hash)
- # Now make sure that malleating the witness nonce doesn't
+ # Now make sure that malleating the witness reserved value doesn't
# result in a block permanently marked bad.
block = self.build_next_block()
add_witness_commitment(block)
@@ -436,7 +436,7 @@ class SegWitTest(BitcoinTestFramework):
block.vtx[0].wit.vtxinwit[0].scriptWitness.stack = [ ser_uint256(1) ]
test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False)
- # Changing the witness nonce doesn't change the block hash
+ # Changing the witness reserved value doesn't change the block hash
block.vtx[0].wit.vtxinwit[0].scriptWitness.stack = [ ser_uint256(0) ]
test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
@@ -833,7 +833,7 @@ class SegWitTest(BitcoinTestFramework):
self.test_node.announce_tx_and_wait_for_getdata(tx, timeout=2)
self.log.error("Error: duplicate tx getdata!")
assert(False)
- except AssertionError as e:
+ except AssertionError:
pass
# Delivering this transaction with witness should fail (no matter who
diff --git a/test/functional/p2p_sendheaders.py b/test/functional/p2p_sendheaders.py
index 8869aeaaea..4c7d5e65c5 100755
--- a/test/functional/p2p_sendheaders.py
+++ b/test/functional/p2p_sendheaders.py
@@ -411,21 +411,18 @@ class SendHeadersTest(BitcoinTestFramework):
inv_node.check_last_announcement(inv=[tip], headers=[])
test_node.check_last_announcement(inv=[tip], headers=[])
if i == 0:
- # Just get the data -- shouldn't cause headers announcements to resume
+ self.log.debug("Just get the data -- shouldn't cause headers announcements to resume")
test_node.send_get_data([tip])
test_node.wait_for_block(tip)
elif i == 1:
- # Send a getheaders message that shouldn't trigger headers announcements
- # to resume (best header sent will be too old)
+ self.log.debug("Send a getheaders message that shouldn't trigger headers announcements to resume (best header sent will be too old)")
test_node.send_get_headers(locator=[fork_point], hashstop=new_block_hashes[1])
test_node.send_get_data([tip])
test_node.wait_for_block(tip)
elif i == 2:
test_node.send_get_data([tip])
test_node.wait_for_block(tip)
- # This time, try sending either a getheaders to trigger resumption
- # of headers announcements, or mine a new block and inv it, also
- # triggering resumption of headers announcements.
+ self.log.debug("This time, try sending either a getheaders to trigger resumption of headers announcements, or mine a new block and inv it, also triggering resumption of headers announcements.")
if j == 0:
test_node.send_get_headers(locator=[tip], hashstop=0)
test_node.sync_with_ping()
diff --git a/test/functional/rpc_net.py b/test/functional/rpc_net.py
index d8348432aa..72b5f4748f 100755
--- a/test/functional/rpc_net.py
+++ b/test/functional/rpc_net.py
@@ -10,6 +10,7 @@ Tests correspond to code in rpc/net.cpp.
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
+ assert_greater_than_or_equal,
assert_raises_rpc_error,
connect_nodes_bi,
p2p_port,
@@ -33,26 +34,34 @@ class NetTest(BitcoinTestFramework):
assert_equal(self.nodes[0].getconnectioncount(), 2)
def _test_getnettotals(self):
- # check that getnettotals totalbytesrecv and totalbytessent
- # are consistent with getpeerinfo
+ # getnettotals totalbytesrecv and totalbytessent should be
+ # consistent with getpeerinfo. Since the RPC calls are not atomic,
+ # and messages might have been recvd or sent between RPC calls, call
+ # getnettotals before and after and verify that the returned values
+ # from getpeerinfo are bounded by those values.
+ net_totals_before = self.nodes[0].getnettotals()
peer_info = self.nodes[0].getpeerinfo()
+ net_totals_after = self.nodes[0].getnettotals()
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'])
+ peers_recv = sum([peer['bytesrecv'] for peer in peer_info])
+ peers_sent = sum([peer['bytessent'] for peer in peer_info])
+
+ assert_greater_than_or_equal(peers_recv, net_totals_before['totalbytesrecv'])
+ assert_greater_than_or_equal(net_totals_after['totalbytesrecv'], peers_recv)
+ assert_greater_than_or_equal(peers_sent, net_totals_before['totalbytessent'])
+ assert_greater_than_or_equal(net_totals_after['totalbytessent'], peers_sent)
+
# 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()
- wait_until(lambda: (net_totals['totalbytessent'] + 32*2) == self.nodes[0].getnettotals()['totalbytessent'], timeout=1)
- wait_until(lambda: (net_totals['totalbytesrecv'] + 32*2) == self.nodes[0].getnettotals()['totalbytesrecv'], timeout=1)
+ wait_until(lambda: (self.nodes[0].getnettotals()['totalbytessent'] >= net_totals_after['totalbytessent'] + 32 * 2), timeout=1)
+ wait_until(lambda: (self.nodes[0].getnettotals()['totalbytesrecv'] >= net_totals_after['totalbytesrecv'] + 32 * 2), timeout=1)
peer_info_after_ping = self.nodes[0].getpeerinfo()
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_greater_than_or_equal(after['bytesrecv_per_msg']['pong'], before['bytesrecv_per_msg']['pong'] + 32)
+ assert_greater_than_or_equal(after['bytessent_per_msg']['ping'], before['bytessent_per_msg']['ping'] + 32)
def _test_getnetworkinginfo(self):
assert_equal(self.nodes[0].getnetworkinfo()['networkactive'], True)
@@ -78,8 +87,7 @@ class NetTest(BitcoinTestFramework):
assert_equal(len(added_nodes), 1)
assert_equal(added_nodes[0]['addednode'], ip_port)
# check that a non-existent node returns an error
- assert_raises_rpc_error(-24, "Node has not been added",
- self.nodes[0].getaddednodeinfo, '1.1.1.1')
+ assert_raises_rpc_error(-24, "Node has not been added", self.nodes[0].getaddednodeinfo, '1.1.1.1')
def _test_getpeerinfo(self):
peer_info = [x.getpeerinfo() for x in self.nodes]
diff --git a/test/functional/test_framework/authproxy.py b/test/functional/test_framework/authproxy.py
index bd3a3b3fab..900090bb66 100644
--- a/test/functional/test_framework/authproxy.py
+++ b/test/functional/test_framework/authproxy.py
@@ -151,7 +151,7 @@ class AuthServiceProxy():
req_start_time = time.time()
try:
http_response = self.__conn.getresponse()
- except socket.timeout as e:
+ except socket.timeout:
raise JSONRPCException({
'code': -344,
'message': '%r RPC took longer than %f seconds. Consider '
diff --git a/test/functional/test_framework/blockstore.py b/test/functional/test_framework/blockstore.py
deleted file mode 100644
index 6067a407cc..0000000000
--- a/test/functional/test_framework/blockstore.py
+++ /dev/null
@@ -1,160 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (c) 2015-2017 The Bitcoin Core developers
-# Distributed under the MIT software license, see the accompanying
-# file COPYING or http://www.opensource.org/licenses/mit-license.php.
-"""BlockStore and TxStore helper classes."""
-
-from .mininode import *
-from io import BytesIO
-import dbm.dumb as dbmd
-
-logger = logging.getLogger("TestFramework.blockstore")
-
-class BlockStore():
- """BlockStore helper class.
-
- BlockStore keeps a map of blocks and implements helper functions for
- responding to getheaders and getdata, and for constructing a getheaders
- message.
- """
-
- def __init__(self, datadir):
- self.blockDB = dbmd.open(datadir + "/blocks", 'c')
- self.currentBlock = 0
- self.headers_map = dict()
-
- def close(self):
- self.blockDB.close()
-
- def erase(self, blockhash):
- del self.blockDB[repr(blockhash)]
-
- # lookup an entry and return the item as raw bytes
- def get(self, blockhash):
- value = None
- try:
- value = self.blockDB[repr(blockhash)]
- except KeyError:
- return None
- return value
-
- # lookup an entry and return it as a CBlock
- def get_block(self, blockhash):
- ret = None
- serialized_block = self.get(blockhash)
- if serialized_block is not None:
- f = BytesIO(serialized_block)
- ret = CBlock()
- ret.deserialize(f)
- ret.calc_sha256()
- return ret
-
- def get_header(self, blockhash):
- try:
- return self.headers_map[blockhash]
- except KeyError:
- return None
-
- # Note: this pulls full blocks out of the database just to retrieve
- # the headers -- perhaps we could keep a separate data structure
- # to avoid this overhead.
- def headers_for(self, locator, hash_stop, current_tip=None):
- if current_tip is None:
- current_tip = self.currentBlock
- current_block_header = self.get_header(current_tip)
- if current_block_header is None:
- return None
-
- response = msg_headers()
- headersList = [ current_block_header ]
- maxheaders = 2000
- while (headersList[0].sha256 not in locator.vHave):
- prevBlockHash = headersList[0].hashPrevBlock
- prevBlockHeader = self.get_header(prevBlockHash)
- if prevBlockHeader is not None:
- headersList.insert(0, prevBlockHeader)
- else:
- break
- headersList = headersList[:maxheaders] # truncate if we have too many
- hashList = [x.sha256 for x in headersList]
- index = len(headersList)
- if (hash_stop in hashList):
- index = hashList.index(hash_stop)+1
- response.headers = headersList[:index]
- return response
-
- def add_block(self, block):
- block.calc_sha256()
- try:
- self.blockDB[repr(block.sha256)] = bytes(block.serialize())
- except TypeError as e:
- logger.exception("Unexpected error")
- self.currentBlock = block.sha256
- self.headers_map[block.sha256] = CBlockHeader(block)
-
- def add_header(self, header):
- self.headers_map[header.sha256] = header
-
- # lookup the hashes in "inv", and return p2p messages for delivering
- # blocks found.
- def get_blocks(self, inv):
- responses = []
- for i in inv:
- if (i.type == 2 or i.type == (2 | (1 << 30))): # MSG_BLOCK or MSG_WITNESS_BLOCK
- data = self.get(i.hash)
- if data is not None:
- # Use msg_generic to avoid re-serialization
- responses.append(msg_generic(b"block", data))
- return responses
-
- def get_locator(self, current_tip=None):
- if current_tip is None:
- current_tip = self.currentBlock
- r = []
- counter = 0
- step = 1
- lastBlock = self.get_block(current_tip)
- while lastBlock is not None:
- r.append(lastBlock.hashPrevBlock)
- for i in range(step):
- lastBlock = self.get_block(lastBlock.hashPrevBlock)
- if lastBlock is None:
- break
- counter += 1
- if counter > 10:
- step *= 2
- locator = CBlockLocator()
- locator.vHave = r
- return locator
-
-class TxStore():
- def __init__(self, datadir):
- self.txDB = dbmd.open(datadir + "/transactions", 'c')
-
- def close(self):
- self.txDB.close()
-
- # lookup an entry and return the item as raw bytes
- def get(self, txhash):
- value = None
- try:
- value = self.txDB[repr(txhash)]
- except KeyError:
- return None
- return value
-
- def add_transaction(self, tx):
- tx.calc_sha256()
- try:
- self.txDB[repr(tx.sha256)] = bytes(tx.serialize())
- except TypeError as e:
- logger.exception("Unexpected error")
-
- def get_transactions(self, inv):
- responses = []
- for i in inv:
- if (i.type == 1 or i.type == (1 | (1 << 30))): # MSG_TX or MSG_WITNESS_TX
- tx = self.get(i.hash)
- if tx is not None:
- responses.append(msg_generic(b"tx", tx))
- return responses
diff --git a/test/functional/test_framework/comptool.py b/test/functional/test_framework/comptool.py
deleted file mode 100755
index e0ca78e5d1..0000000000
--- a/test/functional/test_framework/comptool.py
+++ /dev/null
@@ -1,397 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (c) 2015-2017 The Bitcoin Core developers
-# Distributed under the MIT software license, see the accompanying
-# file COPYING or http://www.opensource.org/licenses/mit-license.php.
-"""Compare two or more bitcoinds to each other.
-
-To use, create a class that implements get_tests(), and pass it in
-as the test generator to TestManager. get_tests() should be a python
-generator that returns TestInstance objects. See below for definition.
-
-TestP2PConn behaves as follows:
- Configure with a BlockStore and TxStore
- on_inv: log the message but don't request
- on_headers: log the chain tip
- on_pong: update ping response map (for synchronization)
- on_getheaders: provide headers via BlockStore
- on_getdata: provide blocks via BlockStore
-"""
-
-from .mininode import *
-from .blockstore import BlockStore, TxStore
-from .util import p2p_port, wait_until
-
-import logging
-
-logger=logging.getLogger("TestFramework.comptool")
-
-global mininode_lock
-
-class RejectResult():
- """Outcome that expects rejection of a transaction or block."""
- def __init__(self, code, reason=b''):
- self.code = code
- self.reason = reason
- def match(self, other):
- if self.code != other.code:
- return False
- return other.reason.startswith(self.reason)
- def __repr__(self):
- return '%i:%s' % (self.code,self.reason or '*')
-
-class TestP2PConn(P2PInterface):
-
- def __init__(self, block_store, tx_store):
- super().__init__()
- self.bestblockhash = None
- self.block_store = block_store
- self.block_request_map = {}
- self.tx_store = tx_store
- self.tx_request_map = {}
- self.block_reject_map = {}
- self.tx_reject_map = {}
-
- # When the pingmap is non-empty we're waiting for
- # a response
- self.pingMap = {}
- self.lastInv = []
- self.closed = False
-
- def on_close(self):
- self.closed = True
-
- def on_headers(self, message):
- if len(message.headers) > 0:
- best_header = message.headers[-1]
- best_header.calc_sha256()
- self.bestblockhash = best_header.sha256
-
- def on_getheaders(self, message):
- response = self.block_store.headers_for(message.locator, message.hashstop)
- if response is not None:
- self.send_message(response)
-
- def on_getdata(self, message):
- [self.send_message(r) for r in self.block_store.get_blocks(message.inv)]
- [self.send_message(r) for r in self.tx_store.get_transactions(message.inv)]
-
- for i in message.inv:
- if i.type == 1 or i.type == 1 | (1 << 30): # MSG_TX or MSG_WITNESS_TX
- self.tx_request_map[i.hash] = True
- elif i.type == 2 or i.type == 2 | (1 << 30): # MSG_BLOCK or MSG_WITNESS_BLOCK
- self.block_request_map[i.hash] = True
-
- def on_inv(self, message):
- self.lastInv = [x.hash for x in message.inv]
-
- def on_pong(self, message):
- try:
- del self.pingMap[message.nonce]
- except KeyError:
- raise AssertionError("Got pong for unknown ping [%s]" % repr(message))
-
- def on_reject(self, message):
- if message.message == b'tx':
- self.tx_reject_map[message.data] = RejectResult(message.code, message.reason)
- if message.message == b'block':
- self.block_reject_map[message.data] = RejectResult(message.code, message.reason)
-
- def send_inv(self, obj):
- mtype = 2 if isinstance(obj, CBlock) else 1
- self.send_message(msg_inv([CInv(mtype, obj.sha256)]))
-
- def send_getheaders(self):
- # We ask for headers from their last tip.
- m = msg_getheaders()
- m.locator = self.block_store.get_locator(self.bestblockhash)
- self.send_message(m)
-
- def send_header(self, header):
- m = msg_headers()
- m.headers.append(header)
- self.send_message(m)
-
- # This assumes BIP31
- def send_ping(self, nonce):
- self.pingMap[nonce] = True
- self.send_message(msg_ping(nonce))
-
- def received_ping_response(self, nonce):
- return nonce not in self.pingMap
-
- def send_mempool(self):
- self.lastInv = []
- self.send_message(msg_mempool())
-
-# TestInstance:
-#
-# Instances of these are generated by the test generator, and fed into the
-# comptool.
-#
-# "blocks_and_transactions" should be an array of
-# [obj, True/False/None, hash/None]:
-# - obj is either a CBlock, CBlockHeader, or a CTransaction, and
-# - the second value indicates whether the object should be accepted
-# into the blockchain or mempool (for tests where we expect a certain
-# answer), or "None" if we don't expect a certain answer and are just
-# comparing the behavior of the nodes being tested.
-# - the third value is the hash to test the tip against (if None or omitted,
-# use the hash of the block)
-# - NOTE: if a block header, no test is performed; instead the header is
-# just added to the block_store. This is to facilitate block delivery
-# when communicating with headers-first clients (when withholding an
-# intermediate block).
-# sync_every_block: if True, then each block will be inv'ed, synced, and
-# nodes will be tested based on the outcome for the block. If False,
-# then inv's accumulate until all blocks are processed (or max inv size
-# is reached) and then sent out in one inv message. Then the final block
-# will be synced across all connections, and the outcome of the final
-# block will be tested.
-# sync_every_tx: analogous to behavior for sync_every_block, except if outcome
-# on the final tx is None, then contents of entire mempool are compared
-# across all connections. (If outcome of final tx is specified as true
-# or false, then only the last tx is tested against outcome.)
-
-class TestInstance():
- def __init__(self, objects=None, sync_every_block=True, sync_every_tx=False):
- self.blocks_and_transactions = objects if objects else []
- self.sync_every_block = sync_every_block
- self.sync_every_tx = sync_every_tx
-
-class TestManager():
-
- def __init__(self, testgen, datadir):
- self.test_generator = testgen
- self.p2p_connections= []
- self.block_store = BlockStore(datadir)
- self.tx_store = TxStore(datadir)
- self.ping_counter = 1
-
- def add_all_connections(self, nodes):
- for i in range(len(nodes)):
- # Create a p2p connection to each node
- node = TestP2PConn(self.block_store, self.tx_store)
- node.peer_connect('127.0.0.1', p2p_port(i))
- self.p2p_connections.append(node)
-
- def clear_all_connections(self):
- self.p2p_connections = []
-
- def wait_for_disconnections(self):
- def disconnected():
- return all(node.closed for node in self.p2p_connections)
- wait_until(disconnected, timeout=10, lock=mininode_lock)
-
- def wait_for_verack(self):
- return all(node.wait_for_verack() for node in self.p2p_connections)
-
- def wait_for_pings(self, counter):
- def received_pongs():
- return all(node.received_ping_response(counter) for node in self.p2p_connections)
- wait_until(received_pongs, lock=mininode_lock)
-
- # sync_blocks: Wait for all connections to request the blockhash given
- # then send get_headers to find out the tip of each node, and synchronize
- # the response by using a ping (and waiting for pong with same nonce).
- def sync_blocks(self, blockhash, num_blocks):
- def blocks_requested():
- return all(
- blockhash in node.block_request_map and node.block_request_map[blockhash]
- for node in self.p2p_connections
- )
-
- # --> error if not requested
- wait_until(blocks_requested, attempts=20*num_blocks, lock=mininode_lock)
-
- # Send getheaders message
- [ c.send_getheaders() for c in self.p2p_connections ]
-
- # Send ping and wait for response -- synchronization hack
- [ c.send_ping(self.ping_counter) for c in self.p2p_connections ]
- self.wait_for_pings(self.ping_counter)
- self.ping_counter += 1
-
- # Analogous to sync_block (see above)
- def sync_transaction(self, txhash, num_events):
- # Wait for nodes to request transaction (50ms sleep * 20 tries * num_events)
- def transaction_requested():
- return all(
- txhash in node.tx_request_map and node.tx_request_map[txhash]
- for node in self.p2p_connections
- )
-
- # --> error if not requested
- wait_until(transaction_requested, attempts=20*num_events, lock=mininode_lock)
-
- # Get the mempool
- [ c.send_mempool() for c in self.p2p_connections ]
-
- # Send ping and wait for response -- synchronization hack
- [ c.send_ping(self.ping_counter) for c in self.p2p_connections ]
- self.wait_for_pings(self.ping_counter)
- self.ping_counter += 1
-
- # Sort inv responses from each node
- with mininode_lock:
- [ c.lastInv.sort() for c in self.p2p_connections ]
-
- # Verify that the tip of each connection all agree with each other, and
- # with the expected outcome (if given)
- def check_results(self, blockhash, outcome):
- with mininode_lock:
- for c in self.p2p_connections:
- if outcome is None:
- if c.bestblockhash != self.p2p_connections[0].bestblockhash:
- return False
- elif isinstance(outcome, RejectResult): # Check that block was rejected w/ code
- if c.bestblockhash == blockhash:
- return False
- if blockhash not in c.block_reject_map:
- logger.error('Block not in reject map: %064x' % (blockhash))
- return False
- if not outcome.match(c.block_reject_map[blockhash]):
- logger.error('Block rejected with %s instead of expected %s: %064x' % (c.block_reject_map[blockhash], outcome, blockhash))
- return False
- elif ((c.bestblockhash == blockhash) != outcome):
- return False
- return True
-
- # Either check that the mempools all agree with each other, or that
- # txhash's presence in the mempool matches the outcome specified.
- # This is somewhat of a strange comparison, in that we're either comparing
- # a particular tx to an outcome, or the entire mempools altogether;
- # perhaps it would be useful to add the ability to check explicitly that
- # a particular tx's existence in the mempool is the same across all nodes.
- def check_mempool(self, txhash, outcome):
- with mininode_lock:
- for c in self.p2p_connections:
- if outcome is None:
- # Make sure the mempools agree with each other
- if c.lastInv != self.p2p_connections[0].lastInv:
- return False
- elif isinstance(outcome, RejectResult): # Check that tx was rejected w/ code
- if txhash in c.lastInv:
- return False
- if txhash not in c.tx_reject_map:
- logger.error('Tx not in reject map: %064x' % (txhash))
- return False
- if not outcome.match(c.tx_reject_map[txhash]):
- logger.error('Tx rejected with %s instead of expected %s: %064x' % (c.tx_reject_map[txhash], outcome, txhash))
- return False
- elif ((txhash in c.lastInv) != outcome):
- return False
- return True
-
- def run(self):
- # Wait until verack is received
- self.wait_for_verack()
-
- test_number = 0
- tests = self.test_generator.get_tests()
- for test_instance in tests:
- test_number += 1
- logger.info("Running test %d: %s line %s" % (test_number, tests.gi_code.co_filename, tests.gi_frame.f_lineno))
- # We use these variables to keep track of the last block
- # and last transaction in the tests, which are used
- # if we're not syncing on every block or every tx.
- [ block, block_outcome, tip ] = [ None, None, None ]
- [ tx, tx_outcome ] = [ None, None ]
- invqueue = []
-
- for test_obj in test_instance.blocks_and_transactions:
- b_or_t = test_obj[0]
- outcome = test_obj[1]
- # Determine if we're dealing with a block or tx
- if isinstance(b_or_t, CBlock): # Block test runner
- block = b_or_t
- block_outcome = outcome
- tip = block.sha256
- # each test_obj can have an optional third argument
- # to specify the tip we should compare with
- # (default is to use the block being tested)
- if len(test_obj) >= 3:
- tip = test_obj[2]
-
- # Add to shared block_store, set as current block
- # If there was an open getdata request for the block
- # previously, and we didn't have an entry in the
- # block_store, then immediately deliver, because the
- # node wouldn't send another getdata request while
- # the earlier one is outstanding.
- first_block_with_hash = True
- if self.block_store.get(block.sha256) is not None:
- first_block_with_hash = False
- with mininode_lock:
- self.block_store.add_block(block)
- for c in self.p2p_connections:
- if first_block_with_hash and block.sha256 in c.block_request_map and c.block_request_map[block.sha256] == True:
- # There was a previous request for this block hash
- # Most likely, we delivered a header for this block
- # but never had the block to respond to the getdata
- c.send_message(msg_block(block))
- else:
- c.block_request_map[block.sha256] = False
- # Either send inv's to each node and sync, or add
- # to invqueue for later inv'ing.
- if (test_instance.sync_every_block):
- # if we expect success, send inv and sync every block
- # if we expect failure, just push the block and see what happens.
- if outcome == True:
- [ c.send_inv(block) for c in self.p2p_connections ]
- self.sync_blocks(block.sha256, 1)
- else:
- [ c.send_message(msg_block(block)) for c in self.p2p_connections ]
- [ c.send_ping(self.ping_counter) for c in self.p2p_connections ]
- self.wait_for_pings(self.ping_counter)
- self.ping_counter += 1
- if (not self.check_results(tip, outcome)):
- raise AssertionError("Test failed at test %d" % test_number)
- else:
- invqueue.append(CInv(2, block.sha256))
- elif isinstance(b_or_t, CBlockHeader):
- block_header = b_or_t
- self.block_store.add_header(block_header)
- [ c.send_header(block_header) for c in self.p2p_connections ]
-
- else: # Tx test runner
- assert(isinstance(b_or_t, CTransaction))
- tx = b_or_t
- tx_outcome = outcome
- # Add to shared tx store and clear map entry
- with mininode_lock:
- self.tx_store.add_transaction(tx)
- for c in self.p2p_connections:
- c.tx_request_map[tx.sha256] = False
- # Again, either inv to all nodes or save for later
- if (test_instance.sync_every_tx):
- [ c.send_inv(tx) for c in self.p2p_connections ]
- self.sync_transaction(tx.sha256, 1)
- if (not self.check_mempool(tx.sha256, outcome)):
- raise AssertionError("Test failed at test %d" % test_number)
- else:
- invqueue.append(CInv(1, tx.sha256))
- # Ensure we're not overflowing the inv queue
- if len(invqueue) == MAX_INV_SZ:
- [ c.send_message(msg_inv(invqueue)) for c in self.p2p_connections ]
- invqueue = []
-
- # Do final sync if we weren't syncing on every block or every tx.
- if (not test_instance.sync_every_block and block is not None):
- if len(invqueue) > 0:
- [ c.send_message(msg_inv(invqueue)) for c in self.p2p_connections ]
- invqueue = []
- self.sync_blocks(block.sha256, len(test_instance.blocks_and_transactions))
- if (not self.check_results(tip, block_outcome)):
- raise AssertionError("Block test failed at test %d" % test_number)
- if (not test_instance.sync_every_tx and tx is not None):
- if len(invqueue) > 0:
- [ c.send_message(msg_inv(invqueue)) for c in self.p2p_connections ]
- invqueue = []
- self.sync_transaction(tx.sha256, len(test_instance.blocks_and_transactions))
- if (not self.check_mempool(tx.sha256, tx_outcome)):
- raise AssertionError("Mempool test failed at test %d" % test_number)
-
- [ c.disconnect_node() for c in self.p2p_connections ]
- self.wait_for_disconnections()
- self.block_store.close()
- self.tx_store.close()
diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py
index ee573e01cc..ca2e425bd6 100755
--- a/test/functional/test_framework/messages.py
+++ b/test/functional/test_framework/messages.py
@@ -34,7 +34,9 @@ MY_RELAY = 1 # from version 70001 onwards, fRelay should be appended to version
MAX_INV_SZ = 50000
MAX_BLOCK_BASE_SIZE = 1000000
-COIN = 100000000 # 1 btc in satoshis
+COIN = 100000000 # 1 btc in satoshis
+
+BIP125_SEQUENCE_NUMBER = 0xfffffffd # Sequence number that is BIP 125 opt-in and BIP 68-opt-out
NODE_NETWORK = (1 << 0)
# NODE_GETUTXO = (1 << 1)
@@ -470,6 +472,7 @@ class CTransaction():
def rehash(self):
self.sha256 = None
self.calc_sha256()
+ return self.hash
# We will only cache the serialization without witness in
# self.sha256 and self.hash -- those are expected to be the txid.
diff --git a/test/functional/test_framework/mininode.py b/test/functional/test_framework/mininode.py
index f1f7d0c0cd..aba2841682 100755
--- a/test/functional/test_framework/mininode.py
+++ b/test/functional/test_framework/mininode.py
@@ -498,7 +498,7 @@ class P2PDataStore(P2PInterface):
# as we go.
prev_block_hash = headers_list[-1].hashPrevBlock
if prev_block_hash in self.block_store:
- prev_block_header = self.block_store[prev_block_hash]
+ prev_block_header = CBlockHeader(self.block_store[prev_block_hash])
headers_list.append(prev_block_header)
if prev_block_header.sha256 == hash_stop:
# if this is the hashstop header, stop here
@@ -539,7 +539,7 @@ class P2PDataStore(P2PInterface):
self.block_store[block.sha256] = block
self.last_block_hash = block.sha256
- self.send_message(msg_headers([blocks[-1]]))
+ self.send_message(msg_headers([CBlockHeader(blocks[-1])]))
if request_block:
wait_until(lambda: blocks[-1].sha256 in self.getdata_requests, timeout=timeout, lock=mininode_lock)
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index d427f62856..543643f273 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -432,35 +432,6 @@ class BitcoinTestFramework():
for i in range(self.num_nodes):
initialize_datadir(self.options.tmpdir, i)
-class ComparisonTestFramework(BitcoinTestFramework):
- """Test framework for doing p2p comparison testing
-
- Sets up some bitcoind binaries:
- - 1 binary: test binary
- - 2 binaries: 1 test binary, 1 ref binary
- - n>2 binaries: 1 test binary, n-1 ref binaries"""
-
- def set_test_params(self):
- self.num_nodes = 2
- self.setup_clean_chain = True
-
- def add_options(self, parser):
- parser.add_option("--testbinary", dest="testbinary",
- default=os.getenv("BITCOIND", "bitcoind"),
- help="bitcoind binary to test")
- parser.add_option("--refbinary", dest="refbinary",
- default=os.getenv("BITCOIND", "bitcoind"),
- help="bitcoind binary to use for reference nodes (if any)")
-
- def setup_network(self):
- extra_args = [['-whitelist=127.0.0.1']] * self.num_nodes
- if hasattr(self, "extra_args"):
- extra_args = self.extra_args
- self.add_nodes(self.num_nodes, extra_args,
- binary=[self.options.testbinary] +
- [self.options.refbinary] * (self.num_nodes - 1))
- self.start_nodes()
-
class SkipTest(Exception):
"""This exception is raised to skip a test"""
def __init__(self, message):
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index 583d07deec..4a4ab046c5 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -6,6 +6,7 @@
import decimal
import errno
+from enum import Enum
import http.client
import json
import logging
@@ -30,6 +31,17 @@ JSONDecodeError = getattr(json, "JSONDecodeError", ValueError)
BITCOIND_PROC_WAIT_TIMEOUT = 60
+
+class FailedToStartError(Exception):
+ """Raised when a node fails to start correctly."""
+
+
+class ErrorMatch(Enum):
+ FULL_TEXT = 1
+ FULL_REGEX = 2
+ PARTIAL_REGEX = 3
+
+
class TestNode():
"""A class for representing a bitcoind node under test.
@@ -102,7 +114,8 @@ class TestNode():
# Poll at a rate of four times per second
poll_per_s = 4
for _ in range(poll_per_s * self.rpc_timeout):
- assert self.process.poll() is None, "bitcoind exited with status %i during initialization" % self.process.returncode
+ if self.process.poll() is not None:
+ raise FailedToStartError('bitcoind exited with status {} during initialization'.format(self.process.returncode))
try:
self.rpc = get_rpc_proxy(rpc_url(self.datadir, self.index, self.rpchost), self.index, timeout=self.rpc_timeout, coveragedir=self.coverage_dir)
self.rpc.getblockcount()
@@ -166,7 +179,7 @@ class TestNode():
def wait_until_stopped(self, timeout=BITCOIND_PROC_WAIT_TIMEOUT):
wait_until(self.is_node_stopped, timeout=timeout)
- def assert_start_raises_init_error(self, extra_args=None, expected_msg=None, partial_match=False, *args, **kwargs):
+ def assert_start_raises_init_error(self, extra_args=None, expected_msg=None, match=ErrorMatch.FULL_TEXT, *args, **kwargs):
"""Attempt to start the node and expect it to raise an error.
extra_args: extra arguments to pass through to bitcoind
@@ -179,21 +192,24 @@ class TestNode():
self.start(extra_args, stderr=log_stderr, *args, **kwargs)
self.wait_for_rpc_connection()
self.stop_node()
- self.wait_util_stopped()
- except Exception as e:
- assert 'bitcoind exited' in str(e) # node must have shutdown
+ self.wait_until_stopped()
+ except FailedToStartError as e:
+ self.log.debug('bitcoind failed to start: %s', e)
self.running = False
self.process = None
# Check stderr for expected message
if expected_msg is not None:
log_stderr.seek(0)
stderr = log_stderr.read().decode('utf-8').strip()
- if partial_match:
+ if match == ErrorMatch.PARTIAL_REGEX:
if re.search(expected_msg, stderr, flags=re.MULTILINE) is None:
raise AssertionError('Expected message "{}" does not partially match stderr:\n"{}"'.format(expected_msg, stderr))
- else:
+ elif match == ErrorMatch.FULL_REGEX:
if re.fullmatch(expected_msg, stderr) is None:
raise AssertionError('Expected message "{}" does not fully match stderr:\n"{}"'.format(expected_msg, stderr))
+ elif match == ErrorMatch.FULL_TEXT:
+ if expected_msg != stderr:
+ raise AssertionError('Expected message "{}" does not fully match stderr:\n"{}"'.format(expected_msg, stderr))
else:
if expected_msg is None:
assert_msg = "bitcoind should have exited with an error"
diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py
index 041e2b86e8..a24a2ec4f5 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -8,6 +8,7 @@ from base64 import b64encode
from binascii import hexlify, unhexlify
from decimal import Decimal, ROUND_DOWN
import hashlib
+import inspect
import json
import logging
import os
@@ -204,9 +205,9 @@ def wait_until(predicate, *, attempts=float('inf'), timeout=float('inf'), lock=N
if attempts == float('inf') and timeout == float('inf'):
timeout = 60
attempt = 0
- timeout += time.time()
+ time_end = time.time() + timeout
- while attempt < attempts and time.time() < timeout:
+ while attempt < attempts and time.time() < time_end:
if lock:
with lock:
if predicate():
@@ -218,8 +219,12 @@ def wait_until(predicate, *, attempts=float('inf'), timeout=float('inf'), lock=N
time.sleep(0.05)
# Print the cause of the timeout
- assert_greater_than(attempts, attempt)
- assert_greater_than(timeout, time.time())
+ predicate_source = inspect.getsourcelines(predicate)
+ logger.error("wait_until() failed. Predicate: {}".format(predicate_source))
+ if attempt >= attempts:
+ raise AssertionError("Predicate {} not true after {} attempts".format(predicate_source, attempts))
+ elif time.time() >= time_end:
+ raise AssertionError("Predicate {} not true after {} seconds".format(predicate_source, timeout))
raise RuntimeError('Unreachable')
# RPC/P2P connection constants and functions
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index a2eaf99146..518c16b5f1 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -55,7 +55,7 @@ TEST_EXIT_SKIPPED = 77
# 20 minutes represented in seconds
TRAVIS_TIMEOUT_DURATION = 20 * 60
-BASE_SCRIPTS= [
+BASE_SCRIPTS = [
# Scripts that are run by the travis build process.
# Longest test should go first, to favor running tests in parallel
'wallet_hd.py',
@@ -118,6 +118,7 @@ BASE_SCRIPTS= [
'wallet_importprunedfunds.py',
'rpc_signmessage.py',
'feature_nulldummy.py',
+ 'mempool_accept.py',
'wallet_import_rescan.py',
'mining_basic.py',
'wallet_bumpfee.py',
@@ -136,7 +137,9 @@ BASE_SCRIPTS= [
'p2p_unrequested_blocks.py',
'feature_logging.py',
'p2p_node_network_limited.py',
+ 'feature_blocksdir.py',
'feature_config_args.py',
+ 'feature_help.py',
# Don't append tests at the end to avoid merge conflicts
# Put them in a random line within the section that fits their approximate run-time
]
@@ -156,7 +159,6 @@ EXTENDED_SCRIPTS = [
'mining_getblocktemplate_longpoll.py',
'p2p_timeouts.py',
# vv Tests less than 60s vv
- 'feature_bip9_softforks.py',
'p2p_feefilter.py',
'rpc_bind.py',
# vv Tests less than 30s vv
@@ -366,7 +368,7 @@ def run_tests(test_list, src_dir, build_dir, exeext, tmpdir, jobs=1, enable_cove
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())
+ test_results.sort(key=TestResult.sort_key)
all_passed = True
time_sum = 0
@@ -377,7 +379,11 @@ def print_results(test_results, max_len_name, runtime):
results += str(test_result)
status = TICK + "Passed" if all_passed else CROSS + "Failed"
+ if not all_passed:
+ results += RED[1]
results += BOLD[1] + "\n%s | %s | %s s (accumulated) \n" % ("ALL".ljust(max_len_name), status.ljust(9), time_sum) + BOLD[0]
+ if not all_passed:
+ results += RED[0]
results += "Runtime: %s s\n" % (runtime)
print(results)
@@ -454,6 +460,14 @@ class TestResult():
self.time = time
self.padding = 0
+ def sort_key(self):
+ if self.status == "Passed":
+ return 0, self.name.lower()
+ elif self.status == "Failed":
+ return 2, self.name.lower()
+ elif self.status == "Skipped":
+ return 1, self.name.lower()
+
def __repr__(self):
if self.status == "Passed":
color = BLUE
diff --git a/test/functional/wallet_bumpfee.py b/test/functional/wallet_bumpfee.py
index 3e496248fd..5efc846b6e 100755
--- a/test/functional/wallet_bumpfee.py
+++ b/test/functional/wallet_bumpfee.py
@@ -17,14 +17,12 @@ make assumptions about execution order.
from test_framework.blocktools import send_to_witness
from test_framework.test_framework import BitcoinTestFramework
from test_framework import blocktools
+from test_framework.messages import BIP125_SEQUENCE_NUMBER
from test_framework.mininode import CTransaction
from test_framework.util import *
import io
-# Sequence number that is BIP 125 opt-in and BIP 68-compliant
-BIP125_SEQUENCE_NUMBER = 0xfffffffd
-
WALLET_PASSPHRASE = "test"
WALLET_PASSPHRASE_TIMEOUT = 3600
diff --git a/test/functional/wallet_importmulti.py b/test/functional/wallet_importmulti.py
index 56ebc2622a..91acdb01d0 100755
--- a/test/functional/wallet_importmulti.py
+++ b/test/functional/wallet_importmulti.py
@@ -230,7 +230,7 @@ class ImportMultiTest (BitcoinTestFramework):
sig_address_3 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
multi_sig_script = self.nodes[0].createmultisig(2, [sig_address_1['pubkey'], sig_address_2['pubkey'], sig_address_3['pubkey']])
self.nodes[1].generate(100)
- transactionid = self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00)
+ self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00)
self.nodes[1].generate(1)
timestamp = self.nodes[1].getblock(self.nodes[1].getbestblockhash())['mediantime']
@@ -257,7 +257,7 @@ class ImportMultiTest (BitcoinTestFramework):
sig_address_3 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
multi_sig_script = self.nodes[0].createmultisig(2, [sig_address_1['pubkey'], sig_address_2['pubkey'], sig_address_3['pubkey']])
self.nodes[1].generate(100)
- transactionid = self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00)
+ self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00)
self.nodes[1].generate(1)
timestamp = self.nodes[1].getblock(self.nodes[1].getbestblockhash())['mediantime']
@@ -284,7 +284,7 @@ class ImportMultiTest (BitcoinTestFramework):
sig_address_3 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
multi_sig_script = self.nodes[0].createmultisig(2, [sig_address_1['pubkey'], sig_address_2['pubkey'], sig_address_3['pubkey']])
self.nodes[1].generate(100)
- transactionid = self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00)
+ self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00)
self.nodes[1].generate(1)
timestamp = self.nodes[1].getblock(self.nodes[1].getbestblockhash())['mediantime']
@@ -311,7 +311,7 @@ class ImportMultiTest (BitcoinTestFramework):
sig_address_3 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
multi_sig_script = self.nodes[0].createmultisig(2, [sig_address_1['pubkey'], sig_address_2['pubkey'], sig_address_3['pubkey']])
self.nodes[1].generate(100)
- transactionid = self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00)
+ self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00)
self.nodes[1].generate(1)
timestamp = self.nodes[1].getblock(self.nodes[1].getbestblockhash())['mediantime']
diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py
index 0285263ef9..5ff313997e 100755
--- a/test/functional/wallet_multiwallet.py
+++ b/test/functional/wallet_multiwallet.py
@@ -7,10 +7,10 @@
Verify that a bitcoind node can load multiple wallet files
"""
import os
-import re
import shutil
from test_framework.test_framework import BitcoinTestFramework
+from test_framework.test_node import ErrorMatch
from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
@@ -66,7 +66,7 @@ class MultiWalletTest(BitcoinTestFramework):
# should not initialize if wallet path can't be created
exp_stderr = "boost::filesystem::create_directory: (The system cannot find the path specified|Not a directory):"
- self.nodes[0].assert_start_raises_init_error(['-wallet=wallet.dat/bad'], exp_stderr, partial_match=True)
+ self.nodes[0].assert_start_raises_init_error(['-wallet=wallet.dat/bad'], exp_stderr, match=ErrorMatch.PARTIAL_REGEX)
self.nodes[0].assert_start_raises_init_error(['-walletdir=wallets'], 'Error: Specified -walletdir "wallets" does not exist')
self.nodes[0].assert_start_raises_init_error(['-walletdir=wallets'], 'Error: Specified -walletdir "wallets" is a relative path', cwd=data_dir())
@@ -78,18 +78,18 @@ class MultiWalletTest(BitcoinTestFramework):
# should not initialize if one wallet is a copy of another
shutil.copyfile(wallet_dir('w8'), wallet_dir('w8_copy'))
exp_stderr = "CDB: Can't open database w8_copy \(duplicates fileid \w+ from w8\)"
- self.nodes[0].assert_start_raises_init_error(['-wallet=w8', '-wallet=w8_copy'], exp_stderr, partial_match=True)
+ self.nodes[0].assert_start_raises_init_error(['-wallet=w8', '-wallet=w8_copy'], exp_stderr, match=ErrorMatch.PARTIAL_REGEX)
# should not initialize if wallet file is a symlink
os.symlink('w8', wallet_dir('w8_symlink'))
- self.nodes[0].assert_start_raises_init_error(['-wallet=w8_symlink'], 'Error: Invalid -wallet path \'w8_symlink\'\. .*')
+ self.nodes[0].assert_start_raises_init_error(['-wallet=w8_symlink'], 'Error: Invalid -wallet path \'w8_symlink\'\. .*', match=ErrorMatch.FULL_REGEX)
# should not initialize if the specified walletdir does not exist
self.nodes[0].assert_start_raises_init_error(['-walletdir=bad'], 'Error: Specified -walletdir "bad" does not exist')
# should not initialize if the specified walletdir is not a directory
not_a_dir = wallet_dir('notadir')
open(not_a_dir, 'a').close()
- self.nodes[0].assert_start_raises_init_error(['-walletdir=' + not_a_dir], 'Error: Specified -walletdir "' + re.escape(not_a_dir) + '" is not a directory')
+ self.nodes[0].assert_start_raises_init_error(['-walletdir=' + not_a_dir], 'Error: Specified -walletdir "' + not_a_dir + '" is not a directory')
# if wallets/ doesn't exist, datadir should be the default wallet dir
wallet_dir2 = data_dir('walletdir')
@@ -111,7 +111,7 @@ class MultiWalletTest(BitcoinTestFramework):
os.mkdir(competing_wallet_dir)
self.restart_node(0, ['-walletdir=' + competing_wallet_dir])
exp_stderr = "Error: Error initializing wallet database environment \"\S+competing_walletdir\"!"
- self.nodes[1].assert_start_raises_init_error(['-walletdir=' + competing_wallet_dir], exp_stderr, partial_match=True)
+ self.nodes[1].assert_start_raises_init_error(['-walletdir=' + competing_wallet_dir], exp_stderr, match=ErrorMatch.PARTIAL_REGEX)
self.restart_node(0, extra_args)