aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml45
-rw-r--r--CONTRIBUTING.md3
-rw-r--r--Makefile.am3
-rw-r--r--configure.ac9
-rwxr-xr-xcontrib/devtools/check-doc.py2
-rwxr-xr-xcontrib/devtools/copyright_header.py4
-rwxr-xr-xcontrib/devtools/lint-logs.sh5
-rwxr-xr-xcontrib/devtools/lint-python.sh6
-rwxr-xr-xcontrib/devtools/security-check.py2
-rwxr-xr-xcontrib/devtools/test-security-check.py32
-rwxr-xr-xcontrib/linearize/linearize-data.py532
-rwxr-xr-xcontrib/linearize/linearize-hashes.py248
-rwxr-xr-xcontrib/seeds/generate-seeds.py2
-rwxr-xr-xcontrib/tidy_datadir.sh62
-rw-r--r--depends/Makefile15
-rw-r--r--doc/bips.md3
-rw-r--r--doc/build-windows.md12
-rw-r--r--doc/developer-notes.md9
-rw-r--r--doc/files.md1
-rw-r--r--doc/release-notes-pr10267.md13
-rw-r--r--doc/release-notes-pr12892.md17
-rw-r--r--doc/release-notes-pr13033.md11
-rw-r--r--doc/release-notes.md24
-rw-r--r--doc/tor.md18
-rw-r--r--share/rpcauth/README.md4
-rwxr-xr-xshare/rpcauth/rpcauth.py49
-rw-r--r--share/setup.nsi.in3
-rw-r--r--src/.clang-format5
-rw-r--r--src/Makefile.am6
-rw-r--r--src/Makefile.bench.include2
-rw-r--r--src/Makefile.qttest.include5
-rw-r--r--src/Makefile.test.include3
-rw-r--r--src/addrdb.cpp3
-rw-r--r--src/bench/bench.cpp4
-rw-r--r--src/bench/bench_bitcoin.cpp26
-rw-r--r--src/bench/mempool_eviction.cpp41
-rw-r--r--src/bench/perf.cpp53
-rw-r--r--src/bench/perf.h37
-rw-r--r--src/bench/prevector.cpp2
-rw-r--r--src/bench/verify_script.cpp2
-rw-r--r--src/bitcoin-cli.cpp46
-rw-r--r--src/bitcoin-tx.cpp76
-rw-r--r--src/bitcoind.cpp17
-rw-r--r--src/chainparams.cpp8
-rw-r--r--src/chainparams.h6
-rw-r--r--src/chainparamsbase.cpp11
-rw-r--r--src/chainparamsbase.h5
-rw-r--r--src/compat.h6
-rw-r--r--src/compat/endian.h45
-rw-r--r--src/consensus/params.h4
-rw-r--r--src/core_read.cpp2
-rw-r--r--src/core_write.cpp3
-rw-r--r--src/dbwrapper.h3
-rw-r--r--src/httpserver.cpp4
-rw-r--r--src/httpserver.h2
-rw-r--r--src/index/txindex.cpp311
-rw-r--r--src/index/txindex.h94
-rw-r--r--src/init.cpp467
-rw-r--r--src/init.h14
-rw-r--r--src/interfaces/node.cpp25
-rw-r--r--src/interfaces/node.h30
-rw-r--r--src/interfaces/wallet.cpp25
-rw-r--r--src/interfaces/wallet.h21
-rw-r--r--src/keystore.cpp7
-rw-r--r--src/keystore.h5
-rw-r--r--src/logging.cpp273
-rw-r--r--src/logging.h166
-rw-r--r--src/net.cpp33
-rw-r--r--src/net_processing.cpp45
-rw-r--r--src/netaddress.cpp50
-rw-r--r--src/netaddress.h10
-rw-r--r--src/netbase.cpp8
-rw-r--r--src/policy/policy.h5
-rw-r--r--src/policy/rbf.h2
-rw-r--r--src/qt/README.md2
-rw-r--r--src/qt/addressbookpage.h2
-rw-r--r--src/qt/addresstablemodel.cpp36
-rw-r--r--src/qt/addresstablemodel.h15
-rw-r--r--src/qt/bitcoin.cpp22
-rw-r--r--src/qt/bitcoingui.cpp5
-rw-r--r--src/qt/coincontroldialog.cpp2
-rw-r--r--src/qt/editaddressdialog.cpp21
-rw-r--r--src/qt/editaddressdialog.h5
-rw-r--r--src/qt/paymentserver.cpp2
-rw-r--r--src/qt/rpcconsole.cpp8
-rw-r--r--src/qt/rpcconsole.h18
-rw-r--r--src/qt/sendcoinsdialog.cpp14
-rw-r--r--src/qt/test/addressbooktests.cpp143
-rw-r--r--src/qt/test/addressbooktests.h15
-rw-r--r--src/qt/test/test_main.cpp5
-rw-r--r--src/qt/test/util.cpp22
-rw-r--r--src/qt/test/util.h12
-rw-r--r--src/qt/test/wallettests.cpp24
-rw-r--r--src/qt/transactiondesc.cpp10
-rw-r--r--src/qt/utilitydialog.cpp16
-rw-r--r--src/qt/walletmodel.cpp5
-rw-r--r--src/qt/walletmodel.h2
-rw-r--r--src/random.cpp2
-rw-r--r--src/rest.cpp9
-rw-r--r--src/rpc/blockchain.cpp87
-rw-r--r--src/rpc/client.cpp1
-rw-r--r--src/rpc/mining.cpp24
-rw-r--r--src/rpc/misc.cpp53
-rw-r--r--src/rpc/net.cpp24
-rw-r--r--src/rpc/protocol.h4
-rw-r--r--src/rpc/rawtransaction.cpp98
-rw-r--r--src/rpc/safemode.cpp14
-rw-r--r--src/rpc/safemode.h12
-rw-r--r--src/rpc/server.cpp8
-rw-r--r--src/script/interpreter.cpp43
-rw-r--r--src/script/interpreter.h6
-rw-r--r--src/script/ismine.cpp78
-rw-r--r--src/script/ismine.h7
-rw-r--r--src/script/script.cpp52
-rw-r--r--src/script/script.h102
-rw-r--r--src/script/script_error.cpp4
-rw-r--r--src/script/script_error.h4
-rw-r--r--src/script/standard.h2
-rw-r--r--src/support/lockedpool.cpp2
-rw-r--r--src/test/DoS_tests.cpp6
-rw-r--r--src/test/arith_uint256_tests.cpp10
-rw-r--r--src/test/bech32_tests.cpp2
-rw-r--r--src/test/bip32_tests.cpp2
-rw-r--r--src/test/blockchain_tests.cpp10
-rw-r--r--src/test/blockencodings_tests.cpp23
-rw-r--r--src/test/checkqueue_tests.cpp2
-rw-r--r--src/test/coins_tests.cpp12
-rw-r--r--src/test/crypto_tests.cpp26
-rw-r--r--src/test/cuckoocache_tests.cpp12
-rw-r--r--src/test/data/tx_invalid.json48
-rw-r--r--src/test/data/tx_valid.json10
-rw-r--r--src/test/dbwrapper_tests.cpp2
-rw-r--r--src/test/main_tests.cpp4
-rw-r--r--src/test/mempool_tests.cpp2
-rw-r--r--src/test/miner_tests.cpp6
-rw-r--r--src/test/multisig_tests.cpp4
-rw-r--r--src/test/net_tests.cpp2
-rw-r--r--src/test/rpc_tests.cpp4
-rw-r--r--src/test/script_standard_tests.cpp9
-rw-r--r--src/test/script_tests.cpp46
-rw-r--r--src/test/serialize_tests.cpp7
-rw-r--r--src/test/sighash_tests.cpp2
-rw-r--r--src/test/sigopcount_tests.cpp4
-rw-r--r--src/test/test_bitcoin.cpp13
-rw-r--r--src/test/test_bitcoin.h6
-rw-r--r--src/test/test_bitcoin_fuzzy.cpp4
-rw-r--r--src/test/torcontrol_tests.cpp4
-rw-r--r--src/test/transaction_tests.cpp7
-rw-r--r--src/test/txindex_tests.cpp66
-rw-r--r--src/test/txvalidationcache_tests.cpp6
-rw-r--r--src/test/uint256_tests.cpp2
-rw-r--r--src/test/util_tests.cpp2
-rw-r--r--src/test/versionbits_tests.cpp2
-rw-r--r--src/threadinterrupt.cpp2
-rw-r--r--src/threadinterrupt.h1
-rw-r--r--src/timedata.cpp6
-rw-r--r--src/txdb.cpp171
-rw-r--r--src/txdb.h38
-rw-r--r--src/txmempool.h6
-rw-r--r--src/util.cpp381
-rw-r--r--src/util.h143
-rw-r--r--src/utiltime.cpp12
-rw-r--r--src/validation.cpp224
-rw-r--r--src/validation.h4
-rw-r--r--src/validationinterface.cpp16
-rw-r--r--src/validationinterface.h15
-rw-r--r--src/wallet/coincontrol.h8
-rw-r--r--src/wallet/coinselection.cpp4
-rw-r--r--src/wallet/db.cpp8
-rw-r--r--src/wallet/feebumper.cpp10
-rw-r--r--src/wallet/fees.cpp42
-rw-r--r--src/wallet/fees.h17
-rw-r--r--src/wallet/init.cpp179
-rw-r--r--src/wallet/rpcdump.cpp32
-rw-r--r--src/wallet/rpcwallet.cpp547
-rw-r--r--src/wallet/test/wallet_tests.cpp25
-rw-r--r--src/wallet/wallet.cpp317
-rw-r--r--src/wallet/wallet.h156
-rw-r--r--src/wallet/walletdb.cpp6
-rw-r--r--src/walletinitinterface.h18
-rw-r--r--src/warnings.cpp16
-rw-r--r--src/warnings.h3
-rw-r--r--test/config.ini.in1
-rwxr-xr-xtest/functional/feature_bip68_sequence.py8
-rwxr-xr-xtest/functional/feature_block.py31
-rwxr-xr-xtest/functional/feature_includeconf.py75
-rwxr-xr-xtest/functional/feature_logging.py21
-rwxr-xr-xtest/functional/feature_maxuploadtarget.py2
-rwxr-xr-xtest/functional/feature_proxy.py6
-rwxr-xr-xtest/functional/feature_pruning.py4
-rwxr-xr-xtest/functional/feature_rbf.py46
-rwxr-xr-xtest/functional/feature_segwit.py28
-rwxr-xr-xtest/functional/interface_zmq.py3
-rwxr-xr-xtest/functional/mempool_packages.py7
-rwxr-xr-xtest/functional/mining_prioritisetransaction.py2
-rwxr-xr-xtest/functional/p2p_compactblocks.py5
-rwxr-xr-xtest/functional/p2p_feefilter.py2
-rwxr-xr-xtest/functional/p2p_invalid_tx.py112
-rwxr-xr-xtest/functional/p2p_segwit.py125
-rwxr-xr-xtest/functional/p2p_sendheaders.py96
-rwxr-xr-xtest/functional/p2p_unrequested_blocks.py6
-rwxr-xr-xtest/functional/rpc_bind.py104
-rwxr-xr-xtest/functional/rpc_decodescript.py60
-rwxr-xr-xtest/functional/rpc_deprecated.py87
-rwxr-xr-xtest/functional/rpc_fundrawtransaction.py22
-rwxr-xr-xtest/functional/rpc_rawtransaction.py23
-rwxr-xr-xtest/functional/rpc_users.py47
-rw-r--r--test/functional/test_framework/blocktools.py112
-rwxr-xr-xtest/functional/test_framework/mininode.py11
-rwxr-xr-xtest/functional/test_framework/test_framework.py35
-rwxr-xr-xtest/functional/test_framework/test_node.py94
-rw-r--r--test/functional/test_framework/util.py23
-rwxr-xr-xtest/functional/test_runner.py51
-rwxr-xr-xtest/functional/wallet_address_types.py5
-rwxr-xr-xtest/functional/wallet_basic.py182
-rwxr-xr-xtest/functional/wallet_dump.py2
-rwxr-xr-xtest/functional/wallet_hd.py35
-rwxr-xr-xtest/functional/wallet_import_rescan.py42
-rwxr-xr-xtest/functional/wallet_importprunedfunds.py30
-rwxr-xr-xtest/functional/wallet_keypool_topup.py2
-rwxr-xr-xtest/functional/wallet_labels.py57
-rwxr-xr-xtest/functional/wallet_listreceivedby.py42
-rwxr-xr-xtest/functional/wallet_listsinceblock.py16
-rwxr-xr-xtest/functional/wallet_listtransactions.py (renamed from test/functional/rpc_listtransactions.py)120
-rwxr-xr-xtest/functional/wallet_multiwallet.py20
-rwxr-xr-xtest/functional/wallet_txn_clone.py91
-rwxr-xr-xtest/functional/wallet_txn_doublespend.py60
-rw-r--r--test/util/data/blanktxv1.json1
-rw-r--r--test/util/data/blanktxv2.json1
-rw-r--r--test/util/data/tt-delin1-out.json1
-rw-r--r--test/util/data/tt-delout1-out.json1
-rw-r--r--test/util/data/tt-locktime317000-out.json1
-rw-r--r--test/util/data/txcreate1.json1
-rw-r--r--test/util/data/txcreate2.json1
-rw-r--r--test/util/data/txcreatedata1.json1
-rw-r--r--test/util/data/txcreatedata2.json1
-rw-r--r--test/util/data/txcreatedata_seq0.json1
-rw-r--r--test/util/data/txcreatedata_seq1.json1
-rw-r--r--test/util/data/txcreatemultisig1.json1
-rw-r--r--test/util/data/txcreatemultisig2.json1
-rw-r--r--test/util/data/txcreatemultisig3.json1
-rw-r--r--test/util/data/txcreatemultisig4.json1
-rw-r--r--test/util/data/txcreatemultisig5.json1
-rw-r--r--test/util/data/txcreateoutpubkey1.json1
-rw-r--r--test/util/data/txcreateoutpubkey2.json1
-rw-r--r--test/util/data/txcreateoutpubkey3.json1
-rw-r--r--test/util/data/txcreatescript1.json1
-rw-r--r--test/util/data/txcreatescript2.json1
-rw-r--r--test/util/data/txcreatescript3.json1
-rw-r--r--test/util/data/txcreatescript4.json1
-rw-r--r--test/util/data/txcreatesignv1.json1
-rwxr-xr-xtest/util/rpcauth-test.py48
252 files changed, 5685 insertions, 3308 deletions
diff --git a/.travis.yml b/.travis.yml
index 7f8bc638ff..af5553f3b7 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -8,11 +8,13 @@ cache:
- depends/built
- depends/sdk-sources
- $HOME/.ccache
+stages:
+ - lint
+ - test
env:
global:
- MAKEJOBS=-j3
- RUN_TESTS=false
- - CHECK_DOC=0
- BOOST_TEST_RANDOM=1$TRAVIS_BUILD_ID
- CCACHE_SIZE=100M
- CCACHE_TEMPDIR=/tmp/.ccache-temp
@@ -22,7 +24,7 @@ env:
- WINEDEBUG=fixme-all
matrix:
# ARM
- - HOST=arm-linux-gnueabihf PACKAGES="g++-arm-linux-gnueabihf python3-pip shellcheck" DEP_OPTS="NO_QT=1" CHECK_DOC=1 GOAL="install" BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports"
+ - HOST=arm-linux-gnueabihf PACKAGES="g++-arm-linux-gnueabihf" DEP_OPTS="NO_QT=1" GOAL="install" BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports"
# Win32
- HOST=i686-w64-mingw32 DPKG_ADD_ARCH="i386" DEP_OPTS="NO_QT=1" PACKAGES="python3 nsis g++-mingw-w64-i686 wine1.6" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-reduce-exports"
# Win64
@@ -44,17 +46,7 @@ install:
- if [ -n "$DPKG_ADD_ARCH" ]; then sudo dpkg --add-architecture "$DPKG_ADD_ARCH" ; fi
- if [ -n "$PACKAGES" ]; then travis_retry sudo apt-get update; fi
- if [ -n "$PACKAGES" ]; then travis_retry sudo apt-get install --no-install-recommends --no-upgrade -qq $PACKAGES; fi
- - if [ "$CHECK_DOC" = 1 -a "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then travis_retry pip3 install flake8 --user; fi
before_script:
- - if [ "$CHECK_DOC" = 1 ]; then git fetch --unshallow; fi
- - if [ "$CHECK_DOC" = 1 -a "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then contrib/devtools/commit-script-check.sh $TRAVIS_COMMIT_RANGE; fi
- - if [ "$CHECK_DOC" = 1 ]; then contrib/devtools/git-subtree-check.sh src/crypto/ctaes; fi
- - if [ "$CHECK_DOC" = 1 ]; then contrib/devtools/git-subtree-check.sh src/secp256k1; fi
- - if [ "$CHECK_DOC" = 1 ]; then contrib/devtools/git-subtree-check.sh src/univalue; fi
- - if [ "$CHECK_DOC" = 1 ]; then contrib/devtools/git-subtree-check.sh src/leveldb; fi
- - if [ "$CHECK_DOC" = 1 ]; then contrib/devtools/check-doc.py; fi
- - if [ "$CHECK_DOC" = 1 ]; then contrib/devtools/check-rpc-mappings.py .; fi
- - if [ "$CHECK_DOC" = 1 -a "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then contrib/devtools/lint-all.sh; fi
- unset CC; unset CXX
- mkdir -p depends/SDKs depends/sdk-sources
- if [ -n "$OSX_SDK" -a ! -f depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz ]; then curl --location --fail $SDK_URL/MacOSX${OSX_SDK}.sdk.tar.gz -o depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz; fi
@@ -63,8 +55,6 @@ before_script:
# Start xvfb if needed, as documented at https://docs.travis-ci.com/user/gui-and-headless-browsers/#Using-xvfb-to-Run-Tests-That-Require-a-GUI
- 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_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
@@ -80,7 +70,32 @@ script:
- export LD_LIBRARY_PATH=$TRAVIS_BUILD_DIR/depends/$HOST/lib
- if [ "$RUN_TESTS" = "true" ]; then travis_wait 50 make $MAKEJOBS check VERBOSE=1; fi
- if [ "$TRAVIS_EVENT_TYPE" = "cron" ]; then extended="--extended --exclude feature_pruning,feature_dbcrash"; fi
- - if [ "$RUN_TESTS" = "true" ]; then test/functional/test_runner.py --combinedlogslen=4000 --coverage --quiet ${extended}; fi
+ - if [ "$RUN_TESTS" = "true" ]; then test/functional/test_runner.py --combinedlogslen=4000 --coverage --quiet --failfast ${extended}; fi
after_script:
- echo $TRAVIS_COMMIT_RANGE
- echo $TRAVIS_COMMIT_LOG
+
+jobs:
+ include:
+ - stage: lint
+ sudo: false
+ addons:
+ apt:
+ packages:
+ - python3-pip
+ - shellcheck
+ install:
+ - if [ "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then travis_retry pip3 install flake8 --user; fi
+ before_script:
+ - git fetch --unshallow
+ - if [ "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then contrib/devtools/commit-script-check.sh $TRAVIS_COMMIT_RANGE; fi
+ - contrib/devtools/git-subtree-check.sh src/crypto/ctaes
+ - contrib/devtools/git-subtree-check.sh src/secp256k1
+ - contrib/devtools/git-subtree-check.sh src/univalue
+ - contrib/devtools/git-subtree-check.sh src/leveldb
+ - contrib/devtools/check-doc.py
+ - contrib/devtools/check-rpc-mappings.py .
+ - if [ "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then contrib/devtools/lint-all.sh; fi
+ script:
+ - if [ "$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 [ "$TRAVIS_REPO_SLUG" = "bitcoin/bitcoin" -a "$TRAVIS_EVENT_TYPE" = "cron" ]; then travis_wait 30 contrib/verify-commits/verify-commits.sh; fi
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index ea475f8cfb..c390595abf 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -14,6 +14,9 @@ purposes. As such there are repository "maintainers" who are responsible for
merging pull requests as well as a "lead maintainer" who is responsible for the
release cycle, overall merging, moderation and appointment of maintainers.
+If you're looking for somewhere to start contributing, check out the
+[good first issue](https://github.com/bitcoin/bitcoin/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)
+list.
Communication Channels
----------------------
diff --git a/Makefile.am b/Makefile.am
index f554501b2e..8a8debb079 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -269,7 +269,8 @@ EXTRA_DIST += \
test/util/data/txcreatescript4.json \
test/util/data/txcreatesignv1.hex \
test/util/data/txcreatesignv1.json \
- test/util/data/txcreatesignv2.hex
+ test/util/data/txcreatesignv2.hex \
+ test/util/rpcauth-test.py
CLEANFILES = $(OSX_DMG) $(BITCOIN_WIN_INSTALLER)
diff --git a/configure.ac b/configure.ac
index 03e024128a..95acd2bd86 100644
--- a/configure.ac
+++ b/configure.ac
@@ -84,7 +84,7 @@ AC_PATH_TOOL(STRIP, strip)
AC_PATH_TOOL(GCOV, gcov)
AC_PATH_PROG(LCOV, lcov)
dnl Python 3.x is supported from 3.4 on (see https://github.com/bitcoin/bitcoin/issues/7893)
-AC_PATH_PROGS([PYTHON], [python3.6 python3.5 python3.4 python3 python2.7 python2 python])
+AC_PATH_PROGS([PYTHON], [python3.7 python3.6 python3.5 python3.4 python3 python])
AC_PATH_PROG(GENHTML, genhtml)
AC_PATH_PROG([GIT], [git])
AC_PATH_PROG(CCACHE,ccache)
@@ -638,11 +638,7 @@ if test x$use_hardening != xno; then
AX_CHECK_LINK_FLAG([[-Wl,--high-entropy-va]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,--high-entropy-va"])
AX_CHECK_LINK_FLAG([[-Wl,-z,relro]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,-z,relro"])
AX_CHECK_LINK_FLAG([[-Wl,-z,now]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,-z,now"])
-
- if test x$TARGET_OS != xwindows; then
- AX_CHECK_COMPILE_FLAG([-fPIE],[PIE_FLAGS="-fPIE"])
- AX_CHECK_LINK_FLAG([[-pie]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -pie"])
- fi
+ AX_CHECK_LINK_FLAG([[-fPIE -pie]], [PIE_FLAGS="-fPIE"; HARDENED_LDFLAGS="$HARDENED_LDFLAGS -pie"],, [[$CXXFLAG_WERROR]])
case $host in
*mingw*)
@@ -1318,6 +1314,7 @@ AM_COND_IF([HAVE_DOXYGEN], [AC_CONFIG_FILES([doc/Doxyfile])])
AC_CONFIG_LINKS([contrib/filter-lcov.py:contrib/filter-lcov.py])
AC_CONFIG_LINKS([test/functional/test_runner.py:test/functional/test_runner.py])
AC_CONFIG_LINKS([test/util/bitcoin-util-test.py:test/util/bitcoin-util-test.py])
+AC_CONFIG_LINKS([test/util/rpcauth-test.py:test/util/rpcauth-test.py])
dnl boost's m4 checks do something really nasty: they export these vars. As a
dnl result, they leak into secp256k1's configure and crazy things happen.
diff --git a/contrib/devtools/check-doc.py b/contrib/devtools/check-doc.py
index 0c2e1a24be..de5719eb29 100755
--- a/contrib/devtools/check-doc.py
+++ b/contrib/devtools/check-doc.py
@@ -17,7 +17,7 @@ import sys
FOLDER_GREP = 'src'
FOLDER_TEST = 'src/test/'
REGEX_ARG = '(?:ForceSet|SoftSet|Get|Is)(?:Bool)?Args?(?:Set)?\("(-[^"]+)"'
-REGEX_DOC = 'HelpMessageOpt\("(-[^"=]+?)(?:=|")'
+REGEX_DOC = 'AddArg\("(-[^"=]+?)(?:=|")'
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)
diff --git a/contrib/devtools/copyright_header.py b/contrib/devtools/copyright_header.py
index e7cccaab03..82d3c19683 100755
--- a/contrib/devtools/copyright_header.py
+++ b/contrib/devtools/copyright_header.py
@@ -506,7 +506,7 @@ def file_has_hashbang(file_lines):
def insert_python_header(filename, file_lines, start_year, end_year):
if file_has_hashbang(file_lines):
- insert_idx = 1
+ insert_idx = 1
else:
insert_idx = 0
header_lines = get_python_header_lines_to_insert(start_year, end_year)
@@ -571,7 +571,7 @@ def insert_cmd(argv):
if extension not in ['.h', '.cpp', '.cc', '.c', '.py']:
sys.exit("*** cannot insert for file extension %s" % extension)
- if extension == '.py':
+ if extension == '.py':
style = 'python'
else:
style = 'cpp'
diff --git a/contrib/devtools/lint-logs.sh b/contrib/devtools/lint-logs.sh
index 3bb54359a8..35be13ec19 100755
--- a/contrib/devtools/lint-logs.sh
+++ b/contrib/devtools/lint-logs.sh
@@ -13,12 +13,13 @@
# ignored
-UNTERMINATED_LOGS=$(git grep "LogPrintf(" -- "*.cpp" | \
+UNTERMINATED_LOGS=$(git grep --extended-regexp "LogPrintf?\(" -- "*.cpp" | \
grep -v '\\n"' | \
grep -v "/\* Continued \*/" | \
+ grep -v "LogPrint()" | \
grep -v "LogPrintf()")
if [[ ${UNTERMINATED_LOGS} != "" ]]; then
- echo "All calls to LogPrintf() should be terminated with \\n"
+ echo "All calls to LogPrintf() and LogPrint() should be terminated with \\n"
echo
echo "${UNTERMINATED_LOGS}"
exit 1
diff --git a/contrib/devtools/lint-python.sh b/contrib/devtools/lint-python.sh
index 239337000d..7d3555b6d4 100755
--- a/contrib/devtools/lint-python.sh
+++ b/contrib/devtools/lint-python.sh
@@ -6,11 +6,13 @@
#
# Check for specified flake8 warnings in python files.
+# E101 indentation contains mixed spaces and tabs
# E112 expected an indented block
# E113 unexpected indentation
# E115 expected an indented block (comment)
# E116 unexpected indentation (comment)
# E125 continuation line with same indent as next logical line
+# E129 visually indented line with same indent as next logical line
# E131 continuation line unaligned for hanging indent
# E133 closing bracket is missing indentation
# E223 tab before operator
@@ -61,6 +63,8 @@
# F823 local variable name … referenced before assignment
# F831 duplicate argument name in function definition
# F841 local variable 'foo' is assigned to but never used
+# W191 indentation contains tabs
+# W291 trailing whitespace
# W292 no newline at end of file
# W293 blank line contains whitespace
# W504 line break after binary operator
@@ -71,4 +75,4 @@
# W605 invalid escape sequence "x"
# W606 'async' and 'await' are reserved keywords starting with Python 3.7
-flake8 --ignore=B,C,E,F,I,N,W --select=E112,E113,E115,E116,E125,E131,E133,E223,E224,E242,E266,E271,E272,E273,E274,E275,E304,E306,E401,E402,E502,E701,E702,E703,E714,E721,E741,E742,E743,F401,E901,E902,F402,F404,F406,F407,F601,F602,F621,F622,F631,F701,F702,F703,F704,F705,F706,F707,F811,F812,F821,F822,F823,F831,F841,W292,W293,W504,W601,W602,W603,W604,W605,W606 .
+flake8 --ignore=B,C,E,F,I,N,W --select=E101,E112,E113,E115,E116,E125,E129,E131,E133,E223,E224,E242,E266,E271,E272,E273,E274,E275,E304,E306,E401,E402,E502,E701,E702,E703,E714,E721,E741,E742,E743,F401,E901,E902,F402,F404,F406,F407,F601,F602,F621,F622,F631,F701,F702,F703,F704,F705,F706,F707,F811,F812,F821,F822,F823,F831,F841,W191,W291,W292,W293,W504,W601,W602,W603,W604,W605,W606 .
diff --git a/contrib/devtools/security-check.py b/contrib/devtools/security-check.py
index 0f2099953f..c9516ef83f 100755
--- a/contrib/devtools/security-check.py
+++ b/contrib/devtools/security-check.py
@@ -150,7 +150,7 @@ def check_PE_DYNAMIC_BASE(executable):
def check_PE_HIGH_ENTROPY_VA(executable):
'''PIE: DllCharacteristics bit 0x20 signifies high-entropy ASLR'''
(arch,bits) = get_PE_dll_characteristics(executable)
- if arch == 'i386:x86-64':
+ if arch == 'i386:x86-64':
reqbits = IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA
else: # Unnecessary on 32-bit
assert(arch == 'i386')
diff --git a/contrib/devtools/test-security-check.py b/contrib/devtools/test-security-check.py
index ee87c8bab4..307e057736 100755
--- a/contrib/devtools/test-security-check.py
+++ b/contrib/devtools/test-security-check.py
@@ -32,29 +32,39 @@ class TestSecurityChecks(unittest.TestCase):
cc = 'gcc'
write_testcode(source)
- self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-zexecstack','-fno-stack-protector','-Wl,-znorelro']),
+ self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-zexecstack','-fno-stack-protector','-Wl,-znorelro']),
(1, executable+': failed PIE NX RELRO Canary'))
- self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fno-stack-protector','-Wl,-znorelro']),
+ self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fno-stack-protector','-Wl,-znorelro']),
(1, executable+': failed PIE RELRO Canary'))
- self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fstack-protector-all','-Wl,-znorelro']),
+ self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fstack-protector-all','-Wl,-znorelro']),
(1, executable+': failed PIE RELRO'))
- self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fstack-protector-all','-Wl,-znorelro','-pie','-fPIE']),
+ self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fstack-protector-all','-Wl,-znorelro','-pie','-fPIE']),
(1, executable+': failed RELRO'))
- self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fstack-protector-all','-Wl,-zrelro','-Wl,-z,now','-pie','-fPIE']),
+ self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fstack-protector-all','-Wl,-zrelro','-Wl,-z,now','-pie','-fPIE']),
(0, ''))
- def test_PE(self):
+ def test_32bit_PE(self):
source = 'test1.c'
executable = 'test1.exe'
cc = 'i686-w64-mingw32-gcc'
write_testcode(source)
- self.assertEqual(call_security_check(cc, source, executable, []),
- (1, executable+': failed PIE NX'))
- self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat']),
- (1, executable+': failed PIE'))
- self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat','-Wl,--dynamicbase']),
+ self.assertEqual(call_security_check(cc, source, executable, []),
+ (1, executable+': failed DYNAMIC_BASE NX'))
+ self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat']),
+ (1, executable+': failed DYNAMIC_BASE'))
+ self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat','-Wl,--dynamicbase']),
(0, ''))
+ def test_64bit_PE(self):
+ source = 'test1.c'
+ executable = 'test1.exe'
+ cc = 'x86_64-w64-mingw32-gcc'
+ write_testcode(source)
+
+ self.assertEqual(call_security_check(cc, source, executable, []), (1, executable+': failed DYNAMIC_BASE NX\n'+executable+': warning HIGH_ENTROPY_VA'))
+ self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat']), (1, executable+': failed DYNAMIC_BASE\n'+executable+': warning HIGH_ENTROPY_VA'))
+ self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat','-Wl,--dynamicbase']), (0, executable+': warning HIGH_ENTROPY_VA'))
+ self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat','-Wl,--dynamicbase','-Wl,--high-entropy-va']), (0, ''))
if __name__ == '__main__':
unittest.main()
diff --git a/contrib/linearize/linearize-data.py b/contrib/linearize/linearize-data.py
index c609e9b336..f8aea27342 100755
--- a/contrib/linearize/linearize-data.py
+++ b/contrib/linearize/linearize-data.py
@@ -22,300 +22,300 @@ from binascii import hexlify, unhexlify
settings = {}
def hex_switchEndian(s):
- """ Switches the endianness of a hex string (in pairs of hex chars) """
- pairList = [s[i:i+2].encode() for i in range(0, len(s), 2)]
- return b''.join(pairList[::-1]).decode()
+ """ Switches the endianness of a hex string (in pairs of hex chars) """
+ pairList = [s[i:i+2].encode() for i in range(0, len(s), 2)]
+ return b''.join(pairList[::-1]).decode()
def uint32(x):
- return x & 0xffffffff
+ return x & 0xffffffff
def bytereverse(x):
- return uint32(( ((x) << 24) | (((x) << 8) & 0x00ff0000) |
- (((x) >> 8) & 0x0000ff00) | ((x) >> 24) ))
+ return uint32(( ((x) << 24) | (((x) << 8) & 0x00ff0000) |
+ (((x) >> 8) & 0x0000ff00) | ((x) >> 24) ))
def bufreverse(in_buf):
- out_words = []
- for i in range(0, len(in_buf), 4):
- word = struct.unpack('@I', in_buf[i:i+4])[0]
- out_words.append(struct.pack('@I', bytereverse(word)))
- return b''.join(out_words)
+ out_words = []
+ for i in range(0, len(in_buf), 4):
+ word = struct.unpack('@I', in_buf[i:i+4])[0]
+ out_words.append(struct.pack('@I', bytereverse(word)))
+ return b''.join(out_words)
def wordreverse(in_buf):
- out_words = []
- for i in range(0, len(in_buf), 4):
- out_words.append(in_buf[i:i+4])
- out_words.reverse()
- return b''.join(out_words)
+ out_words = []
+ for i in range(0, len(in_buf), 4):
+ out_words.append(in_buf[i:i+4])
+ out_words.reverse()
+ return b''.join(out_words)
def calc_hdr_hash(blk_hdr):
- hash1 = hashlib.sha256()
- hash1.update(blk_hdr)
- hash1_o = hash1.digest()
+ hash1 = hashlib.sha256()
+ hash1.update(blk_hdr)
+ hash1_o = hash1.digest()
- hash2 = hashlib.sha256()
- hash2.update(hash1_o)
- hash2_o = hash2.digest()
+ hash2 = hashlib.sha256()
+ hash2.update(hash1_o)
+ hash2_o = hash2.digest()
- return hash2_o
+ return hash2_o
def calc_hash_str(blk_hdr):
- hash = calc_hdr_hash(blk_hdr)
- hash = bufreverse(hash)
- hash = wordreverse(hash)
- hash_str = hexlify(hash).decode('utf-8')
- return hash_str
+ hash = calc_hdr_hash(blk_hdr)
+ hash = bufreverse(hash)
+ hash = wordreverse(hash)
+ hash_str = hexlify(hash).decode('utf-8')
+ return hash_str
def get_blk_dt(blk_hdr):
- members = struct.unpack("<I", blk_hdr[68:68+4])
- nTime = members[0]
- dt = datetime.datetime.fromtimestamp(nTime)
- dt_ym = datetime.datetime(dt.year, dt.month, 1)
- return (dt_ym, nTime)
+ members = struct.unpack("<I", blk_hdr[68:68+4])
+ nTime = members[0]
+ dt = datetime.datetime.fromtimestamp(nTime)
+ dt_ym = datetime.datetime(dt.year, dt.month, 1)
+ return (dt_ym, nTime)
# When getting the list of block hashes, undo any byte reversals.
def get_block_hashes(settings):
- blkindex = []
- f = open(settings['hashlist'], "r")
- for line in f:
- line = line.rstrip()
- if settings['rev_hash_bytes'] == 'true':
- line = hex_switchEndian(line)
- blkindex.append(line)
+ blkindex = []
+ f = open(settings['hashlist'], "r")
+ for line in f:
+ line = line.rstrip()
+ if settings['rev_hash_bytes'] == 'true':
+ line = hex_switchEndian(line)
+ blkindex.append(line)
- print("Read " + str(len(blkindex)) + " hashes")
+ print("Read " + str(len(blkindex)) + " hashes")
- return blkindex
+ return blkindex
# The block map shouldn't give or receive byte-reversed hashes.
def mkblockmap(blkindex):
- blkmap = {}
- for height,hash in enumerate(blkindex):
- blkmap[hash] = height
- return blkmap
+ blkmap = {}
+ for height,hash in enumerate(blkindex):
+ blkmap[hash] = height
+ return blkmap
# Block header and extent on disk
BlockExtent = namedtuple('BlockExtent', ['fn', 'offset', 'inhdr', 'blkhdr', 'size'])
class BlockDataCopier:
- def __init__(self, settings, blkindex, blkmap):
- self.settings = settings
- self.blkindex = blkindex
- self.blkmap = blkmap
-
- self.inFn = 0
- self.inF = None
- self.outFn = 0
- self.outsz = 0
- self.outF = None
- self.outFname = None
- self.blkCountIn = 0
- self.blkCountOut = 0
-
- self.lastDate = datetime.datetime(2000, 1, 1)
- self.highTS = 1408893517 - 315360000
- self.timestampSplit = False
- self.fileOutput = True
- self.setFileTime = False
- self.maxOutSz = settings['max_out_sz']
- if 'output' in settings:
- self.fileOutput = False
- if settings['file_timestamp'] != 0:
- self.setFileTime = True
- if settings['split_timestamp'] != 0:
- self.timestampSplit = True
- # Extents and cache for out-of-order blocks
- self.blockExtents = {}
- self.outOfOrderData = {}
- self.outOfOrderSize = 0 # running total size for items in outOfOrderData
-
- def writeBlock(self, inhdr, blk_hdr, rawblock):
- blockSizeOnDisk = len(inhdr) + len(blk_hdr) + len(rawblock)
- if not self.fileOutput and ((self.outsz + blockSizeOnDisk) > self.maxOutSz):
- self.outF.close()
- if self.setFileTime:
- os.utime(self.outFname, (int(time.time()), self.highTS))
- self.outF = None
- self.outFname = None
- self.outFn = self.outFn + 1
- self.outsz = 0
-
- (blkDate, blkTS) = get_blk_dt(blk_hdr)
- if self.timestampSplit and (blkDate > self.lastDate):
- print("New month " + blkDate.strftime("%Y-%m") + " @ " + self.hash_str)
- self.lastDate = blkDate
- if self.outF:
- self.outF.close()
- if self.setFileTime:
- os.utime(self.outFname, (int(time.time()), self.highTS))
- self.outF = None
- self.outFname = None
- self.outFn = self.outFn + 1
- self.outsz = 0
-
- if not self.outF:
- if self.fileOutput:
- self.outFname = self.settings['output_file']
- else:
- self.outFname = os.path.join(self.settings['output'], "blk%05d.dat" % self.outFn)
- print("Output file " + self.outFname)
- self.outF = open(self.outFname, "wb")
-
- self.outF.write(inhdr)
- self.outF.write(blk_hdr)
- self.outF.write(rawblock)
- self.outsz = self.outsz + len(inhdr) + len(blk_hdr) + len(rawblock)
-
- self.blkCountOut = self.blkCountOut + 1
- if blkTS > self.highTS:
- self.highTS = blkTS
-
- if (self.blkCountOut % 1000) == 0:
- print('%i blocks scanned, %i blocks written (of %i, %.1f%% complete)' %
- (self.blkCountIn, self.blkCountOut, len(self.blkindex), 100.0 * self.blkCountOut / len(self.blkindex)))
-
- def inFileName(self, fn):
- return os.path.join(self.settings['input'], "blk%05d.dat" % fn)
-
- def fetchBlock(self, extent):
- '''Fetch block contents from disk given extents'''
- with open(self.inFileName(extent.fn), "rb") as f:
- f.seek(extent.offset)
- return f.read(extent.size)
-
- def copyOneBlock(self):
- '''Find the next block to be written in the input, and copy it to the output.'''
- extent = self.blockExtents.pop(self.blkCountOut)
- if self.blkCountOut in self.outOfOrderData:
- # If the data is cached, use it from memory and remove from the cache
- rawblock = self.outOfOrderData.pop(self.blkCountOut)
- self.outOfOrderSize -= len(rawblock)
- else: # Otherwise look up data on disk
- rawblock = self.fetchBlock(extent)
-
- self.writeBlock(extent.inhdr, extent.blkhdr, rawblock)
-
- def run(self):
- while self.blkCountOut < len(self.blkindex):
- if not self.inF:
- fname = self.inFileName(self.inFn)
- print("Input file " + fname)
- try:
- self.inF = open(fname, "rb")
- except IOError:
- print("Premature end of block data")
- return
-
- inhdr = self.inF.read(8)
- if (not inhdr or (inhdr[0] == "\0")):
- self.inF.close()
- self.inF = None
- self.inFn = self.inFn + 1
- continue
-
- inMagic = inhdr[:4]
- if (inMagic != self.settings['netmagic']):
- print("Invalid magic: " + hexlify(inMagic).decode('utf-8'))
- return
- inLenLE = inhdr[4:]
- su = struct.unpack("<I", inLenLE)
- inLen = su[0] - 80 # length without header
- blk_hdr = self.inF.read(80)
- inExtent = BlockExtent(self.inFn, self.inF.tell(), inhdr, blk_hdr, inLen)
-
- self.hash_str = calc_hash_str(blk_hdr)
- if not self.hash_str in blkmap:
- # Because blocks can be written to files out-of-order as of 0.10, the script
- # may encounter blocks it doesn't know about. Treat as debug output.
- if settings['debug_output'] == 'true':
- print("Skipping unknown block " + self.hash_str)
- self.inF.seek(inLen, os.SEEK_CUR)
- continue
-
- blkHeight = self.blkmap[self.hash_str]
- self.blkCountIn += 1
-
- if self.blkCountOut == blkHeight:
- # If in-order block, just copy
- rawblock = self.inF.read(inLen)
- self.writeBlock(inhdr, blk_hdr, rawblock)
-
- # See if we can catch up to prior out-of-order blocks
- while self.blkCountOut in self.blockExtents:
- self.copyOneBlock()
-
- else: # If out-of-order, skip over block data for now
- self.blockExtents[blkHeight] = inExtent
- if self.outOfOrderSize < self.settings['out_of_order_cache_sz']:
- # If there is space in the cache, read the data
- # Reading the data in file sequence instead of seeking and fetching it later is preferred,
- # but we don't want to fill up memory
- self.outOfOrderData[blkHeight] = self.inF.read(inLen)
- self.outOfOrderSize += inLen
- else: # If no space in cache, seek forward
- self.inF.seek(inLen, os.SEEK_CUR)
-
- print("Done (%i blocks written)" % (self.blkCountOut))
+ def __init__(self, settings, blkindex, blkmap):
+ self.settings = settings
+ self.blkindex = blkindex
+ self.blkmap = blkmap
+
+ self.inFn = 0
+ self.inF = None
+ self.outFn = 0
+ self.outsz = 0
+ self.outF = None
+ self.outFname = None
+ self.blkCountIn = 0
+ self.blkCountOut = 0
+
+ self.lastDate = datetime.datetime(2000, 1, 1)
+ self.highTS = 1408893517 - 315360000
+ self.timestampSplit = False
+ self.fileOutput = True
+ self.setFileTime = False
+ self.maxOutSz = settings['max_out_sz']
+ if 'output' in settings:
+ self.fileOutput = False
+ if settings['file_timestamp'] != 0:
+ self.setFileTime = True
+ if settings['split_timestamp'] != 0:
+ self.timestampSplit = True
+ # Extents and cache for out-of-order blocks
+ self.blockExtents = {}
+ self.outOfOrderData = {}
+ self.outOfOrderSize = 0 # running total size for items in outOfOrderData
+
+ def writeBlock(self, inhdr, blk_hdr, rawblock):
+ blockSizeOnDisk = len(inhdr) + len(blk_hdr) + len(rawblock)
+ if not self.fileOutput and ((self.outsz + blockSizeOnDisk) > self.maxOutSz):
+ self.outF.close()
+ if self.setFileTime:
+ os.utime(self.outFname, (int(time.time()), self.highTS))
+ self.outF = None
+ self.outFname = None
+ self.outFn = self.outFn + 1
+ self.outsz = 0
+
+ (blkDate, blkTS) = get_blk_dt(blk_hdr)
+ if self.timestampSplit and (blkDate > self.lastDate):
+ print("New month " + blkDate.strftime("%Y-%m") + " @ " + self.hash_str)
+ self.lastDate = blkDate
+ if self.outF:
+ self.outF.close()
+ if self.setFileTime:
+ os.utime(self.outFname, (int(time.time()), self.highTS))
+ self.outF = None
+ self.outFname = None
+ self.outFn = self.outFn + 1
+ self.outsz = 0
+
+ if not self.outF:
+ if self.fileOutput:
+ self.outFname = self.settings['output_file']
+ else:
+ self.outFname = os.path.join(self.settings['output'], "blk%05d.dat" % self.outFn)
+ print("Output file " + self.outFname)
+ self.outF = open(self.outFname, "wb")
+
+ self.outF.write(inhdr)
+ self.outF.write(blk_hdr)
+ self.outF.write(rawblock)
+ self.outsz = self.outsz + len(inhdr) + len(blk_hdr) + len(rawblock)
+
+ self.blkCountOut = self.blkCountOut + 1
+ if blkTS > self.highTS:
+ self.highTS = blkTS
+
+ if (self.blkCountOut % 1000) == 0:
+ print('%i blocks scanned, %i blocks written (of %i, %.1f%% complete)' %
+ (self.blkCountIn, self.blkCountOut, len(self.blkindex), 100.0 * self.blkCountOut / len(self.blkindex)))
+
+ def inFileName(self, fn):
+ return os.path.join(self.settings['input'], "blk%05d.dat" % fn)
+
+ def fetchBlock(self, extent):
+ '''Fetch block contents from disk given extents'''
+ with open(self.inFileName(extent.fn), "rb") as f:
+ f.seek(extent.offset)
+ return f.read(extent.size)
+
+ def copyOneBlock(self):
+ '''Find the next block to be written in the input, and copy it to the output.'''
+ extent = self.blockExtents.pop(self.blkCountOut)
+ if self.blkCountOut in self.outOfOrderData:
+ # If the data is cached, use it from memory and remove from the cache
+ rawblock = self.outOfOrderData.pop(self.blkCountOut)
+ self.outOfOrderSize -= len(rawblock)
+ else: # Otherwise look up data on disk
+ rawblock = self.fetchBlock(extent)
+
+ self.writeBlock(extent.inhdr, extent.blkhdr, rawblock)
+
+ def run(self):
+ while self.blkCountOut < len(self.blkindex):
+ if not self.inF:
+ fname = self.inFileName(self.inFn)
+ print("Input file " + fname)
+ try:
+ self.inF = open(fname, "rb")
+ except IOError:
+ print("Premature end of block data")
+ return
+
+ inhdr = self.inF.read(8)
+ if (not inhdr or (inhdr[0] == "\0")):
+ self.inF.close()
+ self.inF = None
+ self.inFn = self.inFn + 1
+ continue
+
+ inMagic = inhdr[:4]
+ if (inMagic != self.settings['netmagic']):
+ print("Invalid magic: " + hexlify(inMagic).decode('utf-8'))
+ return
+ inLenLE = inhdr[4:]
+ su = struct.unpack("<I", inLenLE)
+ inLen = su[0] - 80 # length without header
+ blk_hdr = self.inF.read(80)
+ inExtent = BlockExtent(self.inFn, self.inF.tell(), inhdr, blk_hdr, inLen)
+
+ self.hash_str = calc_hash_str(blk_hdr)
+ if not self.hash_str in blkmap:
+ # Because blocks can be written to files out-of-order as of 0.10, the script
+ # may encounter blocks it doesn't know about. Treat as debug output.
+ if settings['debug_output'] == 'true':
+ print("Skipping unknown block " + self.hash_str)
+ self.inF.seek(inLen, os.SEEK_CUR)
+ continue
+
+ blkHeight = self.blkmap[self.hash_str]
+ self.blkCountIn += 1
+
+ if self.blkCountOut == blkHeight:
+ # If in-order block, just copy
+ rawblock = self.inF.read(inLen)
+ self.writeBlock(inhdr, blk_hdr, rawblock)
+
+ # See if we can catch up to prior out-of-order blocks
+ while self.blkCountOut in self.blockExtents:
+ self.copyOneBlock()
+
+ else: # If out-of-order, skip over block data for now
+ self.blockExtents[blkHeight] = inExtent
+ if self.outOfOrderSize < self.settings['out_of_order_cache_sz']:
+ # If there is space in the cache, read the data
+ # Reading the data in file sequence instead of seeking and fetching it later is preferred,
+ # but we don't want to fill up memory
+ self.outOfOrderData[blkHeight] = self.inF.read(inLen)
+ self.outOfOrderSize += inLen
+ else: # If no space in cache, seek forward
+ self.inF.seek(inLen, os.SEEK_CUR)
+
+ print("Done (%i blocks written)" % (self.blkCountOut))
if __name__ == '__main__':
- if len(sys.argv) != 2:
- print("Usage: linearize-data.py CONFIG-FILE")
- sys.exit(1)
-
- f = open(sys.argv[1])
- for line in f:
- # skip comment lines
- m = re.search('^\s*#', line)
- if m:
- continue
-
- # parse key=value lines
- m = re.search('^(\w+)\s*=\s*(\S.*)$', line)
- if m is None:
- continue
- settings[m.group(1)] = m.group(2)
- f.close()
-
- # Force hash byte format setting to be lowercase to make comparisons easier.
- # Also place upfront in case any settings need to know about it.
- if 'rev_hash_bytes' not in settings:
- settings['rev_hash_bytes'] = 'false'
- settings['rev_hash_bytes'] = settings['rev_hash_bytes'].lower()
-
- if 'netmagic' not in settings:
- settings['netmagic'] = 'f9beb4d9'
- if 'genesis' not in settings:
- settings['genesis'] = '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f'
- if 'input' not in settings:
- settings['input'] = 'input'
- if 'hashlist' not in settings:
- settings['hashlist'] = 'hashlist.txt'
- if 'file_timestamp' not in settings:
- settings['file_timestamp'] = 0
- if 'split_timestamp' not in settings:
- settings['split_timestamp'] = 0
- if 'max_out_sz' not in settings:
- settings['max_out_sz'] = 1000 * 1000 * 1000
- if 'out_of_order_cache_sz' not in settings:
- settings['out_of_order_cache_sz'] = 100 * 1000 * 1000
- if 'debug_output' not in settings:
- settings['debug_output'] = 'false'
-
- settings['max_out_sz'] = int(settings['max_out_sz'])
- settings['split_timestamp'] = int(settings['split_timestamp'])
- settings['file_timestamp'] = int(settings['file_timestamp'])
- settings['netmagic'] = unhexlify(settings['netmagic'].encode('utf-8'))
- settings['out_of_order_cache_sz'] = int(settings['out_of_order_cache_sz'])
- settings['debug_output'] = settings['debug_output'].lower()
-
- if 'output_file' not in settings and 'output' not in settings:
- print("Missing output file / directory")
- sys.exit(1)
-
- blkindex = get_block_hashes(settings)
- blkmap = mkblockmap(blkindex)
-
- # Block hash map won't be byte-reversed. Neither should the genesis hash.
- if not settings['genesis'] in blkmap:
- print("Genesis block not found in hashlist")
- else:
- BlockDataCopier(settings, blkindex, blkmap).run()
+ if len(sys.argv) != 2:
+ print("Usage: linearize-data.py CONFIG-FILE")
+ sys.exit(1)
+
+ f = open(sys.argv[1])
+ for line in f:
+ # skip comment lines
+ m = re.search('^\s*#', line)
+ if m:
+ continue
+
+ # parse key=value lines
+ m = re.search('^(\w+)\s*=\s*(\S.*)$', line)
+ if m is None:
+ continue
+ settings[m.group(1)] = m.group(2)
+ f.close()
+
+ # Force hash byte format setting to be lowercase to make comparisons easier.
+ # Also place upfront in case any settings need to know about it.
+ if 'rev_hash_bytes' not in settings:
+ settings['rev_hash_bytes'] = 'false'
+ settings['rev_hash_bytes'] = settings['rev_hash_bytes'].lower()
+
+ if 'netmagic' not in settings:
+ settings['netmagic'] = 'f9beb4d9'
+ if 'genesis' not in settings:
+ settings['genesis'] = '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f'
+ if 'input' not in settings:
+ settings['input'] = 'input'
+ if 'hashlist' not in settings:
+ settings['hashlist'] = 'hashlist.txt'
+ if 'file_timestamp' not in settings:
+ settings['file_timestamp'] = 0
+ if 'split_timestamp' not in settings:
+ settings['split_timestamp'] = 0
+ if 'max_out_sz' not in settings:
+ settings['max_out_sz'] = 1000 * 1000 * 1000
+ if 'out_of_order_cache_sz' not in settings:
+ settings['out_of_order_cache_sz'] = 100 * 1000 * 1000
+ if 'debug_output' not in settings:
+ settings['debug_output'] = 'false'
+
+ settings['max_out_sz'] = int(settings['max_out_sz'])
+ settings['split_timestamp'] = int(settings['split_timestamp'])
+ settings['file_timestamp'] = int(settings['file_timestamp'])
+ settings['netmagic'] = unhexlify(settings['netmagic'].encode('utf-8'))
+ settings['out_of_order_cache_sz'] = int(settings['out_of_order_cache_sz'])
+ settings['debug_output'] = settings['debug_output'].lower()
+
+ if 'output_file' not in settings and 'output' not in settings:
+ print("Missing output file / directory")
+ sys.exit(1)
+
+ blkindex = get_block_hashes(settings)
+ blkmap = mkblockmap(blkindex)
+
+ # Block hash map won't be byte-reversed. Neither should the genesis hash.
+ if not settings['genesis'] in blkmap:
+ print("Genesis block not found in hashlist")
+ else:
+ BlockDataCopier(settings, blkindex, blkmap).run()
diff --git a/contrib/linearize/linearize-hashes.py b/contrib/linearize/linearize-hashes.py
index e1304e26d0..8e1266ae0b 100755
--- a/contrib/linearize/linearize-hashes.py
+++ b/contrib/linearize/linearize-hashes.py
@@ -22,135 +22,135 @@ import os.path
settings = {}
def hex_switchEndian(s):
- """ Switches the endianness of a hex string (in pairs of hex chars) """
- pairList = [s[i:i+2].encode() for i in range(0, len(s), 2)]
- return b''.join(pairList[::-1]).decode()
+ """ Switches the endianness of a hex string (in pairs of hex chars) """
+ pairList = [s[i:i+2].encode() for i in range(0, len(s), 2)]
+ return b''.join(pairList[::-1]).decode()
class BitcoinRPC:
- def __init__(self, host, port, username, password):
- authpair = "%s:%s" % (username, password)
- authpair = authpair.encode('utf-8')
- self.authhdr = b"Basic " + base64.b64encode(authpair)
- self.conn = httplib.HTTPConnection(host, port=port, timeout=30)
-
- def execute(self, obj):
- try:
- self.conn.request('POST', '/', json.dumps(obj),
- { 'Authorization' : self.authhdr,
- 'Content-type' : 'application/json' })
- except ConnectionRefusedError:
- print('RPC connection refused. Check RPC settings and the server status.',
- file=sys.stderr)
- return None
-
- resp = self.conn.getresponse()
- if resp is None:
- print("JSON-RPC: no response", file=sys.stderr)
- return None
-
- body = resp.read().decode('utf-8')
- resp_obj = json.loads(body)
- return resp_obj
-
- @staticmethod
- def build_request(idx, method, params):
- obj = { 'version' : '1.1',
- 'method' : method,
- 'id' : idx }
- if params is None:
- obj['params'] = []
- else:
- obj['params'] = params
- return obj
-
- @staticmethod
- def response_is_error(resp_obj):
- return 'error' in resp_obj and resp_obj['error'] is not None
+ def __init__(self, host, port, username, password):
+ authpair = "%s:%s" % (username, password)
+ authpair = authpair.encode('utf-8')
+ self.authhdr = b"Basic " + base64.b64encode(authpair)
+ self.conn = httplib.HTTPConnection(host, port=port, timeout=30)
+
+ def execute(self, obj):
+ try:
+ self.conn.request('POST', '/', json.dumps(obj),
+ { 'Authorization' : self.authhdr,
+ 'Content-type' : 'application/json' })
+ except ConnectionRefusedError:
+ print('RPC connection refused. Check RPC settings and the server status.',
+ file=sys.stderr)
+ return None
+
+ resp = self.conn.getresponse()
+ if resp is None:
+ print("JSON-RPC: no response", file=sys.stderr)
+ return None
+
+ body = resp.read().decode('utf-8')
+ resp_obj = json.loads(body)
+ return resp_obj
+
+ @staticmethod
+ def build_request(idx, method, params):
+ obj = { 'version' : '1.1',
+ 'method' : method,
+ 'id' : idx }
+ if params is None:
+ obj['params'] = []
+ else:
+ obj['params'] = params
+ return obj
+
+ @staticmethod
+ def response_is_error(resp_obj):
+ return 'error' in resp_obj and resp_obj['error'] is not None
def get_block_hashes(settings, max_blocks_per_call=10000):
- rpc = BitcoinRPC(settings['host'], settings['port'],
- settings['rpcuser'], settings['rpcpassword'])
-
- height = settings['min_height']
- while height < settings['max_height']+1:
- num_blocks = min(settings['max_height']+1-height, max_blocks_per_call)
- batch = []
- for x in range(num_blocks):
- batch.append(rpc.build_request(x, 'getblockhash', [height + x]))
-
- reply = rpc.execute(batch)
- if reply is None:
- print('Cannot continue. Program will halt.')
- return None
-
- for x,resp_obj in enumerate(reply):
- if rpc.response_is_error(resp_obj):
- print('JSON-RPC: error at height', height+x, ': ', resp_obj['error'], file=sys.stderr)
- sys.exit(1)
- assert(resp_obj['id'] == x) # assume replies are in-sequence
- if settings['rev_hash_bytes'] == 'true':
- resp_obj['result'] = hex_switchEndian(resp_obj['result'])
- print(resp_obj['result'])
-
- height += num_blocks
+ rpc = BitcoinRPC(settings['host'], settings['port'],
+ settings['rpcuser'], settings['rpcpassword'])
+
+ height = settings['min_height']
+ while height < settings['max_height']+1:
+ num_blocks = min(settings['max_height']+1-height, max_blocks_per_call)
+ batch = []
+ for x in range(num_blocks):
+ batch.append(rpc.build_request(x, 'getblockhash', [height + x]))
+
+ reply = rpc.execute(batch)
+ if reply is None:
+ print('Cannot continue. Program will halt.')
+ return None
+
+ for x,resp_obj in enumerate(reply):
+ if rpc.response_is_error(resp_obj):
+ print('JSON-RPC: error at height', height+x, ': ', resp_obj['error'], file=sys.stderr)
+ sys.exit(1)
+ assert(resp_obj['id'] == x) # assume replies are in-sequence
+ if settings['rev_hash_bytes'] == 'true':
+ resp_obj['result'] = hex_switchEndian(resp_obj['result'])
+ print(resp_obj['result'])
+
+ height += num_blocks
def get_rpc_cookie():
- # Open the cookie file
- with open(os.path.join(os.path.expanduser(settings['datadir']), '.cookie'), 'r') as f:
- combined = f.readline()
- combined_split = combined.split(":")
- settings['rpcuser'] = combined_split[0]
- settings['rpcpassword'] = combined_split[1]
+ # Open the cookie file
+ with open(os.path.join(os.path.expanduser(settings['datadir']), '.cookie'), 'r') as f:
+ combined = f.readline()
+ combined_split = combined.split(":")
+ settings['rpcuser'] = combined_split[0]
+ settings['rpcpassword'] = combined_split[1]
if __name__ == '__main__':
- if len(sys.argv) != 2:
- print("Usage: linearize-hashes.py CONFIG-FILE")
- sys.exit(1)
-
- f = open(sys.argv[1])
- for line in f:
- # skip comment lines
- m = re.search('^\s*#', line)
- if m:
- continue
-
- # parse key=value lines
- m = re.search('^(\w+)\s*=\s*(\S.*)$', line)
- if m is None:
- continue
- settings[m.group(1)] = m.group(2)
- f.close()
-
- if 'host' not in settings:
- settings['host'] = '127.0.0.1'
- if 'port' not in settings:
- settings['port'] = 8332
- if 'min_height' not in settings:
- settings['min_height'] = 0
- if 'max_height' not in settings:
- settings['max_height'] = 313000
- if 'rev_hash_bytes' not in settings:
- settings['rev_hash_bytes'] = 'false'
-
- use_userpass = True
- use_datadir = False
- if 'rpcuser' not in settings or 'rpcpassword' not in settings:
- use_userpass = False
- if 'datadir' in settings and not use_userpass:
- use_datadir = True
- if not use_userpass and not use_datadir:
- print("Missing datadir or username and/or password in cfg file", file=sys.stderr)
- sys.exit(1)
-
- settings['port'] = int(settings['port'])
- settings['min_height'] = int(settings['min_height'])
- settings['max_height'] = int(settings['max_height'])
-
- # Force hash byte format setting to be lowercase to make comparisons easier.
- settings['rev_hash_bytes'] = settings['rev_hash_bytes'].lower()
-
- # Get the rpc user and pass from the cookie if the datadir is set
- if use_datadir:
- get_rpc_cookie()
-
- get_block_hashes(settings)
+ if len(sys.argv) != 2:
+ print("Usage: linearize-hashes.py CONFIG-FILE")
+ sys.exit(1)
+
+ f = open(sys.argv[1])
+ for line in f:
+ # skip comment lines
+ m = re.search('^\s*#', line)
+ if m:
+ continue
+
+ # parse key=value lines
+ m = re.search('^(\w+)\s*=\s*(\S.*)$', line)
+ if m is None:
+ continue
+ settings[m.group(1)] = m.group(2)
+ f.close()
+
+ if 'host' not in settings:
+ settings['host'] = '127.0.0.1'
+ if 'port' not in settings:
+ settings['port'] = 8332
+ if 'min_height' not in settings:
+ settings['min_height'] = 0
+ if 'max_height' not in settings:
+ settings['max_height'] = 313000
+ if 'rev_hash_bytes' not in settings:
+ settings['rev_hash_bytes'] = 'false'
+
+ use_userpass = True
+ use_datadir = False
+ if 'rpcuser' not in settings or 'rpcpassword' not in settings:
+ use_userpass = False
+ if 'datadir' in settings and not use_userpass:
+ use_datadir = True
+ if not use_userpass and not use_datadir:
+ print("Missing datadir or username and/or password in cfg file", file=sys.stderr)
+ sys.exit(1)
+
+ settings['port'] = int(settings['port'])
+ settings['min_height'] = int(settings['min_height'])
+ settings['max_height'] = int(settings['max_height'])
+
+ # Force hash byte format setting to be lowercase to make comparisons easier.
+ settings['rev_hash_bytes'] = settings['rev_hash_bytes'].lower()
+
+ # Get the rpc user and pass from the cookie if the datadir is set
+ if use_datadir:
+ get_rpc_cookie()
+
+ get_block_hashes(settings)
diff --git a/contrib/seeds/generate-seeds.py b/contrib/seeds/generate-seeds.py
index 72eb7255f3..ace7d3534f 100755
--- a/contrib/seeds/generate-seeds.py
+++ b/contrib/seeds/generate-seeds.py
@@ -11,7 +11,7 @@ argument:
nodes_main.txt
nodes_test.txt
-These files must consist of lines in the format
+These files must consist of lines in the format
<ip>
<ip>:<port>
diff --git a/contrib/tidy_datadir.sh b/contrib/tidy_datadir.sh
deleted file mode 100755
index b845b34e41..0000000000
--- a/contrib/tidy_datadir.sh
+++ /dev/null
@@ -1,62 +0,0 @@
-#!/bin/bash
-# Copyright (c) 2013 The Bitcoin Core developers
-# Distributed under the MIT software license, see the accompanying
-# file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-if [ -d "$1" ]; then
- cd "$1" || exit 1
-else
- echo "Usage: $0 <datadir>" >&2
- echo "Removes obsolete Bitcoin database files" >&2
- exit 1
-fi
-
-LEVEL=0
-if [ -f wallet.dat -a -f addr.dat -a -f blkindex.dat -a -f blk0001.dat ]; then LEVEL=1; fi
-if [ -f wallet.dat -a -f peers.dat -a -f blkindex.dat -a -f blk0001.dat ]; then LEVEL=2; fi
-if [ -f wallet.dat -a -f peers.dat -a -f coins/CURRENT -a -f blktree/CURRENT -a -f blocks/blk00000.dat ]; then LEVEL=3; fi
-if [ -f wallet.dat -a -f peers.dat -a -f chainstate/CURRENT -a -f blocks/index/CURRENT -a -f blocks/blk00000.dat ]; then LEVEL=4; fi
-
-case $LEVEL in
- 0)
- echo "Error: no Bitcoin datadir detected."
- exit 1
- ;;
- 1)
- echo "Detected old Bitcoin datadir (before 0.7)."
- echo "Nothing to do."
- exit 0
- ;;
- 2)
- echo "Detected Bitcoin 0.7 datadir."
- ;;
- 3)
- echo "Detected Bitcoin pre-0.8 datadir."
- ;;
- 4)
- echo "Detected Bitcoin 0.8 datadir."
- ;;
-esac
-
-FILES=""
-DIRS=""
-
-if [ $LEVEL -ge 3 ]; then FILES=$(echo $FILES blk????.dat blkindex.dat); fi
-if [ $LEVEL -ge 2 ]; then FILES=$(echo $FILES addr.dat); fi
-if [ $LEVEL -ge 4 ]; then DIRS=$(echo $DIRS coins blktree); fi
-
-for FILE in $FILES; do
- if [ -f $FILE ]; then
- echo "Deleting: $FILE"
- rm -f $FILE
- fi
-done
-
-for DIR in $DIRS; do
- if [ -d $DIR ]; then
- echo "Deleting: $DIR/"
- rm -rf $DIR
- fi
-done
-
-echo "Done."
diff --git a/depends/Makefile b/depends/Makefile
index 14e94ba453..8b67bce9d8 100644
--- a/depends/Makefile
+++ b/depends/Makefile
@@ -1,6 +1,7 @@
.NOTPARALLEL :
SOURCES_PATH ?= $(BASEDIR)/sources
+WORK_PATH = $(BASEDIR)/work
BASE_CACHE ?= $(BASEDIR)/built
SDK_PATH ?= $(BASEDIR)/SDKs
NO_QT ?=
@@ -29,9 +30,9 @@ else
release_type=release
endif
-base_build_dir=$(BASEDIR)/work/build
-base_staging_dir=$(BASEDIR)/work/staging
-base_download_dir=$(BASEDIR)/work/download
+base_build_dir=$(WORK_PATH)/build
+base_staging_dir=$(WORK_PATH)/staging
+base_download_dir=$(WORK_PATH)/download
canonical_host:=$(shell ./config.sub $(HOST))
build:=$(shell ./config.sub $(BUILD))
@@ -165,6 +166,12 @@ $(host_prefix)/share/config.site: check-packages
check-packages: check-sources
+clean-all: clean
+ @rm -rf $(SOURCES_PATH) x86_64* i686* mips* arm* aarch64*
+
+clean:
+ @rm -rf $(WORK_PATH) $(BASE_CACHE) $(BUILD)
+
install: check-packages $(host_prefix)/share/config.site
@@ -178,4 +185,4 @@ download-win:
@$(MAKE) -s HOST=x86_64-w64-mingw32 download-one
download: download-osx download-linux download-win
-.PHONY: install cached download-one download-osx download-linux download-win download check-packages check-sources
+.PHONY: install cached clean clean-all download-one download-osx download-linux download-win download check-packages check-sources
diff --git a/doc/bips.md b/doc/bips.md
index e587275f0f..5b8eabd6df 100644
--- a/doc/bips.md
+++ b/doc/bips.md
@@ -1,4 +1,4 @@
-BIPs that are implemented by Bitcoin Core (up-to-date up to **v0.13.0**):
+BIPs that are implemented by Bitcoin Core (up-to-date up to **v0.16.0**):
* [`BIP 9`](https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki): The changes allowing multiple soft-forks to be deployed in parallel have been implemented since **v0.12.1** ([PR #7575](https://github.com/bitcoin/bitcoin/pull/7575))
* [`BIP 11`](https://github.com/bitcoin/bips/blob/master/bip-0011.mediawiki): Multisig outputs are standard since **v0.6.0** ([PR #669](https://github.com/bitcoin/bitcoin/pull/669)).
@@ -34,4 +34,5 @@ BIPs that are implemented by Bitcoin Core (up-to-date up to **v0.13.0**):
* [`BIP 147`](https://github.com/bitcoin/bips/blob/master/bip-0147.mediawiki): NULLDUMMY softfork as of **v0.13.1** ([PR 8636](https://github.com/bitcoin/bitcoin/pull/8636) and [PR 8937](https://github.com/bitcoin/bitcoin/pull/8937)).
* [`BIP 152`](https://github.com/bitcoin/bips/blob/master/bip-0152.mediawiki): Compact block transfer and related optimizations are used as of **v0.13.0** ([PR 8068](https://github.com/bitcoin/bitcoin/pull/8068)).
* [`BIP 159`](https://github.com/bitcoin/bips/blob/master/bip-0159.mediawiki): NODE_NETWORK_LIMITED service bit [signaling only] is supported as of **v0.16.0** ([PR 11740](https://github.com/bitcoin/bitcoin/pull/11740)).
+* [`BIP 173`](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki): Bech32 addresses for native Segregated Witness outputs are supported as of **v0.16.0** ([PR 11167](https://github.com/bitcoin/bitcoin/pull/11167)).
* [`BIP 176`](https://github.com/bitcoin/bips/blob/master/bip-0176.mediawiki): Bits Denomination [QT only] is supported as of **v0.16.0** ([PR 12035](https://github.com/bitcoin/bitcoin/pull/12035)).
diff --git a/doc/build-windows.md b/doc/build-windows.md
index 0a4136173b..07bb0c096a 100644
--- a/doc/build-windows.md
+++ b/doc/build-windows.md
@@ -53,8 +53,8 @@ Cross-compilation for Ubuntu and Windows Subsystem for Linux
At the time of writing the Windows Subsystem for Linux installs Ubuntu Xenial 16.04. The Mingw-w64 package
for Ubuntu Xenial does not produce working executables for some of the Bitcoin Core applications.
-It is possible to build on Ubuntu Xenial by installing the cross compiler packages from Ubuntu Zesty, see the steps below.
-Building on Ubuntu Zesty 17.04 up to 17.10 has been verified to work.
+It is possible to build on Ubuntu Xenial by installing the cross compiler packages from Ubuntu Artful, see the steps below.
+Building on Ubuntu Artful 17.10 has been verified to work.
The steps below can be performed on Ubuntu (including in a VM) or WSL. The depends system
will also work on other Linux distributions, however the commands for
@@ -88,12 +88,12 @@ Ubuntu Trusty 14.04:
Ubuntu Xenial 16.04 and Windows Subsystem for Linux <sup>[1](#footnote1),[2](#footnote2)</sup>:
sudo apt install software-properties-common
- sudo add-apt-repository "deb http://archive.ubuntu.com/ubuntu zesty universe"
+ sudo add-apt-repository "deb http://archive.ubuntu.com/ubuntu artful universe"
sudo apt update
sudo apt upgrade
sudo update-alternatives --config x86_64-w64-mingw32-g++ # Set the default mingw32 g++ compiler option to posix.
-Ubuntu Zesty 17.04 <sup>[2](#footnote2)</sup>:
+Ubuntu Artful 17.10 <sup>[2](#footnote2)</sup>:
sudo update-alternatives --config x86_64-w64-mingw32-g++ # Set the default mingw32 g++ compiler option to posix.
@@ -123,7 +123,7 @@ To build executables for Windows 32-bit, install the following dependencies:
sudo apt install g++-mingw-w64-i686 mingw-w64-i686-dev
-For Ubuntu Xenial 16.04, Ubuntu Zesty 17.04 and Windows Subsystem for Linux <sup>[2](#footnote2)</sup>:
+For Ubuntu Xenial 16.04, Ubuntu Artful 17.10 and Windows Subsystem for Linux <sup>[2](#footnote2)</sup>:
sudo update-alternatives --config i686-w64-mingw32-g++ # Set the default mingw32 g++ compiler option to posix.
@@ -165,7 +165,7 @@ Footnotes
<a name="footnote1">1</a>: There is currently a bug in the 64 bit Mingw-w64 cross compiler packaged for WSL/Ubuntu Xenial 16.04 that
causes two of the bitcoin executables to crash shortly after start up. The bug is related to the
-fstack-protector-all g++ compiler flag which is used to mitigate buffer overflows.
-Installing the Mingw-w64 packages from the Ubuntu 17 distribution solves the issue, however, this is not
+Installing the Mingw-w64 packages from the Ubuntu 17.10 distribution solves the issue, however, this is not
an officially supported approach and it's only recommended if you are prepared to reinstall WSL/Ubuntu should
something break.
diff --git a/doc/developer-notes.md b/doc/developer-notes.md
index 6b5311d3e3..1f237b750e 100644
--- a/doc/developer-notes.md
+++ b/doc/developer-notes.md
@@ -50,7 +50,7 @@ Do not submit patches solely to modify the style of existing code.
[src/.clang-format](/src/.clang-format). You can use the provided
[clang-format-diff script](/contrib/devtools/README.md#clang-format-diffpy)
tool to clean up patches automatically before submission.
- - Braces on new lines for namespaces, classes, functions, methods.
+ - Braces on new lines for classes, functions, methods.
- Braces on the same line for everything else.
- 4 space indentation (no tabs) for every block except namespaces.
- No indentation for `public`/`protected`/`private` or for `namespace`.
@@ -85,8 +85,7 @@ Block style example:
```c++
int g_count = 0;
-namespace foo
-{
+namespace foo {
class Class
{
std::string m_name;
@@ -585,11 +584,11 @@ Source code organization
```c++
namespace mynamespace {
- ...
+...
} // namespace mynamespace
namespace {
- ...
+...
} // namespace
```
diff --git a/doc/files.md b/doc/files.md
index 2eac7ed664..5657b1e6cb 100644
--- a/doc/files.md
+++ b/doc/files.md
@@ -10,6 +10,7 @@
* db.log: wallet database log file; moved to wallets/ directory on new installs since 0.16.0
* debug.log: contains debug information and general logging generated by bitcoind or bitcoin-qt
* fee_estimates.dat: stores statistics used to estimate minimum transaction fees and priorities required for confirmation; since 0.10.0
+* indexes/txindex/*: optional transaction index database (LevelDB); since 0.17.0
* mempool.dat: dump of the mempool's transactions; since 0.14.0.
* peers.dat: peer IP address database (custom format); since 0.7.0
* wallet.dat: personal wallet (BDB) with keys and transactions; moved to wallets/ directory on new installs since 0.16.0
diff --git a/doc/release-notes-pr10267.md b/doc/release-notes-pr10267.md
new file mode 100644
index 0000000000..7e1967daf0
--- /dev/null
+++ b/doc/release-notes-pr10267.md
@@ -0,0 +1,13 @@
+Changed command-line options
+----------------------------
+
+- `-includeconf=<file>` can be used to include additional configuration files.
+ Only works inside the `bitcoin.conf` file, not inside included files or from
+ command-line. Multiple files may be included. Can be disabled from command-
+ line via `-noincludeconf`. Note that multi-argument commands like
+ `-includeconf` will override preceding `-noincludeconf`, i.e.
+
+ noincludeconf=1
+ includeconf=relative.conf
+
+ as bitcoin.conf will still include `relative.conf`.
diff --git a/doc/release-notes-pr12892.md b/doc/release-notes-pr12892.md
index 894bf19dee..8105eca5c0 100644
--- a/doc/release-notes-pr12892.md
+++ b/doc/release-notes-pr12892.md
@@ -1,8 +1,10 @@
-'label' API for wallet
-----------------------
+'label' and 'account' APIs for wallet
+-------------------------------------
A new 'label' API has been introduced for the wallet. This is intended as a
-replacement for the deprecated 'account' API.
+replacement for the deprecated 'account' API. The 'account' can continue to
+be used in V0.17 by starting bitcoind with the '-deprecatedrpc=accounts'
+argument, and will be fully removed in V0.18.
The label RPC methods mirror the account functionality, with the following functional differences:
@@ -27,6 +29,9 @@ Here are the changes to RPC methods:
| Changed Method | Notes |
| :--------------------- | :------ |
-| `addmultisigaddress` | Renamed `account` named parameter to `label`. Still accepts `account` for backward compatibility. |
-| `getnewaddress` | Renamed `account` named parameter to `label`. Still accepts `account` for backward compatibility. |
-| `listunspent` | Returns new `label` fields, along with `account` fields for backward compatibility. |
+| `addmultisigaddress` | Renamed `account` named parameter to `label`. Still accepts `account` for backward compatibility if running with '-deprecatedrpc=accounts'. |
+| `getnewaddress` | Renamed `account` named parameter to `label`. Still accepts `account` for backward compatibility. if running with '-deprecatedrpc=accounts' |
+| `listunspent` | Returns new `label` fields. `account` field will be returned for backward compatibility if running with '-deprecatedrpc=accounts' |
+| `sendmany` | The `account` named parameter has been renamed to `dummy`. If provided, the `dummy` parameter must be set to the empty string, unless running with the `-deprecatedrpc=accounts` argument (in which case functionality is unchanged). |
+| `listtransactions` | The `account` named parameter has been renamed to `dummy`. If provided, the `dummy` parameter must be set to the string `*`, unless running with the `-deprecatedrpc=accounts` argument (in which case functionality is unchanged). |
+| `getbalance` | `account`, `minconf` and `include_watchonly` parameters are deprecated, and can only be used if running with '-deprecatedrpc=accounts' |
diff --git a/doc/release-notes-pr13033.md b/doc/release-notes-pr13033.md
new file mode 100644
index 0000000000..3ab4a984db
--- /dev/null
+++ b/doc/release-notes-pr13033.md
@@ -0,0 +1,11 @@
+Transaction index changes
+-------------------------
+
+The transaction index is now built separately from the main node procedure,
+meaning the `-txindex` flag can be toggled without a full reindex. If bitcoind
+is run with `-txindex` on a node that is already partially or fully synced
+without one, the transaction index will be built in the background and become
+available once caught up. When switching from running `-txindex` to running
+without the flag, the transaction index database will *not* be deleted
+automatically, meaning it could be turned back on at a later time without a full
+resync.
diff --git a/doc/release-notes.md b/doc/release-notes.md
index 9e9c891de9..7a9a98bfec 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -65,6 +65,15 @@ RPC changes
- The `fundrawtransaction` RPC will reject the previously deprecated `reserveChangeKey` option.
- `sendmany` now shuffles outputs to improve privacy, so any previously expected behavior with regards to output ordering can no longer be relied upon.
- The new RPC `testmempoolaccept` can be used to test acceptance of a transaction to the mempool without adding it.
+- JSON transaction decomposition now includes a `weight` field which provides
+ the transaction's exact weight. This is included in REST /rest/tx/ and
+ /rest/block/ endpoints when in json mode. This is also included in `getblock`
+ (with verbosity=2), `listsinceblock`, `listtransactions`, and
+ `getrawtransaction` RPC commands.
+- New `fees` field introduced in `getrawmempool`, `getmempoolancestors`, `getmempooldescendants` and
+ `getmempoolentry` when verbosity is set to `true` with sub-fields `ancestor`, `base`, `modified`
+ and `descendant` denominated in BTC. This new field deprecates previous fee fields, such as
+ `fee`, `modifiedfee`, `ancestorfee` and `descendantfee`.
External wallet files
---------------------
@@ -95,6 +104,21 @@ Low-level RPC changes
now the empty string `""` instead of `"wallet.dat"`. If bitcoin is started
with any `-wallet=<path>` options, there is no change in behavior, and the
name of any wallet is just its `<path>` string.
+- Passing an empty string (`""`) as the `address_type` parameter to
+ `getnewaddress`, `getrawchangeaddress`, `addmultisigaddress`,
+ `fundrawtransaction` RPCs is now an error. Previously, this would fall back
+ to using the default address type. It is still possible to pass null or leave
+ the parameter unset to use the default address type.
+
+- Bare multisig outputs to our keys are no longer automatically treated as
+ incoming payments. As this feature was only available for multisig outputs for
+ which you had all private keys in your wallet, there was generally no use for
+ them compared to single-key schemes. Furthermore, no address format for such
+ outputs is defined, and wallet software can't easily send to it. These outputs
+ will no longer show up in `listtransactions`, `listunspent`, or contribute to
+ your balance, unless they are explicitly watched (using `importaddress` or
+ `importmulti` with hex script argument). `signrawtransaction*` also still
+ works for them.
### Logging
diff --git a/doc/tor.md b/doc/tor.md
index 931c83abdd..f0f98b7d12 100644
--- a/doc/tor.md
+++ b/doc/tor.md
@@ -1,5 +1,4 @@
-TOR SUPPORT IN BITCOIN
-======================
+# TOR SUPPORT IN BITCOIN
It is possible to run Bitcoin as a Tor hidden service, and connect to such services.
@@ -7,8 +6,7 @@ The following directions assume you have a Tor proxy running on port 9050. Many
configure Tor.
-1. Run bitcoin behind a Tor proxy
----------------------------------
+## 1. Run bitcoin behind a Tor proxy
The first step is running Bitcoin behind a Tor proxy. This will already make all
outgoing connections be anonymized, but more is possible.
@@ -34,12 +32,12 @@ In a typical situation, this suffices to run behind a Tor proxy:
./bitcoind -proxy=127.0.0.1:9050
-2. Run a bitcoin hidden server
-------------------------------
+## 2. Run a bitcoin hidden server
If you configure your Tor system accordingly, it is possible to make your node also
reachable from the Tor network. Add these lines to your /etc/tor/torrc (or equivalent
-config file):
+config file): *Needed for Tor version 0.2.7.0 and older versions of Tor only. For newer
+versions of Tor see [Section 3](#3-automatically-listen-on-tor).*
HiddenServiceDir /var/lib/tor/bitcoin-service/
HiddenServicePort 8333 127.0.0.1:8333
@@ -88,8 +86,7 @@ for normal IPv4/IPv6 communication, use:
./bitcoind -onion=127.0.0.1:9050 -externalip=57qr3yd1nyntf5k.onion -discover
-3. Automatically listen on Tor
---------------------------------
+## 3. Automatically listen on Tor
Starting with Tor version 0.2.7.1 it is possible, through Tor's control socket
API, to create and destroy 'ephemeral' hidden services programmatically.
@@ -115,8 +112,7 @@ which has the appropriate permissions. An alternative authentication method is t
of the `-torpassword` flag and a `hash-password` which can be enabled and specified in
Tor configuration.
-4. Privacy recommendations
----------------------------
+## 4. Privacy recommendations
- Do not add anything but bitcoin ports to the hidden service created in section 2.
If you run a web service too, create a new hidden service for that.
diff --git a/share/rpcauth/README.md b/share/rpcauth/README.md
index 389278a125..20d16f0a97 100644
--- a/share/rpcauth/README.md
+++ b/share/rpcauth/README.md
@@ -8,3 +8,7 @@ Create login credentials for a JSON-RPC user.
Usage:
./rpcauth.py <username>
+
+in which case the script will generate a password. To specify a custom password do:
+
+ ./rpcauth.py <username> <password>
diff --git a/share/rpcauth/rpcauth.py b/share/rpcauth/rpcauth.py
index f9b9787514..566c55aba9 100755
--- a/share/rpcauth/rpcauth.py
+++ b/share/rpcauth/rpcauth.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# Copyright (c) 2015-2017 The Bitcoin Core developers
-# Distributed under the MIT software license, see the accompanying
+# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
import sys
@@ -9,26 +9,39 @@ from random import SystemRandom
import base64
import hmac
-if len(sys.argv) < 2:
- sys.stderr.write('Please include username as an argument.\n')
- sys.exit(0)
+def generate_salt():
+ # This uses os.urandom() underneath
+ cryptogen = SystemRandom()
-username = sys.argv[1]
+ # Create 16 byte hex salt
+ salt_sequence = [cryptogen.randrange(256) for _ in range(16)]
+ return ''.join([format(r, 'x') for r in salt_sequence])
-#This uses os.urandom() underneath
-cryptogen = SystemRandom()
+def generate_password():
+ """Create 32 byte b64 password"""
+ return base64.urlsafe_b64encode(os.urandom(32)).decode('utf-8')
-#Create 16 byte hex salt
-salt_sequence = [cryptogen.randrange(256) for i in range(16)]
-hexseq = list(map(hex, salt_sequence))
-salt = "".join([x[2:] for x in hexseq])
+def password_to_hmac(salt, password):
+ m = hmac.new(bytearray(salt, 'utf-8'), bytearray(password, 'utf-8'), 'SHA256')
+ return m.hexdigest()
-#Create 32 byte b64 password
-password = base64.urlsafe_b64encode(os.urandom(32)).decode("utf-8")
+def main():
+ if len(sys.argv) < 2:
+ sys.stderr.write('Please include username (and an optional password, will generate one if not provided) as an argument.\n')
+ sys.exit(0)
-m = hmac.new(bytearray(salt, 'utf-8'), bytearray(password, 'utf-8'), "SHA256")
-result = m.hexdigest()
+ username = sys.argv[1]
-print("String to be appended to bitcoin.conf:")
-print("rpcauth="+username+":"+salt+"$"+result)
-print("Your password:\n"+password)
+ salt = generate_salt()
+ if len(sys.argv) > 2:
+ password = sys.argv[2]
+ else:
+ password = generate_password()
+ password_hmac = password_to_hmac(salt, password)
+
+ print('String to be appended to bitcoin.conf:')
+ print('rpcauth={0}:{1}${2}'.format(username, salt, password_hmac))
+ print('Your password:\n{0}'.format(password))
+
+if __name__ == '__main__':
+ main()
diff --git a/share/setup.nsi.in b/share/setup.nsi.in
index dd42085a27..9fee9b42e0 100644
--- a/share/setup.nsi.in
+++ b/share/setup.nsi.in
@@ -20,7 +20,8 @@ SetCompressor /SOLID lzma
!define MUI_STARTMENUPAGE_REGISTRY_KEY ${REGKEY}
!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME StartMenuGroup
!define MUI_STARTMENUPAGE_DEFAULTFOLDER "@PACKAGE_NAME@"
-!define MUI_FINISHPAGE_RUN $INSTDIR\@BITCOIN_GUI_NAME@@EXEEXT@
+!define MUI_FINISHPAGE_RUN "$WINDIR\explorer.exe"
+!define MUI_FINISHPAGE_RUN_PARAMETERS $INSTDIR\@BITCOIN_GUI_NAME@@EXEEXT@
!define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\modern-uninstall.ico"
!define MUI_UNWELCOMEFINISHPAGE_BITMAP "@abs_top_srcdir@/share/pixmaps/nsis-wizard.bmp"
!define MUI_UNFINISHPAGE_NOAUTOCLOSE
diff --git a/src/.clang-format b/src/.clang-format
index 2d2ee67035..38e19edf2c 100644
--- a/src/.clang-format
+++ b/src/.clang-format
@@ -12,7 +12,10 @@ AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: true
BinPackParameters: false
BreakBeforeBinaryOperators: false
-BreakBeforeBraces: Linux
+BreakBeforeBraces: Custom
+BraceWrapping:
+ AfterClass: true
+ AfterFunction: true
BreakBeforeTernaryOperators: false
BreakConstructorInitializersBeforeComma: false
ColumnLimit: 0
diff --git a/src/Makefile.am b/src/Makefile.am
index 4c8cd2c38c..5dbf4bef8c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -103,6 +103,7 @@ BITCOIN_CORE_H = \
fs.h \
httprpc.h \
httpserver.h \
+ index/txindex.h \
indirectmap.h \
init.h \
interfaces/handler.h \
@@ -113,6 +114,7 @@ BITCOIN_CORE_H = \
keystore.h \
dbwrapper.h \
limitedmap.h \
+ logging.h \
memusage.h \
merkleblock.h \
miner.h \
@@ -135,7 +137,6 @@ BITCOIN_CORE_H = \
rpc/client.h \
rpc/mining.h \
rpc/protocol.h \
- rpc/safemode.h \
rpc/server.h \
rpc/rawtransaction.h \
rpc/register.h \
@@ -203,6 +204,7 @@ libbitcoin_server_a_SOURCES = \
consensus/tx_verify.cpp \
httprpc.cpp \
httpserver.cpp \
+ index/txindex.cpp \
init.cpp \
dbwrapper.cpp \
merkleblock.cpp \
@@ -220,7 +222,6 @@ libbitcoin_server_a_SOURCES = \
rpc/misc.cpp \
rpc/net.cpp \
rpc/rawtransaction.cpp \
- rpc/safemode.cpp \
rpc/server.cpp \
script/sigcache.cpp \
timedata.cpp \
@@ -364,6 +365,7 @@ libbitcoin_util_a_SOURCES = \
fs.cpp \
interfaces/handler.cpp \
interfaces/node.cpp \
+ logging.cpp \
random.cpp \
rpc/protocol.cpp \
rpc/util.cpp \
diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include
index 748c5b7887..3306dcf598 100644
--- a/src/Makefile.bench.include
+++ b/src/Makefile.bench.include
@@ -25,8 +25,6 @@ bench_bench_bitcoin_SOURCES = \
bench/verify_script.cpp \
bench/base58.cpp \
bench/lockedpool.cpp \
- bench/perf.cpp \
- bench/perf.h \
bench/prevector.cpp
nodist_bench_bench_bitcoin_SOURCES = $(GENERATED_BENCH_FILES)
diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include
index 1279152198..4b14212b2e 100644
--- a/src/Makefile.qttest.include
+++ b/src/Makefile.qttest.include
@@ -12,14 +12,17 @@ TEST_QT_MOC_CPP = \
if ENABLE_WALLET
TEST_QT_MOC_CPP += \
+ qt/test/moc_addressbooktests.cpp \
qt/test/moc_paymentservertests.cpp \
qt/test/moc_wallettests.cpp
endif
TEST_QT_H = \
+ qt/test/addressbooktests.h \
qt/test/compattests.h \
qt/test/rpcnestedtests.h \
qt/test/uritests.h \
+ qt/test/util.h \
qt/test/paymentrequestdata.h \
qt/test/paymentservertests.h \
qt/test/wallettests.h
@@ -38,11 +41,13 @@ qt_test_test_bitcoin_qt_SOURCES = \
qt/test/rpcnestedtests.cpp \
qt/test/test_main.cpp \
qt/test/uritests.cpp \
+ qt/test/util.cpp \
$(TEST_QT_H) \
$(TEST_BITCOIN_CPP) \
$(TEST_BITCOIN_H)
if ENABLE_WALLET
qt_test_test_bitcoin_qt_SOURCES += \
+ qt/test/addressbooktests.cpp \
qt/test/paymentservertests.cpp \
qt/test/wallettests.cpp \
wallet/test/wallet_test_fixture.cpp
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index c4f18bb371..91d3a3d47c 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -83,6 +83,7 @@ BITCOIN_TESTS =\
test/timedata_tests.cpp \
test/torcontrol_tests.cpp \
test/transaction_tests.cpp \
+ test/txindex_tests.cpp \
test/txvalidation_tests.cpp \
test/txvalidationcache_tests.cpp \
test/versionbits_tests.cpp \
@@ -156,6 +157,8 @@ bitcoin_test_clean : FORCE
check-local: $(BITCOIN_TESTS:.cpp=.cpp.test)
@echo "Running test/util/bitcoin-util-test.py..."
$(PYTHON) $(top_builddir)/test/util/bitcoin-util-test.py
+ @echo "Running test/util/rpcauth-test.py..."
+ $(PYTHON) $(top_builddir)/test/util/rpcauth-test.py
$(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C secp256k1 check
if EMBEDDED_UNIVALUE
$(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C univalue check
diff --git a/src/addrdb.cpp b/src/addrdb.cpp
index e4620e63c6..59305ff187 100644
--- a/src/addrdb.cpp
+++ b/src/addrdb.cpp
@@ -49,7 +49,8 @@ bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data
// Serialize
if (!SerializeDB(fileout, data)) return false;
- FileCommit(fileout.Get());
+ if (!FileCommit(fileout.Get()))
+ return error("%s: Failed to flush file %s", __func__, pathTmp.string());
fileout.fclose();
// replace existing file, if any, with new file
diff --git a/src/bench/bench.cpp b/src/bench/bench.cpp
index 21329a5151..de3e57b04f 100644
--- a/src/bench/bench.cpp
+++ b/src/bench/bench.cpp
@@ -3,7 +3,6 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <bench/bench.h>
-#include <bench/perf.h>
#include <assert.h>
#include <iostream>
@@ -96,7 +95,6 @@ benchmark::BenchRunner::BenchRunner(std::string name, benchmark::BenchFunction f
void benchmark::BenchRunner::RunAll(Printer& printer, uint64_t num_evals, double scaling, const std::string& filter, bool is_list_only)
{
- perf_init();
if (!std::ratio_less_equal<benchmark::clock::period, std::micro>::value) {
std::cerr << "WARNING: Clock precision is worse than microsecond - benchmarks may be less accurate!\n";
}
@@ -126,8 +124,6 @@ void benchmark::BenchRunner::RunAll(Printer& printer, uint64_t num_evals, double
}
printer.footer();
-
- perf_fini();
}
bool benchmark::State::UpdateTimer(const benchmark::time_point current_time)
diff --git a/src/bench/bench_bitcoin.cpp b/src/bench/bench_bitcoin.cpp
index 1d87883522..81648f534c 100644
--- a/src/bench/bench_bitcoin.cpp
+++ b/src/bench/bench_bitcoin.cpp
@@ -22,22 +22,27 @@ static const char* DEFAULT_PLOT_PLOTLYURL = "https://cdn.plot.ly/plotly-latest.m
static const int64_t DEFAULT_PLOT_WIDTH = 1024;
static const int64_t DEFAULT_PLOT_HEIGHT = 768;
+static void SetupBenchArgs()
+{
+ gArgs.AddArg("-?", _("Print this help message and exit"), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-list", _("List benchmarks without executing them. Can be combined with -scaling and -filter"), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-evals=<n>", strprintf(_("Number of measurement evaluations to perform. (default: %u)"), DEFAULT_BENCH_EVALUATIONS), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-filter=<regex>", strprintf(_("Regular expression filter to select benchmark by name (default: %s)"), DEFAULT_BENCH_FILTER), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-scaling=<n>", strprintf(_("Scaling factor for benchmark's runtime (default: %u)"), DEFAULT_BENCH_SCALING), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-printer=(console|plot)", strprintf(_("Choose printer format. console: print data to console. plot: Print results as HTML graph (default: %s)"), DEFAULT_BENCH_PRINTER), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-plot-plotlyurl=<uri>", strprintf(_("URL to use for plotly.js (default: %s)"), DEFAULT_PLOT_PLOTLYURL), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-plot-width=<x>", strprintf(_("Plot width in pixel (default: %u)"), DEFAULT_PLOT_WIDTH), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-plot-height=<x>", strprintf(_("Plot height in pixel (default: %u)"), DEFAULT_PLOT_HEIGHT), false, OptionsCategory::OPTIONS);
+}
+
int
main(int argc, char** argv)
{
+ SetupBenchArgs();
gArgs.ParseParameters(argc, argv);
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"))
- << HelpMessageOpt("-evals=<n>", strprintf(_("Number of measurement evaluations to perform. (default: %u)"), DEFAULT_BENCH_EVALUATIONS))
- << HelpMessageOpt("-filter=<regex>", strprintf(_("Regular expression filter to select benchmark by name (default: %s)"), DEFAULT_BENCH_FILTER))
- << HelpMessageOpt("-scaling=<n>", strprintf(_("Scaling factor for benchmark's runtime (default: %u)"), DEFAULT_BENCH_SCALING))
- << HelpMessageOpt("-printer=(console|plot)", strprintf(_("Choose printer format. console: print data to console. plot: Print results as HTML graph (default: %s)"), DEFAULT_BENCH_PRINTER))
- << HelpMessageOpt("-plot-plotlyurl=<uri>", strprintf(_("URL to use for plotly.js (default: %s)"), DEFAULT_PLOT_PLOTLYURL))
- << HelpMessageOpt("-plot-width=<x>", strprintf(_("Plot width in pixel (default: %u)"), DEFAULT_PLOT_WIDTH))
- << HelpMessageOpt("-plot-height=<x>", strprintf(_("Plot height in pixel (default: %u)"), DEFAULT_PLOT_HEIGHT));
+ std::cout << gArgs.GetHelpMessage();
return 0;
}
@@ -46,7 +51,6 @@ main(int argc, char** argv)
RandomInit();
ECC_Start();
SetupEnvironment();
- fPrintToDebugLog = false; // don't want to write to debug.log file
int64_t evaluations = gArgs.GetArg("-evals", DEFAULT_BENCH_EVALUATIONS);
std::string regex_filter = gArgs.GetArg("-filter", DEFAULT_BENCH_FILTER);
diff --git a/src/bench/mempool_eviction.cpp b/src/bench/mempool_eviction.cpp
index cdda0bd9be..4c947a519f 100644
--- a/src/bench/mempool_eviction.cpp
+++ b/src/bench/mempool_eviction.cpp
@@ -9,16 +9,16 @@
#include <list>
#include <vector>
-static void AddTx(const CTransaction& tx, const CAmount& nFee, CTxMemPool& pool)
+static void AddTx(const CTransactionRef& tx, const CAmount& nFee, CTxMemPool& pool)
{
int64_t nTime = 0;
unsigned int nHeight = 1;
bool spendsCoinbase = false;
unsigned int sigOpCost = 4;
LockPoints lp;
- pool.addUnchecked(tx.GetHash(), CTxMemPoolEntry(
- MakeTransactionRef(tx), nFee, nTime, nHeight,
- spendsCoinbase, sigOpCost, lp));
+ pool.addUnchecked(tx->GetHash(), CTxMemPoolEntry(
+ tx, nFee, nTime, nHeight,
+ spendsCoinbase, sigOpCost, lp));
}
// Right now this is only testing eviction performance in an extremely small
@@ -29,6 +29,7 @@ static void MempoolEviction(benchmark::State& state)
CMutableTransaction tx1 = CMutableTransaction();
tx1.vin.resize(1);
tx1.vin[0].scriptSig = CScript() << OP_1;
+ tx1.vin[0].scriptWitness.stack.push_back({1});
tx1.vout.resize(1);
tx1.vout[0].scriptPubKey = CScript() << OP_1 << OP_EQUAL;
tx1.vout[0].nValue = 10 * COIN;
@@ -36,6 +37,7 @@ static void MempoolEviction(benchmark::State& state)
CMutableTransaction tx2 = CMutableTransaction();
tx2.vin.resize(1);
tx2.vin[0].scriptSig = CScript() << OP_2;
+ tx2.vin[0].scriptWitness.stack.push_back({2});
tx2.vout.resize(1);
tx2.vout[0].scriptPubKey = CScript() << OP_2 << OP_EQUAL;
tx2.vout[0].nValue = 10 * COIN;
@@ -44,6 +46,7 @@ static void MempoolEviction(benchmark::State& state)
tx3.vin.resize(1);
tx3.vin[0].prevout = COutPoint(tx2.GetHash(), 0);
tx3.vin[0].scriptSig = CScript() << OP_2;
+ tx3.vin[0].scriptWitness.stack.push_back({3});
tx3.vout.resize(1);
tx3.vout[0].scriptPubKey = CScript() << OP_3 << OP_EQUAL;
tx3.vout[0].nValue = 10 * COIN;
@@ -52,8 +55,10 @@ static void MempoolEviction(benchmark::State& state)
tx4.vin.resize(2);
tx4.vin[0].prevout.SetNull();
tx4.vin[0].scriptSig = CScript() << OP_4;
+ tx4.vin[0].scriptWitness.stack.push_back({4});
tx4.vin[1].prevout.SetNull();
tx4.vin[1].scriptSig = CScript() << OP_4;
+ tx4.vin[1].scriptWitness.stack.push_back({4});
tx4.vout.resize(2);
tx4.vout[0].scriptPubKey = CScript() << OP_4 << OP_EQUAL;
tx4.vout[0].nValue = 10 * COIN;
@@ -64,8 +69,10 @@ static void MempoolEviction(benchmark::State& state)
tx5.vin.resize(2);
tx5.vin[0].prevout = COutPoint(tx4.GetHash(), 0);
tx5.vin[0].scriptSig = CScript() << OP_4;
+ tx5.vin[0].scriptWitness.stack.push_back({4});
tx5.vin[1].prevout.SetNull();
tx5.vin[1].scriptSig = CScript() << OP_5;
+ tx5.vin[1].scriptWitness.stack.push_back({5});
tx5.vout.resize(2);
tx5.vout[0].scriptPubKey = CScript() << OP_5 << OP_EQUAL;
tx5.vout[0].nValue = 10 * COIN;
@@ -76,8 +83,10 @@ static void MempoolEviction(benchmark::State& state)
tx6.vin.resize(2);
tx6.vin[0].prevout = COutPoint(tx4.GetHash(), 1);
tx6.vin[0].scriptSig = CScript() << OP_4;
+ tx6.vin[0].scriptWitness.stack.push_back({4});
tx6.vin[1].prevout.SetNull();
tx6.vin[1].scriptSig = CScript() << OP_6;
+ tx6.vin[1].scriptWitness.stack.push_back({6});
tx6.vout.resize(2);
tx6.vout[0].scriptPubKey = CScript() << OP_6 << OP_EQUAL;
tx6.vout[0].nValue = 10 * COIN;
@@ -88,8 +97,10 @@ static void MempoolEviction(benchmark::State& state)
tx7.vin.resize(2);
tx7.vin[0].prevout = COutPoint(tx5.GetHash(), 0);
tx7.vin[0].scriptSig = CScript() << OP_5;
+ tx7.vin[0].scriptWitness.stack.push_back({5});
tx7.vin[1].prevout = COutPoint(tx6.GetHash(), 0);
tx7.vin[1].scriptSig = CScript() << OP_6;
+ tx7.vin[1].scriptWitness.stack.push_back({6});
tx7.vout.resize(2);
tx7.vout[0].scriptPubKey = CScript() << OP_7 << OP_EQUAL;
tx7.vout[0].nValue = 10 * COIN;
@@ -97,15 +108,23 @@ static void MempoolEviction(benchmark::State& state)
tx7.vout[1].nValue = 10 * COIN;
CTxMemPool pool;
+ // Create transaction references outside the "hot loop"
+ const CTransactionRef tx1_r{MakeTransactionRef(tx1)};
+ const CTransactionRef tx2_r{MakeTransactionRef(tx2)};
+ const CTransactionRef tx3_r{MakeTransactionRef(tx3)};
+ const CTransactionRef tx4_r{MakeTransactionRef(tx4)};
+ const CTransactionRef tx5_r{MakeTransactionRef(tx5)};
+ const CTransactionRef tx6_r{MakeTransactionRef(tx6)};
+ const CTransactionRef tx7_r{MakeTransactionRef(tx7)};
while (state.KeepRunning()) {
- AddTx(tx1, 10000LL, pool);
- AddTx(tx2, 5000LL, pool);
- AddTx(tx3, 20000LL, pool);
- AddTx(tx4, 7000LL, pool);
- AddTx(tx5, 1000LL, pool);
- AddTx(tx6, 1100LL, pool);
- AddTx(tx7, 9000LL, pool);
+ AddTx(tx1_r, 10000LL, pool);
+ AddTx(tx2_r, 5000LL, pool);
+ AddTx(tx3_r, 20000LL, pool);
+ AddTx(tx4_r, 7000LL, pool);
+ AddTx(tx5_r, 1000LL, pool);
+ AddTx(tx6_r, 1100LL, pool);
+ AddTx(tx7_r, 9000LL, pool);
pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4);
pool.TrimToSize(GetVirtualTransactionSize(tx1));
}
diff --git a/src/bench/perf.cpp b/src/bench/perf.cpp
deleted file mode 100644
index f92d08c56e..0000000000
--- a/src/bench/perf.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright (c) 2016-2017 The Bitcoin Core developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-#include <bench/perf.h>
-
-#if defined(__i386__) || defined(__x86_64__)
-
-/* These architectures support querying the cycle counter
- * from user space, no need for any syscall overhead.
- */
-void perf_init(void) { }
-void perf_fini(void) { }
-
-#elif defined(__linux__)
-
-#include <unistd.h>
-#include <sys/syscall.h>
-#include <linux/perf_event.h>
-
-static int fd = -1;
-static struct perf_event_attr attr;
-
-void perf_init(void)
-{
- attr.type = PERF_TYPE_HARDWARE;
- attr.config = PERF_COUNT_HW_CPU_CYCLES;
- fd = syscall(__NR_perf_event_open, &attr, 0, -1, -1, 0);
-}
-
-void perf_fini(void)
-{
- if (fd != -1) {
- close(fd);
- }
-}
-
-uint64_t perf_cpucycles(void)
-{
- uint64_t result = 0;
- if (fd == -1 || read(fd, &result, sizeof(result)) < (ssize_t)sizeof(result)) {
- return 0;
- }
- return result;
-}
-
-#else /* Unhandled platform */
-
-void perf_init(void) { }
-void perf_fini(void) { }
-uint64_t perf_cpucycles(void) { return 0; }
-
-#endif
diff --git a/src/bench/perf.h b/src/bench/perf.h
deleted file mode 100644
index 73ea8b9647..0000000000
--- a/src/bench/perf.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright (c) 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.
-
-/** Functions for measurement of CPU cycles */
-#ifndef BITCOIN_BENCH_PERF_H
-#define BITCOIN_BENCH_PERF_H
-
-#include <stdint.h>
-
-#if defined(__i386__)
-
-static inline uint64_t perf_cpucycles(void)
-{
- uint64_t x;
- __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x));
- return x;
-}
-
-#elif defined(__x86_64__)
-
-static inline uint64_t perf_cpucycles(void)
-{
- uint32_t hi, lo;
- __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
- return ((uint64_t)lo)|(((uint64_t)hi)<<32);
-}
-#else
-
-uint64_t perf_cpucycles(void);
-
-#endif
-
-void perf_init(void);
-void perf_fini(void);
-
-#endif // BITCOIN_BENCH_PERF_H
diff --git a/src/bench/prevector.cpp b/src/bench/prevector.cpp
index d0f28d1a3e..3cfad1b2c4 100644
--- a/src/bench/prevector.cpp
+++ b/src/bench/prevector.cpp
@@ -48,7 +48,7 @@ static void PrevectorClear(benchmark::State& state)
}
template <typename T>
-void PrevectorResize(benchmark::State& state)
+static void PrevectorResize(benchmark::State& state)
{
while (state.KeepRunning()) {
prevector<28, T> t0;
diff --git a/src/bench/verify_script.cpp b/src/bench/verify_script.cpp
index 705fa368a5..4100519d48 100644
--- a/src/bench/verify_script.cpp
+++ b/src/bench/verify_script.cpp
@@ -71,7 +71,7 @@ static void VerifyScriptBench(benchmark::State& state)
CScript scriptPubKey = CScript() << witnessversion << ToByteVector(pubkeyHash);
CScript scriptSig;
CScript witScriptPubkey = CScript() << OP_DUP << OP_HASH160 << ToByteVector(pubkeyHash) << OP_EQUALVERIFY << OP_CHECKSIG;
- CTransaction txCredit = BuildCreditingTransaction(scriptPubKey);
+ const CMutableTransaction& txCredit = BuildCreditingTransaction(scriptPubKey);
CMutableTransaction txSpend = BuildSpendingTransaction(scriptSig, txCredit);
CScriptWitness& witness = txSpend.vin[0].scriptWitness;
witness.stack.emplace_back();
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index 26a9231308..b26b172305 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -29,29 +29,26 @@ static const int DEFAULT_HTTP_CLIENT_TIMEOUT=900;
static const bool DEFAULT_NAMED=false;
static const int CONTINUE_EXECUTION=-1;
-std::string HelpMessageCli()
+static void SetupCliArgs()
{
const auto defaultBaseParams = CreateBaseChainParams(CBaseChainParams::MAIN);
const auto testnetBaseParams = CreateBaseChainParams(CBaseChainParams::TESTNET);
- std::string strUsage;
- strUsage += HelpMessageGroup(_("Options:"));
- strUsage += HelpMessageOpt("-?", _("This help message"));
- strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)"), BITCOIN_CONF_FILENAME));
- strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory"));
- strUsage += HelpMessageOpt("-getinfo", _("Get general information from the remote server. Note that unlike server-side RPC calls, the results of -getinfo is the result of multiple non-atomic requests. Some entries in the result may represent results from different states (e.g. wallet balance may be as of a different block from the chain state reported)"));
- AppendParamsHelpMessages(strUsage);
- strUsage += HelpMessageOpt("-named", strprintf(_("Pass named instead of positional arguments (default: %s)"), DEFAULT_NAMED));
- strUsage += HelpMessageOpt("-rpcclienttimeout=<n>", strprintf(_("Timeout in seconds during HTTP requests, or 0 for no timeout. (default: %d)"), DEFAULT_HTTP_CLIENT_TIMEOUT));
- strUsage += HelpMessageOpt("-rpcconnect=<ip>", strprintf(_("Send commands to node running on <ip> (default: %s)"), DEFAULT_RPCCONNECT));
- strUsage += HelpMessageOpt("-rpcpassword=<pw>", _("Password for JSON-RPC connections"));
- strUsage += HelpMessageOpt("-rpcport=<port>", strprintf(_("Connect to JSON-RPC on <port> (default: %u or testnet: %u)"), defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort()));
- strUsage += HelpMessageOpt("-rpcuser=<user>", _("Username for JSON-RPC connections"));
- strUsage += HelpMessageOpt("-rpcwait", _("Wait for RPC server to start"));
- strUsage += HelpMessageOpt("-rpcwallet=<walletname>", _("Send RPC for non-default wallet on RPC server (needs to exactly match corresponding -wallet option passed to bitcoind)"));
- strUsage += HelpMessageOpt("-stdin", _("Read extra arguments from standard input, one per line until EOF/Ctrl-D (recommended for sensitive information such as passphrases). When combined with -stdinrpcpass, the first line from standard input is used for the RPC password."));
- strUsage += HelpMessageOpt("-stdinrpcpass", strprintf(_("Read RPC password from standard input as a single line. When combined with -stdin, the first line from standard input is used for the RPC password.")));
-
- return strUsage;
+
+ gArgs.AddArg("-?", _("This help message"), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-conf=<file>", strprintf(_("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)"), BITCOIN_CONF_FILENAME), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-datadir=<dir>", _("Specify data directory"), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-getinfo", _("Get general information from the remote server. Note that unlike server-side RPC calls, the results of -getinfo is the result of multiple non-atomic requests. Some entries in the result may represent results from different states (e.g. wallet balance may be as of a different block from the chain state reported)"), false, OptionsCategory::OPTIONS);
+ SetupChainParamsBaseOptions();
+ gArgs.AddArg("-named", strprintf(_("Pass named instead of positional arguments (default: %s)"), DEFAULT_NAMED), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-rpcclienttimeout=<n>", strprintf(_("Timeout in seconds during HTTP requests, or 0 for no timeout. (default: %d)"), DEFAULT_HTTP_CLIENT_TIMEOUT), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-rpcconnect=<ip>", strprintf(_("Send commands to node running on <ip> (default: %s)"), DEFAULT_RPCCONNECT), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-rpcpassword=<pw>", _("Password for JSON-RPC connections"), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-rpcport=<port>", strprintf(_("Connect to JSON-RPC on <port> (default: %u or testnet: %u)"), defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort()), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-rpcuser=<user>", _("Username for JSON-RPC connections"), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-rpcwait", _("Wait for RPC server to start"), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-rpcwallet=<walletname>", _("Send RPC for non-default wallet on RPC server (needs to exactly match corresponding -wallet option passed to bitcoind)"), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-stdin", _("Read extra arguments from standard input, one per line until EOF/Ctrl-D (recommended for sensitive information such as passphrases). When combined with -stdinrpcpass, the first line from standard input is used for the RPC password."), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-stdinrpcpass", strprintf(_("Read RPC password from standard input as a single line. When combined with -stdin, the first line from standard input is used for the RPC password.")), false, OptionsCategory::OPTIONS);
}
//////////////////////////////////////////////////////////////////////////////
@@ -82,6 +79,7 @@ static int AppInitRPC(int argc, char* argv[])
//
// Parameters
//
+ SetupCliArgs();
gArgs.ParseParameters(argc, argv);
if (argc < 2 || HelpRequested(gArgs) || gArgs.IsArgSet("-version")) {
std::string strUsage = strprintf(_("%s RPC client version"), _(PACKAGE_NAME)) + " " + FormatFullVersion() + "\n";
@@ -92,7 +90,7 @@ static int AppInitRPC(int argc, char* argv[])
" bitcoin-cli [options] help " + _("List commands") + "\n" +
" bitcoin-cli [options] help <command> " + _("Get help for a command") + "\n";
- strUsage += "\n" + HelpMessageCli();
+ strUsage += "\n" + gArgs.GetHelpMessage();
}
fprintf(stdout, "%s", strUsage.c_str());
@@ -107,7 +105,7 @@ static int AppInitRPC(int argc, char* argv[])
return EXIT_FAILURE;
}
try {
- gArgs.ReadConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME));
+ gArgs.ReadConfigFiles();
} catch (const std::exception& e) {
fprintf(stderr,"Error reading configuration file: %s\n", e.what());
return EXIT_FAILURE;
@@ -138,7 +136,7 @@ struct HTTPReply
std::string body;
};
-const char *http_errorstring(int code)
+static const char *http_errorstring(int code)
{
switch(code) {
#if LIBEVENT_VERSION_NUMBER >= 0x02010300
@@ -387,7 +385,7 @@ static UniValue CallRPC(BaseRequestHandler *rh, const std::string& strMethod, co
return reply;
}
-int CommandLineRPC(int argc, char *argv[])
+static int CommandLineRPC(int argc, char *argv[])
{
std::string strPrint;
int nRet = 0;
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index 07ad09ea7b..bf42307df5 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -31,6 +31,41 @@ static bool fCreateBlank;
static std::map<std::string,UniValue> registers;
static const int CONTINUE_EXECUTION=-1;
+static void SetupBitcoinTxArgs()
+{
+ gArgs.AddArg("-?", _("This help message"), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-create", _("Create new, empty TX."), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-json", _("Select JSON output"), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-txid", _("Output only the hex-encoded transaction id of the resultant transaction."), false, OptionsCategory::OPTIONS);
+ SetupChainParamsBaseOptions();
+
+ gArgs.AddArg("delin=N", _("Delete input N from TX"), false, OptionsCategory::COMMANDS);
+ gArgs.AddArg("delout=N", _("Delete output N from TX"), false, OptionsCategory::COMMANDS);
+ gArgs.AddArg("in=TXID:VOUT(:SEQUENCE_NUMBER)", _("Add input to TX"), false, OptionsCategory::COMMANDS);
+ gArgs.AddArg("locktime=N", _("Set TX lock time to N"), false, OptionsCategory::COMMANDS);
+ gArgs.AddArg("nversion=N", _("Set TX version to N"), false, OptionsCategory::COMMANDS);
+ gArgs.AddArg("outaddr=VALUE:ADDRESS", _("Add address-based output to TX"), false, OptionsCategory::COMMANDS);
+ gArgs.AddArg("outdata=[VALUE:]DATA", _("Add data-based output to TX"), false, OptionsCategory::COMMANDS);
+ gArgs.AddArg("outmultisig=VALUE:REQUIRED:PUBKEYS:PUBKEY1:PUBKEY2:....[:FLAGS]", _("Add Pay To n-of-m Multi-sig output to TX. n = REQUIRED, m = PUBKEYS") + ". " +
+ _("Optionally add the \"W\" flag to produce a pay-to-witness-script-hash output") + ". " +
+ _("Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash."), false, OptionsCategory::COMMANDS);
+ gArgs.AddArg("outpubkey=VALUE:PUBKEY[:FLAGS]", _("Add pay-to-pubkey output to TX") + ". " +
+ _("Optionally add the \"W\" flag to produce a pay-to-witness-pubkey-hash output") + ". " +
+ _("Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash."), false, OptionsCategory::COMMANDS);
+ gArgs.AddArg("outscript=VALUE:SCRIPT[:FLAGS]", _("Add raw script output to TX") + ". " +
+ _("Optionally add the \"W\" flag to produce a pay-to-witness-script-hash output") + ". " +
+ _("Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash."), false, OptionsCategory::COMMANDS);
+ gArgs.AddArg("replaceable(=N)", _("Set RBF opt-in sequence number for input N (if not provided, opt-in all available inputs)"), false, OptionsCategory::COMMANDS);
+ gArgs.AddArg("sign=SIGHASH-FLAGS", _("Add zero or more signatures to transaction") + ". " +
+ _("This command requires JSON registers:") +
+ _("prevtxs=JSON object") + ", " +
+ _("privatekeys=JSON object") + ". " +
+ _("See signrawtransaction docs for format of sighash flags, JSON objects."), false, OptionsCategory::COMMANDS);
+
+ gArgs.AddArg("load=NAME:FILENAME", _("Load JSON file FILENAME into register NAME"), false, OptionsCategory::REGISTER_COMMANDS);
+ gArgs.AddArg("set=NAME:JSON-STRING", _("Set register NAME to given JSON-STRING"), false, OptionsCategory::REGISTER_COMMANDS);
+}
+
//
// This function returns either one of EXIT_ codes when it's expected to stop the process or
// CONTINUE_EXECUTION when it's expected to continue further.
@@ -40,6 +75,7 @@ static int AppInitRawTx(int argc, char* argv[])
//
// Parameters
//
+ SetupBitcoinTxArgs();
gArgs.ParseParameters(argc, argv);
// Check for -testnet or -regtest parameter (Params() calls are only valid after this clause)
@@ -59,48 +95,10 @@ static int AppInitRawTx(int argc, char* argv[])
" bitcoin-tx [options] <hex-tx> [commands] " + _("Update hex-encoded bitcoin transaction") + "\n" +
" bitcoin-tx [options] -create [commands] " + _("Create hex-encoded bitcoin transaction") + "\n" +
"\n";
+ strUsage += gArgs.GetHelpMessage();
fprintf(stdout, "%s", strUsage.c_str());
- strUsage = HelpMessageGroup(_("Options:"));
- strUsage += HelpMessageOpt("-?", _("This help message"));
- strUsage += HelpMessageOpt("-create", _("Create new, empty TX."));
- strUsage += HelpMessageOpt("-json", _("Select JSON output"));
- strUsage += HelpMessageOpt("-txid", _("Output only the hex-encoded transaction id of the resultant transaction."));
- AppendParamsHelpMessages(strUsage);
-
- fprintf(stdout, "%s", strUsage.c_str());
-
- strUsage = HelpMessageGroup(_("Commands:"));
- strUsage += HelpMessageOpt("delin=N", _("Delete input N from TX"));
- strUsage += HelpMessageOpt("delout=N", _("Delete output N from TX"));
- strUsage += HelpMessageOpt("in=TXID:VOUT(:SEQUENCE_NUMBER)", _("Add input to TX"));
- strUsage += HelpMessageOpt("locktime=N", _("Set TX lock time to N"));
- strUsage += HelpMessageOpt("nversion=N", _("Set TX version to N"));
- strUsage += HelpMessageOpt("outaddr=VALUE:ADDRESS", _("Add address-based output to TX"));
- strUsage += HelpMessageOpt("outdata=[VALUE:]DATA", _("Add data-based output to TX"));
- strUsage += HelpMessageOpt("outmultisig=VALUE:REQUIRED:PUBKEYS:PUBKEY1:PUBKEY2:....[:FLAGS]", _("Add Pay To n-of-m Multi-sig output to TX. n = REQUIRED, m = PUBKEYS") + ". " +
- _("Optionally add the \"W\" flag to produce a pay-to-witness-script-hash output") + ". " +
- _("Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash."));
- strUsage += HelpMessageOpt("outpubkey=VALUE:PUBKEY[:FLAGS]", _("Add pay-to-pubkey output to TX") + ". " +
- _("Optionally add the \"W\" flag to produce a pay-to-witness-pubkey-hash output") + ". " +
- _("Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash."));
- strUsage += HelpMessageOpt("outscript=VALUE:SCRIPT[:FLAGS]", _("Add raw script output to TX") + ". " +
- _("Optionally add the \"W\" flag to produce a pay-to-witness-script-hash output") + ". " +
- _("Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash."));
- strUsage += HelpMessageOpt("replaceable(=N)", _("Set RBF opt-in sequence number for input N (if not provided, opt-in all available inputs)"));
- strUsage += HelpMessageOpt("sign=SIGHASH-FLAGS", _("Add zero or more signatures to transaction") + ". " +
- _("This command requires JSON registers:") +
- _("prevtxs=JSON object") + ", " +
- _("privatekeys=JSON object") + ". " +
- _("See signrawtransaction docs for format of sighash flags, JSON objects."));
- fprintf(stdout, "%s", strUsage.c_str());
-
- strUsage = HelpMessageGroup(_("Register Commands:"));
- strUsage += HelpMessageOpt("load=NAME:FILENAME", _("Load JSON file FILENAME into register NAME"));
- strUsage += HelpMessageOpt("set=NAME:JSON-STRING", _("Set register NAME to given JSON-STRING"));
- fprintf(stdout, "%s", strUsage.c_str());
-
if (argc < 2) {
fprintf(stderr, "Error: too few parameters\n");
return EXIT_FAILURE;
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index 83d9719df2..1539e4025a 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -40,14 +40,11 @@
* Use the buttons <code>Namespaces</code>, <code>Classes</code> or <code>Files</code> at the top of the page to start navigating the code.
*/
-void WaitForShutdown()
+static void WaitForShutdown()
{
- bool fShutdown = ShutdownRequested();
- // Tell the main threads to shutdown.
- while (!fShutdown)
+ while (!ShutdownRequested())
{
MilliSleep(200);
- fShutdown = ShutdownRequested();
}
Interrupt();
}
@@ -56,7 +53,7 @@ void WaitForShutdown()
//
// Start
//
-bool AppInit(int argc, char* argv[])
+static bool AppInit(int argc, char* argv[])
{
bool fRet = false;
@@ -64,6 +61,10 @@ bool AppInit(int argc, char* argv[])
// Parameters
//
// If Qt is used, parameters/bitcoin.conf are parsed in qt/bitcoin.cpp's main()
+ SetupServerArgs();
+#if HAVE_DECL_DAEMON
+ gArgs.AddArg("-daemon", _("Run in the background as a daemon and accept commands"), false, OptionsCategory::OPTIONS);
+#endif
gArgs.ParseParameters(argc, argv);
// Process help and version before taking care about datadir
@@ -79,7 +80,7 @@ bool AppInit(int argc, char* argv[])
strUsage += "\n" + _("Usage:") + "\n" +
" bitcoind [options] " + strprintf(_("Start %s Daemon"), _(PACKAGE_NAME)) + "\n";
- strUsage += "\n" + HelpMessage(HelpMessageMode::BITCOIND);
+ strUsage += "\n" + gArgs.GetHelpMessage();
}
fprintf(stdout, "%s", strUsage.c_str());
@@ -95,7 +96,7 @@ bool AppInit(int argc, char* argv[])
}
try
{
- gArgs.ReadConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME));
+ gArgs.ReadConfigFiles();
} catch (const std::exception& e) {
fprintf(stderr,"Error reading configuration file: %s\n", e.what());
return false;
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index 6067503b0b..71762158f0 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -75,7 +75,7 @@ public:
CMainParams() {
strNetworkID = "main";
consensus.nSubsidyHalvingInterval = 210000;
- consensus.BIP16Height = 173805; // 00000000000000ce80a7e057163a4db1d5ad7b20fb6f598c9597b9665c8fb0d4 - April 1, 2012
+ consensus.BIP16Exception = uint256S("0x00000000000002dc756eebf4f49723ed8d30cc28a5f108eb94b1ba88ac4f9c22");
consensus.BIP34Height = 227931;
consensus.BIP34Hash = uint256S("0x000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8");
consensus.BIP65Height = 388381; // 000000000000000004c2b624ed5d7756c508d90fd0da2c7c679febfa6c4735f0
@@ -173,7 +173,7 @@ public:
// Data as of block 0000000000000000002d6cca6761c99b3c2e936f9a0e304b7c7651a993f461de (height 506081).
1516903077, // * UNIX timestamp of last known number of transactions
295363220, // * total number of transactions between genesis and that timestamp
- // (the tx=... number in the SetBestChain debug.log lines)
+ // (the tx=... number in the ChainStateFlushed debug.log lines)
3.5 // * estimated number of transactions per second after that timestamp
};
@@ -190,7 +190,7 @@ public:
CTestNetParams() {
strNetworkID = "test";
consensus.nSubsidyHalvingInterval = 210000;
- consensus.BIP16Height = 514; // 00000000040b4e986385315e14bee30ad876d8b47f748025b26683116d21aa65
+ consensus.BIP16Exception = uint256S("0x00000000dd30457c001f4095d208cc1296b0eed002427aa599874af7a432b105");
consensus.BIP34Height = 21111;
consensus.BIP34Hash = uint256S("0x0000000023b3a96d3484e5abb3755c413e7d41500f8e2a5c3f0dd01299cd8ef8");
consensus.BIP65Height = 581885; // 00000000007f6655f22f98e72ed80d8b06dc761d5da09df0fa1dc4be4f861eb6
@@ -283,7 +283,7 @@ public:
CRegTestParams() {
strNetworkID = "regtest";
consensus.nSubsidyHalvingInterval = 150;
- consensus.BIP16Height = 0; // always enforce P2SH BIP16 on regtest
+ consensus.BIP16Exception = uint256();
consensus.BIP34Height = 100000000; // BIP34 has not activated on regtest (far in the future so block v1 are not rejected in tests)
consensus.BIP34Hash = uint256();
consensus.BIP65Height = 1351; // BIP65 activated on regtest (Used in rpc activation tests)
diff --git a/src/chainparams.h b/src/chainparams.h
index 6b1f813afb..dd029b9d5b 100644
--- a/src/chainparams.h
+++ b/src/chainparams.h
@@ -25,6 +25,12 @@ struct CCheckpointData {
MapCheckpoints mapCheckpoints;
};
+/**
+ * Holds various statistics on transactions within a chain. Used to estimate
+ * verification progress during chain sync.
+ *
+ * See also: CChainParams::TxData, GuessVerificationProgress.
+ */
struct ChainTxData {
int64_t nTime;
int64_t nTxCount;
diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp
index 3ef9c2cfe5..91ba3ac9d9 100644
--- a/src/chainparamsbase.cpp
+++ b/src/chainparamsbase.cpp
@@ -14,14 +14,11 @@ const std::string CBaseChainParams::MAIN = "main";
const std::string CBaseChainParams::TESTNET = "test";
const std::string CBaseChainParams::REGTEST = "regtest";
-void AppendParamsHelpMessages(std::string& strUsage, bool debugHelp)
+void SetupChainParamsBaseOptions()
{
- strUsage += HelpMessageGroup(_("Chain selection options:"));
- if (debugHelp) {
- strUsage += HelpMessageOpt("-regtest", "Enter regression test mode, which uses a special chain in which blocks can be solved instantly. "
- "This is intended for regression testing tools and app development.");
- }
- strUsage += HelpMessageOpt("-testnet", _("Use the test chain"));
+ gArgs.AddArg("-regtest", "Enter regression test mode, which uses a special chain in which blocks can be solved instantly. "
+ "This is intended for regression testing tools and app development.", true, OptionsCategory::CHAINPARAMS);
+ gArgs.AddArg("-testnet", _("Use the test chain"), false, OptionsCategory::CHAINPARAMS);
}
static std::unique_ptr<CBaseChainParams> globalChainBaseParams;
diff --git a/src/chainparamsbase.h b/src/chainparamsbase.h
index 5b11f36770..9f8bbafcd5 100644
--- a/src/chainparamsbase.h
+++ b/src/chainparamsbase.h
@@ -40,10 +40,9 @@ private:
std::unique_ptr<CBaseChainParams> CreateBaseChainParams(const std::string& chain);
/**
- * Append the help messages for the chainparams options to the
- * parameter string.
+ *Set the arguments for chainparams
*/
-void AppendParamsHelpMessages(std::string& strUsage, bool debugHelp=true);
+void SetupChainParamsBaseOptions();
/**
* Return the currently selected parameters. This won't change after app
diff --git a/src/compat.h b/src/compat.h
index 8a0f901304..920b3f776d 100644
--- a/src/compat.h
+++ b/src/compat.h
@@ -96,6 +96,12 @@ typedef int32_t ssize_t;
size_t strnlen( const char *start, size_t max_len);
#endif // HAVE_DECL_STRNLEN
+#ifndef WIN32
+typedef void* sockopt_arg_type;
+#else
+typedef char* sockopt_arg_type;
+#endif
+
bool static inline IsSelectableSocket(const SOCKET& s) {
#ifdef WIN32
return true;
diff --git a/src/compat/endian.h b/src/compat/endian.h
index e5c7e50223..4f244c3930 100644
--- a/src/compat/endian.h
+++ b/src/compat/endian.h
@@ -19,6 +19,51 @@
#include <sys/endian.h>
#endif
+#ifndef HAVE_CONFIG_H
+// While not technically a supported configuration, defaulting to defining these
+// DECLs when we were compiled without autotools makes it easier for other build
+// systems to build things like libbitcoinconsensus for strange targets.
+#ifdef htobe16
+#define HAVE_DECL_HTOBE16 1
+#endif
+#ifdef htole16
+#define HAVE_DECL_HTOLE16 1
+#endif
+#ifdef be16toh
+#define HAVE_DECL_BE16TOH 1
+#endif
+#ifdef le16toh
+#define HAVE_DECL_LE16TOH 1
+#endif
+
+#ifdef htobe32
+#define HAVE_DECL_HTOBE32 1
+#endif
+#ifdef htole32
+#define HAVE_DECL_HTOLE32 1
+#endif
+#ifdef be32toh
+#define HAVE_DECL_BE32TOH 1
+#endif
+#ifdef le32toh
+#define HAVE_DECL_LE32TOH 1
+#endif
+
+#ifdef htobe64
+#define HAVE_DECL_HTOBE64 1
+#endif
+#ifdef htole64
+#define HAVE_DECL_HTOLE64 1
+#endif
+#ifdef be64toh
+#define HAVE_DECL_BE64TOH 1
+#endif
+#ifdef le64toh
+#define HAVE_DECL_LE64TOH 1
+#endif
+
+#endif // HAVE_CONFIG_H
+
#if defined(WORDS_BIGENDIAN)
#if HAVE_DECL_HTOBE16 == 0
diff --git a/src/consensus/params.h b/src/consensus/params.h
index 4ef808c856..0559304fc2 100644
--- a/src/consensus/params.h
+++ b/src/consensus/params.h
@@ -49,8 +49,8 @@ struct BIP9Deployment {
struct Params {
uint256 hashGenesisBlock;
int nSubsidyHalvingInterval;
- /** Block height at which BIP16 becomes active */
- int BIP16Height;
+ /* Block hash that is excepted from BIP16 enforcement */
+ uint256 BIP16Exception;
/** Block height and hash at which BIP34 becomes active */
int BIP34Height;
uint256 BIP34Hash;
diff --git a/src/core_read.cpp b/src/core_read.cpp
index 6a8308f869..aade7e21ca 100644
--- a/src/core_read.cpp
+++ b/src/core_read.cpp
@@ -88,7 +88,7 @@ CScript ParseScript(const std::string& s)
}
// Check that all of the input and output scripts of a transaction contains valid opcodes
-bool CheckTxScriptsSanity(const CMutableTransaction& tx)
+static bool CheckTxScriptsSanity(const CMutableTransaction& tx)
{
// Check input scripts for non-coinbase txs
if (!CTransaction(tx).IsCoinBase()) {
diff --git a/src/core_write.cpp b/src/core_write.cpp
index 54b18a4931..ee6737201b 100644
--- a/src/core_write.cpp
+++ b/src/core_write.cpp
@@ -34,7 +34,7 @@ std::string FormatScript(const CScript& script)
while (it != script.end()) {
CScript::const_iterator it2 = it;
std::vector<unsigned char> vch;
- if (script.GetOp2(it, op, &vch)) {
+ if (script.GetOp(it, op, vch)) {
if (op == OP_0) {
ret += "0 ";
continue;
@@ -161,6 +161,7 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry,
entry.pushKV("version", tx.nVersion);
entry.pushKV("size", (int)::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION));
entry.pushKV("vsize", (GetTransactionWeight(tx) + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR);
+ entry.pushKV("weight", GetTransactionWeight(tx));
entry.pushKV("locktime", (int64_t)tx.nLockTime);
UniValue vin(UniValue::VARR);
diff --git a/src/dbwrapper.h b/src/dbwrapper.h
index 6f80eedc7a..2a5e0cab00 100644
--- a/src/dbwrapper.h
+++ b/src/dbwrapper.h
@@ -224,6 +224,9 @@ public:
CDBWrapper(const fs::path& path, size_t nCacheSize, bool fMemory = false, bool fWipe = false, bool obfuscate = false);
~CDBWrapper();
+ CDBWrapper(const CDBWrapper&) = delete;
+ CDBWrapper& operator=(const CDBWrapper&) = delete;
+
template <typename K, typename V>
bool Read(const K& key, V& value) const
{
diff --git a/src/httpserver.cpp b/src/httpserver.cpp
index b8b338dfbc..bd08b04c0f 100644
--- a/src/httpserver.cpp
+++ b/src/httpserver.cpp
@@ -364,8 +364,8 @@ bool InitHTTPServer()
// Update libevent's log handling. Returns false if our version of
// libevent doesn't support debug logging, in which case we should
// clear the BCLog::LIBEVENT flag.
- if (!UpdateHTTPServerLogging(logCategories & BCLog::LIBEVENT)) {
- logCategories &= ~BCLog::LIBEVENT;
+ if (!UpdateHTTPServerLogging(g_logger->WillLogCategory(BCLog::LIBEVENT))) {
+ g_logger->DisableCategory(BCLog::LIBEVENT);
}
#ifdef WIN32
diff --git a/src/httpserver.h b/src/httpserver.h
index f17be65962..8132c887b5 100644
--- a/src/httpserver.h
+++ b/src/httpserver.h
@@ -32,7 +32,7 @@ void InterruptHTTPServer();
/** Stop HTTP server */
void StopHTTPServer();
-/** Change logging level for libevent. Removes BCLog::LIBEVENT from logCategories if
+/** Change logging level for libevent. Removes BCLog::LIBEVENT from log categories if
* libevent doesn't support debug logging.*/
bool UpdateHTTPServerLogging(bool enable);
diff --git a/src/index/txindex.cpp b/src/index/txindex.cpp
new file mode 100644
index 0000000000..5b6e0f9980
--- /dev/null
+++ b/src/index/txindex.cpp
@@ -0,0 +1,311 @@
+// Copyright (c) 2017-2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <chainparams.h>
+#include <index/txindex.h>
+#include <init.h>
+#include <tinyformat.h>
+#include <ui_interface.h>
+#include <util.h>
+#include <validation.h>
+#include <warnings.h>
+
+constexpr int64_t SYNC_LOG_INTERVAL = 30; // seconds
+constexpr int64_t SYNC_LOCATOR_WRITE_INTERVAL = 30; // seconds
+
+std::unique_ptr<TxIndex> g_txindex;
+
+template<typename... Args>
+static void FatalError(const char* fmt, const Args&... args)
+{
+ std::string strMessage = tfm::format(fmt, args...);
+ SetMiscWarning(strMessage);
+ LogPrintf("*** %s\n", strMessage);
+ uiInterface.ThreadSafeMessageBox(
+ "Error: A fatal internal error occurred, see debug.log for details",
+ "", CClientUIInterface::MSG_ERROR);
+ StartShutdown();
+}
+
+TxIndex::TxIndex(std::unique_ptr<TxIndexDB> db) :
+ m_db(std::move(db)), m_synced(false), m_best_block_index(nullptr)
+{}
+
+TxIndex::~TxIndex()
+{
+ Interrupt();
+ Stop();
+}
+
+bool TxIndex::Init()
+{
+ LOCK(cs_main);
+
+ // Attempt to migrate txindex from the old database to the new one. Even if
+ // chain_tip is null, the node could be reindexing and we still want to
+ // delete txindex records in the old database.
+ if (!m_db->MigrateData(*pblocktree, chainActive.GetLocator())) {
+ return false;
+ }
+
+ CBlockLocator locator;
+ if (!m_db->ReadBestBlock(locator)) {
+ locator.SetNull();
+ }
+
+ m_best_block_index = FindForkInGlobalIndex(chainActive, locator);
+ m_synced = m_best_block_index.load() == chainActive.Tip();
+ return true;
+}
+
+static const CBlockIndex* NextSyncBlock(const CBlockIndex* pindex_prev)
+{
+ AssertLockHeld(cs_main);
+
+ if (!pindex_prev) {
+ return chainActive.Genesis();
+ }
+
+ const CBlockIndex* pindex = chainActive.Next(pindex_prev);
+ if (pindex) {
+ return pindex;
+ }
+
+ return chainActive.Next(chainActive.FindFork(pindex_prev));
+}
+
+void TxIndex::ThreadSync()
+{
+ const CBlockIndex* pindex = m_best_block_index.load();
+ if (!m_synced) {
+ auto& consensus_params = Params().GetConsensus();
+
+ int64_t last_log_time = 0;
+ int64_t last_locator_write_time = 0;
+ while (true) {
+ if (m_interrupt) {
+ WriteBestBlock(pindex);
+ return;
+ }
+
+ {
+ LOCK(cs_main);
+ const CBlockIndex* pindex_next = NextSyncBlock(pindex);
+ if (!pindex_next) {
+ WriteBestBlock(pindex);
+ m_best_block_index = pindex;
+ m_synced = true;
+ break;
+ }
+ pindex = pindex_next;
+ }
+
+ int64_t current_time = GetTime();
+ if (last_log_time + SYNC_LOG_INTERVAL < current_time) {
+ LogPrintf("Syncing txindex with block chain from height %d\n", pindex->nHeight);
+ last_log_time = current_time;
+ }
+
+ if (last_locator_write_time + SYNC_LOCATOR_WRITE_INTERVAL < current_time) {
+ WriteBestBlock(pindex);
+ last_locator_write_time = current_time;
+ }
+
+ CBlock block;
+ if (!ReadBlockFromDisk(block, pindex, consensus_params)) {
+ FatalError("%s: Failed to read block %s from disk",
+ __func__, pindex->GetBlockHash().ToString());
+ return;
+ }
+ if (!WriteBlock(block, pindex)) {
+ FatalError("%s: Failed to write block %s to tx index database",
+ __func__, pindex->GetBlockHash().ToString());
+ return;
+ }
+ }
+ }
+
+ if (pindex) {
+ LogPrintf("txindex is enabled at height %d\n", pindex->nHeight);
+ } else {
+ LogPrintf("txindex is enabled\n");
+ }
+}
+
+bool TxIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex)
+{
+ CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size()));
+ std::vector<std::pair<uint256, CDiskTxPos>> vPos;
+ vPos.reserve(block.vtx.size());
+ for (const auto& tx : block.vtx) {
+ vPos.emplace_back(tx->GetHash(), pos);
+ pos.nTxOffset += ::GetSerializeSize(*tx, SER_DISK, CLIENT_VERSION);
+ }
+ return m_db->WriteTxs(vPos);
+}
+
+bool TxIndex::WriteBestBlock(const CBlockIndex* block_index)
+{
+ LOCK(cs_main);
+ if (!m_db->WriteBestBlock(chainActive.GetLocator(block_index))) {
+ return error("%s: Failed to write locator to disk", __func__);
+ }
+ return true;
+}
+
+void TxIndex::BlockConnected(const std::shared_ptr<const CBlock>& block, const CBlockIndex* pindex,
+ const std::vector<CTransactionRef>& txn_conflicted)
+{
+ if (!m_synced) {
+ return;
+ }
+
+ const CBlockIndex* best_block_index = m_best_block_index.load();
+ if (!best_block_index) {
+ if (pindex->nHeight != 0) {
+ FatalError("%s: First block connected is not the genesis block (height=%d)",
+ __func__, pindex->nHeight);
+ return;
+ }
+ } else {
+ // Ensure block connects to an ancestor of the current best block. This should be the case
+ // most of the time, but may not be immediately after the the sync thread catches up and sets
+ // m_synced. Consider the case where there is a reorg and the blocks on the stale branch are
+ // in the ValidationInterface queue backlog even after the sync thread has caught up to the
+ // new chain tip. In this unlikely event, log a warning and let the queue clear.
+ if (best_block_index->GetAncestor(pindex->nHeight - 1) != pindex->pprev) {
+ LogPrintf("%s: WARNING: Block %s does not connect to an ancestor of " /* Continued */
+ "known best chain (tip=%s); not updating txindex\n",
+ __func__, pindex->GetBlockHash().ToString(),
+ best_block_index->GetBlockHash().ToString());
+ return;
+ }
+ }
+
+ if (WriteBlock(*block, pindex)) {
+ m_best_block_index = pindex;
+ } else {
+ FatalError("%s: Failed to write block %s to txindex",
+ __func__, pindex->GetBlockHash().ToString());
+ return;
+ }
+}
+
+void TxIndex::ChainStateFlushed(const CBlockLocator& locator)
+{
+ if (!m_synced) {
+ return;
+ }
+
+ const uint256& locator_tip_hash = locator.vHave.front();
+ const CBlockIndex* locator_tip_index;
+ {
+ LOCK(cs_main);
+ locator_tip_index = LookupBlockIndex(locator_tip_hash);
+ }
+
+ if (!locator_tip_index) {
+ FatalError("%s: First block (hash=%s) in locator was not found",
+ __func__, locator_tip_hash.ToString());
+ return;
+ }
+
+ // This checks that ChainStateFlushed callbacks are received after BlockConnected. The check may fail
+ // immediately after the the sync thread catches up and sets m_synced. Consider the case where
+ // there is a reorg and the blocks on the stale branch are in the ValidationInterface queue
+ // backlog even after the sync thread has caught up to the new chain tip. In this unlikely
+ // event, log a warning and let the queue clear.
+ const CBlockIndex* best_block_index = m_best_block_index.load();
+ if (best_block_index->GetAncestor(locator_tip_index->nHeight) != locator_tip_index) {
+ LogPrintf("%s: WARNING: Locator contains block (hash=%s) not on known best " /* Continued */
+ "chain (tip=%s); not writing txindex locator\n",
+ __func__, locator_tip_hash.ToString(),
+ best_block_index->GetBlockHash().ToString());
+ return;
+ }
+
+ if (!m_db->WriteBestBlock(locator)) {
+ error("%s: Failed to write locator to disk", __func__);
+ }
+}
+
+bool TxIndex::BlockUntilSyncedToCurrentChain()
+{
+ AssertLockNotHeld(cs_main);
+
+ if (!m_synced) {
+ return false;
+ }
+
+ {
+ // Skip the queue-draining stuff if we know we're caught up with
+ // chainActive.Tip().
+ LOCK(cs_main);
+ const CBlockIndex* chain_tip = chainActive.Tip();
+ const CBlockIndex* best_block_index = m_best_block_index.load();
+ if (best_block_index->GetAncestor(chain_tip->nHeight) == chain_tip) {
+ return true;
+ }
+ }
+
+ LogPrintf("%s: txindex is catching up on block notifications\n", __func__);
+ SyncWithValidationInterfaceQueue();
+ return true;
+}
+
+bool TxIndex::FindTx(const uint256& tx_hash, uint256& block_hash, CTransactionRef& tx) const
+{
+ CDiskTxPos postx;
+ if (!m_db->ReadTxPos(tx_hash, postx)) {
+ return false;
+ }
+
+ CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION);
+ if (file.IsNull()) {
+ return error("%s: OpenBlockFile failed", __func__);
+ }
+ CBlockHeader header;
+ try {
+ file >> header;
+ if (fseek(file.Get(), postx.nTxOffset, SEEK_CUR)) {
+ return error("%s: fseek(...) failed", __func__);
+ }
+ file >> tx;
+ } catch (const std::exception& e) {
+ return error("%s: Deserialize or I/O error - %s", __func__, e.what());
+ }
+ if (tx->GetHash() != tx_hash) {
+ return error("%s: txid mismatch", __func__);
+ }
+ block_hash = header.GetHash();
+ return true;
+}
+
+void TxIndex::Interrupt()
+{
+ m_interrupt();
+}
+
+void TxIndex::Start()
+{
+ // Need to register this ValidationInterface before running Init(), so that
+ // callbacks are not missed if Init sets m_synced to true.
+ RegisterValidationInterface(this);
+ if (!Init()) {
+ FatalError("%s: txindex failed to initialize", __func__);
+ return;
+ }
+
+ m_thread_sync = std::thread(&TraceThread<std::function<void()>>, "txindex",
+ std::bind(&TxIndex::ThreadSync, this));
+}
+
+void TxIndex::Stop()
+{
+ UnregisterValidationInterface(this);
+
+ if (m_thread_sync.joinable()) {
+ m_thread_sync.join();
+ }
+}
diff --git a/src/index/txindex.h b/src/index/txindex.h
new file mode 100644
index 0000000000..4937bd64e9
--- /dev/null
+++ b/src/index/txindex.h
@@ -0,0 +1,94 @@
+// Copyright (c) 2017-2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_INDEX_TXINDEX_H
+#define BITCOIN_INDEX_TXINDEX_H
+
+#include <primitives/block.h>
+#include <primitives/transaction.h>
+#include <threadinterrupt.h>
+#include <txdb.h>
+#include <uint256.h>
+#include <validationinterface.h>
+
+class CBlockIndex;
+
+/**
+ * TxIndex is used to look up transactions included in the blockchain by hash.
+ * The index is written to a LevelDB database and records the filesystem
+ * location of each transaction by transaction hash.
+ */
+class TxIndex final : public CValidationInterface
+{
+private:
+ const std::unique_ptr<TxIndexDB> m_db;
+
+ /// Whether the index is in sync with the main chain. The flag is flipped
+ /// from false to true once, after which point this starts processing
+ /// ValidationInterface notifications to stay in sync.
+ std::atomic<bool> m_synced;
+
+ /// The last block in the chain that the TxIndex is in sync with.
+ std::atomic<const CBlockIndex*> m_best_block_index;
+
+ std::thread m_thread_sync;
+ CThreadInterrupt m_interrupt;
+
+ /// Initialize internal state from the database and block index.
+ bool Init();
+
+ /// Sync the tx index with the block index starting from the current best
+ /// block. Intended to be run in its own thread, m_thread_sync, and can be
+ /// interrupted with m_interrupt. Once the txindex gets in sync, the
+ /// m_synced flag is set and the BlockConnected ValidationInterface callback
+ /// takes over and the sync thread exits.
+ void ThreadSync();
+
+ /// Write update index entries for a newly connected block.
+ bool WriteBlock(const CBlock& block, const CBlockIndex* pindex);
+
+ /// Write the current chain block locator to the DB.
+ bool WriteBestBlock(const CBlockIndex* block_index);
+
+protected:
+ void BlockConnected(const std::shared_ptr<const CBlock>& block, const CBlockIndex* pindex,
+ const std::vector<CTransactionRef>& txn_conflicted) override;
+
+ void ChainStateFlushed(const CBlockLocator& locator) override;
+
+public:
+ /// Constructs the TxIndex, which becomes available to be queried.
+ explicit TxIndex(std::unique_ptr<TxIndexDB> db);
+
+ /// Destructor interrupts sync thread if running and blocks until it exits.
+ ~TxIndex();
+
+ /// Blocks the current thread until the transaction index is caught up to
+ /// the current state of the block chain. This only blocks if the index has gotten in sync once
+ /// and only needs to process blocks in the ValidationInterface queue. If the index is catching
+ /// up from far behind, this method does not block and immediately returns false.
+ bool BlockUntilSyncedToCurrentChain();
+
+ /// Look up a transaction by hash.
+ ///
+ /// @param[in] tx_hash The hash of the transaction to be returned.
+ /// @param[out] block_hash The hash of the block the transaction is found in.
+ /// @param[out] tx The transaction itself.
+ /// @return true if transaction is found, false otherwise
+ bool FindTx(const uint256& tx_hash, uint256& block_hash, CTransactionRef& tx) const;
+
+ void Interrupt();
+
+ /// Start initializes the sync state and registers the instance as a
+ /// ValidationInterface so that it stays in sync with blockchain updates.
+ void Start();
+
+ /// Stops the instance from staying in sync with blockchain updates.
+ void Stop();
+};
+
+/// The global transaction index, used in GetTransaction. May be null.
+extern std::unique_ptr<TxIndex> g_txindex;
+
+#endif // BITCOIN_INDEX_TXINDEX_H
diff --git a/src/init.cpp b/src/init.cpp
index abff5ec79d..4ad68b73f2 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -19,6 +19,7 @@
#include <fs.h>
#include <httpserver.h>
#include <httprpc.h>
+#include <index/txindex.h>
#include <key.h>
#include <validation.h>
#include <miner.h>
@@ -30,7 +31,6 @@
#include <policy/policy.h>
#include <rpc/server.h>
#include <rpc/register.h>
-#include <rpc/safemode.h>
#include <rpc/blockchain.h>
#include <script/standard.h>
#include <script/sigcache.h>
@@ -76,19 +76,18 @@ std::unique_ptr<PeerLogicValidation> peerLogic;
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 {LogPrintf("No wallet support compiled in!\n"); return true;}
- void Start(CScheduler& scheduler) override {}
- void Flush() override {}
- void Stop() override {}
- void Close() override {}
+ void AddWalletOptions() const override {}
+ bool ParameterInteraction() const override {return true;}
+ void RegisterRPC(CRPCTable &) const override {}
+ bool Verify() const override {return true;}
+ bool Open() const override {LogPrintf("No wallet support compiled in!\n"); return true;}
+ void Start(CScheduler& scheduler) const override {}
+ void Flush() const override {}
+ void Stop() const override {}
+ void Close() const override {}
};
-static DummyWalletInit g_dummy_wallet_init;
-WalletInitInterface* const g_wallet_init_interface = &g_dummy_wallet_init;
+const WalletInitInterface& g_wallet_init_interface = DummyWalletInit();
#endif
#if ENABLE_ZMQ
@@ -183,6 +182,9 @@ void Interrupt()
InterruptMapPort();
if (g_connman)
g_connman->Interrupt();
+ if (g_txindex) {
+ g_txindex->Interrupt();
+ }
}
void Shutdown()
@@ -204,7 +206,7 @@ void Shutdown()
StopREST();
StopRPC();
StopHTTPServer();
- g_wallet_init_interface->Flush();
+ g_wallet_init_interface.Flush();
StopMapPort();
// Because these depend on each-other, we make sure that neither can be
@@ -213,6 +215,9 @@ void Shutdown()
if (g_connman) g_connman->Stop();
peerLogic.reset();
g_connman.reset();
+ if (g_txindex) {
+ g_txindex.reset();
+ }
StopTorControl();
@@ -237,7 +242,7 @@ void Shutdown()
fFeeEstimatesInitialized = false;
}
- // FlushStateToDisk generates a SetBestChain callback, which we should avoid missing
+ // FlushStateToDisk generates a ChainStateFlushed callback, which we should avoid missing
if (pcoinsTip != nullptr) {
FlushStateToDisk();
}
@@ -262,7 +267,7 @@ void Shutdown()
pcoinsdbview.reset();
pblocktree.reset();
}
- g_wallet_init_interface->Stop();
+ g_wallet_init_interface.Stop();
#if ENABLE_ZMQ
if (pzmqNotificationInterface) {
@@ -282,7 +287,7 @@ void Shutdown()
UnregisterAllValidationInterfaces();
GetMainSignals().UnregisterBackgroundSignalScheduler();
GetMainSignals().UnregisterWithMempoolSignals(mempool);
- g_wallet_init_interface->Close();
+ g_wallet_init_interface.Close();
globalVerifyHandle.reset();
ECC_Stop();
LogPrintf("%s: done\n", __func__);
@@ -293,6 +298,7 @@ void Shutdown()
* The execution context the handler is invoked in is not guaranteed,
* so we restrict handler operations to just touching variables:
*/
+#ifndef WIN32
static void HandleSIGTERM(int)
{
fRequestShutdown = true;
@@ -300,8 +306,16 @@ static void HandleSIGTERM(int)
static void HandleSIGHUP(int)
{
- fReopenDebugLog = true;
+ g_logger->m_reopen_file = true;
+}
+#else
+static BOOL WINAPI consoleCtrlHandler(DWORD dwCtrlType)
+{
+ fRequestShutdown = true;
+ Sleep(INFINITE);
+ return true;
}
+#endif
#ifndef WIN32
static void registerSignalHandler(int signal, void(*handler)(int))
@@ -314,12 +328,12 @@ static void registerSignalHandler(int signal, void(*handler)(int))
}
#endif
-void OnRPCStarted()
+static void OnRPCStarted()
{
uiInterface.NotifyBlockTip.connect(&RPCNotifyBlockChange);
}
-void OnRPCStopped()
+static void OnRPCStopped()
{
uiInterface.NotifyBlockTip.disconnect(&RPCNotifyBlockChange);
RPCNotifyBlockChange(false, nullptr);
@@ -327,199 +341,164 @@ void OnRPCStopped()
LogPrint(BCLog::RPC, "RPC stopped.\n");
}
-std::string HelpMessage(HelpMessageMode mode)
+void SetupServerArgs()
{
const auto defaultBaseParams = CreateBaseChainParams(CBaseChainParams::MAIN);
const auto testnetBaseParams = CreateBaseChainParams(CBaseChainParams::TESTNET);
const auto defaultChainParams = CreateChainParams(CBaseChainParams::MAIN);
const auto testnetChainParams = CreateChainParams(CBaseChainParams::TESTNET);
- const bool showDebug = gArgs.GetBoolArg("-help-debug", false);
+ // Set all of the args and their help
// When adding new options to the categories, please keep and ensure alphabetical ordering.
// Do not translate _(...) -help-debug options, Many technical terms, and only a very small audience, so is unnecessary stress to translators.
- std::string strUsage = HelpMessageGroup(_("Options:"));
- strUsage += HelpMessageOpt("-?", _("Print this help message and exit"));
- 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 == HelpMessageMode::BITCOIND)
- {
-#if HAVE_DECL_DAEMON
- strUsage += HelpMessageOpt("-daemon", _("Run in the background as a daemon and accept commands"));
-#endif
- }
- strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory"));
- if (showDebug) {
- strUsage += HelpMessageOpt("-dbbatchsize", strprintf("Maximum database write batch size in bytes (default: %u)", nDefaultDbBatchSize));
- }
- strUsage += HelpMessageOpt("-dbcache=<n>", strprintf(_("Set database cache size in megabytes (%d to %d, default: %d)"), nMinDbCache, nMaxDbCache, nDefaultDbCache));
- strUsage += HelpMessageOpt("-debuglogfile=<file>", strprintf(_("Specify location of debug log file. Relative paths will be prefixed by a net-specific datadir location. (default: %s)"), DEFAULT_DEBUGLOGFILE));
- if (showDebug)
- strUsage += HelpMessageOpt("-feefilter", strprintf("Tell other nodes to filter invs to us by our mempool min fee (default: %u)", DEFAULT_FEEFILTER));
- strUsage += HelpMessageOpt("-loadblock=<file>", _("Imports blocks from external blk000??.dat file on startup"));
- strUsage += HelpMessageOpt("-maxmempool=<n>", strprintf(_("Keep the transaction memory pool below <n> megabytes (default: %u)"), DEFAULT_MAX_MEMPOOL_SIZE));
- strUsage += HelpMessageOpt("-maxorphantx=<n>", strprintf(_("Keep at most <n> unconnectable transactions in memory (default: %u)"), DEFAULT_MAX_ORPHAN_TRANSACTIONS));
- strUsage += HelpMessageOpt("-mempoolexpiry=<n>", strprintf(_("Do not keep transactions in the mempool longer than <n> hours (default: %u)"), DEFAULT_MEMPOOL_EXPIRY));
- if (showDebug) {
- strUsage += HelpMessageOpt("-minimumchainwork=<hex>", strprintf("Minimum work assumed to exist on a valid chain in hex (default: %s, testnet: %s)", defaultChainParams->GetConsensus().nMinimumChainWork.GetHex(), testnetChainParams->GetConsensus().nMinimumChainWork.GetHex()));
- }
- strUsage += HelpMessageOpt("-par=<n>", strprintf(_("Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)"),
- -GetNumCores(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS));
- strUsage += HelpMessageOpt("-persistmempool", strprintf(_("Whether to save the mempool on shutdown and load on restart (default: %u)"), DEFAULT_PERSIST_MEMPOOL));
+ gArgs.AddArg("-?", _("Print this help message and exit"), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-version", _("Print version and exit"), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-alertnotify=<cmd>", _("Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)"), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-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()), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-blocksdir=<dir>", _("Specify blocks directory (default: <datadir>/blocks)"), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-blocknotify=<cmd>", _("Execute command when the best block changes (%s in cmd is replaced by block hash)"), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-blockreconstructionextratxn=<n>", strprintf(_("Extra transactions to keep in memory for compact block reconstructions (default: %u)"), DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-blocksonly", strprintf(_("Whether to operate in a blocks only mode (default: %u)"), DEFAULT_BLOCKSONLY), true, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-conf=<file>", strprintf(_("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)"), BITCOIN_CONF_FILENAME), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-datadir=<dir>", _("Specify data directory"), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-dbbatchsize", strprintf("Maximum database write batch size in bytes (default: %u)", nDefaultDbBatchSize), true, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-dbcache=<n>", strprintf(_("Set database cache size in megabytes (%d to %d, default: %d)"), nMinDbCache, nMaxDbCache, nDefaultDbCache), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-debuglogfile=<file>", strprintf(_("Specify location of debug log file. Relative paths will be prefixed by a net-specific datadir location. (default: %s)"), DEFAULT_DEBUGLOGFILE), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-feefilter", strprintf("Tell other nodes to filter invs to us by our mempool min fee (default: %u)", DEFAULT_FEEFILTER), true, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-includeconf=<file>", _("Specify additional configuration file, relative to the -datadir path (only useable from configuration file, not command line)"), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-loadblock=<file>", _("Imports blocks from external blk000??.dat file on startup"), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-maxmempool=<n>", strprintf(_("Keep the transaction memory pool below <n> megabytes (default: %u)"), DEFAULT_MAX_MEMPOOL_SIZE), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-maxorphantx=<n>", strprintf(_("Keep at most <n> unconnectable transactions in memory (default: %u)"), DEFAULT_MAX_ORPHAN_TRANSACTIONS), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-mempoolexpiry=<n>", strprintf(_("Do not keep transactions in the mempool longer than <n> hours (default: %u)"), DEFAULT_MEMPOOL_EXPIRY), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-minimumchainwork=<hex>", strprintf("Minimum work assumed to exist on a valid chain in hex (default: %s, testnet: %s)", defaultChainParams->GetConsensus().nMinimumChainWork.GetHex(), testnetChainParams->GetConsensus().nMinimumChainWork.GetHex()), true, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-par=<n>", strprintf(_("Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)"),
+ -GetNumCores(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-persistmempool", strprintf(_("Whether to save the mempool on shutdown and load on restart (default: %u)"), DEFAULT_PERSIST_MEMPOOL), false, OptionsCategory::OPTIONS);
#ifndef WIN32
- strUsage += HelpMessageOpt("-pid=<file>", strprintf(_("Specify pid file. Relative paths will be prefixed by a net-specific datadir location. (default: %s)"), BITCOIN_PID_FILENAME));
+ gArgs.AddArg("-pid=<file>", strprintf(_("Specify pid file. Relative paths will be prefixed by a net-specific datadir location. (default: %s)"), BITCOIN_PID_FILENAME), false, OptionsCategory::OPTIONS);
#endif
- strUsage += HelpMessageOpt("-prune=<n>", strprintf(_("Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks, and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -txindex and -rescan. "
+ gArgs.AddArg("-prune=<n>", strprintf(_("Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks, and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -txindex and -rescan. "
"Warning: Reverting this setting requires re-downloading the entire blockchain. "
- "(default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >%u = automatically prune block files to stay under the specified target size in MiB)"), MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024));
- strUsage += HelpMessageOpt("-reindex", _("Rebuild chain state and block index from the blk*.dat files on disk"));
- strUsage += HelpMessageOpt("-reindex-chainstate", _("Rebuild chain state from the currently indexed blocks"));
+ "(default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >%u = automatically prune block files to stay under the specified target size in MiB)"), MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-reindex", _("Rebuild chain state and block index from the blk*.dat files on disk"), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-reindex-chainstate", _("Rebuild chain state from the currently indexed blocks"), false, OptionsCategory::OPTIONS);
#ifndef WIN32
- strUsage += HelpMessageOpt("-sysperms", _("Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality)"));
+ gArgs.AddArg("-sysperms", _("Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality)"), false, OptionsCategory::OPTIONS);
#endif
- strUsage += HelpMessageOpt("-txindex", strprintf(_("Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u)"), DEFAULT_TXINDEX));
-
- strUsage += HelpMessageGroup(_("Connection options:"));
- strUsage += HelpMessageOpt("-addnode=<ip>", _("Add a node to connect to and attempt to keep the connection open (see the `addnode` RPC command help for more info)"));
- strUsage += HelpMessageOpt("-banscore=<n>", strprintf(_("Threshold for disconnecting misbehaving peers (default: %u)"), DEFAULT_BANSCORE_THRESHOLD));
- strUsage += HelpMessageOpt("-bantime=<n>", strprintf(_("Number of seconds to keep misbehaving peers from reconnecting (default: %u)"), DEFAULT_MISBEHAVING_BANTIME));
- strUsage += HelpMessageOpt("-bind=<addr>", _("Bind to given address and always listen on it. Use [host]:port notation for IPv6"));
- strUsage += HelpMessageOpt("-connect=<ip>", _("Connect only to the specified node(s); -connect=0 disables automatic connections (the rules for this peer are the same as for -addnode)"));
- strUsage += HelpMessageOpt("-discover", _("Discover own IP addresses (default: 1 when listening and no -externalip or -proxy)"));
- strUsage += HelpMessageOpt("-dns", _("Allow DNS lookups for -addnode, -seednode and -connect") + " " + strprintf(_("(default: %u)"), DEFAULT_NAME_LOOKUP));
- strUsage += HelpMessageOpt("-dnsseed", _("Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect used)"));
- strUsage += HelpMessageOpt("-externalip=<ip>", _("Specify your own public address"));
- strUsage += HelpMessageOpt("-forcednsseed", strprintf(_("Always query for peer addresses via DNS lookup (default: %u)"), DEFAULT_FORCEDNSSEED));
- strUsage += HelpMessageOpt("-listen", _("Accept connections from outside (default: 1 if no -proxy or -connect)"));
- strUsage += HelpMessageOpt("-listenonion", strprintf(_("Automatically create Tor hidden service (default: %d)"), DEFAULT_LISTEN_ONION));
- strUsage += HelpMessageOpt("-maxconnections=<n>", strprintf(_("Maintain at most <n> connections to peers (default: %u)"), DEFAULT_MAX_PEER_CONNECTIONS));
- strUsage += HelpMessageOpt("-maxreceivebuffer=<n>", strprintf(_("Maximum per-connection receive buffer, <n>*1000 bytes (default: %u)"), DEFAULT_MAXRECEIVEBUFFER));
- strUsage += HelpMessageOpt("-maxsendbuffer=<n>", strprintf(_("Maximum per-connection send buffer, <n>*1000 bytes (default: %u)"), DEFAULT_MAXSENDBUFFER));
- strUsage += HelpMessageOpt("-maxtimeadjustment", strprintf(_("Maximum allowed median peer time offset adjustment. Local perspective of time may be influenced by peers forward or backward by this amount. (default: %u seconds)"), DEFAULT_MAX_TIME_ADJUSTMENT));
- strUsage += HelpMessageOpt("-maxuploadtarget=<n>", strprintf(_("Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = no limit (default: %d)"), DEFAULT_MAX_UPLOAD_TARGET));
- strUsage += HelpMessageOpt("-onion=<ip:port>", strprintf(_("Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: %s)"), "-proxy"));
- strUsage += HelpMessageOpt("-onlynet=<net>", _("Only connect to nodes in network <net> (ipv4, ipv6 or onion)"));
- strUsage += HelpMessageOpt("-peerbloomfilters", strprintf(_("Support filtering of blocks and transaction with bloom filters (default: %u)"), DEFAULT_PEERBLOOMFILTERS));
- strUsage += HelpMessageOpt("-permitbaremultisig", strprintf(_("Relay non-P2SH multisig (default: %u)"), DEFAULT_PERMIT_BAREMULTISIG));
- strUsage += HelpMessageOpt("-port=<port>", strprintf(_("Listen for connections on <port> (default: %u or testnet: %u)"), defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort()));
- strUsage += HelpMessageOpt("-proxy=<ip:port>", _("Connect through SOCKS5 proxy"));
- strUsage += HelpMessageOpt("-proxyrandomize", strprintf(_("Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)"), DEFAULT_PROXYRANDOMIZE));
- strUsage += HelpMessageOpt("-seednode=<ip>", _("Connect to a node to retrieve peer addresses, and disconnect"));
- strUsage += HelpMessageOpt("-timeout=<n>", strprintf(_("Specify connection timeout in milliseconds (minimum: 1, default: %d)"), DEFAULT_CONNECT_TIMEOUT));
- strUsage += HelpMessageOpt("-torcontrol=<ip>:<port>", strprintf(_("Tor control port to use if onion listening enabled (default: %s)"), DEFAULT_TOR_CONTROL));
- strUsage += HelpMessageOpt("-torpassword=<pass>", _("Tor control port password (default: empty)"));
+ gArgs.AddArg("-txindex", strprintf(_("Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u)"), DEFAULT_TXINDEX), false, OptionsCategory::OPTIONS);
+
+ gArgs.AddArg("-addnode=<ip>", _("Add a node to connect to and attempt to keep the connection open (see the `addnode` RPC command help for more info)"), false, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-banscore=<n>", strprintf(_("Threshold for disconnecting misbehaving peers (default: %u)"), DEFAULT_BANSCORE_THRESHOLD), false, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-bantime=<n>", strprintf(_("Number of seconds to keep misbehaving peers from reconnecting (default: %u)"), DEFAULT_MISBEHAVING_BANTIME), false, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-bind=<addr>", _("Bind to given address and always listen on it. Use [host]:port notation for IPv6"), false, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-connect=<ip>", _("Connect only to the specified node(s); -connect=0 disables automatic connections (the rules for this peer are the same as for -addnode)"), false, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-discover", _("Discover own IP addresses (default: 1 when listening and no -externalip or -proxy)"), false, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-dns", _("Allow DNS lookups for -addnode, -seednode and -connect") + " " + strprintf(_("(default: %u)"), DEFAULT_NAME_LOOKUP), false, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-dnsseed", _("Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect used)"), false, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-externalip=<ip>", _("Specify your own public address"), false, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-forcednsseed", strprintf(_("Always query for peer addresses via DNS lookup (default: %u)"), DEFAULT_FORCEDNSSEED), false, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-listen", _("Accept connections from outside (default: 1 if no -proxy or -connect)"), false, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-listenonion", strprintf(_("Automatically create Tor hidden service (default: %d)"), DEFAULT_LISTEN_ONION), false, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-maxconnections=<n>", strprintf(_("Maintain at most <n> connections to peers (default: %u)"), DEFAULT_MAX_PEER_CONNECTIONS), false, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-maxreceivebuffer=<n>", strprintf(_("Maximum per-connection receive buffer, <n>*1000 bytes (default: %u)"), DEFAULT_MAXRECEIVEBUFFER), false, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-maxsendbuffer=<n>", strprintf(_("Maximum per-connection send buffer, <n>*1000 bytes (default: %u)"), DEFAULT_MAXSENDBUFFER), false, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-maxtimeadjustment", strprintf(_("Maximum allowed median peer time offset adjustment. Local perspective of time may be influenced by peers forward or backward by this amount. (default: %u seconds)"), DEFAULT_MAX_TIME_ADJUSTMENT), false, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-maxuploadtarget=<n>", strprintf(_("Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = no limit (default: %d)"), DEFAULT_MAX_UPLOAD_TARGET), false, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-onion=<ip:port>", strprintf(_("Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: %s)"), "-proxy"), false, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-onlynet=<net>", _("Only connect to nodes in network <net> (ipv4, ipv6 or onion)"), false, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-peerbloomfilters", strprintf(_("Support filtering of blocks and transaction with bloom filters (default: %u)"), DEFAULT_PEERBLOOMFILTERS), false, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-permitbaremultisig", strprintf(_("Relay non-P2SH multisig (default: %u)"), DEFAULT_PERMIT_BAREMULTISIG), false, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-port=<port>", strprintf(_("Listen for connections on <port> (default: %u or testnet: %u)"), defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort()), false, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-proxy=<ip:port>", _("Connect through SOCKS5 proxy"), false, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-proxyrandomize", strprintf(_("Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)"), DEFAULT_PROXYRANDOMIZE), false, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-seednode=<ip>", _("Connect to a node to retrieve peer addresses, and disconnect"), false, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-timeout=<n>", strprintf(_("Specify connection timeout in milliseconds (minimum: 1, default: %d)"), DEFAULT_CONNECT_TIMEOUT), false, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-torcontrol=<ip>:<port>", strprintf(_("Tor control port to use if onion listening enabled (default: %s)"), DEFAULT_TOR_CONTROL), false, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-torpassword=<pass>", _("Tor control port password (default: empty)"), false, OptionsCategory::CONNECTION);
#ifdef USE_UPNP
#if USE_UPNP
- strUsage += HelpMessageOpt("-upnp", _("Use UPnP to map the listening port (default: 1 when listening and no -proxy)"));
+ gArgs.AddArg("-upnp", _("Use UPnP to map the listening port (default: 1 when listening and no -proxy)"), false, OptionsCategory::CONNECTION);
#else
- strUsage += HelpMessageOpt("-upnp", strprintf(_("Use UPnP to map the listening port (default: %u)"), 0));
+ gArgs.AddArg("-upnp", strprintf(_("Use UPnP to map the listening port (default: %u)"), 0), false, OptionsCategory::CONNECTION);
#endif
#endif
- strUsage += HelpMessageOpt("-whitebind=<addr>", _("Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6"));
- 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"));
+ gArgs.AddArg("-whitebind=<addr>", _("Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6"), false, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-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"), false, OptionsCategory::CONNECTION);
- strUsage += g_wallet_init_interface->GetHelpString(showDebug);
+ g_wallet_init_interface.AddWalletOptions();
#if ENABLE_ZMQ
- strUsage += HelpMessageGroup(_("ZeroMQ notification options:"));
- strUsage += HelpMessageOpt("-zmqpubhashblock=<address>", _("Enable publish hash block in <address>"));
- strUsage += HelpMessageOpt("-zmqpubhashtx=<address>", _("Enable publish hash transaction in <address>"));
- strUsage += HelpMessageOpt("-zmqpubrawblock=<address>", _("Enable publish raw block in <address>"));
- strUsage += HelpMessageOpt("-zmqpubrawtx=<address>", _("Enable publish raw transaction in <address>"));
+ gArgs.AddArg("-zmqpubhashblock=<address>", _("Enable publish hash block in <address>"), false, OptionsCategory::ZMQ);
+ gArgs.AddArg("-zmqpubhashtx=<address>", _("Enable publish hash transaction in <address>"), false, OptionsCategory::ZMQ);
+ gArgs.AddArg("-zmqpubrawblock=<address>", _("Enable publish raw block in <address>"), false, OptionsCategory::ZMQ);
+ gArgs.AddArg("-zmqpubrawtx=<address>", _("Enable publish raw transaction in <address>"), false, OptionsCategory::ZMQ);
#endif
- strUsage += HelpMessageGroup(_("Debugging/Testing options:"));
- 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. (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("-stopafterblockimport", strprintf("Stop running after importing blocks from disk (default: %u)", DEFAULT_STOPAFTERBLOCKIMPORT));
- strUsage += HelpMessageOpt("-stopatheight", strprintf("Stop running after reaching the given height in the main chain (default: %u)", DEFAULT_STOPATHEIGHT));
-
- strUsage += HelpMessageOpt("-limitancestorcount=<n>", strprintf("Do not accept transactions if number of in-mempool ancestors is <n> or more (default: %u)", DEFAULT_ANCESTOR_LIMIT));
- strUsage += HelpMessageOpt("-limitancestorsize=<n>", strprintf("Do not accept transactions whose size with all in-mempool ancestors exceeds <n> kilobytes (default: %u)", DEFAULT_ANCESTOR_SIZE_LIMIT));
- strUsage += HelpMessageOpt("-limitdescendantcount=<n>", strprintf("Do not accept transactions if any ancestor would have <n> or more in-mempool descendants (default: %u)", DEFAULT_DESCENDANT_LIMIT));
- strUsage += HelpMessageOpt("-limitdescendantsize=<n>", strprintf("Do not accept transactions if any ancestor would have more than <n> kilobytes of in-mempool descendants (default: %u).", DEFAULT_DESCENDANT_SIZE_LIMIT));
- strUsage += HelpMessageOpt("-vbparams=deployment:start:end", "Use given start/end times for specified version bits deployment (regtest-only)");
- strUsage += HelpMessageOpt("-addrmantest", "Allows to test address relay on localhost");
- }
- strUsage += HelpMessageOpt("-debug=<category>", strprintf(_("Output debugging information (default: %u, supplying <category> is optional)"), 0) + ". " +
- _("If <category> is not supplied or if <category> = 1, output all debugging information.") + " " + _("<category> can be:") + " " + ListLogCategories() + ".");
- strUsage += HelpMessageOpt("-debugexclude=<category>", strprintf(_("Exclude debugging information for a category. Can be used in conjunction with -debug=1 to output debug logs for all categories except one or more specified categories.")));
- strUsage += HelpMessageOpt("-help-debug", _("Show all debugging options (usage: --help -help-debug)"));
- strUsage += HelpMessageOpt("-logips", strprintf(_("Include IP addresses in debug output (default: %u)"), DEFAULT_LOGIPS));
- strUsage += HelpMessageOpt("-logtimestamps", strprintf(_("Prepend debug output with timestamp (default: %u)"), DEFAULT_LOGTIMESTAMPS));
- if (showDebug)
- {
- strUsage += HelpMessageOpt("-logtimemicros", strprintf("Add microsecond precision to debug timestamps (default: %u)", DEFAULT_LOGTIMEMICROS));
- strUsage += HelpMessageOpt("-mocktime=<n>", "Replace actual time with <n> seconds since epoch (default: 0)");
- strUsage += HelpMessageOpt("-maxsigcachesize=<n>", strprintf("Limit sum of signature cache and script execution cache sizes to <n> MiB (default: %u)", DEFAULT_MAX_SIG_CACHE_SIZE));
- strUsage += HelpMessageOpt("-maxtipage=<n>", strprintf("Maximum tip age in seconds to consider node in initial block download (default: %u)", DEFAULT_MAX_TIP_AGE));
- }
- strUsage += HelpMessageOpt("-maxtxfee=<amt>", strprintf(_("Maximum total fees (in %s) to use in a single wallet transaction or raw transaction; setting this too low may abort large transactions (default: %s)"),
- CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MAXFEE)));
- if (showDebug)
- {
- strUsage += HelpMessageOpt("-printpriority", strprintf("Log transaction fee per kB when mining blocks (default: %u)", DEFAULT_PRINTPRIORITY));
- }
- strUsage += HelpMessageOpt("-printtoconsole", _("Send trace/debug info to console instead of debug.log file"));
- strUsage += HelpMessageOpt("-shrinkdebugfile", _("Shrink debug.log file on client startup (default: 1 when no -debug)"));
- strUsage += HelpMessageOpt("-uacomment=<cmt>", _("Append comment to the user agent string"));
-
- AppendParamsHelpMessages(strUsage, showDebug);
-
- strUsage += HelpMessageGroup(_("Node relay options:"));
- if (showDebug) {
- strUsage += HelpMessageOpt("-acceptnonstdtxn", strprintf("Relay and mine \"non-standard\" transactions (%sdefault: %u)", "testnet/regtest only; ", !testnetChainParams->RequireStandard()));
- strUsage += HelpMessageOpt("-incrementalrelayfee=<amt>", strprintf("Fee rate (in %s/kB) used to define cost of relay, used for mempool limiting and BIP 125 replacement. (default: %s)", CURRENCY_UNIT, FormatMoney(DEFAULT_INCREMENTAL_RELAY_FEE)));
- strUsage += HelpMessageOpt("-dustrelayfee=<amt>", strprintf("Fee rate (in %s/kB) used to defined dust, the value of an output such that it will cost more than its value in fees at this fee rate to spend it. (default: %s)", CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE)));
- }
- strUsage += HelpMessageOpt("-bytespersigop", strprintf(_("Equivalent bytes per sigop in transactions for relay and mining (default: %u)"), DEFAULT_BYTES_PER_SIGOP));
- strUsage += HelpMessageOpt("-datacarrier", strprintf(_("Relay and mine data carrier transactions (default: %u)"), DEFAULT_ACCEPT_DATACARRIER));
- strUsage += HelpMessageOpt("-datacarriersize", strprintf(_("Maximum size of data in data carrier transactions we relay and mine (default: %u)"), MAX_OP_RETURN_RELAY));
- strUsage += HelpMessageOpt("-mempoolreplacement", strprintf(_("Enable transaction replacement in the memory pool (default: %u)"), DEFAULT_ENABLE_REPLACEMENT));
- strUsage += HelpMessageOpt("-minrelaytxfee=<amt>", strprintf(_("Fees (in %s/kB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)"),
- CURRENCY_UNIT, FormatMoney(DEFAULT_MIN_RELAY_TX_FEE)));
- strUsage += HelpMessageOpt("-whitelistforcerelay", strprintf(_("Force relay of transactions from whitelisted peers even if they violate local relay policy (default: %d)"), DEFAULT_WHITELISTFORCERELAY));
- strUsage += HelpMessageOpt("-whitelistrelay", strprintf(_("Accept relayed transactions received from whitelisted peers even when not relaying transactions (default: %d)"), DEFAULT_WHITELISTRELAY));
-
- strUsage += HelpMessageGroup(_("Block creation options:"));
- strUsage += HelpMessageOpt("-blockmaxweight=<n>", strprintf(_("Set maximum BIP141 block weight (default: %d)"), DEFAULT_BLOCK_MAX_WEIGHT));
- strUsage += HelpMessageOpt("-blockmintxfee=<amt>", strprintf(_("Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s)"), CURRENCY_UNIT, FormatMoney(DEFAULT_BLOCK_MIN_TX_FEE)));
- if (showDebug)
- strUsage += HelpMessageOpt("-blockversion=<n>", "Override block version to test forking scenarios");
-
- strUsage += HelpMessageGroup(_("RPC server options:"));
- strUsage += HelpMessageOpt("-rest", strprintf(_("Accept public REST requests (default: %u)"), DEFAULT_REST_ENABLE));
- strUsage += HelpMessageOpt("-rpcallowip=<ip>", _("Allow JSON-RPC connections from specified source. Valid for <ip> are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). This option can be specified multiple times"));
- strUsage += HelpMessageOpt("-rpcauth=<userpw>", _("Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. The client then connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> pair of arguments. This option can be specified multiple times"));
- strUsage += HelpMessageOpt("-rpcbind=<addr>[:port]", _("Bind to given address to listen for JSON-RPC connections. This option is ignored unless -rpcallowip is also passed. Port is optional and overrides -rpcport. Use [host]:port notation for IPv6. This option can be specified multiple times (default: 127.0.0.1 and ::1 i.e., localhost, or if -rpcallowip has been specified, 0.0.0.0 and :: i.e., all addresses)"));
- strUsage += HelpMessageOpt("-rpccookiefile=<loc>", _("Location of the auth cookie. Relative paths will be prefixed by a net-specific datadir location. (default: data dir)"));
- strUsage += HelpMessageOpt("-rpcpassword=<pw>", _("Password for JSON-RPC connections"));
- strUsage += HelpMessageOpt("-rpcport=<port>", strprintf(_("Listen for JSON-RPC connections on <port> (default: %u or testnet: %u)"), defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort()));
- strUsage += HelpMessageOpt("-rpcserialversion", strprintf(_("Sets the serialization of raw transaction or block hex returned in non-verbose mode, non-segwit(0) or segwit(1) (default: %d)"), DEFAULT_RPC_SERIALIZE_VERSION));
- if (showDebug)
- strUsage += HelpMessageOpt("-rpcservertimeout=<n>", strprintf("Timeout during HTTP requests (default: %d)", DEFAULT_HTTP_SERVER_TIMEOUT));
- strUsage += HelpMessageOpt("-rpcthreads=<n>", strprintf(_("Set the number of threads to service RPC calls (default: %d)"), DEFAULT_HTTP_THREADS));
- strUsage += HelpMessageOpt("-rpcuser=<user>", _("Username for JSON-RPC connections"));
- if (showDebug)
- strUsage += HelpMessageOpt("-rpcworkqueue=<n>", strprintf("Set the depth of the work queue to service RPC calls (default: %d)", DEFAULT_HTTP_WORKQUEUE));
- strUsage += HelpMessageOpt("-server", _("Accept command line and JSON-RPC commands"));
-
- return strUsage;
+ gArgs.AddArg("-checkblocks=<n>", strprintf(_("How many blocks to check at startup (default: %u, 0 = all)"), DEFAULT_CHECKBLOCKS), true, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-checklevel=<n>", strprintf(_("How thorough the block verification of -checkblocks is (0-4, default: %u)"), DEFAULT_CHECKLEVEL), true, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-checkblockindex", strprintf("Do a full consistency check for mapBlockIndex, setBlockIndexCandidates, chainActive and mapBlocksUnlinked occasionally. (default: %u)", defaultChainParams->DefaultConsistencyChecks()), true, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-checkmempool=<n>", strprintf("Run checks every <n> transactions (default: %u)", defaultChainParams->DefaultConsistencyChecks()), true, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-checkpoints", strprintf("Disable expensive verification for known chain history (default: %u)", DEFAULT_CHECKPOINTS_ENABLED), true, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-deprecatedrpc=<method>", "Allows deprecated RPC method(s) to be used", true, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-dropmessagestest=<n>", "Randomly drop 1 of every <n> network messages", true, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-stopafterblockimport", strprintf("Stop running after importing blocks from disk (default: %u)", DEFAULT_STOPAFTERBLOCKIMPORT), true, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-stopatheight", strprintf("Stop running after reaching the given height in the main chain (default: %u)", DEFAULT_STOPATHEIGHT), true, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-limitancestorcount=<n>", strprintf("Do not accept transactions if number of in-mempool ancestors is <n> or more (default: %u)", DEFAULT_ANCESTOR_LIMIT), true, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-limitancestorsize=<n>", strprintf("Do not accept transactions whose size with all in-mempool ancestors exceeds <n> kilobytes (default: %u)", DEFAULT_ANCESTOR_SIZE_LIMIT), true, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-limitdescendantcount=<n>", strprintf("Do not accept transactions if any ancestor would have <n> or more in-mempool descendants (default: %u)", DEFAULT_DESCENDANT_LIMIT), true, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-limitdescendantsize=<n>", strprintf("Do not accept transactions if any ancestor would have more than <n> kilobytes of in-mempool descendants (default: %u).", DEFAULT_DESCENDANT_SIZE_LIMIT), true, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-vbparams=deployment:start:end", "Use given start/end times for specified version bits deployment (regtest-only)", true, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-addrmantest", "Allows to test address relay on localhost", true, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-debug=<category>", strprintf(_("Output debugging information (default: %u, supplying <category> is optional)"), 0) + ". " +
+ _("If <category> is not supplied or if <category> = 1, output all debugging information.") + " " + _("<category> can be:") + " " + ListLogCategories() + ".", false, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-debugexclude=<category>", strprintf(_("Exclude debugging information for a category. Can be used in conjunction with -debug=1 to output debug logs for all categories except one or more specified categories.")), false, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-help-debug", _("Show all debugging options (usage: --help -help-debug)"), false, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-logips", strprintf(_("Include IP addresses in debug output (default: %u)"), DEFAULT_LOGIPS), false, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-logtimestamps", strprintf(_("Prepend debug output with timestamp (default: %u)"), DEFAULT_LOGTIMESTAMPS), false, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-logtimemicros", strprintf("Add microsecond precision to debug timestamps (default: %u)", DEFAULT_LOGTIMEMICROS), true, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-mocktime=<n>", "Replace actual time with <n> seconds since epoch (default: 0)", true, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-maxsigcachesize=<n>", strprintf("Limit sum of signature cache and script execution cache sizes to <n> MiB (default: %u)", DEFAULT_MAX_SIG_CACHE_SIZE), true, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-maxtipage=<n>", strprintf("Maximum tip age in seconds to consider node in initial block download (default: %u)", DEFAULT_MAX_TIP_AGE), true, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-maxtxfee=<amt>", strprintf(_("Maximum total fees (in %s) to use in a single wallet transaction or raw transaction; setting this too low may abort large transactions (default: %s)"),
+ CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MAXFEE)), false, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-printpriority", strprintf("Log transaction fee per kB when mining blocks (default: %u)", DEFAULT_PRINTPRIORITY), true, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-printtoconsole", _("Send trace/debug info to console instead of debug.log file"), false, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-shrinkdebugfile", _("Shrink debug.log file on client startup (default: 1 when no -debug)"), false, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-uacomment=<cmt>", _("Append comment to the user agent string"), false, OptionsCategory::DEBUG_TEST);
+
+ SetupChainParamsBaseOptions();
+
+ gArgs.AddArg("-acceptnonstdtxn", strprintf("Relay and mine \"non-standard\" transactions (%sdefault: %u)", "testnet/regtest only; ", !testnetChainParams->RequireStandard()), true, OptionsCategory::NODE_RELAY);
+ gArgs.AddArg("-incrementalrelayfee=<amt>", strprintf("Fee rate (in %s/kB) used to define cost of relay, used for mempool limiting and BIP 125 replacement. (default: %s)", CURRENCY_UNIT, FormatMoney(DEFAULT_INCREMENTAL_RELAY_FEE)), true, OptionsCategory::NODE_RELAY);
+ gArgs.AddArg("-dustrelayfee=<amt>", strprintf("Fee rate (in %s/kB) used to defined dust, the value of an output such that it will cost more than its value in fees at this fee rate to spend it. (default: %s)", CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE)), true, OptionsCategory::NODE_RELAY);
+ gArgs.AddArg("-bytespersigop", strprintf(_("Equivalent bytes per sigop in transactions for relay and mining (default: %u)"), DEFAULT_BYTES_PER_SIGOP), false, OptionsCategory::NODE_RELAY);
+ gArgs.AddArg("-datacarrier", strprintf(_("Relay and mine data carrier transactions (default: %u)"), DEFAULT_ACCEPT_DATACARRIER), false, OptionsCategory::NODE_RELAY);
+ gArgs.AddArg("-datacarriersize", strprintf(_("Maximum size of data in data carrier transactions we relay and mine (default: %u)"), MAX_OP_RETURN_RELAY), false, OptionsCategory::NODE_RELAY);
+ gArgs.AddArg("-mempoolreplacement", strprintf(_("Enable transaction replacement in the memory pool (default: %u)"), DEFAULT_ENABLE_REPLACEMENT), false, OptionsCategory::NODE_RELAY);
+ gArgs.AddArg("-minrelaytxfee=<amt>", strprintf(_("Fees (in %s/kB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)"),
+ CURRENCY_UNIT, FormatMoney(DEFAULT_MIN_RELAY_TX_FEE)), false, OptionsCategory::NODE_RELAY);
+ gArgs.AddArg("-whitelistforcerelay", strprintf(_("Force relay of transactions from whitelisted peers even if they violate local relay policy (default: %d)"), DEFAULT_WHITELISTFORCERELAY), false, OptionsCategory::NODE_RELAY);
+ gArgs.AddArg("-whitelistrelay", strprintf(_("Accept relayed transactions received from whitelisted peers even when not relaying transactions (default: %d)"), DEFAULT_WHITELISTRELAY), false, OptionsCategory::NODE_RELAY);
+
+
+ gArgs.AddArg("-blockmaxweight=<n>", strprintf(_("Set maximum BIP141 block weight (default: %d)"), DEFAULT_BLOCK_MAX_WEIGHT), false, OptionsCategory::BLOCK_CREATION);
+ gArgs.AddArg("-blockmintxfee=<amt>", strprintf(_("Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s)"), CURRENCY_UNIT, FormatMoney(DEFAULT_BLOCK_MIN_TX_FEE)), false, OptionsCategory::BLOCK_CREATION);
+ gArgs.AddArg("-blockversion=<n>", "Override block version to test forking scenarios", true, OptionsCategory::BLOCK_CREATION);
+
+ gArgs.AddArg("-rest", strprintf(_("Accept public REST requests (default: %u)"), DEFAULT_REST_ENABLE), false, OptionsCategory::RPC);
+ gArgs.AddArg("-rpcallowip=<ip>", _("Allow JSON-RPC connections from specified source. Valid for <ip> are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). This option can be specified multiple times"), false, OptionsCategory::RPC);
+ gArgs.AddArg("-rpcauth=<userpw>", _("Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. The client then connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> pair of arguments. This option can be specified multiple times"), false, OptionsCategory::RPC);
+ gArgs.AddArg("-rpcbind=<addr>[:port]", _("Bind to given address to listen for JSON-RPC connections. This option is ignored unless -rpcallowip is also passed. Port is optional and overrides -rpcport. Use [host]:port notation for IPv6. This option can be specified multiple times (default: 127.0.0.1 and ::1 i.e., localhost, or if -rpcallowip has been specified, 0.0.0.0 and :: i.e., all addresses)"), false, OptionsCategory::RPC);
+ gArgs.AddArg("-rpccookiefile=<loc>", _("Location of the auth cookie. Relative paths will be prefixed by a net-specific datadir location. (default: data dir)"), false, OptionsCategory::RPC);
+ gArgs.AddArg("-rpcpassword=<pw>", _("Password for JSON-RPC connections"), false, OptionsCategory::RPC);
+ gArgs.AddArg("-rpcport=<port>", strprintf(_("Listen for JSON-RPC connections on <port> (default: %u or testnet: %u)"), defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort()), false, OptionsCategory::RPC);
+ gArgs.AddArg("-rpcserialversion", strprintf(_("Sets the serialization of raw transaction or block hex returned in non-verbose mode, non-segwit(0) or segwit(1) (default: %d)"), DEFAULT_RPC_SERIALIZE_VERSION), false, OptionsCategory::RPC);
+ gArgs.AddArg("-rpcservertimeout=<n>", strprintf("Timeout during HTTP requests (default: %d)", DEFAULT_HTTP_SERVER_TIMEOUT), true, OptionsCategory::RPC);
+ gArgs.AddArg("-rpcthreads=<n>", strprintf(_("Set the number of threads to service RPC calls (default: %d)"), DEFAULT_HTTP_THREADS), false, OptionsCategory::RPC);
+ gArgs.AddArg("-rpcuser=<user>", _("Username for JSON-RPC connections"), false, OptionsCategory::RPC);
+ gArgs.AddArg("-rpcworkqueue=<n>", strprintf("Set the depth of the work queue to service RPC calls (default: %d)", DEFAULT_HTTP_WORKQUEUE), true, OptionsCategory::RPC);
+ gArgs.AddArg("-server", _("Accept command line and JSON-RPC commands"), false, OptionsCategory::RPC);
}
std::string LicenseInfo()
@@ -592,7 +571,7 @@ struct CImportingNow
// rev files since they'll be rewritten by the reindex anyway. This ensures that vinfoBlockFile
// is in sync with what's actually on disk by the time we start downloading, so that pruning
// works correctly.
-void CleanupBlockRevFiles()
+static void CleanupBlockRevFiles()
{
std::map<std::string, fs::path> mapBlockFiles;
@@ -627,7 +606,7 @@ void CleanupBlockRevFiles()
}
}
-void ThreadImport(std::vector<fs::path> vImportFiles)
+static void ThreadImport(std::vector<fs::path> vImportFiles)
{
const CChainParams& chainparams = Params();
RenameThread("bitcoin-loadblk");
@@ -685,7 +664,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\n");
+ LogPrintf("Failed to connect best block (%s)\n", FormatStateMessage(state));
StartShutdown();
return;
}
@@ -706,7 +685,7 @@ void ThreadImport(std::vector<fs::path> vImportFiles)
* Ensure that Bitcoin is running in a usable environment with all
* necessary library support.
*/
-bool InitSanityCheck(void)
+static bool InitSanityCheck(void)
{
if(!ECC_InitSanityCheck()) {
InitError("Elliptic curve cryptography sanity check failure. Aborting.");
@@ -724,7 +703,7 @@ bool InitSanityCheck(void)
return true;
}
-bool AppInitServers()
+static bool AppInitServers()
{
RPCServer::OnStarted(&OnRPCStarted);
RPCServer::OnStopped(&OnRPCStopped);
@@ -815,14 +794,28 @@ static std::string ResolveErrMsg(const char * const optname, const std::string&
return strprintf(_("Cannot resolve -%s address: '%s'"), optname, strBind);
}
+/**
+ * Initialize global loggers.
+ *
+ * Note that this is called very early in the process lifetime, so you should be
+ * careful about what global state you rely on here.
+ */
void InitLogging()
{
- fPrintToConsole = gArgs.GetBoolArg("-printtoconsole", false);
- fLogTimestamps = gArgs.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS);
- fLogTimeMicros = gArgs.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS);
+ g_logger->m_print_to_file = !gArgs.IsArgNegated("-debuglogfile");
+ g_logger->m_file_path = AbsPathForConfigVal(gArgs.GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE));
+
+ // Add newlines to the logfile to distinguish this execution from the last
+ // one; called before console logging is set up, so this is only sent to
+ // debug.log.
+ LogPrintf("\n\n\n\n\n");
+
+ g_logger->m_print_to_console = gArgs.GetBoolArg("-printtoconsole", !gArgs.GetBoolArg("-daemon", false));
+ g_logger->m_log_timestamps = gArgs.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS);
+ g_logger->m_log_time_micros = gArgs.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS);
+
fLogIPs = gArgs.GetBoolArg("-logips", DEFAULT_LOGIPS);
- LogPrintf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
std::string version_string = FormatFullVersion();
#ifdef DEBUG
version_string += " (debug build)";
@@ -895,6 +888,8 @@ bool AppInitBasicSetup()
// Ignore SIGPIPE, otherwise it will bring the daemon down if the client closes unexpectedly
signal(SIGPIPE, SIG_IGN);
+#else
+ SetConsoleCtrlHandler(consoleCtrlHandler, true);
#endif
std::set_new_handler(new_handler_terminate);
@@ -948,24 +943,18 @@ bool AppInitParameterInteraction()
if (std::none_of(categories.begin(), categories.end(),
[](std::string cat){return cat == "0" || cat == "none";})) {
for (const auto& cat : categories) {
- uint32_t flag = 0;
- if (!GetLogCategory(&flag, &cat)) {
+ if (!g_logger->EnableCategory(cat)) {
InitWarning(strprintf(_("Unsupported logging category %s=%s."), "-debug", cat));
- continue;
}
- logCategories |= flag;
}
}
}
// Now remove the logging categories which were explicitly excluded
for (const std::string& cat : gArgs.GetArgs("-debugexclude")) {
- uint32_t flag = 0;
- if (!GetLogCategory(&flag, &cat)) {
+ if (!g_logger->DisableCategory(cat)) {
InitWarning(strprintf(_("Unsupported logging category %s=%s."), "-debugexclude", cat));
- continue;
}
- logCategories &= ~flag;
}
// Check for -debugnet
@@ -1098,7 +1087,7 @@ bool AppInitParameterInteraction()
return InitError(strprintf("acceptnonstdtxn is not currently supported for %s chain", chainparams.NetworkIDString()));
nBytesPerSigOp = gArgs.GetArg("-bytespersigop", nBytesPerSigOp);
- if (!g_wallet_init_interface->ParameterInteraction()) return false;
+ if (!g_wallet_init_interface.ParameterInteraction()) return false;
fIsBareMultisigStd = gArgs.GetBoolArg("-permitbaremultisig", DEFAULT_PERMIT_BAREMULTISIG);
fAcceptDatacarrier = gArgs.GetBoolArg("-datacarrier", DEFAULT_ACCEPT_DATACARRIER);
@@ -1216,19 +1205,19 @@ bool AppInitMain()
#ifndef WIN32
CreatePidFile(GetPidFile(), getpid());
#endif
- if (gArgs.GetBoolArg("-shrinkdebugfile", logCategories == BCLog::NONE)) {
- // Do this first since it both loads a bunch of debug.log into memory,
- // and because this needs to happen before any other debug.log printing
- ShrinkDebugFile();
- }
-
- if (fPrintToDebugLog) {
- if (!OpenDebugLog()) {
- return InitError(strprintf("Could not open debug log file %s", GetDebugLogPath().string()));
+ if (g_logger->m_print_to_file) {
+ if (gArgs.GetBoolArg("-shrinkdebugfile", g_logger->DefaultShrinkDebugFile())) {
+ // Do this first since it both loads a bunch of debug.log into memory,
+ // and because this needs to happen before any other debug.log printing
+ g_logger->ShrinkDebugFile();
+ }
+ if (!g_logger->OpenDebugLog()) {
+ return InitError(strprintf("Could not open debug log file %s",
+ g_logger->m_file_path.string()));
}
}
- if (!fLogTimestamps)
+ if (!g_logger->m_log_timestamps)
LogPrintf("Startup time: %s\n", FormatISO8601DateTime(GetTime()));
LogPrintf("Default data directory %s\n", GetDefaultDataDir().string());
LogPrintf("Using data directory %s\n", GetDataDir().string());
@@ -1264,7 +1253,7 @@ bool AppInitMain()
* available in the GUI RPC console even if external calls are disabled.
*/
RegisterAllCoreRPCCommands(tableRPC);
- g_wallet_init_interface->RegisterRPC(tableRPC);
+ 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
@@ -1281,7 +1270,7 @@ bool AppInitMain()
int64_t nStart;
// ********************************************************* Step 5: verify wallet database integrity
- if (!g_wallet_init_interface->Verify()) return false;
+ if (!g_wallet_init_interface.Verify()) return false;
// ********************************************************* Step 6: network initialization
// Note that we absolutely cannot open any actual connections
@@ -1405,9 +1394,10 @@ bool AppInitMain()
int64_t nTotalCache = (gArgs.GetArg("-dbcache", nDefaultDbCache) << 20);
nTotalCache = std::max(nTotalCache, nMinDbCache << 20); // total cache cannot be less than nMinDbCache
nTotalCache = std::min(nTotalCache, nMaxDbCache << 20); // total cache cannot be greater than nMaxDbcache
- int64_t nBlockTreeDBCache = nTotalCache / 8;
- nBlockTreeDBCache = std::min(nBlockTreeDBCache, (gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX) ? nMaxBlockDBAndTxIndexCache : nMaxBlockDBCache) << 20);
+ int64_t nBlockTreeDBCache = std::min(nTotalCache / 8, nMaxBlockDBCache << 20);
nTotalCache -= nBlockTreeDBCache;
+ int64_t nTxIndexCache = std::min(nTotalCache / 8, gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX) ? nMaxTxIndexCache << 20 : 0);
+ nTotalCache -= nTxIndexCache;
int64_t nCoinDBCache = std::min(nTotalCache / 2, (nTotalCache / 4) + (1 << 23)); // use 25%-50% of the remainder for disk cache
nCoinDBCache = std::min(nCoinDBCache, nMaxCoinsDBCache << 20); // cap total coins db cache
nTotalCache -= nCoinDBCache;
@@ -1415,6 +1405,9 @@ bool AppInitMain()
int64_t nMempoolSizeMax = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
LogPrintf("Cache configuration:\n");
LogPrintf("* Using %.1fMiB for block index database\n", nBlockTreeDBCache * (1.0 / 1024 / 1024));
+ if (gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
+ LogPrintf("* Using %.1fMiB for transaction index database\n", nTxIndexCache * (1.0 / 1024 / 1024));
+ }
LogPrintf("* Using %.1fMiB for chain state database\n", nCoinDBCache * (1.0 / 1024 / 1024));
LogPrintf("* Using %.1fMiB for in-memory UTXO set (plus up to %.1fMiB of unused mempool space)\n", nCoinCacheUsage * (1.0 / 1024 / 1024), nMempoolSizeMax * (1.0 / 1024 / 1024));
@@ -1448,9 +1441,8 @@ bool AppInitMain()
if (fRequestShutdown) break;
- // LoadBlockIndex will load fTxIndex from the db, or set it if
- // we're reindexing. It will also load fHavePruned if we've
- // ever removed a block file from disk.
+ // LoadBlockIndex will load fHavePruned if we've ever removed a
+ // block file from disk.
// Note that it also sets fReindex based on the disk flag!
// From here on out fReindex and fReset mean something different!
if (!LoadBlockIndex(chainparams)) {
@@ -1464,12 +1456,6 @@ bool AppInitMain()
return InitError(_("Incorrect or no genesis block found. Wrong datadir for network?"));
}
- // Check for changed -txindex state
- if (fTxIndex != gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
- strLoadError = _("You need to rebuild the database using -reindex to change -txindex");
- break;
- }
-
// Check for changed -prune state. What we are concerned about is a user who has pruned blocks
// in the past, but is now trying to run unpruned.
if (fHavePruned && !fPruneMode) {
@@ -1599,10 +1585,17 @@ bool AppInitMain()
::feeEstimator.Read(est_filein);
fFeeEstimatesInitialized = true;
- // ********************************************************* Step 8: load wallet
- if (!g_wallet_init_interface->Open()) return false;
+ // ********************************************************* Step 8: start indexers
+ if (gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
+ auto txindex_db = MakeUnique<TxIndexDB>(nTxIndexCache, false, fReindex);
+ g_txindex = MakeUnique<TxIndex>(std::move(txindex_db));
+ g_txindex->Start();
+ }
+
+ // ********************************************************* Step 9: load wallet
+ if (!g_wallet_init_interface.Open()) return false;
- // ********************************************************* Step 9: data directory maintenance
+ // ********************************************************* Step 10: data directory maintenance
// if pruning, unset the service bit and perform the initial blockstore prune
// after any wallet rescanning has taken place.
@@ -1624,7 +1617,7 @@ bool AppInitMain()
nLocalServices = ServiceFlags(nLocalServices | NODE_WITNESS);
}
- // ********************************************************* Step 10: import blocks
+ // ********************************************************* Step 11: import blocks
if (!CheckDiskSpace() && !CheckDiskSpace(0, true))
return false;
@@ -1663,7 +1656,7 @@ bool AppInitMain()
return false;
}
- // ********************************************************* Step 11: start node
+ // ********************************************************* Step 12: start node
int chain_active_height;
@@ -1741,12 +1734,12 @@ bool AppInitMain()
return false;
}
- // ********************************************************* Step 12: finished
+ // ********************************************************* Step 13: finished
SetRPCWarmupFinished();
uiInterface.InitMessage(_("Done loading"));
- g_wallet_init_interface->Start(scheduler);
+ g_wallet_init_interface.Start(scheduler);
return true;
}
diff --git a/src/init.h b/src/init.h
index 829c110112..5423a042a6 100644
--- a/src/init.h
+++ b/src/init.h
@@ -8,12 +8,13 @@
#include <memory>
#include <string>
+#include <util.h>
class CScheduler;
class CWallet;
class WalletInitInterface;
-extern WalletInitInterface* const g_wallet_init_interface;
+extern const WalletInitInterface& g_wallet_init_interface;
namespace boost
{
@@ -60,14 +61,11 @@ bool AppInitLockDataDirectory();
*/
bool AppInitMain();
-/** The help message mode determines what help message to show */
-enum class HelpMessageMode {
- BITCOIND,
- BITCOIN_QT
-};
+/**
+ * Setup the arguments for gArgs
+ */
+void SetupServerArgs();
-/** Help for options shared between UI and daemon (for -help) */
-std::string HelpMessage(HelpMessageMode mode);
/** Returns licensing information (for -version) */
std::string LicenseInfo();
diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp
index 919748f942..73657d4f5a 100644
--- a/src/interfaces/node.cpp
+++ b/src/interfaces/node.cpp
@@ -52,7 +52,7 @@ class NodeImpl : public Node
{
gArgs.ParseParameters(argc, argv);
}
- void readConfigFile(const std::string& conf_path) override { gArgs.ReadConfigFile(conf_path); }
+ void readConfigFiles() override { gArgs.ReadConfigFiles(); }
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); }
@@ -60,7 +60,7 @@ class NodeImpl : public Node
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; }
+ uint32_t getLogCategories() override { return g_logger->GetCategoryMask(); }
bool baseInitialize() override
{
return AppInitBasicSetup() && AppInitParameterInteraction() && AppInitSanityChecks() &&
@@ -83,7 +83,7 @@ class NodeImpl : public Node
StopMapPort();
}
}
- std::string helpMessage(HelpMessageMode mode) override { return HelpMessage(mode); }
+ void setupServerArgs() override { return SetupServerArgs(); }
bool getProxy(Network net, proxyType& proxy_info) override { return GetProxy(net, proxy_info); }
size_t getNodeCount(CConnman::NumConnections flags) override
{
@@ -191,20 +191,6 @@ class NodeImpl : public Node
}
}
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
{
@@ -216,9 +202,6 @@ class NodeImpl : public Node
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;
@@ -239,7 +222,7 @@ class NodeImpl : public Node
{
#ifdef ENABLE_WALLET
std::vector<std::unique_ptr<Wallet>> wallets;
- for (CWalletRef wallet : ::vpwallets) {
+ for (CWallet* wallet : GetWallets()) {
wallets.emplace_back(MakeWallet(*wallet));
}
return wallets;
diff --git a/src/interfaces/node.h b/src/interfaces/node.h
index f375af2f19..2a1a8152df 100644
--- a/src/interfaces/node.h
+++ b/src/interfaces/node.h
@@ -7,7 +7,6 @@
#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
@@ -26,11 +25,9 @@ class Coin;
class RPCTimerInterface;
class UniValue;
class proxyType;
-enum class FeeReason;
struct CNodeStateStats;
namespace interfaces {
-
class Handler;
class Wallet;
@@ -50,7 +47,7 @@ public:
virtual bool softSetBoolArg(const std::string& arg, bool value) = 0;
//! Load settings from configuration file.
- virtual void readConfigFile(const std::string& conf_path) = 0;
+ virtual void readConfigFiles() = 0;
//! Choose network parameters.
virtual void selectParams(const std::string& network) = 0;
@@ -85,8 +82,8 @@ public:
//! Return whether shutdown was requested.
virtual bool shutdownRequested() = 0;
- //! Get help message string.
- virtual std::string helpMessage(HelpMessageMode mode) = 0;
+ //! Setup arguments
+ virtual void setupServerArgs() = 0;
//! Map port.
virtual void mapPort(bool use_upnp) = 0;
@@ -152,18 +149,6 @@ public:
//! 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;
@@ -173,15 +158,6 @@ public:
//! 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;
diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp
index 3e6f0d6728..63b9d80a92 100644
--- a/src/interfaces/wallet.cpp
+++ b/src/interfaces/wallet.cpp
@@ -9,6 +9,8 @@
#include <consensus/validation.h>
#include <interfaces/handler.h>
#include <net.h>
+#include <policy/feerate.h>
+#include <policy/fees.h>
#include <policy/policy.h>
#include <primitives/transaction.h>
#include <script/ismine.h>
@@ -20,6 +22,7 @@
#include <uint256.h>
#include <validation.h>
#include <wallet/feebumper.h>
+#include <wallet/fees.h>
#include <wallet/wallet.h>
namespace interfaces {
@@ -149,7 +152,10 @@ public:
{
return m_wallet.DelAddressBook(dest);
}
- bool getAddress(const CTxDestination& dest, std::string* name, isminetype* is_mine) override
+ bool getAddress(const CTxDestination& dest,
+ std::string* name,
+ isminetype* is_mine,
+ std::string* purpose) override
{
LOCK(m_wallet.cs_wallet);
auto it = m_wallet.mapAddressBook.find(dest);
@@ -162,6 +168,9 @@ public:
if (is_mine) {
*is_mine = IsMine(m_wallet, dest);
}
+ if (purpose) {
+ *purpose = it->second.purpose;
+ }
return true;
}
std::vector<WalletAddress> getAddresses() override
@@ -403,6 +412,20 @@ public:
}
return result;
}
+ CAmount getRequiredFee(unsigned int tx_bytes) override { return GetRequiredFee(m_wallet, tx_bytes); }
+ CAmount getMinimumFee(unsigned int tx_bytes,
+ const CCoinControl& coin_control,
+ int* returned_target,
+ FeeReason* reason) override
+ {
+ FeeCalculation fee_calc;
+ CAmount result;
+ result = GetMinimumFee(m_wallet, tx_bytes, coin_control, ::mempool, ::feeEstimator, &fee_calc);
+ if (returned_target) *returned_target = fee_calc.returnedTarget;
+ if (reason) *reason = fee_calc.reason;
+ return result;
+ }
+ unsigned int getConfirmTarget() override { return m_wallet.m_confirm_target; }
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; }
diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h
index dfe3d5f711..ff779cd0ad 100644
--- a/src/interfaces/wallet.h
+++ b/src/interfaces/wallet.h
@@ -6,7 +6,7 @@
#define BITCOIN_INTERFACES_WALLET_H
#include <amount.h> // For CAmount
-#include <pubkey.h> // For CTxDestination (CKeyID and CScriptID)
+#include <pubkey.h> // For CKeyID and CScriptID (definitions needed in CTxDestination instantiation)
#include <script/ismine.h> // For isminefilter, isminetype
#include <script/standard.h> // For CTxDestination
#include <support/allocators/secure.h> // For SecureString
@@ -22,8 +22,10 @@
#include <vector>
class CCoinControl;
+class CFeeRate;
class CKey;
class CWallet;
+enum class FeeReason;
enum class OutputType;
struct CRecipient;
@@ -97,8 +99,9 @@ public:
//! Look up address in wallet, return whether exists.
virtual bool getAddress(const CTxDestination& dest,
- std::string* name = nullptr,
- isminetype* is_mine = nullptr) = 0;
+ std::string* name,
+ isminetype* is_mine,
+ std::string* purpose) = 0;
//! Get wallet address list.
virtual std::vector<WalletAddress> getAddresses() = 0;
@@ -218,6 +221,18 @@ public:
//! Return wallet transaction output information.
virtual std::vector<WalletTxOut> getCoins(const std::vector<COutPoint>& outputs) = 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 tx confirm target.
+ virtual unsigned int getConfirmTarget() = 0;
+
// Return whether HD enabled.
virtual bool hdEnabled() = 0;
diff --git a/src/keystore.cpp b/src/keystore.cpp
index e69d518890..ea93ed69fa 100644
--- a/src/keystore.cpp
+++ b/src/keystore.cpp
@@ -195,3 +195,10 @@ CKeyID GetKeyForDestination(const CKeyStore& store, const CTxDestination& dest)
}
return CKeyID();
}
+
+bool HaveKey(const CKeyStore& store, const CKey& key)
+{
+ CKey key2;
+ key2.Set(key.begin(), key.end(), !key.IsCompressed());
+ return store.HaveKey(key.GetPubKey().GetID()) || store.HaveKey(key2.GetPubKey().GetID());
+}
diff --git a/src/keystore.h b/src/keystore.h
index fa912cb195..cd5ded9203 100644
--- a/src/keystore.h
+++ b/src/keystore.h
@@ -54,7 +54,7 @@ protected:
ScriptMap mapScripts;
WatchOnlySet setWatchOnly;
- void ImplicitlyLearnRelatedKeyScripts(const CPubKey& pubkey);
+ void ImplicitlyLearnRelatedKeyScripts(const CPubKey& pubkey) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
public:
bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override;
@@ -80,4 +80,7 @@ typedef std::map<CKeyID, std::pair<CPubKey, std::vector<unsigned char> > > Crypt
/** Return the CKeyID of the key involved in a script (if there is a unique one). */
CKeyID GetKeyForDestination(const CKeyStore& store, const CTxDestination& dest);
+/** Checks if a CKey is in the given CKeyStore compressed or otherwise*/
+bool HaveKey(const CKeyStore& store, const CKey& key);
+
#endif // BITCOIN_KEYSTORE_H
diff --git a/src/logging.cpp b/src/logging.cpp
new file mode 100644
index 0000000000..e8e22cbf97
--- /dev/null
+++ b/src/logging.cpp
@@ -0,0 +1,273 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <logging.h>
+#include <utiltime.h>
+
+const char * const DEFAULT_DEBUGLOGFILE = "debug.log";
+
+/**
+ * NOTE: the logger instances is leaked on exit. This is ugly, but will be
+ * cleaned up by the OS/libc. Defining a logger as a global object doesn't work
+ * since the order of destruction of static/global objects is undefined.
+ * Consider if the logger gets destroyed, and then some later destructor calls
+ * LogPrintf, maybe indirectly, and you get a core dump at shutdown trying to
+ * access the logger. When the shutdown sequence is fully audited and tested,
+ * explicit destruction of these objects can be implemented by changing this
+ * from a raw pointer to a std::unique_ptr.
+ *
+ * This method of initialization was originally introduced in
+ * ee3374234c60aba2cc4c5cd5cac1c0aefc2d817c.
+ */
+BCLog::Logger* const g_logger = new BCLog::Logger();
+
+bool fLogIPs = DEFAULT_LOGIPS;
+
+static int FileWriteStr(const std::string &str, FILE *fp)
+{
+ return fwrite(str.data(), 1, str.size(), fp);
+}
+
+bool BCLog::Logger::OpenDebugLog()
+{
+ std::lock_guard<std::mutex> scoped_lock(m_file_mutex);
+
+ assert(m_fileout == nullptr);
+ assert(!m_file_path.empty());
+
+ m_fileout = fsbridge::fopen(m_file_path, "a");
+ if (!m_fileout) {
+ return false;
+ }
+
+ setbuf(m_fileout, nullptr); // unbuffered
+ // dump buffered messages from before we opened the log
+ while (!m_msgs_before_open.empty()) {
+ FileWriteStr(m_msgs_before_open.front(), m_fileout);
+ m_msgs_before_open.pop_front();
+ }
+
+ return true;
+}
+
+void BCLog::Logger::EnableCategory(BCLog::LogFlags flag)
+{
+ m_categories |= flag;
+}
+
+bool BCLog::Logger::EnableCategory(const std::string& str)
+{
+ BCLog::LogFlags flag;
+ if (!GetLogCategory(flag, str)) return false;
+ EnableCategory(flag);
+ return true;
+}
+
+void BCLog::Logger::DisableCategory(BCLog::LogFlags flag)
+{
+ m_categories &= ~flag;
+}
+
+bool BCLog::Logger::DisableCategory(const std::string& str)
+{
+ BCLog::LogFlags flag;
+ if (!GetLogCategory(flag, str)) return false;
+ DisableCategory(flag);
+ return true;
+}
+
+bool BCLog::Logger::WillLogCategory(BCLog::LogFlags category) const
+{
+ return (m_categories.load(std::memory_order_relaxed) & category) != 0;
+}
+
+bool BCLog::Logger::DefaultShrinkDebugFile() const
+{
+ return m_categories == BCLog::NONE;
+}
+
+struct CLogCategoryDesc
+{
+ BCLog::LogFlags flag;
+ std::string category;
+};
+
+const CLogCategoryDesc LogCategories[] =
+{
+ {BCLog::NONE, "0"},
+ {BCLog::NONE, "none"},
+ {BCLog::NET, "net"},
+ {BCLog::TOR, "tor"},
+ {BCLog::MEMPOOL, "mempool"},
+ {BCLog::HTTP, "http"},
+ {BCLog::BENCH, "bench"},
+ {BCLog::ZMQ, "zmq"},
+ {BCLog::DB, "db"},
+ {BCLog::RPC, "rpc"},
+ {BCLog::ESTIMATEFEE, "estimatefee"},
+ {BCLog::ADDRMAN, "addrman"},
+ {BCLog::SELECTCOINS, "selectcoins"},
+ {BCLog::REINDEX, "reindex"},
+ {BCLog::CMPCTBLOCK, "cmpctblock"},
+ {BCLog::RAND, "rand"},
+ {BCLog::PRUNE, "prune"},
+ {BCLog::PROXY, "proxy"},
+ {BCLog::MEMPOOLREJ, "mempoolrej"},
+ {BCLog::LIBEVENT, "libevent"},
+ {BCLog::COINDB, "coindb"},
+ {BCLog::QT, "qt"},
+ {BCLog::LEVELDB, "leveldb"},
+ {BCLog::ALL, "1"},
+ {BCLog::ALL, "all"},
+};
+
+bool GetLogCategory(BCLog::LogFlags& flag, const std::string& str)
+{
+ if (str == "") {
+ flag = BCLog::ALL;
+ return true;
+ }
+ for (const CLogCategoryDesc& category_desc : LogCategories) {
+ if (category_desc.category == str) {
+ flag = category_desc.flag;
+ return true;
+ }
+ }
+ return false;
+}
+
+std::string ListLogCategories()
+{
+ std::string ret;
+ int outcount = 0;
+ for (const CLogCategoryDesc& category_desc : LogCategories) {
+ // Omit the special cases.
+ if (category_desc.flag != BCLog::NONE && category_desc.flag != BCLog::ALL) {
+ if (outcount != 0) ret += ", ";
+ ret += category_desc.category;
+ outcount++;
+ }
+ }
+ return ret;
+}
+
+std::vector<CLogCategoryActive> ListActiveLogCategories()
+{
+ std::vector<CLogCategoryActive> ret;
+ for (const CLogCategoryDesc& category_desc : LogCategories) {
+ // Omit the special cases.
+ if (category_desc.flag != BCLog::NONE && category_desc.flag != BCLog::ALL) {
+ CLogCategoryActive catActive;
+ catActive.category = category_desc.category;
+ catActive.active = LogAcceptCategory(category_desc.flag);
+ ret.push_back(catActive);
+ }
+ }
+ return ret;
+}
+
+std::string BCLog::Logger::LogTimestampStr(const std::string &str)
+{
+ std::string strStamped;
+
+ if (!m_log_timestamps)
+ return str;
+
+ if (m_started_new_line) {
+ int64_t nTimeMicros = GetTimeMicros();
+ strStamped = FormatISO8601DateTime(nTimeMicros/1000000);
+ if (m_log_time_micros) {
+ strStamped.pop_back();
+ strStamped += strprintf(".%06dZ", nTimeMicros%1000000);
+ }
+ int64_t mocktime = GetMockTime();
+ if (mocktime) {
+ strStamped += " (mocktime: " + FormatISO8601DateTime(mocktime) + ")";
+ }
+ strStamped += ' ' + str;
+ } else
+ strStamped = str;
+
+ if (!str.empty() && str[str.size()-1] == '\n')
+ m_started_new_line = true;
+ else
+ m_started_new_line = false;
+
+ return strStamped;
+}
+
+void BCLog::Logger::LogPrintStr(const std::string &str)
+{
+ std::string strTimestamped = LogTimestampStr(str);
+
+ if (m_print_to_console) {
+ // print to console
+ fwrite(strTimestamped.data(), 1, strTimestamped.size(), stdout);
+ fflush(stdout);
+ }
+ if (m_print_to_file) {
+ std::lock_guard<std::mutex> scoped_lock(m_file_mutex);
+
+ // buffer if we haven't opened the log yet
+ if (m_fileout == nullptr) {
+ m_msgs_before_open.push_back(strTimestamped);
+ }
+ else
+ {
+ // reopen the log file, if requested
+ if (m_reopen_file) {
+ m_reopen_file = false;
+ m_fileout = fsbridge::freopen(m_file_path, "a", m_fileout);
+ if (!m_fileout) {
+ return;
+ }
+ setbuf(m_fileout, nullptr); // unbuffered
+ }
+
+ FileWriteStr(strTimestamped, m_fileout);
+ }
+ }
+}
+
+void BCLog::Logger::ShrinkDebugFile()
+{
+ // Amount of debug.log to save at end when shrinking (must fit in memory)
+ constexpr size_t RECENT_DEBUG_HISTORY_SIZE = 10 * 1000000;
+
+ assert(!m_file_path.empty());
+
+ // Scroll debug.log if it's getting too big
+ FILE* file = fsbridge::fopen(m_file_path, "r");
+
+ // Special files (e.g. device nodes) may not have a size.
+ size_t log_size = 0;
+ try {
+ log_size = fs::file_size(m_file_path);
+ } catch (boost::filesystem::filesystem_error &) {}
+
+ // If debug.log file is more than 10% bigger the RECENT_DEBUG_HISTORY_SIZE
+ // trim it down by saving only the last RECENT_DEBUG_HISTORY_SIZE bytes
+ if (file && log_size > 11 * (RECENT_DEBUG_HISTORY_SIZE / 10))
+ {
+ // Restart the file with some of the end
+ std::vector<char> vch(RECENT_DEBUG_HISTORY_SIZE, 0);
+ if (fseek(file, -((long)vch.size()), SEEK_END)) {
+ LogPrintf("Failed to shrink debug log file: fseek(...) failed\n");
+ fclose(file);
+ return;
+ }
+ int nBytes = fread(vch.data(), 1, vch.size(), file);
+ fclose(file);
+
+ file = fsbridge::fopen(m_file_path, "w");
+ if (file)
+ {
+ fwrite(vch.data(), 1, nBytes, file);
+ fclose(file);
+ }
+ }
+ else if (file != nullptr)
+ fclose(file);
+}
diff --git a/src/logging.h b/src/logging.h
new file mode 100644
index 0000000000..6400b131c2
--- /dev/null
+++ b/src/logging.h
@@ -0,0 +1,166 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_LOGGING_H
+#define BITCOIN_LOGGING_H
+
+#include <fs.h>
+#include <tinyformat.h>
+
+#include <atomic>
+#include <cstdint>
+#include <list>
+#include <mutex>
+#include <string>
+#include <vector>
+
+static const bool DEFAULT_LOGTIMEMICROS = false;
+static const bool DEFAULT_LOGIPS = false;
+static const bool DEFAULT_LOGTIMESTAMPS = true;
+extern const char * const DEFAULT_DEBUGLOGFILE;
+
+extern bool fLogIPs;
+
+struct CLogCategoryActive
+{
+ std::string category;
+ bool active;
+};
+
+namespace BCLog {
+ enum LogFlags : uint32_t {
+ NONE = 0,
+ NET = (1 << 0),
+ TOR = (1 << 1),
+ MEMPOOL = (1 << 2),
+ HTTP = (1 << 3),
+ BENCH = (1 << 4),
+ ZMQ = (1 << 5),
+ DB = (1 << 6),
+ RPC = (1 << 7),
+ ESTIMATEFEE = (1 << 8),
+ ADDRMAN = (1 << 9),
+ SELECTCOINS = (1 << 10),
+ REINDEX = (1 << 11),
+ CMPCTBLOCK = (1 << 12),
+ RAND = (1 << 13),
+ PRUNE = (1 << 14),
+ PROXY = (1 << 15),
+ MEMPOOLREJ = (1 << 16),
+ LIBEVENT = (1 << 17),
+ COINDB = (1 << 18),
+ QT = (1 << 19),
+ LEVELDB = (1 << 20),
+ ALL = ~(uint32_t)0,
+ };
+
+ class Logger
+ {
+ private:
+ FILE* m_fileout = nullptr;
+ std::mutex m_file_mutex;
+ std::list<std::string> m_msgs_before_open;
+
+ /**
+ * m_started_new_line is a state variable that will suppress printing of
+ * the timestamp when multiple calls are made that don't end in a
+ * newline.
+ */
+ std::atomic_bool m_started_new_line{true};
+
+ /** Log categories bitfield. */
+ std::atomic<uint32_t> m_categories{0};
+
+ std::string LogTimestampStr(const std::string& str);
+
+ public:
+ bool m_print_to_console = false;
+ bool m_print_to_file = false;
+
+ bool m_log_timestamps = DEFAULT_LOGTIMESTAMPS;
+ bool m_log_time_micros = DEFAULT_LOGTIMEMICROS;
+
+ fs::path m_file_path;
+ std::atomic<bool> m_reopen_file{false};
+
+ /** Send a string to the log output */
+ void LogPrintStr(const std::string &str);
+
+ /** Returns whether logs will be written to any output */
+ bool Enabled() const { return m_print_to_console || m_print_to_file; }
+
+ bool OpenDebugLog();
+ void ShrinkDebugFile();
+
+ uint32_t GetCategoryMask() const { return m_categories.load(); }
+
+ void EnableCategory(LogFlags flag);
+ bool EnableCategory(const std::string& str);
+ void DisableCategory(LogFlags flag);
+ bool DisableCategory(const std::string& str);
+
+ bool WillLogCategory(LogFlags category) const;
+
+ bool DefaultShrinkDebugFile() const;
+ };
+
+} // namespace BCLog
+
+extern BCLog::Logger* const g_logger;
+
+/** Return true if log accepts specified category */
+static inline bool LogAcceptCategory(BCLog::LogFlags category)
+{
+ return g_logger->WillLogCategory(category);
+}
+
+/** Returns a string with the log categories. */
+std::string ListLogCategories();
+
+/** Returns a vector of the active log categories. */
+std::vector<CLogCategoryActive> ListActiveLogCategories();
+
+/** Return true if str parses as a log category and set the flag */
+bool GetLogCategory(BCLog::LogFlags& flag, const std::string& str);
+
+/** Get format string from VA_ARGS for error reporting */
+template<typename... Args> std::string FormatStringFromLogArgs(const char *fmt, const Args&... args) { return fmt; }
+
+static inline void MarkUsed() {}
+template<typename T, typename... Args> static inline void MarkUsed(const T& t, const Args&... args)
+{
+ (void)t;
+ MarkUsed(args...);
+}
+
+// Be conservative when using LogPrintf/error or other things which
+// unconditionally log to debug.log! It should not be the case that an inbound
+// peer can fill up a user's disk with debug.log entries.
+
+#ifdef USE_COVERAGE
+#define LogPrintf(...) do { MarkUsed(__VA_ARGS__); } while(0)
+#define LogPrint(category, ...) do { MarkUsed(__VA_ARGS__); } while(0)
+#else
+#define LogPrintf(...) do { \
+ if (g_logger->Enabled()) { \
+ std::string _log_msg_; /* Unlikely name to avoid shadowing variables */ \
+ try { \
+ _log_msg_ = tfm::format(__VA_ARGS__); \
+ } catch (tinyformat::format_error &fmterr) { \
+ /* Original format string will have newline so don't add one here */ \
+ _log_msg_ = "Error \"" + std::string(fmterr.what()) + "\" while formatting log message: " + FormatStringFromLogArgs(__VA_ARGS__); \
+ } \
+ g_logger->LogPrintStr(_log_msg_); \
+ } \
+} while(0)
+
+#define LogPrint(category, ...) do { \
+ if (LogAcceptCategory((category))) { \
+ LogPrintf(__VA_ARGS__); \
+ } \
+} while(0)
+#endif
+
+#endif // BITCOIN_LOGGING_H
diff --git a/src/net.cpp b/src/net.cpp
index 356a66563f..55043ffe30 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -160,7 +160,7 @@ CAddress GetLocalAddress(const CNetAddr *paddrPeer, ServiceFlags nLocalServices)
return ret;
}
-int GetnScore(const CService& addr)
+static int GetnScore(const CService& addr)
{
LOCK(cs_mapLocalHost);
if (mapLocalHost.count(addr) == LOCAL_NONE)
@@ -395,7 +395,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
if (Lookup(pszDest, resolved, default_port, fNameLookup && !HaveNameProxy(), 256) && !resolved.empty()) {
addrConnect = CAddress(resolved[GetRand(resolved.size())], NODE_NONE);
if (!addrConnect.IsValid()) {
- LogPrint(BCLog::NET, "Resolver returned invalid address %s for %s", addrConnect.ToString(), pszDest);
+ LogPrint(BCLog::NET, "Resolver returned invalid address %s for %s\n", addrConnect.ToString(), pszDest);
return nullptr;
}
// It is possible that we already have a connection to the IP/port pszDest resolved to.
@@ -1466,7 +1466,7 @@ void CConnman::WakeMessageHandler()
#ifdef USE_UPNP
static CThreadInterrupt g_upnp_interrupt;
static std::thread g_upnp_thread;
-void ThreadMapPort()
+static void ThreadMapPort()
{
std::string port = strprintf("%u", GetListenPort());
const char * multicastif = nullptr;
@@ -1923,23 +1923,25 @@ std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo()
for (const std::string& strAddNode : lAddresses) {
CService service(LookupNumeric(strAddNode.c_str(), Params().GetDefaultPort()));
+ AddedNodeInfo addedNode{strAddNode, CService(), false, false};
if (service.IsValid()) {
// strAddNode is an IP:port
auto it = mapConnected.find(service);
if (it != mapConnected.end()) {
- ret.push_back(AddedNodeInfo{strAddNode, service, true, it->second});
- } else {
- ret.push_back(AddedNodeInfo{strAddNode, CService(), false, false});
+ addedNode.resolvedAddress = service;
+ addedNode.fConnected = true;
+ addedNode.fInbound = it->second;
}
} else {
// strAddNode is a name
auto it = mapConnectedByName.find(strAddNode);
if (it != mapConnectedByName.end()) {
- ret.push_back(AddedNodeInfo{strAddNode, it->second.second, true, it->second.first});
- } else {
- ret.push_back(AddedNodeInfo{strAddNode, CService(), false, false});
+ addedNode.resolvedAddress = it->second.second;
+ addedNode.fConnected = true;
+ addedNode.fInbound = it->second.first;
}
}
+ ret.emplace_back(std::move(addedNode));
}
return ret;
@@ -2088,23 +2090,16 @@ bool CConnman::BindListenPort(const CService &addrBind, std::string& strError, b
LogPrintf("%s\n", strError);
return false;
}
-#ifndef WIN32
+
// Allow binding if the port is still in TIME_WAIT state after
// the program was closed and restarted.
- setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (void*)&nOne, sizeof(int));
-#else
- setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&nOne, sizeof(int));
-#endif
+ setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (sockopt_arg_type)&nOne, sizeof(int));
// some systems don't have IPV6_V6ONLY but are always v6only; others do have the option
// and enable it by default or not. Try to enable it, if possible.
if (addrBind.IsIPv6()) {
#ifdef IPV6_V6ONLY
-#ifdef WIN32
- setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&nOne, sizeof(int));
-#else
- setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&nOne, sizeof(int));
-#endif
+ setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (sockopt_arg_type)&nOne, sizeof(int));
#endif
#ifdef WIN32
int nProtLevel = PROTECTION_LEVEL_UNRESTRICTED;
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index dbdae705de..ed2fb598d2 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -277,7 +277,7 @@ CNodeState *State(NodeId pnode) {
return &it->second;
}
-void UpdatePreferredDownload(CNode* node, CNodeState* state)
+static void UpdatePreferredDownload(CNode* node, CNodeState* state)
{
nPreferredDownload -= state->fPreferredDownload;
@@ -287,7 +287,7 @@ void UpdatePreferredDownload(CNode* node, CNodeState* state)
nPreferredDownload += state->fPreferredDownload;
}
-void PushNodeVersion(CNode *pnode, CConnman* connman, int64_t nTime)
+static void PushNodeVersion(CNode *pnode, CConnman* connman, int64_t nTime)
{
ServiceFlags nLocalNodeServices = pnode->GetLocalServices();
uint64_t nonce = pnode->GetLocalNonce();
@@ -311,7 +311,7 @@ void PushNodeVersion(CNode *pnode, CConnman* connman, int64_t nTime)
// Requires cs_main.
// Returns a bool indicating whether we requested this block.
// Also used if a block was /not/ received and timed out or started with another peer
-bool MarkBlockAsReceived(const uint256& hash) {
+static bool MarkBlockAsReceived(const uint256& hash) {
std::map<uint256, std::pair<NodeId, std::list<QueuedBlock>::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash);
if (itInFlight != mapBlocksInFlight.end()) {
CNodeState *state = State(itInFlight->second.first);
@@ -337,7 +337,7 @@ bool MarkBlockAsReceived(const uint256& hash) {
// Requires cs_main.
// returns false, still setting pit, if the block was already in flight from the same peer
// pit will only be valid as long as the same cs_main lock is being held
-bool MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const CBlockIndex* pindex = nullptr, std::list<QueuedBlock>::iterator** pit = nullptr) {
+static bool MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const CBlockIndex* pindex = nullptr, std::list<QueuedBlock>::iterator** pit = nullptr) {
CNodeState *state = State(nodeid);
assert(state != nullptr);
@@ -371,7 +371,7 @@ bool MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const CBlockIndex*
}
/** Check whether the last unknown block a peer advertised is not yet known. */
-void ProcessBlockAvailability(NodeId nodeid) {
+static void ProcessBlockAvailability(NodeId nodeid) {
CNodeState *state = State(nodeid);
assert(state != nullptr);
@@ -387,7 +387,7 @@ void ProcessBlockAvailability(NodeId nodeid) {
}
/** Update tracking information about which blocks a peer is assumed to have. */
-void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) {
+static void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) {
CNodeState *state = State(nodeid);
assert(state != nullptr);
@@ -411,7 +411,7 @@ void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) {
* lNodesAnnouncingHeaderAndIDs, and keeping that list under a certain size by
* removing the first element if necessary.
*/
-void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman* connman) {
+static void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman* connman) {
AssertLockHeld(cs_main);
CNodeState* nodestate = State(nodeid);
if (!nodestate || !nodestate->fSupportsDesiredCmpctVersion) {
@@ -444,7 +444,7 @@ void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman* connman) {
}
}
-bool TipMayBeStale(const Consensus::Params &consensusParams)
+static bool TipMayBeStale(const Consensus::Params &consensusParams)
{
AssertLockHeld(cs_main);
if (g_last_tip_update == 0) {
@@ -454,13 +454,13 @@ bool TipMayBeStale(const Consensus::Params &consensusParams)
}
// Requires cs_main
-bool CanDirectFetch(const Consensus::Params &consensusParams)
+static bool CanDirectFetch(const Consensus::Params &consensusParams)
{
return chainActive.Tip()->GetBlockTime() > GetAdjustedTime() - consensusParams.nPowTargetSpacing * 20;
}
// Requires cs_main
-bool PeerHasHeader(CNodeState *state, const CBlockIndex *pindex)
+static bool PeerHasHeader(CNodeState *state, const CBlockIndex *pindex)
{
if (state->pindexBestKnownBlock && pindex == state->pindexBestKnownBlock->GetAncestor(pindex->nHeight))
return true;
@@ -471,7 +471,7 @@ bool PeerHasHeader(CNodeState *state, const CBlockIndex *pindex)
/** Update pindexLastCommonBlock and add not-in-flight missing successors to vBlocks, until it has
* at most count entries. */
-void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<const CBlockIndex*>& vBlocks, NodeId& nodeStaller, const Consensus::Params& consensusParams) {
+static void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<const CBlockIndex*>& vBlocks, NodeId& nodeStaller, const Consensus::Params& consensusParams) {
if (count == 0)
return;
@@ -570,7 +570,7 @@ void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds)
// Returns true for outbound peers, excluding manual connections, feelers, and
// one-shots
-bool IsOutboundDisconnectionCandidate(const CNode *node)
+static bool IsOutboundDisconnectionCandidate(const CNode *node)
{
return !(node->fInbound || node->m_manual_connection || node->fFeeler || node->fOneShot);
}
@@ -642,7 +642,7 @@ bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) {
// mapOrphanTransactions
//
-void AddToCompactExtraTransactions(const CTransactionRef& tx) EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans)
+static void AddToCompactExtraTransactions(const CTransactionRef& tx) EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans)
{
size_t max_extra_txn = gArgs.GetArg("-blockreconstructionextratxn", DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN);
if (max_extra_txn <= 0)
@@ -1100,8 +1100,10 @@ void static ProcessGetBlockData(CNode* pfrom, const Consensus::Params& consensus
}
} // release cs_main before calling ActivateBestChain
if (need_activate_chain) {
- CValidationState dummy;
- ActivateBestChain(dummy, Params(), a_recent_block);
+ CValidationState state;
+ if (!ActivateBestChain(state, Params(), a_recent_block)) {
+ LogPrint(BCLog::NET, "failed to activate chain (%s)\n", FormatStateMessage(state));
+ }
}
LOCK(cs_main);
@@ -1278,7 +1280,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
}
}
-uint32_t GetFetchFlags(CNode* pfrom) {
+static uint32_t GetFetchFlags(CNode* pfrom) {
uint32_t nFetchFlags = 0;
if ((pfrom->GetLocalServices() & NODE_WITNESS) && State(pfrom->GetId())->fHaveWitness) {
nFetchFlags |= MSG_WITNESS_FLAG;
@@ -1569,6 +1571,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
LogPrint(BCLog::NET, "Unparseable reject message received\n");
}
}
+ return true;
}
else if (strCommand == NetMsgType::VERSION)
@@ -1992,8 +1995,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
LOCK(cs_most_recent_block);
a_recent_block = most_recent_block;
}
- CValidationState dummy;
- ActivateBestChain(dummy, Params(), a_recent_block);
+ CValidationState state;
+ if (!ActivateBestChain(state, Params(), a_recent_block)) {
+ LogPrint(BCLog::NET, "failed to activate chain (%s)\n", FormatStateMessage(state));
+ }
}
LOCK(cs_main);
@@ -2055,7 +2060,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
const CBlockIndex* pindex = LookupBlockIndex(req.blockhash);
if (!pindex || !(pindex->nStatus & BLOCK_HAVE_DATA)) {
- LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block we don't have", pfrom->GetId());
+ LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block we don't have\n", pfrom->GetId());
return true;
}
@@ -2067,7 +2072,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// might maliciously send lots of getblocktxn requests to trigger
// expensive disk reads, because it will require the peer to
// actually receive all the data read from disk over the network.
- LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block > %i deep", pfrom->GetId(), MAX_BLOCKTXN_DEPTH);
+ LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block > %i deep\n", pfrom->GetId(), MAX_BLOCKTXN_DEPTH);
CInv inv;
inv.type = State(pfrom->GetId())->fWantsCmpctWitness ? MSG_WITNESS_BLOCK : MSG_BLOCK;
inv.hash = req.blockhash;
diff --git a/src/netaddress.cpp b/src/netaddress.cpp
index 4f231d73c8..18d5948f85 100644
--- a/src/netaddress.cpp
+++ b/src/netaddress.cpp
@@ -14,7 +14,7 @@ static const unsigned char pchOnionCat[] = {0xFD,0x87,0xD8,0x7E,0xEB,0x43};
// 0xFD + sha256("bitcoin")[0:5]
static const unsigned char g_internal_prefix[] = { 0xFD, 0x6B, 0x88, 0xC0, 0x87, 0x24 };
-void CNetAddr::Init()
+CNetAddr::CNetAddr()
{
memset(ip, 0, sizeof(ip));
scopeId = 0;
@@ -67,11 +67,6 @@ bool CNetAddr::SetSpecial(const std::string &strName)
return false;
}
-CNetAddr::CNetAddr()
-{
- Init();
-}
-
CNetAddr::CNetAddr(const struct in_addr& ipv4Addr)
{
SetRaw(NET_IPV4, (const uint8_t*)&ipv4Addr);
@@ -290,11 +285,6 @@ bool operator==(const CNetAddr& a, const CNetAddr& b)
return (memcmp(a.ip, b.ip, 16) == 0);
}
-bool operator!=(const CNetAddr& a, const CNetAddr& b)
-{
- return (memcmp(a.ip, b.ip, 16) != 0);
-}
-
bool operator<(const CNetAddr& a, const CNetAddr& b)
{
return (memcmp(a.ip, b.ip, 16) < 0);
@@ -469,14 +459,8 @@ int CNetAddr::GetReachabilityFrom(const CNetAddr *paddrPartner) const
}
}
-void CService::Init()
+CService::CService() : port(0)
{
- port = 0;
-}
-
-CService::CService()
-{
- Init();
}
CService::CService(const CNetAddr& cip, unsigned short portIn) : CNetAddr(cip), port(portIn)
@@ -525,11 +509,6 @@ bool operator==(const CService& a, const CService& b)
return static_cast<CNetAddr>(a) == static_cast<CNetAddr>(b) && a.port == b.port;
}
-bool operator!=(const CService& a, const CService& b)
-{
- return static_cast<CNetAddr>(a) != static_cast<CNetAddr>(b) || a.port != b.port;
-}
-
bool operator<(const CService& a, const CService& b)
{
return static_cast<CNetAddr>(a) < static_cast<CNetAddr>(b) || (static_cast<CNetAddr>(a) == static_cast<CNetAddr>(b) && a.port < b.port);
@@ -663,16 +642,16 @@ bool CSubNet::Match(const CNetAddr &addr) const
static inline int NetmaskBits(uint8_t x)
{
switch(x) {
- case 0x00: return 0; break;
- case 0x80: return 1; break;
- case 0xc0: return 2; break;
- case 0xe0: return 3; break;
- case 0xf0: return 4; break;
- case 0xf8: return 5; break;
- case 0xfc: return 6; break;
- case 0xfe: return 7; break;
- case 0xff: return 8; break;
- default: return -1; break;
+ case 0x00: return 0;
+ case 0x80: return 1;
+ case 0xc0: return 2;
+ case 0xe0: return 3;
+ case 0xf0: return 4;
+ case 0xf8: return 5;
+ case 0xfc: return 6;
+ case 0xfe: return 7;
+ case 0xff: return 8;
+ default: return -1;
}
}
@@ -724,11 +703,6 @@ bool operator==(const CSubNet& a, const CSubNet& b)
return a.valid == b.valid && a.network == b.network && !memcmp(a.netmask, b.netmask, 16);
}
-bool operator!=(const CSubNet& a, const CSubNet& b)
-{
- return !(a==b);
-}
-
bool operator<(const CSubNet& a, const CSubNet& b)
{
return (a.network < b.network || (a.network == b.network && memcmp(a.netmask, b.netmask, 16) < 0));
diff --git a/src/netaddress.h b/src/netaddress.h
index 38f8709257..f8f2ab99ff 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -38,15 +38,16 @@ class CNetAddr
public:
CNetAddr();
explicit CNetAddr(const struct in_addr& ipv4Addr);
- void Init();
void SetIP(const CNetAddr& ip);
+ private:
/**
* Set raw IPv4 or IPv6 address (in network byte order)
* @note Only NET_IPV4 and NET_IPV6 are allowed for network.
*/
void SetRaw(Network network, const uint8_t *data);
+ public:
/**
* Transform an arbitrary string into a non-routable ipv6 address.
* Useful for mapping resolved addresses back to their source.
@@ -87,7 +88,7 @@ class CNetAddr
bool GetIn6Addr(struct in6_addr* pipv6Addr) const;
friend bool operator==(const CNetAddr& a, const CNetAddr& b);
- friend bool operator!=(const CNetAddr& a, const CNetAddr& b);
+ friend bool operator!=(const CNetAddr& a, const CNetAddr& b) { return !(a == b); }
friend bool operator<(const CNetAddr& a, const CNetAddr& b);
ADD_SERIALIZE_METHODS;
@@ -124,7 +125,7 @@ class CSubNet
bool IsValid() const;
friend bool operator==(const CSubNet& a, const CSubNet& b);
- friend bool operator!=(const CSubNet& a, const CSubNet& b);
+ friend bool operator!=(const CSubNet& a, const CSubNet& b) { return !(a == b); }
friend bool operator<(const CSubNet& a, const CSubNet& b);
ADD_SERIALIZE_METHODS;
@@ -148,12 +149,11 @@ class CService : public CNetAddr
CService(const CNetAddr& ip, unsigned short port);
CService(const struct in_addr& ipv4Addr, unsigned short port);
explicit CService(const struct sockaddr_in& addr);
- void Init();
unsigned short GetPort() const;
bool GetSockAddr(struct sockaddr* paddr, socklen_t *addrlen) const;
bool SetSockAddr(const struct sockaddr* paddr);
friend bool operator==(const CService& a, const CService& b);
- friend bool operator!=(const CService& a, const CService& b);
+ friend bool operator!=(const CService& a, const CService& b) { return !(a == b); }
friend bool operator<(const CService& a, const CService& b);
std::vector<unsigned char> GetKey() const;
std::string ToString() const;
diff --git a/src/netbase.cpp b/src/netbase.cpp
index 5d3d2f25c8..15f9016be8 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -289,7 +289,7 @@ struct ProxyCredentials
};
/** Convert SOCKS5 reply to an error message */
-std::string Socks5ErrorString(uint8_t err)
+static std::string Socks5ErrorString(uint8_t err)
{
switch(err) {
case SOCKS5Reply::GENFAILURE:
@@ -513,11 +513,7 @@ bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocket, i
return false;
}
socklen_t nRetSize = sizeof(nRet);
-#ifdef WIN32
- if (getsockopt(hSocket, SOL_SOCKET, SO_ERROR, (char*)(&nRet), &nRetSize) == SOCKET_ERROR)
-#else
- if (getsockopt(hSocket, SOL_SOCKET, SO_ERROR, &nRet, &nRetSize) == SOCKET_ERROR)
-#endif
+ if (getsockopt(hSocket, SOL_SOCKET, SO_ERROR, (sockopt_arg_type)&nRet, &nRetSize) == SOCKET_ERROR)
{
LogPrintf("getsockopt() for %s failed: %s\n", addrConnect.ToString(), NetworkErrorString(WSAGetLastError()));
return false;
diff --git a/src/policy/policy.h b/src/policy/policy.h
index e4eda4b635..5ce019df4c 100644
--- a/src/policy/policy.h
+++ b/src/policy/policy.h
@@ -22,6 +22,8 @@ static const unsigned int DEFAULT_BLOCK_MAX_WEIGHT = MAX_BLOCK_WEIGHT - 4000;
static const unsigned int DEFAULT_BLOCK_MIN_TX_FEE = 1000;
/** The maximum weight for transactions we're willing to relay/mine */
static const unsigned int MAX_STANDARD_TX_WEIGHT = 400000;
+/** The minimum non-witness size for transactions we're willing to relay/mine (1 segwit input + 1 P2WPKH output = 82 bytes) */
+static const unsigned int MIN_STANDARD_TX_NONWITNESS_SIZE = 82;
/** Maximum number of signature check operations in an IsStandard() P2SH script */
static const unsigned int MAX_P2SH_SIGOPS = 15;
/** The maximum number of sigops we're willing to relay/mine in a single tx */
@@ -63,7 +65,8 @@ static constexpr unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VE
SCRIPT_VERIFY_LOW_S |
SCRIPT_VERIFY_WITNESS |
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM |
- SCRIPT_VERIFY_WITNESS_PUBKEYTYPE;
+ SCRIPT_VERIFY_WITNESS_PUBKEYTYPE |
+ SCRIPT_VERIFY_CONST_SCRIPTCODE;
/** For convenience, standard but not mandatory verify flags. */
static constexpr unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS;
diff --git a/src/policy/rbf.h b/src/policy/rbf.h
index b10532addf..c0a25f75b5 100644
--- a/src/policy/rbf.h
+++ b/src/policy/rbf.h
@@ -23,6 +23,6 @@ bool SignalsOptInRBF(const CTransaction &tx);
// according to BIP 125
// This involves checking sequence numbers of the transaction, as well
// as the sequence numbers of all in-mempool ancestors.
-RBFTransactionState IsRBFOptIn(const CTransaction &tx, CTxMemPool &pool);
+RBFTransactionState IsRBFOptIn(const CTransaction &tx, CTxMemPool &pool) EXCLUSIVE_LOCKS_REQUIRED(pool.cs);
#endif // BITCOIN_POLICY_RBF_H
diff --git a/src/qt/README.md b/src/qt/README.md
index 7c23ccadcc..d8acf96ceb 100644
--- a/src/qt/README.md
+++ b/src/qt/README.md
@@ -16,7 +16,7 @@ To run:
### forms
-Contains [Designer UI](http://doc.qt.io/qt-5.9/designer-using-a-ui-file.html) files. They are created with [Qt Creator](#use-qt-Creator-as IDE), but can be edited using any text editor.
+Contains [Designer UI](http://doc.qt.io/qt-5.9/designer-using-a-ui-file.html) files. They are created with [Qt Creator](#using-qt-creator-as-ide), but can be edited using any text editor.
### locale
diff --git a/src/qt/addressbookpage.h b/src/qt/addressbookpage.h
index 8877d07330..ba420c5e15 100644
--- a/src/qt/addressbookpage.h
+++ b/src/qt/addressbookpage.h
@@ -38,7 +38,7 @@ public:
ForEditing /**< Open address book for editing */
};
- explicit AddressBookPage(const PlatformStyle *platformStyle, Mode mode, Tabs tab, QWidget *parent);
+ explicit AddressBookPage(const PlatformStyle *platformStyle, Mode mode, Tabs tab, QWidget *parent = 0);
~AddressBookPage();
void setModel(AddressTableModel *model);
diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp
index 1e3acd75c0..25b615e6f8 100644
--- a/src/qt/addresstablemodel.cpp
+++ b/src/qt/addresstablemodel.cpp
@@ -159,7 +159,7 @@ public:
};
AddressTableModel::AddressTableModel(WalletModel *parent) :
- QAbstractTableModel(parent),walletModel(parent),priv(0)
+ QAbstractTableModel(parent), walletModel(parent)
{
columns << tr("Label") << tr("Address");
priv = new AddressTablePriv(this);
@@ -266,7 +266,8 @@ 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)
- if (walletModel->wallet().getAddress(newAddress))
+ if (walletModel->wallet().getAddress(
+ newAddress, /* name= */ nullptr, /* is_mine= */ nullptr, /* purpose= */ nullptr))
{
editStatus = DUPLICATE_ADDRESS;
return false;
@@ -351,7 +352,8 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con
}
// Check for duplicate addresses
{
- if(walletModel->wallet().getAddress(DecodeDestination(strAddress)))
+ if (walletModel->wallet().getAddress(
+ DecodeDestination(strAddress), /* name= */ nullptr, /* is_mine= */ nullptr, /* purpose= */ nullptr))
{
editStatus = DUPLICATE_ADDRESS;
return QString();
@@ -405,21 +407,31 @@ bool AddressTableModel::removeRows(int row, int count, const QModelIndex &parent
return true;
}
-/* Look up label for address in address book, if not found return empty string.
- */
QString AddressTableModel::labelForAddress(const QString &address) const
{
- {
- CTxDestination destination = DecodeDestination(address.toStdString());
- std::string name;
- if (walletModel->wallet().getAddress(destination, &name))
- {
- return QString::fromStdString(name);
- }
+ std::string name;
+ if (getAddressData(address, &name, /* purpose= */ nullptr)) {
+ return QString::fromStdString(name);
+ }
+ return QString();
+}
+
+QString AddressTableModel::purposeForAddress(const QString &address) const
+{
+ std::string purpose;
+ if (getAddressData(address, /* name= */ nullptr, &purpose)) {
+ return QString::fromStdString(purpose);
}
return QString();
}
+bool AddressTableModel::getAddressData(const QString &address,
+ std::string* name,
+ std::string* purpose) const {
+ CTxDestination destination = DecodeDestination(address.toStdString());
+ return walletModel->wallet().getAddress(destination, name, /* is_mine= */ nullptr, purpose);
+}
+
int AddressTableModel::lookupAddress(const QString &address) const
{
QModelIndexList lst = match(index(0, Address, QModelIndex()),
diff --git a/src/qt/addresstablemodel.h b/src/qt/addresstablemodel.h
index d7aeda9d8e..6e1b53b049 100644
--- a/src/qt/addresstablemodel.h
+++ b/src/qt/addresstablemodel.h
@@ -67,10 +67,12 @@ public:
*/
QString addRow(const QString &type, const QString &label, const QString &address, const OutputType address_type);
- /* Look up label for address in address book, if not found return empty string.
- */
+ /** Look up label for address in address book, if not found return empty string. */
QString labelForAddress(const QString &address) const;
+ /** Look up purpose for address in address book, if not found return empty string. */
+ QString purposeForAddress(const QString &address) const;
+
/* Look up row index of an address in the model.
Return -1 if not found.
*/
@@ -81,10 +83,13 @@ public:
OutputType GetDefaultAddressType() const;
private:
- WalletModel *walletModel;
- AddressTablePriv *priv;
+ WalletModel* const walletModel;
+ AddressTablePriv *priv = nullptr;
QStringList columns;
- EditStatus editStatus;
+ EditStatus editStatus = OK;
+
+ /** Look up address book data given an address string. */
+ bool getAddressData(const QString &address, std::string* name, std::string* purpose) const;
/** Notify listeners that data changed. */
void emitDataChanged(int index);
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 599c3c0985..9178317601 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -403,6 +403,10 @@ void BitcoinApplication::startThread()
void BitcoinApplication::parameterSetup()
{
+ // Default printtoconsole to false for the GUI. GUI programs should not
+ // print to the console unnecessarily.
+ gArgs.SoftSetBoolArg("-printtoconsole", false);
+
m_node.initLogging();
m_node.initParameterInteraction();
}
@@ -527,6 +531,20 @@ WId BitcoinApplication::getMainWinId() const
return window->winId();
}
+static void SetupUIArgs()
+{
+#ifdef ENABLE_WALLET
+ gArgs.AddArg("-allowselfsignedrootcertificates", strprintf("Allow self signed root certificates (default: %u)", DEFAULT_SELFSIGNED_ROOTCERTS), true, OptionsCategory::GUI);
+#endif
+ gArgs.AddArg("-choosedatadir", strprintf(QObject::tr("Choose data directory on startup (default: %u)").toStdString(), DEFAULT_CHOOSE_DATADIR), false, OptionsCategory::GUI);
+ gArgs.AddArg("-lang=<lang>", QObject::tr("Set language, for example \"de_DE\" (default: system locale)").toStdString(), false, OptionsCategory::GUI);
+ gArgs.AddArg("-min", QObject::tr("Start minimized").toStdString(), false, OptionsCategory::GUI);
+ gArgs.AddArg("-resetguisettings", QObject::tr("Reset all settings changed in the GUI").toStdString(), false, OptionsCategory::GUI);
+ gArgs.AddArg("-rootcertificates=<file>", QObject::tr("Set SSL root certificates for payment request (default: -system-)").toStdString(), false, OptionsCategory::GUI);
+ gArgs.AddArg("-splash", strprintf(QObject::tr("Show splash screen on startup (default: %u)").toStdString(), DEFAULT_SPLASHSCREEN), false, OptionsCategory::GUI);
+ gArgs.AddArg("-uiplatform", strprintf("Select platform to customize UI for (one of windows, macosx, other; default: %s)", BitcoinGUI::DEFAULT_UIPLATFORM), true, OptionsCategory::GUI);
+}
+
#ifndef BITCOIN_QT_TEST
int main(int argc, char *argv[])
{
@@ -536,6 +554,8 @@ int main(int argc, char *argv[])
/// 1. Parse command-line options. These take precedence over anything else.
// Command-line options take precedence:
+ node->setupServerArgs();
+ SetupUIArgs();
node->parseParameters(argc, argv);
// Do not refer to data directory yet, this can be overridden by Intro::pickDataDirectory
@@ -612,7 +632,7 @@ int main(int argc, char *argv[])
return EXIT_FAILURE;
}
try {
- node->readConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME));
+ node->readConfigFiles();
} 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()));
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index bfa8844a09..aed5374a7d 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -968,6 +968,11 @@ void BitcoinGUI::changeEvent(QEvent *e)
QTimer::singleShot(0, this, SLOT(hide()));
e->ignore();
}
+ else if((wsevt->oldState() & Qt::WindowMinimized) && !isMinimized())
+ {
+ QTimer::singleShot(0, this, SLOT(show()));
+ e->ignore();
+ }
}
}
#endif
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index 601a77fc85..b08de27041 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -509,7 +509,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
nBytes -= 34;
// Fee
- nPayFee = model->node().getMinimumFee(nBytes, *coinControl(), nullptr /* returned_target */, nullptr /* reason */);
+ nPayFee = model->wallet().getMinimumFee(nBytes, *coinControl(), nullptr /* returned_target */, nullptr /* reason */);
if (nPayAmount > 0)
{
diff --git a/src/qt/editaddressdialog.cpp b/src/qt/editaddressdialog.cpp
index 38411c499f..f26a31158e 100644
--- a/src/qt/editaddressdialog.cpp
+++ b/src/qt/editaddressdialog.cpp
@@ -109,7 +109,7 @@ void EditAddressDialog::accept()
break;
case AddressTableModel::DUPLICATE_ADDRESS:
QMessageBox::warning(this, windowTitle(),
- tr("The entered address \"%1\" is already in the address book.").arg(ui->addressEdit->text()),
+ getDuplicateAddressWarning(),
QMessageBox::Ok, QMessageBox::Ok);
break;
case AddressTableModel::WALLET_UNLOCK_FAILURE:
@@ -129,6 +129,25 @@ void EditAddressDialog::accept()
QDialog::accept();
}
+QString EditAddressDialog::getDuplicateAddressWarning() const
+{
+ QString dup_address = ui->addressEdit->text();
+ QString existing_label = model->labelForAddress(dup_address);
+ QString existing_purpose = model->purposeForAddress(dup_address);
+
+ if (existing_purpose == "receive" &&
+ (mode == NewSendingAddress || mode == EditSendingAddress)) {
+ return tr(
+ "Address \"%1\" already exists as a receiving address with label "
+ "\"%2\" and so cannot be added as a sending address."
+ ).arg(dup_address).arg(existing_label);
+ }
+ return tr(
+ "The entered address \"%1\" is already in the address book with "
+ "label \"%2\"."
+ ).arg(dup_address).arg(existing_label);
+}
+
QString EditAddressDialog::getAddress() const
{
return address;
diff --git a/src/qt/editaddressdialog.h b/src/qt/editaddressdialog.h
index 41c5d1708a..3aba74bf08 100644
--- a/src/qt/editaddressdialog.h
+++ b/src/qt/editaddressdialog.h
@@ -30,7 +30,7 @@ public:
EditSendingAddress
};
- explicit EditAddressDialog(Mode mode, QWidget *parent);
+ explicit EditAddressDialog(Mode mode, QWidget *parent = 0);
~EditAddressDialog();
void setModel(AddressTableModel *model);
@@ -45,6 +45,9 @@ public Q_SLOTS:
private:
bool saveCurrentRow();
+ /** Return a descriptive string when adding an already-existing address fails. */
+ QString getDuplicateAddressWarning() const;
+
Ui::EditAddressDialog *ui;
QDataWidgetMapper *mapper;
Mode mode;
diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp
index 70cdb3361c..59bb5d5bb6 100644
--- a/src/qt/paymentserver.cpp
+++ b/src/qt/paymentserver.cpp
@@ -648,7 +648,7 @@ void PaymentServer::fetchPaymentACK(WalletModel* walletModel, const SendCoinsRec
// 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 = walletModel->wallet().getDefaultChangeType() != OutputType::NONE ? walletModel->wallet().getDefaultChangeType() : walletModel->wallet().getDefaultAddressType();
+ const OutputType change_type = walletModel->wallet().getDefaultChangeType() != OutputType::CHANGE_AUTO ? 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();
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index 5122bab36f..4032729a84 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -70,6 +70,7 @@ namespace {
const QStringList historyFilter = QStringList()
<< "importprivkey"
<< "importmulti"
+ << "sethdseed"
<< "signmessagewithprivkey"
<< "signrawtransaction"
<< "signrawtransactionwithkey"
@@ -455,12 +456,7 @@ RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformSty
QWidget(parent),
m_node(node),
ui(new Ui::RPCConsole),
- clientModel(0),
- historyPtr(0),
- platformStyle(_platformStyle),
- peersTableContextMenu(0),
- banTableContextMenu(0),
- consoleFontSize(0)
+ platformStyle(_platformStyle)
{
ui->setupUi(this);
QSettings settings;
diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h
index a9a60d09f1..a53c4c24f9 100644
--- a/src/qt/rpcconsole.h
+++ b/src/qt/rpcconsole.h
@@ -145,18 +145,18 @@ private:
};
interfaces::Node& m_node;
- Ui::RPCConsole *ui;
- ClientModel *clientModel;
+ Ui::RPCConsole* const ui;
+ ClientModel *clientModel = nullptr;
QStringList history;
- int historyPtr;
+ int historyPtr = 0;
QString cmdBeforeBrowsing;
QList<NodeId> cachedNodeids;
- const PlatformStyle *platformStyle;
- RPCTimerInterface *rpcTimerInterface;
- QMenu *peersTableContextMenu;
- QMenu *banTableContextMenu;
- int consoleFontSize;
- QCompleter *autoCompleter;
+ const PlatformStyle* const platformStyle;
+ RPCTimerInterface *rpcTimerInterface = nullptr;
+ QMenu *peersTableContextMenu = nullptr;
+ QMenu *banTableContextMenu = nullptr;
+ int consoleFontSize = 0;
+ QCompleter *autoCompleter = nullptr;
QThread thread;
QString m_last_wallet_id;
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 0874a0ada4..261ab7a948 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -114,7 +114,7 @@ SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *p
if (!settings.contains("nSmartFeeSliderPosition"))
settings.setValue("nSmartFeeSliderPosition", 0);
if (!settings.contains("nTransactionFee"))
- settings.setValue("nTransactionFee", (qint64)DEFAULT_TRANSACTION_FEE);
+ settings.setValue("nTransactionFee", (qint64)DEFAULT_PAY_TX_FEE);
if (!settings.contains("fPayOnlyMinFee"))
settings.setValue("fPayOnlyMinFee", false);
ui->groupFee->setId(ui->radioSmartFee, 0);
@@ -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(model->node().getRequiredFee(1000));
+ ui->customFee->setSingleStep(model->wallet().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->node().getTxConfirmTarget()));
+ ui->confTargetSelector->setCurrentIndex(getIndexForConfTarget(model->wallet().getConfirmTarget()));
else
ui->confTargetSelector->setCurrentIndex(getIndexForConfTarget(settings.value("nConfTarget").toInt()));
}
@@ -629,7 +629,7 @@ void SendCoinsDialog::useAvailableBalance(SendCoinsEntry* entry)
void SendCoinsDialog::setMinimumFee()
{
- ui->customFee->setValue(model->node().getRequiredFee(1000));
+ ui->customFee->setValue(model->wallet().getRequiredFee(1000));
}
void SendCoinsDialog::updateFeeSectionControls()
@@ -661,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(), model->node().getRequiredFee(1000)) + "/kB")
+ BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), model->wallet().getRequiredFee(1000)) + "/kB")
);
}
@@ -675,7 +675,7 @@ void SendCoinsDialog::updateCoinControlState(CCoinControl& ctrl)
// Avoid using global defaults when sending money from the GUI
// Either custom fee will be used or if not selected, the confirmation target from dropdown box
ctrl.m_confirm_target = getConfTargetForIndex(ui->confTargetSelector->currentIndex());
- ctrl.signalRbf = ui->optInRBF->isChecked();
+ ctrl.m_signal_bip125_rbf = ui->optInRBF->isChecked();
}
void SendCoinsDialog::updateSmartFeeLabel()
@@ -687,7 +687,7 @@ void SendCoinsDialog::updateSmartFeeLabel()
coin_control.m_feerate.reset(); // Explicitly use only fee estimation rate for smart fee labels
int returned_target;
FeeReason reason;
- CFeeRate feeRate = CFeeRate(model->node().getMinimumFee(1000, coin_control, &returned_target, &reason));
+ CFeeRate feeRate = CFeeRate(model->wallet().getMinimumFee(1000, coin_control, &returned_target, &reason));
ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), feeRate.GetFeePerK()) + "/kB");
diff --git a/src/qt/test/addressbooktests.cpp b/src/qt/test/addressbooktests.cpp
new file mode 100644
index 0000000000..0c2e7ae71d
--- /dev/null
+++ b/src/qt/test/addressbooktests.cpp
@@ -0,0 +1,143 @@
+#include <qt/test/addressbooktests.h>
+#include <qt/test/util.h>
+#include <test/test_bitcoin.h>
+
+#include <interfaces/node.h>
+#include <qt/addressbookpage.h>
+#include <qt/addresstablemodel.h>
+#include <qt/editaddressdialog.h>
+#include <qt/callback.h>
+#include <qt/optionsmodel.h>
+#include <qt/platformstyle.h>
+#include <qt/qvalidatedlineedit.h>
+#include <qt/walletmodel.h>
+
+#include <key.h>
+#include <pubkey.h>
+#include <key_io.h>
+#include <wallet/wallet.h>
+
+#include <QTimer>
+#include <QMessageBox>
+
+namespace
+{
+
+/**
+ * Fill the edit address dialog box with data, submit it, and ensure that
+ * the resulting message meets expectations.
+ */
+void EditAddressAndSubmit(
+ EditAddressDialog* dialog,
+ const QString& label, const QString& address, QString expected_msg)
+{
+ QString warning_text;
+
+ dialog->findChild<QLineEdit*>("labelEdit")->setText(label);
+ dialog->findChild<QValidatedLineEdit*>("addressEdit")->setText(address);
+
+ ConfirmMessage(&warning_text, 5);
+ dialog->accept();
+ QCOMPARE(warning_text, expected_msg);
+}
+
+/**
+ * Test adding various send addresses to the address book.
+ *
+ * There are three cases tested:
+ *
+ * - new_address: a new address which should add as a send address successfully.
+ * - existing_s_address: an existing sending address which won't add successfully.
+ * - existing_r_address: an existing receiving address which won't add successfully.
+ *
+ * In each case, verify the resulting state of the address book and optionally
+ * the warning message presented to the user.
+ */
+void TestAddAddressesToSendBook()
+{
+ TestChain100Setup test;
+ CWallet wallet("mock", WalletDatabase::CreateMock());
+ bool firstRun;
+ wallet.LoadWallet(firstRun);
+
+ auto build_address = [&wallet]() {
+ CKey key;
+ key.MakeNewKey(true);
+ CTxDestination dest(GetDestinationForKey(
+ key.GetPubKey(), wallet.m_default_address_type));
+
+ return std::make_pair(dest, QString::fromStdString(EncodeDestination(dest)));
+ };
+
+ CTxDestination r_key_dest, s_key_dest;
+
+ // Add a preexisting "receive" entry in the address book.
+ QString preexisting_r_address;
+ QString r_label("already here (r)");
+
+ // Add a preexisting "send" entry in the address book.
+ QString preexisting_s_address;
+ QString s_label("already here (s)");
+
+ // Define a new address (which should add to the address book successfully).
+ QString new_address;
+
+ std::tie(r_key_dest, preexisting_r_address) = build_address();
+ std::tie(s_key_dest, preexisting_s_address) = build_address();
+ std::tie(std::ignore, new_address) = build_address();
+
+ {
+ LOCK(wallet.cs_wallet);
+ wallet.SetAddressBook(r_key_dest, r_label.toStdString(), "receive");
+ wallet.SetAddressBook(s_key_dest, s_label.toStdString(), "send");
+ }
+
+ auto check_addbook_size = [&wallet](int expected_size) {
+ QCOMPARE(static_cast<int>(wallet.mapAddressBook.size()), expected_size);
+ };
+
+ // We should start with the two addresses we added earlier and nothing else.
+ check_addbook_size(2);
+
+ // Initialize relevant QT models.
+ std::unique_ptr<const PlatformStyle> platformStyle(PlatformStyle::instantiate("other"));
+ auto node = interfaces::MakeNode();
+ OptionsModel optionsModel(*node);
+ AddWallet(&wallet);
+ WalletModel walletModel(std::move(node->getWallets()[0]), *node, platformStyle.get(), &optionsModel);
+ RemoveWallet(&wallet);
+ EditAddressDialog editAddressDialog(EditAddressDialog::NewSendingAddress);
+ editAddressDialog.setModel(walletModel.getAddressTableModel());
+
+ EditAddressAndSubmit(
+ &editAddressDialog, QString("uhoh"), preexisting_r_address,
+ QString(
+ "Address \"%1\" already exists as a receiving address with label "
+ "\"%2\" and so cannot be added as a sending address."
+ ).arg(preexisting_r_address).arg(r_label));
+
+ check_addbook_size(2);
+
+ EditAddressAndSubmit(
+ &editAddressDialog, QString("uhoh, different"), preexisting_s_address,
+ QString(
+ "The entered address \"%1\" is already in the address book with "
+ "label \"%2\"."
+ ).arg(preexisting_s_address).arg(s_label));
+
+ check_addbook_size(2);
+
+ // Submit a new address which should add successfully - we expect the
+ // warning message to be blank.
+ EditAddressAndSubmit(
+ &editAddressDialog, QString("new"), new_address, QString(""));
+
+ check_addbook_size(3);
+}
+
+} // namespace
+
+void AddressBookTests::addressBookTests()
+{
+ TestAddAddressesToSendBook();
+}
diff --git a/src/qt/test/addressbooktests.h b/src/qt/test/addressbooktests.h
new file mode 100644
index 0000000000..beeb9e76a9
--- /dev/null
+++ b/src/qt/test/addressbooktests.h
@@ -0,0 +1,15 @@
+#ifndef BITCOIN_QT_TEST_ADDRESSBOOKTESTS_H
+#define BITCOIN_QT_TEST_ADDRESSBOOKTESTS_H
+
+#include <QObject>
+#include <QTest>
+
+class AddressBookTests : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void addressBookTests();
+};
+
+#endif // BITCOIN_QT_TEST_ADDRESSBOOKTESTS_H
diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp
index 25ee66dc6b..56d4d3e457 100644
--- a/src/qt/test/test_main.cpp
+++ b/src/qt/test/test_main.cpp
@@ -13,6 +13,7 @@
#include <qt/test/compattests.h>
#ifdef ENABLE_WALLET
+#include <qt/test/addressbooktests.h>
#include <qt/test/paymentservertests.h>
#include <qt/test/wallettests.h>
#endif
@@ -99,6 +100,10 @@ int main(int argc, char *argv[])
if (QTest::qExec(&test5) != 0) {
fInvalid = true;
}
+ AddressBookTests test6;
+ if (QTest::qExec(&test6) != 0) {
+ fInvalid = true;
+ }
#endif
fs::remove_all(pathTemp);
diff --git a/src/qt/test/util.cpp b/src/qt/test/util.cpp
new file mode 100644
index 0000000000..261caaaee5
--- /dev/null
+++ b/src/qt/test/util.cpp
@@ -0,0 +1,22 @@
+#include <qt/callback.h>
+
+#include <QApplication>
+#include <QMessageBox>
+#include <QTimer>
+#include <QString>
+#include <QPushButton>
+#include <QWidget>
+
+void ConfirmMessage(QString* text, int msec)
+{
+ QTimer::singleShot(msec, makeCallback([text](Callback* callback) {
+ for (QWidget* widget : QApplication::topLevelWidgets()) {
+ if (widget->inherits("QMessageBox")) {
+ QMessageBox* messageBox = qobject_cast<QMessageBox*>(widget);
+ if (text) *text = messageBox->text();
+ messageBox->defaultButton()->click();
+ }
+ }
+ delete callback;
+ }), SLOT(call()));
+}
diff --git a/src/qt/test/util.h b/src/qt/test/util.h
new file mode 100644
index 0000000000..324386c139
--- /dev/null
+++ b/src/qt/test/util.h
@@ -0,0 +1,12 @@
+#ifndef BITCOIN_QT_TEST_UTIL_H
+#define BITCOIN_QT_TEST_UTIL_H
+
+/**
+ * Press "Ok" button in message box dialog.
+ *
+ * @param text - Optionally store dialog text.
+ * @param msec - Number of miliseconds to pause before triggering the callback.
+ */
+void ConfirmMessage(QString* text = nullptr, int msec = 0);
+
+#endif // BITCOIN_QT_TEST_UTIL_H
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index dcc834c352..a09d98dfe5 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -1,4 +1,5 @@
#include <qt/test/wallettests.h>
+#include <qt/test/util.h>
#include <interfaces/node.h>
#include <qt/bitcoinamountfield.h>
@@ -35,21 +36,6 @@
namespace
{
-//! Press "Ok" button in message box dialog.
-void ConfirmMessage(QString* text = nullptr)
-{
- QTimer::singleShot(0, makeCallback([text](Callback* callback) {
- for (QWidget* widget : QApplication::topLevelWidgets()) {
- if (widget->inherits("QMessageBox")) {
- QMessageBox* messageBox = qobject_cast<QMessageBox*>(widget);
- if (text) *text = messageBox->text();
- messageBox->defaultButton()->click();
- }
- }
- delete callback;
- }), SLOT(call()));
-}
-
//! Press "Yes" or "Cancel" buttons in modal send confirmation dialog.
void ConfirmSend(QString* text = nullptr, bool cancel = false)
{
@@ -180,9 +166,9 @@ void TestGUI()
TransactionView transactionView(platformStyle.get());
auto node = interfaces::MakeNode();
OptionsModel optionsModel(*node);
- vpwallets.insert(vpwallets.begin(), &wallet);
- WalletModel walletModel(std::move(node->getWallets()[0]), *node, platformStyle.get(), &optionsModel);
- vpwallets.erase(vpwallets.begin());
+ AddWallet(&wallet);
+ WalletModel walletModel(std::move(node->getWallets().back()), *node, platformStyle.get(), &optionsModel);
+ RemoveWallet(&wallet);
sendCoinsDialog.setModel(&walletModel);
transactionView.setModel(&walletModel);
@@ -264,7 +250,7 @@ void TestGUI()
QCOMPARE(requestTableModel->rowCount({}), currentRowCount-1);
}
-}
+} // namespace
void WalletTests::walletTests()
{
diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp
index f316c3ca45..2cb446c459 100644
--- a/src/qt/transactiondesc.cpp
+++ b/src/qt/transactiondesc.cpp
@@ -102,7 +102,7 @@ QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wall
if (IsValidDestination(address)) {
std::string name;
isminetype ismine;
- if (wallet.getAddress(address, &name, &ismine))
+ if (wallet.getAddress(address, &name, &ismine, /* purpose= */ nullptr))
{
strHTML += "<b>" + tr("From") + ":</b> " + tr("unknown") + "<br>";
strHTML += "<b>" + tr("To") + ":</b> ";
@@ -128,7 +128,8 @@ QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wall
strHTML += "<b>" + tr("To") + ":</b> ";
CTxDestination dest = DecodeDestination(strAddress);
std::string name;
- if (wallet.getAddress(dest, &name) && !name.empty())
+ if (wallet.getAddress(
+ dest, &name, /* is_mine= */ nullptr, /* purpose= */ nullptr) && !name.empty())
strHTML += GUIUtil::HtmlEscape(name) + " ";
strHTML += GUIUtil::HtmlEscape(strAddress) + "<br>";
}
@@ -196,7 +197,8 @@ QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wall
{
strHTML += "<b>" + tr("To") + ":</b> ";
std::string name;
- if (wallet.getAddress(address, &name) && !name.empty())
+ if (wallet.getAddress(
+ address, &name, /* is_mine= */ nullptr, /* purpose= */ nullptr) && !name.empty())
strHTML += GUIUtil::HtmlEscape(name) + " ";
strHTML += GUIUtil::HtmlEscape(EncodeDestination(address));
if(toSelf == ISMINE_SPENDABLE)
@@ -319,7 +321,7 @@ QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wall
if (ExtractDestination(vout.scriptPubKey, address))
{
std::string name;
- if (wallet.getAddress(address, &name) && !name.empty())
+ if (wallet.getAddress(address, &name, /* is_mine= */ nullptr, /* purpose= */ nullptr) && !name.empty())
strHTML += GUIUtil::HtmlEscape(name) + " ";
strHTML += QString::fromStdString(EncodeDestination(address));
}
diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp
index d5b98486ae..993d7454d6 100644
--- a/src/qt/utilitydialog.cpp
+++ b/src/qt/utilitydialog.cpp
@@ -78,21 +78,7 @@ HelpMessageDialog::HelpMessageDialog(interfaces::Node& node, QWidget *parent, bo
cursor.insertText(header);
cursor.insertBlock();
- std::string strUsage = node.helpMessage(HelpMessageMode::BITCOIN_QT);
- const bool showDebug = gArgs.GetBoolArg("-help-debug", false);
- strUsage += HelpMessageGroup(tr("UI Options:").toStdString());
- if (showDebug) {
- strUsage += HelpMessageOpt("-allowselfsignedrootcertificates", strprintf("Allow self signed root certificates (default: %u)", DEFAULT_SELFSIGNED_ROOTCERTS));
- }
- strUsage += HelpMessageOpt("-choosedatadir", strprintf(tr("Choose data directory on startup (default: %u)").toStdString(), DEFAULT_CHOOSE_DATADIR));
- strUsage += HelpMessageOpt("-lang=<lang>", tr("Set language, for example \"de_DE\" (default: system locale)").toStdString());
- strUsage += HelpMessageOpt("-min", tr("Start minimized").toStdString());
- strUsage += HelpMessageOpt("-resetguisettings", tr("Reset all settings changed in the GUI").toStdString());
- strUsage += HelpMessageOpt("-rootcertificates=<file>", tr("Set SSL root certificates for payment request (default: -system-)").toStdString());
- strUsage += HelpMessageOpt("-splash", strprintf(tr("Show splash screen on startup (default: %u)").toStdString(), DEFAULT_SPLASHSCREEN));
- if (showDebug) {
- strUsage += HelpMessageOpt("-uiplatform", strprintf("Select platform to customize UI for (one of windows, macosx, other; default: %s)", BitcoinGUI::DEFAULT_UIPLATFORM));
- }
+ std::string strUsage = gArgs.GetHelpMessage();
QString coreOptions = QString::fromStdString(strUsage);
text = version + "\n" + header + "\n" + coreOptions;
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index 00b98901c0..3418b1f1a9 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -274,7 +274,8 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran
{
// Check if we have a new address or an updated label
std::string name;
- if (!m_wallet->getAddress(dest, &name))
+ if (!m_wallet->getAddress(
+ dest, &name, /* is_mine= */ nullptr, /* purpose= */ nullptr))
{
m_wallet->setAddressBook(dest, strLabel, "send");
}
@@ -486,7 +487,7 @@ bool WalletModel::saveReceiveRequest(const std::string &sAddress, const int64_t
bool WalletModel::bumpFee(uint256 hash)
{
CCoinControl coin_control;
- coin_control.signalRbf = true;
+ coin_control.m_signal_bip125_rbf = true;
std::vector<std::string> errors;
CAmount old_fee;
CAmount new_fee;
diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h
index e5ed5b4e82..9173fcae52 100644
--- a/src/qt/walletmodel.h
+++ b/src/qt/walletmodel.h
@@ -204,6 +204,8 @@ public:
QString getWalletName() const;
bool isMultiwallet();
+
+ AddressTableModel* getAddressTableModel() const { return addressTableModel; }
private:
std::unique_ptr<interfaces::Wallet> m_wallet;
std::unique_ptr<interfaces::Handler> m_handler_status_changed;
diff --git a/src/random.cpp b/src/random.cpp
index b004dcac39..4ba86e4e7a 100644
--- a/src/random.cpp
+++ b/src/random.cpp
@@ -178,7 +178,7 @@ static void RandAddSeedPerfmon()
/** Fallback: get 32 bytes of system entropy from /dev/urandom. The most
* compatible way to get cryptographic randomness on UNIX-ish platforms.
*/
-void GetDevURandom(unsigned char *ent32)
+static void GetDevURandom(unsigned char *ent32)
{
int f = open("/dev/urandom", O_RDONLY);
if (f == -1) {
diff --git a/src/rest.cpp b/src/rest.cpp
index 5871b554a6..ffa75c241f 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -6,6 +6,7 @@
#include <chain.h>
#include <chainparams.h>
#include <core_io.h>
+#include <index/txindex.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <validation.h>
@@ -32,7 +33,7 @@ enum class RetFormat {
};
static const struct {
- enum RetFormat rf;
+ RetFormat rf;
const char* name;
} rf_names[] = {
{RetFormat::UNDEF, ""},
@@ -67,7 +68,7 @@ static bool RESTERR(HTTPRequest* req, enum HTTPStatusCode status, std::string me
return false;
}
-static enum RetFormat ParseDataFormat(std::string& param, const std::string& strReq)
+static RetFormat ParseDataFormat(std::string& param, const std::string& strReq)
{
const std::string::size_type pos = strReq.rfind('.');
if (pos == std::string::npos)
@@ -350,6 +351,10 @@ static bool rest_tx(HTTPRequest* req, const std::string& strURIPart)
if (!ParseHashStr(hashStr, hash))
return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
+ if (g_txindex) {
+ g_txindex->BlockUntilSyncedToCurrentChain();
+ }
+
CTransactionRef tx;
uint256 hashBlock = uint256();
if (!GetTransaction(hash, tx, Params().GetConsensus(), hashBlock, true))
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 06c68ea27c..238d8c9d95 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -47,8 +47,6 @@ static std::mutex cs_blockchange;
static std::condition_variable cond_blockchange;
static CUpdatedBlock latestblock;
-extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry);
-
/* Calculate the difficulty for a given block index,
* or the block index of the given chain.
*/
@@ -159,7 +157,7 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx
return result;
}
-UniValue getblockcount(const JSONRPCRequest& request)
+static UniValue getblockcount(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
@@ -176,7 +174,7 @@ UniValue getblockcount(const JSONRPCRequest& request)
return chainActive.Height();
}
-UniValue getbestblockhash(const JSONRPCRequest& request)
+static UniValue getbestblockhash(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
@@ -203,7 +201,7 @@ void RPCNotifyBlockChange(bool ibd, const CBlockIndex * pindex)
cond_blockchange.notify_all();
}
-UniValue waitfornewblock(const JSONRPCRequest& request)
+static UniValue waitfornewblock(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 1)
throw std::runtime_error(
@@ -241,7 +239,7 @@ UniValue waitfornewblock(const JSONRPCRequest& request)
return ret;
}
-UniValue waitforblock(const JSONRPCRequest& request)
+static UniValue waitforblock(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
@@ -283,7 +281,7 @@ UniValue waitforblock(const JSONRPCRequest& request)
return ret;
}
-UniValue waitforblockheight(const JSONRPCRequest& request)
+static UniValue waitforblockheight(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
@@ -325,7 +323,7 @@ UniValue waitforblockheight(const JSONRPCRequest& request)
return ret;
}
-UniValue syncwithvalidationinterfacequeue(const JSONRPCRequest& request)
+static UniValue syncwithvalidationinterfacequeue(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 0) {
throw std::runtime_error(
@@ -340,7 +338,7 @@ UniValue syncwithvalidationinterfacequeue(const JSONRPCRequest& request)
return NullUniValue;
}
-UniValue getdifficulty(const JSONRPCRequest& request)
+static UniValue getdifficulty(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
@@ -357,20 +355,26 @@ UniValue getdifficulty(const JSONRPCRequest& request)
return GetDifficulty();
}
-std::string EntryDescriptionString()
+static std::string EntryDescriptionString()
{
return " \"size\" : n, (numeric) virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted.\n"
- " \"fee\" : n, (numeric) transaction fee in " + CURRENCY_UNIT + "\n"
- " \"modifiedfee\" : n, (numeric) transaction fee with fee deltas used for mining priority\n"
+ " \"fee\" : n, (numeric) transaction fee in " + CURRENCY_UNIT + " (DEPRECATED)\n"
+ " \"modifiedfee\" : n, (numeric) transaction fee with fee deltas used for mining priority (DEPRECATED)\n"
" \"time\" : n, (numeric) local time transaction entered pool in seconds since 1 Jan 1970 GMT\n"
" \"height\" : n, (numeric) block height when transaction entered pool\n"
" \"descendantcount\" : n, (numeric) number of in-mempool descendant transactions (including this one)\n"
" \"descendantsize\" : n, (numeric) virtual transaction size of in-mempool descendants (including this one)\n"
- " \"descendantfees\" : n, (numeric) modified fees (see above) of in-mempool descendants (including this one)\n"
+ " \"descendantfees\" : n, (numeric) modified fees (see above) of in-mempool descendants (including this one) (DEPRECATED)\n"
" \"ancestorcount\" : n, (numeric) number of in-mempool ancestor transactions (including this one)\n"
" \"ancestorsize\" : n, (numeric) virtual transaction size of in-mempool ancestors (including this one)\n"
- " \"ancestorfees\" : n, (numeric) modified fees (see above) of in-mempool ancestors (including this one)\n"
+ " \"ancestorfees\" : n, (numeric) modified fees (see above) of in-mempool ancestors (including this one) (DEPRECATED)\n"
" \"wtxid\" : hash, (string) hash of serialized transaction, including witness data\n"
+ " \"fees\" : {\n"
+ " \"base\" : n, (numeric) transaction fee in " + CURRENCY_UNIT + "\n"
+ " \"modified\" : n, (numeric) transaction fee with fee deltas used for mining priority in " + CURRENCY_UNIT + "\n"
+ " \"ancestor\" : n, (numeric) modified fees (see above) of in-mempool ancestors (including this one) in " + CURRENCY_UNIT + "\n"
+ " \"descendant\" : n, (numeric) modified fees (see above) of in-mempool descendants (including this one) in " + CURRENCY_UNIT + "\n"
+ " }\n"
" \"depends\" : [ (array) unconfirmed transactions used as inputs for this transaction\n"
" \"transactionid\", (string) parent transaction id\n"
" ... ]\n"
@@ -379,10 +383,17 @@ std::string EntryDescriptionString()
" ... ]\n";
}
-void entryToJSON(UniValue &info, const CTxMemPoolEntry &e)
+static void entryToJSON(UniValue &info, const CTxMemPoolEntry &e) EXCLUSIVE_LOCKS_REQUIRED(::mempool.cs)
{
AssertLockHeld(mempool.cs);
+ UniValue fees(UniValue::VOBJ);
+ fees.pushKV("base", ValueFromAmount(e.GetFee()));
+ fees.pushKV("modified", ValueFromAmount(e.GetModifiedFee()));
+ fees.pushKV("ancestor", ValueFromAmount(e.GetModFeesWithAncestors()));
+ fees.pushKV("descendant", ValueFromAmount(e.GetModFeesWithDescendants()));
+ info.pushKV("fees", fees);
+
info.pushKV("size", (int)e.GetTxSize());
info.pushKV("fee", ValueFromAmount(e.GetFee()));
info.pushKV("modifiedfee", ValueFromAmount(e.GetModifiedFee()));
@@ -449,7 +460,7 @@ UniValue mempoolToJSON(bool fVerbose)
}
}
-UniValue getrawmempool(const JSONRPCRequest& request)
+static UniValue getrawmempool(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 1)
throw std::runtime_error(
@@ -481,7 +492,7 @@ UniValue getrawmempool(const JSONRPCRequest& request)
return mempoolToJSON(fVerbose);
}
-UniValue getmempoolancestors(const JSONRPCRequest& request)
+static UniValue getmempoolancestors(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
throw std::runtime_error(
@@ -545,7 +556,7 @@ UniValue getmempoolancestors(const JSONRPCRequest& request)
}
}
-UniValue getmempooldescendants(const JSONRPCRequest& request)
+static UniValue getmempooldescendants(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
throw std::runtime_error(
@@ -609,7 +620,7 @@ UniValue getmempooldescendants(const JSONRPCRequest& request)
}
}
-UniValue getmempoolentry(const JSONRPCRequest& request)
+static UniValue getmempoolentry(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
@@ -642,7 +653,7 @@ UniValue getmempoolentry(const JSONRPCRequest& request)
return info;
}
-UniValue getblockhash(const JSONRPCRequest& request)
+static UniValue getblockhash(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
@@ -667,7 +678,7 @@ UniValue getblockhash(const JSONRPCRequest& request)
return pblockindex->GetBlockHash().GetHex();
}
-UniValue getblockheader(const JSONRPCRequest& request)
+static UniValue getblockheader(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
@@ -726,7 +737,7 @@ UniValue getblockheader(const JSONRPCRequest& request)
return blockheaderToJSON(pblockindex);
}
-UniValue getblock(const JSONRPCRequest& request)
+static UniValue getblock(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
@@ -888,7 +899,7 @@ static bool GetUTXOStats(CCoinsView *view, CCoinsStats &stats)
return true;
}
-UniValue pruneblockchain(const JSONRPCRequest& request)
+static UniValue pruneblockchain(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
@@ -929,7 +940,7 @@ UniValue pruneblockchain(const JSONRPCRequest& request)
else if (height > chainHeight)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Blockchain is shorter than the attempted prune height.");
else if (height > chainHeight - MIN_BLOCKS_TO_KEEP) {
- LogPrint(BCLog::RPC, "Attempt to prune blocks close to the tip. Retaining the minimum number of blocks.");
+ LogPrint(BCLog::RPC, "Attempt to prune blocks close to the tip. Retaining the minimum number of blocks.\n");
height = chainHeight - MIN_BLOCKS_TO_KEEP;
}
@@ -937,7 +948,7 @@ UniValue pruneblockchain(const JSONRPCRequest& request)
return uint64_t(height);
}
-UniValue gettxoutsetinfo(const JSONRPCRequest& request)
+static UniValue gettxoutsetinfo(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
@@ -947,9 +958,9 @@ UniValue gettxoutsetinfo(const JSONRPCRequest& request)
"\nResult:\n"
"{\n"
" \"height\":n, (numeric) The current block height (index)\n"
- " \"bestblock\": \"hex\", (string) the best block hash hex\n"
- " \"transactions\": n, (numeric) The number of transactions\n"
- " \"txouts\": n, (numeric) The number of output transactions\n"
+ " \"bestblock\": \"hex\", (string) The hash of the block at the tip of the chain\n"
+ " \"transactions\": n, (numeric) The number of transactions with unspent outputs\n"
+ " \"txouts\": n, (numeric) The number of unspent transaction outputs\n"
" \"bogosize\": n, (numeric) A meaningless metric for UTXO set size\n"
" \"hash_serialized_2\": \"hash\", (string) The serialized hash\n"
" \"disk_size\": n, (numeric) The estimated size of the chainstate on disk\n"
@@ -992,7 +1003,7 @@ UniValue gettxout(const JSONRPCRequest& request)
" Note that an unspent output that is spent in the mempool won't appear.\n"
"\nResult:\n"
"{\n"
- " \"bestblock\" : \"hash\", (string) the block hash\n"
+ " \"bestblock\": \"hash\", (string) The hash of the block at the tip of the chain\n"
" \"confirmations\" : n, (numeric) The number of confirmations\n"
" \"value\" : x.xxx, (numeric) The transaction value in " + CURRENCY_UNIT + "\n"
" \"scriptPubKey\" : { (json object)\n"
@@ -1058,7 +1069,7 @@ UniValue gettxout(const JSONRPCRequest& request)
return ret;
}
-UniValue verifychain(const JSONRPCRequest& request)
+static UniValue verifychain(const JSONRPCRequest& request)
{
int nCheckLevel = gArgs.GetArg("-checklevel", DEFAULT_CHECKLEVEL);
int nCheckDepth = gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS);
@@ -1148,7 +1159,7 @@ static UniValue BIP9SoftForkDesc(const Consensus::Params& consensusParams, Conse
return rv;
}
-void BIP9SoftForkDescPushBack(UniValue& bip9_softforks, const Consensus::Params& consensusParams, Consensus::DeploymentPos id)
+static void BIP9SoftForkDescPushBack(UniValue& bip9_softforks, const Consensus::Params& consensusParams, Consensus::DeploymentPos id)
{
// Deployments with timeout value of 0 are hidden.
// A timeout value of 0 guarantees a softfork will never be activated.
@@ -1274,7 +1285,7 @@ struct CompareBlocksByHeight
}
};
-UniValue getchaintips(const JSONRPCRequest& request)
+static UniValue getchaintips(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
@@ -1391,7 +1402,7 @@ UniValue mempoolInfoToJSON()
return ret;
}
-UniValue getmempoolinfo(const JSONRPCRequest& request)
+static UniValue getmempoolinfo(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
@@ -1414,7 +1425,7 @@ UniValue getmempoolinfo(const JSONRPCRequest& request)
return mempoolInfoToJSON();
}
-UniValue preciousblock(const JSONRPCRequest& request)
+static UniValue preciousblock(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
@@ -1452,7 +1463,7 @@ UniValue preciousblock(const JSONRPCRequest& request)
return NullUniValue;
}
-UniValue invalidateblock(const JSONRPCRequest& request)
+static UniValue invalidateblock(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
@@ -1491,7 +1502,7 @@ UniValue invalidateblock(const JSONRPCRequest& request)
return NullUniValue;
}
-UniValue reconsiderblock(const JSONRPCRequest& request)
+static UniValue reconsiderblock(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
@@ -1529,7 +1540,7 @@ UniValue reconsiderblock(const JSONRPCRequest& request)
return NullUniValue;
}
-UniValue getchaintxstats(const JSONRPCRequest& request)
+static UniValue getchaintxstats(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 2)
throw std::runtime_error(
@@ -1603,7 +1614,7 @@ UniValue getchaintxstats(const JSONRPCRequest& request)
return ret;
}
-UniValue savemempool(const JSONRPCRequest& request)
+static UniValue savemempool(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0) {
throw std::runtime_error(
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index 34c41b3b6b..475fe1e274 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -38,6 +38,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "sendtoaddress", 5 , "replaceable" },
{ "sendtoaddress", 6 , "conf_target" },
{ "settxfee", 0, "amount" },
+ { "sethdseed", 0, "newkeypool" },
{ "getreceivedbyaddress", 1, "minconf" },
{ "getreceivedbyaccount", 1, "minconf" },
{ "getreceivedbylabel", 1, "minconf" },
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 9a7c4b8e6d..45ec501b9d 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -44,7 +44,7 @@ unsigned int ParseConfirmTarget(const UniValue& value)
* or from the last difficulty change if 'lookup' is nonpositive.
* If 'height' is nonnegative, compute the estimate at the time when a given block was found.
*/
-UniValue GetNetworkHashPS(int lookup, int height) {
+static UniValue GetNetworkHashPS(int lookup, int height) {
CBlockIndex *pb = chainActive.Tip();
if (height >= 0 && height < chainActive.Height())
@@ -81,7 +81,7 @@ UniValue GetNetworkHashPS(int lookup, int height) {
return workDiff.getdouble() / timeDiff;
}
-UniValue getnetworkhashps(const JSONRPCRequest& request)
+static UniValue getnetworkhashps(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 2)
throw std::runtime_error(
@@ -116,7 +116,7 @@ UniValue generateBlocks(std::shared_ptr<CReserveScript> coinbaseScript, int nGen
}
unsigned int nExtraNonce = 0;
UniValue blockHashes(UniValue::VARR);
- while (nHeight < nHeightEnd)
+ while (nHeight < nHeightEnd && !ShutdownRequested())
{
std::unique_ptr<CBlockTemplate> pblocktemplate(BlockAssembler(Params()).CreateNewBlock(coinbaseScript->reserveScript));
if (!pblocktemplate.get())
@@ -151,7 +151,7 @@ UniValue generateBlocks(std::shared_ptr<CReserveScript> coinbaseScript, int nGen
return blockHashes;
}
-UniValue generatetoaddress(const JSONRPCRequest& request)
+static UniValue generatetoaddress(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 2 || request.params.size() > 3)
throw std::runtime_error(
@@ -185,7 +185,7 @@ UniValue generatetoaddress(const JSONRPCRequest& request)
return generateBlocks(coinbaseScript, nGenerate, nMaxTries, false);
}
-UniValue getmininginfo(const JSONRPCRequest& request)
+static UniValue getmininginfo(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
@@ -224,7 +224,7 @@ UniValue getmininginfo(const JSONRPCRequest& request)
// NOTE: Unlike wallet RPC (which use BTC values), mining RPCs follow GBT (BIP 22) in using satoshi amounts
-UniValue prioritisetransaction(const JSONRPCRequest& request)
+static UniValue prioritisetransaction(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 3)
throw std::runtime_error(
@@ -278,7 +278,7 @@ static UniValue BIP22ValidationResult(const CValidationState& state)
return "valid?";
}
-std::string gbt_vb_name(const Consensus::DeploymentPos pos) {
+static std::string gbt_vb_name(const Consensus::DeploymentPos pos) {
const struct VBDeploymentInfo& vbinfo = VersionBitsDeploymentInfo[pos];
std::string s = vbinfo.name;
if (!vbinfo.gbt_force) {
@@ -287,7 +287,7 @@ std::string gbt_vb_name(const Consensus::DeploymentPos pos) {
return s;
}
-UniValue getblocktemplate(const JSONRPCRequest& request)
+static UniValue getblocktemplate(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 1)
throw std::runtime_error(
@@ -694,7 +694,7 @@ protected:
}
};
-UniValue submitblock(const JSONRPCRequest& request)
+static UniValue submitblock(const JSONRPCRequest& request)
{
// We allow 2 arguments for compliance with BIP22. Argument 2 is ignored.
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
@@ -764,13 +764,13 @@ UniValue submitblock(const JSONRPCRequest& request)
return BIP22ValidationResult(sc.state);
}
-UniValue estimatefee(const JSONRPCRequest& request)
+static UniValue estimatefee(const JSONRPCRequest& request)
{
throw JSONRPCError(RPC_METHOD_DEPRECATED, "estimatefee was removed in v0.17.\n"
"Clients should use estimatesmartfee.");
}
-UniValue estimatesmartfee(const JSONRPCRequest& request)
+static UniValue estimatesmartfee(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
@@ -831,7 +831,7 @@ UniValue estimatesmartfee(const JSONRPCRequest& request)
return result;
}
-UniValue estimaterawfee(const JSONRPCRequest& request)
+static UniValue estimaterawfee(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index 49e865a64a..6772784d3d 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -33,7 +33,7 @@
#include <univalue.h>
-UniValue validateaddress(const JSONRPCRequest& request)
+static UniValue validateaddress(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
@@ -69,7 +69,7 @@ UniValue validateaddress(const JSONRPCRequest& request)
{
#ifdef ENABLE_WALLET
- if (!::vpwallets.empty() && IsDeprecatedRPCEnabled("validateaddress")) {
+ if (HasWallets() && IsDeprecatedRPCEnabled("validateaddress")) {
ret.pushKVs(getaddressinfo(request));
}
#endif
@@ -90,7 +90,7 @@ UniValue validateaddress(const JSONRPCRequest& request)
// Needed even with !ENABLE_WALLET, to pass (ignored) pointers around
class CWallet;
-UniValue createmultisig(const JSONRPCRequest& request)
+static UniValue createmultisig(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 2 || request.params.size() > 2)
{
@@ -145,7 +145,7 @@ UniValue createmultisig(const JSONRPCRequest& request)
return result;
}
-UniValue verifymessage(const JSONRPCRequest& request)
+static UniValue verifymessage(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 3)
throw std::runtime_error(
@@ -201,7 +201,7 @@ UniValue verifymessage(const JSONRPCRequest& request)
return (pubkey.GetID() == *keyID);
}
-UniValue signmessagewithprivkey(const JSONRPCRequest& request)
+static UniValue signmessagewithprivkey(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 2)
throw std::runtime_error(
@@ -240,7 +240,7 @@ UniValue signmessagewithprivkey(const JSONRPCRequest& request)
return EncodeBase64(vchSig.data(), vchSig.size());
}
-UniValue setmocktime(const JSONRPCRequest& request)
+static UniValue setmocktime(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
@@ -299,7 +299,7 @@ static std::string RPCMallocInfo()
}
#endif
-UniValue getmemoryinfo(const JSONRPCRequest& request)
+static UniValue getmemoryinfo(const JSONRPCRequest& request)
{
/* Please, avoid using the word "pool" here in the RPC interface or help,
* as users will undoubtedly confuse it with the other "memory pool"
@@ -346,21 +346,22 @@ UniValue getmemoryinfo(const JSONRPCRequest& request)
}
}
-uint32_t getCategoryMask(UniValue cats) {
+static void EnableOrDisableLogCategories(UniValue cats, bool enable) {
cats = cats.get_array();
- uint32_t mask = 0;
for (unsigned int i = 0; i < cats.size(); ++i) {
- uint32_t flag = 0;
std::string cat = cats[i].get_str();
- if (!GetLogCategory(&flag, &cat)) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "unknown logging category " + cat);
+
+ bool success;
+ if (enable) {
+ success = g_logger->EnableCategory(cat);
+ } else {
+ success = g_logger->DisableCategory(cat);
}
- if (flag == BCLog::NONE) {
- return 0;
+
+ if (!success) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "unknown logging category " + cat);
}
- mask |= flag;
}
- return mask;
}
UniValue logging(const JSONRPCRequest& request)
@@ -399,25 +400,25 @@ UniValue logging(const JSONRPCRequest& request)
);
}
- uint32_t originalLogCategories = logCategories;
+ uint32_t original_log_categories = g_logger->GetCategoryMask();
if (request.params[0].isArray()) {
- logCategories |= getCategoryMask(request.params[0]);
+ EnableOrDisableLogCategories(request.params[0], true);
}
-
if (request.params[1].isArray()) {
- logCategories &= ~getCategoryMask(request.params[1]);
+ EnableOrDisableLogCategories(request.params[1], false);
}
+ uint32_t updated_log_categories = g_logger->GetCategoryMask();
+ uint32_t changed_log_categories = original_log_categories ^ updated_log_categories;
// Update libevent logging if BCLog::LIBEVENT has changed.
// If the library version doesn't allow it, UpdateHTTPServerLogging() returns false,
// in which case we should clear the BCLog::LIBEVENT flag.
// Throw an error if the user has explicitly asked to change only the libevent
// flag and it failed.
- uint32_t changedLogCategories = originalLogCategories ^ logCategories;
- if (changedLogCategories & BCLog::LIBEVENT) {
- if (!UpdateHTTPServerLogging(logCategories & BCLog::LIBEVENT)) {
- logCategories &= ~BCLog::LIBEVENT;
- if (changedLogCategories == BCLog::LIBEVENT) {
+ if (changed_log_categories & BCLog::LIBEVENT) {
+ if (!UpdateHTTPServerLogging(g_logger->WillLogCategory(BCLog::LIBEVENT))) {
+ g_logger->DisableCategory(BCLog::LIBEVENT);
+ if (changed_log_categories == BCLog::LIBEVENT) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "libevent logging cannot be updated when using libevent before v2.1.1.");
}
}
@@ -432,7 +433,7 @@ UniValue logging(const JSONRPCRequest& request)
return result;
}
-UniValue echo(const JSONRPCRequest& request)
+static UniValue echo(const JSONRPCRequest& request)
{
if (request.fHelp)
throw std::runtime_error(
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index fee2b765ba..1530d8578b 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -23,7 +23,7 @@
#include <univalue.h>
-UniValue getconnectioncount(const JSONRPCRequest& request)
+static UniValue getconnectioncount(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
@@ -42,7 +42,7 @@ UniValue getconnectioncount(const JSONRPCRequest& request)
return (int)g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL);
}
-UniValue ping(const JSONRPCRequest& request)
+static UniValue ping(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
@@ -65,7 +65,7 @@ UniValue ping(const JSONRPCRequest& request)
return NullUniValue;
}
-UniValue getpeerinfo(const JSONRPCRequest& request)
+static UniValue getpeerinfo(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
@@ -190,7 +190,7 @@ UniValue getpeerinfo(const JSONRPCRequest& request)
return ret;
}
-UniValue addnode(const JSONRPCRequest& request)
+static UniValue addnode(const JSONRPCRequest& request)
{
std::string strCommand;
if (!request.params[1].isNull())
@@ -237,7 +237,7 @@ UniValue addnode(const JSONRPCRequest& request)
return NullUniValue;
}
-UniValue disconnectnode(const JSONRPCRequest& request)
+static UniValue disconnectnode(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() == 0 || request.params.size() >= 3)
throw std::runtime_error(
@@ -280,7 +280,7 @@ UniValue disconnectnode(const JSONRPCRequest& request)
return NullUniValue;
}
-UniValue getaddednodeinfo(const JSONRPCRequest& request)
+static UniValue getaddednodeinfo(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 1)
throw std::runtime_error(
@@ -347,7 +347,7 @@ UniValue getaddednodeinfo(const JSONRPCRequest& request)
return ret;
}
-UniValue getnettotals(const JSONRPCRequest& request)
+static UniValue getnettotals(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 0)
throw std::runtime_error(
@@ -413,7 +413,7 @@ static UniValue GetNetworksInfo()
return networks;
}
-UniValue getnetworkinfo(const JSONRPCRequest& request)
+static UniValue getnetworkinfo(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
@@ -489,7 +489,7 @@ UniValue getnetworkinfo(const JSONRPCRequest& request)
return obj;
}
-UniValue setban(const JSONRPCRequest& request)
+static UniValue setban(const JSONRPCRequest& request)
{
std::string strCommand;
if (!request.params[1].isNull())
@@ -553,7 +553,7 @@ UniValue setban(const JSONRPCRequest& request)
return NullUniValue;
}
-UniValue listbanned(const JSONRPCRequest& request)
+static UniValue listbanned(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
@@ -586,7 +586,7 @@ UniValue listbanned(const JSONRPCRequest& request)
return bannedAddresses;
}
-UniValue clearbanned(const JSONRPCRequest& request)
+static UniValue clearbanned(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
@@ -604,7 +604,7 @@ UniValue clearbanned(const JSONRPCRequest& request)
return NullUniValue;
}
-UniValue setnetworkactive(const JSONRPCRequest& request)
+static UniValue setnetworkactive(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
diff --git a/src/rpc/protocol.h b/src/rpc/protocol.h
index 4a265735d2..6954aed252 100644
--- a/src/rpc/protocol.h
+++ b/src/rpc/protocol.h
@@ -46,7 +46,6 @@ enum RPCErrorCode
//! General application defined errors
RPC_MISC_ERROR = -1, //!< std::exception thrown in command handling
- RPC_FORBIDDEN_BY_SAFE_MODE = -2, //!< Server is in safe mode, and command is not allowed in safe mode
RPC_TYPE_ERROR = -3, //!< Unexpected type was passed as parameter
RPC_INVALID_ADDRESS_OR_KEY = -5, //!< Invalid address or key
RPC_OUT_OF_MEMORY = -7, //!< Ran out of memory during operation
@@ -88,6 +87,9 @@ enum RPCErrorCode
//! Backwards compatible aliases
RPC_WALLET_INVALID_ACCOUNT_NAME = RPC_WALLET_INVALID_LABEL_NAME,
+
+ //! Unused reserved codes, kept around for backwards compatibility. Do not reuse.
+ RPC_FORBIDDEN_BY_SAFE_MODE = -2, //!< Server is in safe mode, and command is not allowed in safe mode
};
UniValue JSONRPCRequestObj(const std::string& strMethod, const UniValue& params, const UniValue& id);
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 543e4fc358..c5185ca599 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -7,6 +7,7 @@
#include <coins.h>
#include <consensus/validation.h>
#include <core_io.h>
+#include <index/txindex.h>
#include <init.h>
#include <keystore.h>
#include <validation.h>
@@ -18,7 +19,6 @@
#include <policy/rbf.h>
#include <primitives/transaction.h>
#include <rpc/rawtransaction.h>
-#include <rpc/safemode.h>
#include <rpc/server.h>
#include <script/script.h>
#include <script/script_error.h>
@@ -37,7 +37,7 @@
#include <univalue.h>
-void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
+static void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
{
// Call into TxToUniv() in bitcoin-common to decode the transaction hex.
//
@@ -47,6 +47,8 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
TxToUniv(tx, uint256(), entry, true, RPCSerializationFlags());
if (!hashBlock.IsNull()) {
+ LOCK(cs_main);
+
entry.pushKV("blockhash", hashBlock.GetHex());
CBlockIndex* pindex = LookupBlockIndex(hashBlock);
if (pindex) {
@@ -61,7 +63,7 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
}
}
-UniValue getrawtransaction(const JSONRPCRequest& request)
+static UniValue getrawtransaction(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
throw std::runtime_error(
@@ -94,6 +96,7 @@ UniValue getrawtransaction(const JSONRPCRequest& request)
" \"hash\" : \"id\", (string) The transaction hash (differs from txid for witness transactions)\n"
" \"size\" : n, (numeric) The serialized transaction size\n"
" \"vsize\" : n, (numeric) The virtual transaction size (differs from size for witness transactions)\n"
+ " \"weight\" : n, (numeric) The transaction's weight (between vsize*4-3 and vsize*4)\n"
" \"version\" : n, (numeric) The version\n"
" \"locktime\" : ttt, (numeric) The lock time\n"
" \"vin\" : [ (array of json objects)\n"
@@ -140,8 +143,6 @@ UniValue getrawtransaction(const JSONRPCRequest& request)
+ HelpExampleCli("getrawtransaction", "\"mytxid\" true \"myblockhash\"")
);
- LOCK(cs_main);
-
bool in_active_chain = true;
uint256 hash = ParseHashV(request.params[0], "parameter 1");
CBlockIndex* blockindex = nullptr;
@@ -158,6 +159,8 @@ UniValue getrawtransaction(const JSONRPCRequest& request)
}
if (!request.params[2].isNull()) {
+ LOCK(cs_main);
+
uint256 blockhash = ParseHashV(request.params[2], "parameter 3");
blockindex = LookupBlockIndex(blockhash);
if (!blockindex) {
@@ -166,6 +169,11 @@ UniValue getrawtransaction(const JSONRPCRequest& request)
in_active_chain = chainActive.Contains(blockindex);
}
+ bool f_txindex_ready = false;
+ if (g_txindex && !blockindex) {
+ f_txindex_ready = g_txindex->BlockUntilSyncedToCurrentChain();
+ }
+
CTransactionRef tx;
uint256 hash_block;
if (!GetTransaction(hash, tx, Params().GetConsensus(), hash_block, true, blockindex)) {
@@ -175,10 +183,12 @@ UniValue getrawtransaction(const JSONRPCRequest& request)
throw JSONRPCError(RPC_MISC_ERROR, "Block not available");
}
errmsg = "No such transaction found in the provided block";
+ } else if (!g_txindex) {
+ errmsg = "No such mempool transaction. Use -txindex to enable blockchain transaction queries";
+ } else if (!f_txindex_ready) {
+ errmsg = "No such mempool transaction. Blockchain transactions are still in the process of being indexed";
} else {
- errmsg = fTxIndex
- ? "No such mempool or blockchain transaction"
- : "No such mempool transaction. Use -txindex to enable blockchain transaction queries";
+ errmsg = "No such mempool or blockchain transaction";
}
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, errmsg + ". Use gettransaction for wallet transactions.");
}
@@ -193,7 +203,7 @@ UniValue getrawtransaction(const JSONRPCRequest& request)
return result;
}
-UniValue gettxoutproof(const JSONRPCRequest& request)
+static UniValue gettxoutproof(const JSONRPCRequest& request)
{
if (request.fHelp || (request.params.size() != 1 && request.params.size() != 2))
throw std::runtime_error(
@@ -228,19 +238,18 @@ UniValue gettxoutproof(const JSONRPCRequest& request)
oneTxid = hash;
}
- LOCK(cs_main);
-
CBlockIndex* pblockindex = nullptr;
-
uint256 hashBlock;
- if (!request.params[1].isNull())
- {
+ if (!request.params[1].isNull()) {
+ LOCK(cs_main);
hashBlock = uint256S(request.params[1].get_str());
pblockindex = LookupBlockIndex(hashBlock);
if (!pblockindex) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
} else {
+ LOCK(cs_main);
+
// Loop through txids and try to find which block they're in. Exit loop once a block is found.
for (const auto& tx : setTxids) {
const Coin& coin = AccessByTxid(*pcoinsTip, tx);
@@ -251,6 +260,14 @@ UniValue gettxoutproof(const JSONRPCRequest& request)
}
}
+
+ // Allow txindex to catch up if we need to query it and before we acquire cs_main.
+ if (g_txindex && !pblockindex) {
+ g_txindex->BlockUntilSyncedToCurrentChain();
+ }
+
+ LOCK(cs_main);
+
if (pblockindex == nullptr)
{
CTransactionRef tx;
@@ -280,7 +297,7 @@ UniValue gettxoutproof(const JSONRPCRequest& request)
return strHex;
}
-UniValue verifytxoutproof(const JSONRPCRequest& request)
+static UniValue verifytxoutproof(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
@@ -316,7 +333,7 @@ UniValue verifytxoutproof(const JSONRPCRequest& request)
return res;
}
-UniValue createrawtransaction(const JSONRPCRequest& request)
+static UniValue createrawtransaction(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 2 || request.params.size() > 4) {
throw std::runtime_error(
@@ -476,7 +493,7 @@ UniValue createrawtransaction(const JSONRPCRequest& request)
return EncodeHexTx(rawTx);
}
-UniValue decoderawtransaction(const JSONRPCRequest& request)
+static UniValue decoderawtransaction(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
@@ -494,6 +511,7 @@ UniValue decoderawtransaction(const JSONRPCRequest& request)
" \"hash\" : \"id\", (string) The transaction hash (differs from txid for witness transactions)\n"
" \"size\" : n, (numeric) The transaction size\n"
" \"vsize\" : n, (numeric) The virtual transaction size (differs from size for witness transactions)\n"
+ " \"weight\" : n, (numeric) The transaction's weight (between vsize*4 - 3 and vsize*4)\n"
" \"version\" : n, (numeric) The version\n"
" \"locktime\" : ttt, (numeric) The lock time\n"
" \"vin\" : [ (array of json objects)\n"
@@ -551,7 +569,7 @@ UniValue decoderawtransaction(const JSONRPCRequest& request)
return result;
}
-UniValue decodescript(const JSONRPCRequest& request)
+static UniValue decodescript(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
@@ -595,6 +613,38 @@ UniValue decodescript(const JSONRPCRequest& request)
// P2SH cannot be wrapped in a P2SH. If this script is already a P2SH,
// don't return the address for a P2SH of the P2SH.
r.pushKV("p2sh", EncodeDestination(CScriptID(script)));
+ // P2SH and witness programs cannot be wrapped in P2WSH, if this script
+ // is a witness program, don't return addresses for a segwit programs.
+ if (type.get_str() == "pubkey" || type.get_str() == "pubkeyhash" || type.get_str() == "multisig" || type.get_str() == "nonstandard") {
+ txnouttype which_type;
+ std::vector<std::vector<unsigned char>> solutions_data;
+ Solver(script, which_type, solutions_data);
+ // Uncompressed pubkeys cannot be used with segwit checksigs.
+ // If the script contains an uncompressed pubkey, skip encoding of a segwit program.
+ if ((which_type == TX_PUBKEY) || (which_type == TX_MULTISIG)) {
+ for (const auto& solution : solutions_data) {
+ if ((solution.size() != 1) && !CPubKey(solution).IsCompressed()) {
+ return r;
+ }
+ }
+ }
+ UniValue sr(UniValue::VOBJ);
+ CScript segwitScr;
+ if (which_type == TX_PUBKEY) {
+ segwitScr = GetScriptForDestination(WitnessV0KeyHash(Hash160(solutions_data[0].begin(), solutions_data[0].end())));
+ } else if (which_type == TX_PUBKEYHASH) {
+ segwitScr = GetScriptForDestination(WitnessV0KeyHash(solutions_data[0]));
+ } else {
+ // Scripts that are not fit for P2WPKH are encoded as P2WSH.
+ // Newer segwit program versions should be considered when then become available.
+ uint256 scriptHash;
+ CSHA256().Write(script.data(), script.size()).Finalize(scriptHash.begin());
+ segwitScr = GetScriptForDestination(WitnessV0ScriptHash(scriptHash));
+ }
+ ScriptPubKeyToUniv(segwitScr, sr, true);
+ sr.pushKV("p2sh-segwit", EncodeDestination(CScriptID(segwitScr)));
+ r.pushKV("segwit", sr);
+ }
}
return r;
@@ -617,7 +667,7 @@ static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::
vErrorsRet.push_back(entry);
}
-UniValue combinerawtransaction(const JSONRPCRequest& request)
+static UniValue combinerawtransaction(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
@@ -856,7 +906,7 @@ UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival
return result;
}
-UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
+static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 2 || request.params.size() > 4)
throw std::runtime_error(
@@ -1034,7 +1084,7 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
}
}
-UniValue sendrawtransaction(const JSONRPCRequest& request)
+static UniValue sendrawtransaction(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
@@ -1057,8 +1107,6 @@ UniValue sendrawtransaction(const JSONRPCRequest& request)
+ HelpExampleRpc("sendrawtransaction", "\"signedhex\"")
);
- ObserveSafeMode();
-
std::promise<void> promise;
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL});
@@ -1131,7 +1179,7 @@ UniValue sendrawtransaction(const JSONRPCRequest& request)
return hashTx.GetHex();
}
-UniValue testmempoolaccept(const JSONRPCRequest& request)
+static UniValue testmempoolaccept(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
throw std::runtime_error(
@@ -1166,8 +1214,6 @@ UniValue testmempoolaccept(const JSONRPCRequest& request)
);
}
- 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");
diff --git a/src/rpc/safemode.cpp b/src/rpc/safemode.cpp
deleted file mode 100644
index 9f3a9d30b8..0000000000
--- a/src/rpc/safemode.cpp
+++ /dev/null
@@ -1,14 +0,0 @@
-#include <rpc/safemode.h>
-
-#include <rpc/protocol.h>
-#include <util.h>
-#include <warnings.h>
-
-void ObserveSafeMode()
-{
- std::string warning = GetWarnings("rpc");
- if (warning != "" && !gArgs.GetBoolArg("-disablesafemode", DEFAULT_DISABLE_SAFEMODE)) {
- throw JSONRPCError(RPC_FORBIDDEN_BY_SAFE_MODE, std::string("Safe mode: ") + warning);
- }
-}
-
diff --git a/src/rpc/safemode.h b/src/rpc/safemode.h
deleted file mode 100644
index 8466d6b2f9..0000000000
--- a/src/rpc/safemode.h
+++ /dev/null
@@ -1,12 +0,0 @@
-// 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_RPC_SAFEMODE_H
-#define BITCOIN_RPC_SAFEMODE_H
-
-static const bool DEFAULT_DISABLE_SAFEMODE = true;
-
-void ObserveSafeMode();
-
-#endif // BITCOIN_RPC_SAFEMODE_H
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
index c7c3b1f0d3..10040b1255 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -23,10 +23,10 @@
#include <memory> // for unique_ptr
#include <unordered_map>
-static bool fRPCRunning = false;
-static bool fRPCInWarmup = true;
-static std::string rpcWarmupStatus("RPC server started");
static CCriticalSection cs_rpcWarmup;
+static bool fRPCRunning = false;
+static bool fRPCInWarmup GUARDED_BY(cs_rpcWarmup) = true;
+static std::string rpcWarmupStatus GUARDED_BY(cs_rpcWarmup) = "RPC server started";
/* Timer-creating functions */
static RPCTimerInterface* timerInterface = nullptr;
/* Map of name to timer. */
@@ -239,7 +239,7 @@ UniValue stop(const JSONRPCRequest& jsonRequest)
return "Bitcoin server stopping";
}
-UniValue uptime(const JSONRPCRequest& jsonRequest)
+static UniValue uptime(const JSONRPCRequest& jsonRequest)
{
if (jsonRequest.fHelp || jsonRequest.params.size() > 1)
throw std::runtime_error(
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index 338e07e24e..13f41a7cbf 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -250,6 +250,34 @@ bool static CheckMinimalPush(const valtype& data, opcodetype opcode) {
return true;
}
+int FindAndDelete(CScript& script, const CScript& b)
+{
+ int nFound = 0;
+ if (b.empty())
+ return nFound;
+ CScript result;
+ CScript::const_iterator pc = script.begin(), pc2 = script.begin(), end = script.end();
+ opcodetype opcode;
+ do
+ {
+ result.insert(result.end(), pc2, pc);
+ while (static_cast<size_t>(end - pc) >= b.size() && std::equal(b.begin(), b.end(), pc))
+ {
+ pc = pc + b.size();
+ ++nFound;
+ }
+ pc2 = pc;
+ }
+ while (script.GetOp(pc, opcode));
+
+ if (nFound > 0) {
+ result.insert(result.end(), pc2, end);
+ script = std::move(result);
+ }
+
+ return nFound;
+}
+
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror)
{
static const CScriptNum bnZero(0);
@@ -308,6 +336,10 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
opcode == OP_RSHIFT)
return set_error(serror, SCRIPT_ERR_DISABLED_OPCODE); // Disabled opcodes.
+ // With SCRIPT_VERIFY_CONST_SCRIPTCODE, OP_CODESEPARATOR in non-segwit script is rejected even in an unexecuted branch
+ if (opcode == OP_CODESEPARATOR && sigversion == SigVersion::BASE && (flags & SCRIPT_VERIFY_CONST_SCRIPTCODE))
+ return set_error(serror, SCRIPT_ERR_OP_CODESEPARATOR);
+
if (fExec && 0 <= opcode && opcode <= OP_PUSHDATA4) {
if (fRequireMinimal && !CheckMinimalPush(vchPushValue, opcode)) {
return set_error(serror, SCRIPT_ERR_MINIMALDATA);
@@ -871,6 +903,9 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
case OP_CODESEPARATOR:
{
+ // If SCRIPT_VERIFY_CONST_SCRIPTCODE flag is set, use of OP_CODESEPARATOR is rejected in pre-segwit
+ // script, even in an unexecuted branch (this is checked above the opcode case statement).
+
// Hash starts after the code separator
pbegincodehash = pc;
}
@@ -891,7 +926,9 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
// Drop the signature in pre-segwit scripts but not segwit scripts
if (sigversion == SigVersion::BASE) {
- scriptCode.FindAndDelete(CScript(vchSig));
+ int found = FindAndDelete(scriptCode, CScript(vchSig));
+ if (found > 0 && (flags & SCRIPT_VERIFY_CONST_SCRIPTCODE))
+ return set_error(serror, SCRIPT_ERR_SIG_FINDANDDELETE);
}
if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, sigversion, serror)) {
@@ -955,7 +992,9 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
{
valtype& vchSig = stacktop(-isig-k);
if (sigversion == SigVersion::BASE) {
- scriptCode.FindAndDelete(CScript(vchSig));
+ int found = FindAndDelete(scriptCode, CScript(vchSig));
+ if (found > 0 && (flags & SCRIPT_VERIFY_CONST_SCRIPTCODE))
+ return set_error(serror, SCRIPT_ERR_SIG_FINDANDDELETE);
}
}
diff --git a/src/script/interpreter.h b/src/script/interpreter.h
index 601a4a866d..2800473a68 100644
--- a/src/script/interpreter.h
+++ b/src/script/interpreter.h
@@ -111,6 +111,10 @@ enum
// Public keys in segregated witness scripts must be compressed
//
SCRIPT_VERIFY_WITNESS_PUBKEYTYPE = (1U << 15),
+
+ // Making OP_CODESEPARATOR and FindAndDelete fail any non-segwit scripts
+ //
+ SCRIPT_VERIFY_CONST_SCRIPTCODE = (1U << 16),
};
bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned int flags, ScriptError* serror);
@@ -189,4 +193,6 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C
size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags);
+int FindAndDelete(CScript& script, const CScript& b);
+
#endif // BITCOIN_SCRIPT_INTERPRETER_H
diff --git a/src/script/ismine.cpp b/src/script/ismine.cpp
index b826bcfe20..fefa02fdef 100644
--- a/src/script/ismine.cpp
+++ b/src/script/ismine.cpp
@@ -13,34 +13,36 @@
typedef std::vector<unsigned char> valtype;
-static bool HaveKeys(const std::vector<valtype>& pubkeys, const CKeyStore& keystore)
-{
- for (const valtype& pubkey : pubkeys) {
- CKeyID keyID = CPubKey(pubkey).GetID();
- if (!keystore.HaveKey(keyID)) return false;
- }
- return true;
-}
+namespace {
-isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey, SigVersion sigversion)
+/**
+ * This is an enum that tracks the execution context of a script, similar to
+ * SigVersion in script/interpreter. It is separate however because we want to
+ * distinguish between top-level scriptPubKey execution and P2SH redeemScript
+ * execution (a distinction that has no impact on consensus rules).
+ */
+enum class IsMineSigVersion
{
- bool isInvalid = false;
- return IsMine(keystore, scriptPubKey, isInvalid, sigversion);
-}
+ TOP = 0, //! scriptPubKey execution
+ P2SH = 1, //! P2SH redeemScript
+ WITNESS_V0 = 2 //! P2WSH witness script execution
+};
-isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest, SigVersion sigversion)
+bool PermitsUncompressed(IsMineSigVersion sigversion)
{
- bool isInvalid = false;
- return IsMine(keystore, dest, isInvalid, sigversion);
+ return sigversion == IsMineSigVersion::TOP || sigversion == IsMineSigVersion::P2SH;
}
-isminetype IsMine(const CKeyStore &keystore, const CTxDestination& dest, bool& isInvalid, SigVersion sigversion)
+bool HaveKeys(const std::vector<valtype>& pubkeys, const CKeyStore& keystore)
{
- CScript script = GetScriptForDestination(dest);
- return IsMine(keystore, script, isInvalid, sigversion);
+ for (const valtype& pubkey : pubkeys) {
+ CKeyID keyID = CPubKey(pubkey).GetID();
+ if (!keystore.HaveKey(keyID)) return false;
+ }
+ return true;
}
-isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool& isInvalid, SigVersion sigversion)
+isminetype IsMineInner(const CKeyStore& keystore, const CScript& scriptPubKey, bool& isInvalid, IsMineSigVersion sigversion)
{
isInvalid = false;
@@ -61,7 +63,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 (!PermitsUncompressed(sigversion) && vSolutions[0].size() != 33) {
isInvalid = true;
return ISMINE_NO;
}
@@ -70,20 +72,20 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool&
break;
case TX_WITNESS_V0_KEYHASH:
{
- if (!keystore.HaveCScript(CScriptID(CScript() << OP_0 << vSolutions[0]))) {
+ if (sigversion == IsMineSigVersion::TOP && !keystore.HaveCScript(CScriptID(CScript() << OP_0 << vSolutions[0]))) {
// We do not support bare witness outputs unless the P2SH version of it would be
// acceptable as well. This protects against matching before segwit activates.
// This also applies to the P2WSH case.
break;
}
- isminetype ret = ::IsMine(keystore, GetScriptForDestination(CKeyID(uint160(vSolutions[0]))), isInvalid, SigVersion::WITNESS_V0);
+ isminetype ret = IsMineInner(keystore, GetScriptForDestination(CKeyID(uint160(vSolutions[0]))), isInvalid, IsMineSigVersion::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 (!PermitsUncompressed(sigversion)) {
CPubKey pubkey;
if (keystore.GetPubKey(keyID, pubkey) && !pubkey.IsCompressed()) {
isInvalid = true;
@@ -98,7 +100,7 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool&
CScriptID scriptID = CScriptID(uint160(vSolutions[0]));
CScript subscript;
if (keystore.GetCScript(scriptID, subscript)) {
- isminetype ret = IsMine(keystore, subscript, isInvalid);
+ isminetype ret = IsMineInner(keystore, subscript, isInvalid, IsMineSigVersion::P2SH);
if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid))
return ret;
}
@@ -106,7 +108,7 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool&
}
case TX_WITNESS_V0_SCRIPTHASH:
{
- if (!keystore.HaveCScript(CScriptID(CScript() << OP_0 << vSolutions[0]))) {
+ if (sigversion == IsMineSigVersion::TOP && !keystore.HaveCScript(CScriptID(CScript() << OP_0 << vSolutions[0]))) {
break;
}
uint160 hash;
@@ -114,7 +116,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 = IsMineInner(keystore, subscript, isInvalid, IsMineSigVersion::WITNESS_V0);
if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid))
return ret;
}
@@ -123,13 +125,16 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool&
case TX_MULTISIG:
{
+ // Never treat bare multisig outputs as ours (they can still be made watchonly-though)
+ if (sigversion == IsMineSigVersion::TOP) break;
+
// Only consider transactions "mine" if we own ALL the
// keys involved. Multi-signature transactions that are
// partially owned (somebody else has a key that can spend
// 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 (!PermitsUncompressed(sigversion)) {
for (size_t i = 0; i < keys.size(); i++) {
if (keys[i].size() != 33) {
isInvalid = true;
@@ -150,3 +155,22 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool&
}
return ISMINE_NO;
}
+
+} // namespace
+
+isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey, bool& isInvalid)
+{
+ return IsMineInner(keystore, scriptPubKey, isInvalid, IsMineSigVersion::TOP);
+}
+
+isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey)
+{
+ bool isInvalid = false;
+ return IsMine(keystore, scriptPubKey, isInvalid);
+}
+
+isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest)
+{
+ CScript script = GetScriptForDestination(dest);
+ return IsMine(keystore, script);
+}
diff --git a/src/script/ismine.h b/src/script/ismine.h
index f93a66e35a..8573bdfbd2 100644
--- a/src/script/ismine.h
+++ b/src/script/ismine.h
@@ -33,9 +33,8 @@ typedef uint8_t isminefilter;
* 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
*/
-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);
+isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey);
+isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest);
#endif // BITCOIN_SCRIPT_ISMINE_H
diff --git a/src/script/script.cpp b/src/script/script.cpp
index 65e5405ebd..7f25d915a8 100644
--- a/src/script/script.cpp
+++ b/src/script/script.cpp
@@ -280,3 +280,55 @@ bool CScript::HasValidOps() const
}
return true;
}
+
+bool GetScriptOp(CScriptBase::const_iterator& pc, CScriptBase::const_iterator end, opcodetype& opcodeRet, std::vector<unsigned char>* pvchRet)
+{
+ opcodeRet = OP_INVALIDOPCODE;
+ if (pvchRet)
+ pvchRet->clear();
+ if (pc >= end)
+ return false;
+
+ // Read instruction
+ if (end - pc < 1)
+ return false;
+ unsigned int opcode = *pc++;
+
+ // Immediate operand
+ if (opcode <= OP_PUSHDATA4)
+ {
+ unsigned int nSize = 0;
+ if (opcode < OP_PUSHDATA1)
+ {
+ nSize = opcode;
+ }
+ else if (opcode == OP_PUSHDATA1)
+ {
+ if (end - pc < 1)
+ return false;
+ nSize = *pc++;
+ }
+ else if (opcode == OP_PUSHDATA2)
+ {
+ if (end - pc < 2)
+ return false;
+ nSize = ReadLE16(&pc[0]);
+ pc += 2;
+ }
+ else if (opcode == OP_PUSHDATA4)
+ {
+ if (end - pc < 4)
+ return false;
+ nSize = ReadLE32(&pc[0]);
+ pc += 4;
+ }
+ if (end - pc < 0 || (unsigned int)(end - pc) < nSize)
+ return false;
+ if (pvchRet)
+ pvchRet->assign(pc, pc + nSize);
+ pc += nSize;
+ }
+
+ opcodeRet = static_cast<opcodetype>(opcode);
+ return true;
+}
diff --git a/src/script/script.h b/src/script/script.h
index 59ceff247c..d8b7c06013 100644
--- a/src/script/script.h
+++ b/src/script/script.h
@@ -385,6 +385,8 @@ private:
*/
typedef prevector<28, unsigned char> CScriptBase;
+bool GetScriptOp(CScriptBase::const_iterator& pc, CScriptBase::const_iterator end, opcodetype& opcodeRet, std::vector<unsigned char>* pvchRet);
+
/** Serialized script, used inside transaction inputs and outputs */
class CScript : public CScriptBase
{
@@ -493,84 +495,16 @@ public:
}
- bool GetOp(iterator& pc, opcodetype& opcodeRet, std::vector<unsigned char>& vchRet)
- {
- // Wrapper so it can be called with either iterator or const_iterator
- const_iterator pc2 = pc;
- bool fRet = GetOp2(pc2, opcodeRet, &vchRet);
- pc = begin() + (pc2 - begin());
- return fRet;
- }
-
- bool GetOp(iterator& pc, opcodetype& opcodeRet)
- {
- const_iterator pc2 = pc;
- bool fRet = GetOp2(pc2, opcodeRet, nullptr);
- pc = begin() + (pc2 - begin());
- return fRet;
- }
-
bool GetOp(const_iterator& pc, opcodetype& opcodeRet, std::vector<unsigned char>& vchRet) const
{
- return GetOp2(pc, opcodeRet, &vchRet);
+ return GetScriptOp(pc, end(), opcodeRet, &vchRet);
}
bool GetOp(const_iterator& pc, opcodetype& opcodeRet) const
{
- return GetOp2(pc, opcodeRet, nullptr);
+ return GetScriptOp(pc, end(), opcodeRet, nullptr);
}
- bool GetOp2(const_iterator& pc, opcodetype& opcodeRet, std::vector<unsigned char>* pvchRet) const
- {
- opcodeRet = OP_INVALIDOPCODE;
- if (pvchRet)
- pvchRet->clear();
- if (pc >= end())
- return false;
-
- // Read instruction
- if (end() - pc < 1)
- return false;
- unsigned int opcode = *pc++;
-
- // Immediate operand
- if (opcode <= OP_PUSHDATA4)
- {
- unsigned int nSize = 0;
- if (opcode < OP_PUSHDATA1)
- {
- nSize = opcode;
- }
- else if (opcode == OP_PUSHDATA1)
- {
- if (end() - pc < 1)
- return false;
- nSize = *pc++;
- }
- else if (opcode == OP_PUSHDATA2)
- {
- if (end() - pc < 2)
- return false;
- nSize = ReadLE16(&pc[0]);
- pc += 2;
- }
- else if (opcode == OP_PUSHDATA4)
- {
- if (end() - pc < 4)
- return false;
- nSize = ReadLE32(&pc[0]);
- pc += 4;
- }
- if (end() - pc < 0 || (unsigned int)(end() - pc) < nSize)
- return false;
- if (pvchRet)
- pvchRet->assign(pc, pc + nSize);
- pc += nSize;
- }
-
- opcodeRet = static_cast<opcodetype>(opcode);
- return true;
- }
/** Encode/decode small integers: */
static int DecodeOP_N(opcodetype opcode)
@@ -588,34 +522,6 @@ public:
return (opcodetype)(OP_1+n-1);
}
- int FindAndDelete(const CScript& b)
- {
- int nFound = 0;
- if (b.empty())
- return nFound;
- CScript result;
- iterator pc = begin(), pc2 = begin();
- opcodetype opcode;
- do
- {
- result.insert(result.end(), pc2, pc);
- while (static_cast<size_t>(end() - pc) >= b.size() && std::equal(b.begin(), b.end(), pc))
- {
- pc = pc + b.size();
- ++nFound;
- }
- pc2 = pc;
- }
- while (GetOp(pc, opcode));
-
- if (nFound > 0) {
- result.insert(result.end(), pc2, end());
- *this = result;
- }
-
- return nFound;
- }
-
/**
* Pre-version-0.6, Bitcoin always counted CHECKMULTISIGs
* as 20 sigops. With pay-to-script-hash, that changed:
diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp
index dbceb1f740..ceda740580 100644
--- a/src/script/script_error.cpp
+++ b/src/script/script_error.cpp
@@ -89,6 +89,10 @@ const char* ScriptErrorString(const ScriptError serror)
return "Witness provided for non-witness script";
case SCRIPT_ERR_WITNESS_PUBKEYTYPE:
return "Using non-compressed keys in segwit";
+ case SCRIPT_ERR_OP_CODESEPARATOR:
+ return "Using OP_CODESEPARATOR in non-witness script";
+ case SCRIPT_ERR_SIG_FINDANDDELETE:
+ return "Signature is found in scriptCode";
case SCRIPT_ERR_UNKNOWN_ERROR:
case SCRIPT_ERR_ERROR_COUNT:
default: break;
diff --git a/src/script/script_error.h b/src/script/script_error.h
index 3200e94707..6982a087f4 100644
--- a/src/script/script_error.h
+++ b/src/script/script_error.h
@@ -64,6 +64,10 @@ typedef enum ScriptError_t
SCRIPT_ERR_WITNESS_UNEXPECTED,
SCRIPT_ERR_WITNESS_PUBKEYTYPE,
+ /* Constant scriptCode */
+ SCRIPT_ERR_OP_CODESEPARATOR,
+ SCRIPT_ERR_SIG_FINDANDDELETE,
+
SCRIPT_ERR_ERROR_COUNT
} ScriptError;
diff --git a/src/script/standard.h b/src/script/standard.h
index 3b2838a5bb..4922b7236b 100644
--- a/src/script/standard.h
+++ b/src/script/standard.h
@@ -23,7 +23,7 @@ class CScriptID : public uint160
{
public:
CScriptID() : uint160() {}
- CScriptID(const CScript& in);
+ explicit CScriptID(const CScript& in);
CScriptID(const uint160& in) : uint160(in) {}
};
diff --git a/src/support/lockedpool.cpp b/src/support/lockedpool.cpp
index f10fd07c63..a273424b51 100644
--- a/src/support/lockedpool.cpp
+++ b/src/support/lockedpool.cpp
@@ -141,7 +141,7 @@ Arena::Stats Arena::stats() const
}
#ifdef ARENA_DEBUG
-void printchunk(char* base, size_t sz, bool used) {
+static void printchunk(char* base, size_t sz, bool used) {
std::cout <<
"0x" << std::hex << std::setw(16) << std::setfill('0') << base <<
" 0x" << std::hex << std::setw(16) << std::setfill('0') << sz <<
diff --git a/src/test/DoS_tests.cpp b/src/test/DoS_tests.cpp
index abc31e6181..1868aed7dd 100644
--- a/src/test/DoS_tests.cpp
+++ b/src/test/DoS_tests.cpp
@@ -31,7 +31,7 @@ struct COrphanTx {
};
extern std::map<uint256, COrphanTx> mapOrphanTransactions;
-CService ip(uint32_t i)
+static CService ip(uint32_t i)
{
struct in_addr s;
s.s_addr = i;
@@ -92,7 +92,7 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
peerLogic->FinalizeNode(dummyNode1.GetId(), dummy);
}
-void AddRandomOutboundPeer(std::vector<CNode *> &vNodes, PeerLogicValidation &peerLogic)
+static void AddRandomOutboundPeer(std::vector<CNode *> &vNodes, PeerLogicValidation &peerLogic)
{
CAddress addr(ip(GetRandInt(0xffffffff)), NODE_NONE);
vNodes.emplace_back(new CNode(id++, ServiceFlags(NODE_NETWORK|NODE_WITNESS), 0, INVALID_SOCKET, addr, 0, 0, CAddress(), "", /*fInboundIn=*/ false));
@@ -291,7 +291,7 @@ BOOST_AUTO_TEST_CASE(DoS_bantime)
peerLogic->FinalizeNode(dummyNode.GetId(), dummy);
}
-CTransactionRef RandomOrphan()
+static CTransactionRef RandomOrphan()
{
std::map<uint256, COrphanTx>::iterator it;
LOCK(cs_main);
diff --git a/src/test/arith_uint256_tests.cpp b/src/test/arith_uint256_tests.cpp
index 21a1153ad0..13ec19834a 100644
--- a/src/test/arith_uint256_tests.cpp
+++ b/src/test/arith_uint256_tests.cpp
@@ -17,7 +17,7 @@
BOOST_FIXTURE_TEST_SUITE(arith_uint256_tests, BasicTestingSetup)
/// Convert vector to arith_uint256, via uint256 blob
-inline arith_uint256 arith_uint256V(const std::vector<unsigned char>& vch)
+static inline arith_uint256 arith_uint256V(const std::vector<unsigned char>& vch)
{
return UintToArith256(uint256(vch));
}
@@ -53,7 +53,7 @@ const unsigned char MaxArray[] =
const arith_uint256 MaxL = arith_uint256V(std::vector<unsigned char>(MaxArray,MaxArray+32));
const arith_uint256 HalfL = (OneL << 255);
-std::string ArrayToString(const unsigned char A[], unsigned int width)
+static std::string ArrayToString(const unsigned char A[], unsigned int width)
{
std::stringstream Stream;
Stream << std::hex;
@@ -122,7 +122,7 @@ BOOST_AUTO_TEST_CASE( basics ) // constructors, equality, inequality
tmpL = ~MaxL; BOOST_CHECK(tmpL == ~MaxL);
}
-void shiftArrayRight(unsigned char* to, const unsigned char* from, unsigned int arrayLength, unsigned int bitsToShift)
+static void shiftArrayRight(unsigned char* to, const unsigned char* from, unsigned int arrayLength, unsigned int bitsToShift)
{
for (unsigned int T=0; T < arrayLength; ++T)
{
@@ -136,7 +136,7 @@ void shiftArrayRight(unsigned char* to, const unsigned char* from, unsigned int
}
}
-void shiftArrayLeft(unsigned char* to, const unsigned char* from, unsigned int arrayLength, unsigned int bitsToShift)
+static void shiftArrayLeft(unsigned char* to, const unsigned char* from, unsigned int arrayLength, unsigned int bitsToShift)
{
for (unsigned int T=0; T < arrayLength; ++T)
{
@@ -369,7 +369,7 @@ BOOST_AUTO_TEST_CASE( divide )
}
-bool almostEqual(double d1, double d2)
+static bool almostEqual(double d1, double d2)
{
return fabs(d1-d2) <= 4*fabs(d1)*std::numeric_limits<double>::epsilon();
}
diff --git a/src/test/bech32_tests.cpp b/src/test/bech32_tests.cpp
index 495290c8d9..c23e23f6a1 100644
--- a/src/test/bech32_tests.cpp
+++ b/src/test/bech32_tests.cpp
@@ -9,7 +9,7 @@
BOOST_FIXTURE_TEST_SUITE(bech32_tests, BasicTestingSetup)
-bool CaseInsensitiveEqual(const std::string &s1, const std::string &s2)
+static bool CaseInsensitiveEqual(const std::string &s1, const std::string &s2)
{
if (s1.size() != s2.size()) return false;
for (size_t i = 0; i < s1.size(); ++i) {
diff --git a/src/test/bip32_tests.cpp b/src/test/bip32_tests.cpp
index 3c9ff1877d..51308847f6 100644
--- a/src/test/bip32_tests.cpp
+++ b/src/test/bip32_tests.cpp
@@ -87,7 +87,7 @@ TestVector test3 =
"xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L",
0);
-void RunTest(const TestVector &test) {
+static void RunTest(const TestVector &test) {
std::vector<unsigned char> seed = ParseHex(test.strHexMaster);
CExtKey key;
CExtPubKey pubkey;
diff --git a/src/test/blockchain_tests.cpp b/src/test/blockchain_tests.cpp
index 32b408838c..5b8df32158 100644
--- a/src/test/blockchain_tests.cpp
+++ b/src/test/blockchain_tests.cpp
@@ -8,12 +8,12 @@
/* Equality between doubles is imprecise. Comparison should be done
* with a small threshold of tolerance, rather than exact equality.
*/
-bool DoubleEquals(double a, double b, double epsilon)
+static bool DoubleEquals(double a, double b, double epsilon)
{
return std::abs(a - b) < epsilon;
}
-CBlockIndex* CreateBlockIndexWithNbits(uint32_t nbits)
+static CBlockIndex* CreateBlockIndexWithNbits(uint32_t nbits)
{
CBlockIndex* block_index = new CBlockIndex();
block_index->nHeight = 46367;
@@ -22,7 +22,7 @@ CBlockIndex* CreateBlockIndexWithNbits(uint32_t nbits)
return block_index;
}
-CChain CreateChainWithNbits(uint32_t nbits)
+static CChain CreateChainWithNbits(uint32_t nbits)
{
CBlockIndex* block_index = CreateBlockIndexWithNbits(nbits);
CChain chain;
@@ -30,7 +30,7 @@ CChain CreateChainWithNbits(uint32_t nbits)
return chain;
}
-void RejectDifficultyMismatch(double difficulty, double expected_difficulty) {
+static void RejectDifficultyMismatch(double difficulty, double expected_difficulty) {
BOOST_CHECK_MESSAGE(
DoubleEquals(difficulty, expected_difficulty, 0.00001),
"Difficulty was " + std::to_string(difficulty)
@@ -40,7 +40,7 @@ void RejectDifficultyMismatch(double difficulty, double expected_difficulty) {
/* Given a BlockIndex with the provided nbits,
* verify that the expected difficulty results.
*/
-void TestDifficulty(uint32_t nbits, double expected_difficulty)
+static void TestDifficulty(uint32_t nbits, double expected_difficulty)
{
CBlockIndex* block_index = CreateBlockIndexWithNbits(nbits);
/* Since we are passing in block index explicitly,
diff --git a/src/test/blockencodings_tests.cpp b/src/test/blockencodings_tests.cpp
index 32330e0548..8cffacbffe 100644
--- a/src/test/blockencodings_tests.cpp
+++ b/src/test/blockencodings_tests.cpp
@@ -52,8 +52,8 @@ static CBlock BuildBlockTestCase() {
}
// Number of shared use_counts we expect for a tx we haven't touched
-// == 2 (mempool + our copy from the GetSharedTx call)
-#define SHARED_TX_OFFSET 2
+// (block + mempool + our copy from the GetSharedTx call)
+constexpr long SHARED_TX_OFFSET{3};
BOOST_AUTO_TEST_CASE(SimpleRoundTripTest)
{
@@ -61,7 +61,7 @@ BOOST_AUTO_TEST_CASE(SimpleRoundTripTest)
TestMemPoolEntryHelper entry;
CBlock block(BuildBlockTestCase());
- pool.addUnchecked(block.vtx[2]->GetHash(), entry.FromTx(*block.vtx[2]));
+ pool.addUnchecked(block.vtx[2]->GetHash(), entry.FromTx(block.vtx[2]));
LOCK(pool.cs);
BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
@@ -161,7 +161,7 @@ BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest)
TestMemPoolEntryHelper entry;
CBlock block(BuildBlockTestCase());
- pool.addUnchecked(block.vtx[2]->GetHash(), entry.FromTx(*block.vtx[2]));
+ pool.addUnchecked(block.vtx[2]->GetHash(), entry.FromTx(block.vtx[2]));
LOCK(pool.cs);
BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
@@ -188,7 +188,7 @@ BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest)
BOOST_CHECK( partialBlock.IsTxAvailable(1));
BOOST_CHECK( partialBlock.IsTxAvailable(2));
- BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1);
+ BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1); // +1 because of partialBlock
CBlock block2;
{
@@ -203,6 +203,7 @@ BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest)
partialBlock.FillBlock(block2, {block.vtx[1]}); // Current implementation doesn't check txn here, but don't require that
partialBlock = tmp;
}
+ BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 2); // +2 because of partialBlock and block2
bool mutated;
BOOST_CHECK(block.hashMerkleRoot != BlockMerkleRoot(block2, &mutated));
@@ -213,13 +214,15 @@ BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest)
BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block3, &mutated).ToString());
BOOST_CHECK(!mutated);
+ BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 3); // +2 because of partialBlock and block2 and block3
+
txhash = block.vtx[2]->GetHash();
block.vtx.clear();
block2.vtx.clear();
block3.vtx.clear();
- BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1); // + 1 because of partialBlockCopy.
+ BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1 - 1); // + 1 because of partialBlock; -1 because of block.
}
- BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
+ BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET - 1); // -1 because of block
}
BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest)
@@ -228,7 +231,7 @@ BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest)
TestMemPoolEntryHelper entry;
CBlock block(BuildBlockTestCase());
- pool.addUnchecked(block.vtx[1]->GetHash(), entry.FromTx(*block.vtx[1]));
+ pool.addUnchecked(block.vtx[1]->GetHash(), entry.FromTx(block.vtx[1]));
LOCK(pool.cs);
BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[1]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
@@ -268,9 +271,9 @@ BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest)
txhash = block.vtx[1]->GetHash();
block.vtx.clear();
block2.vtx.clear();
- BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1); // + 1 because of partialBlockCopy.
+ BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1 - 1); // + 1 because of partialBlock; -1 because of block.
}
- BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
+ BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET - 1); // -1 because of block
}
BOOST_AUTO_TEST_CASE(EmptyBlockRoundTripTest)
diff --git a/src/test/checkqueue_tests.cpp b/src/test/checkqueue_tests.cpp
index de47216449..c8de7f4a7c 100644
--- a/src/test/checkqueue_tests.cpp
+++ b/src/test/checkqueue_tests.cpp
@@ -146,7 +146,7 @@ typedef CCheckQueue<FrozenCleanupCheck> FrozenCleanup_Queue;
/** This test case checks that the CCheckQueue works properly
* with each specified size_t Checks pushed.
*/
-void Correct_Queue_range(std::vector<size_t> range)
+static void Correct_Queue_range(std::vector<size_t> range)
{
auto small_queue = std::unique_ptr<Correct_Queue>(new Correct_Queue {QUEUE_BATCH_SIZE});
boost::thread_group tg;
diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp
index a146c69fd2..276d5b80ee 100644
--- a/src/test/coins_tests.cpp
+++ b/src/test/coins_tests.cpp
@@ -540,7 +540,7 @@ const static auto FLAGS = {char(0), FRESH, DIRTY, char(DIRTY | FRESH)};
const static auto CLEAN_FLAGS = {char(0), FRESH};
const static auto ABSENT_FLAGS = {NO_ENTRY};
-void SetCoinsValue(CAmount value, Coin& coin)
+static void SetCoinsValue(CAmount value, Coin& coin)
{
assert(value != ABSENT);
coin.Clear();
@@ -552,7 +552,7 @@ void SetCoinsValue(CAmount value, Coin& coin)
}
}
-size_t InsertCoinsMapEntry(CCoinsMap& map, CAmount value, char flags)
+static size_t InsertCoinsMapEntry(CCoinsMap& map, CAmount value, char flags)
{
if (value == ABSENT) {
assert(flags == NO_ENTRY);
@@ -605,7 +605,7 @@ public:
CCoinsViewCacheTest cache{&base};
};
-void CheckAccessCoin(CAmount base_value, CAmount cache_value, CAmount expected_value, char cache_flags, char expected_flags)
+static void CheckAccessCoin(CAmount base_value, CAmount cache_value, CAmount expected_value, char cache_flags, char expected_flags)
{
SingleEntryCacheTest test(base_value, cache_value, cache_flags);
test.cache.AccessCoin(OUTPOINT);
@@ -656,7 +656,7 @@ BOOST_AUTO_TEST_CASE(ccoins_access)
CheckAccessCoin(VALUE1, VALUE2, VALUE2, DIRTY|FRESH, DIRTY|FRESH);
}
-void CheckSpendCoins(CAmount base_value, CAmount cache_value, CAmount expected_value, char cache_flags, char expected_flags)
+static void CheckSpendCoins(CAmount base_value, CAmount cache_value, CAmount expected_value, char cache_flags, char expected_flags)
{
SingleEntryCacheTest test(base_value, cache_value, cache_flags);
test.cache.SpendCoin(OUTPOINT);
@@ -707,7 +707,7 @@ BOOST_AUTO_TEST_CASE(ccoins_spend)
CheckSpendCoins(VALUE1, VALUE2, ABSENT, DIRTY|FRESH, NO_ENTRY );
}
-void CheckAddCoinBase(CAmount base_value, CAmount cache_value, CAmount modify_value, CAmount expected_value, char cache_flags, char expected_flags, bool coinbase)
+static void CheckAddCoinBase(CAmount base_value, CAmount cache_value, CAmount modify_value, CAmount expected_value, char cache_flags, char expected_flags, bool coinbase)
{
SingleEntryCacheTest test(base_value, cache_value, cache_flags);
@@ -734,7 +734,7 @@ void CheckAddCoinBase(CAmount base_value, CAmount cache_value, CAmount modify_va
// while still verifying that the CoinsViewCache::AddCoin implementation
// ignores base values.
template <typename... Args>
-void CheckAddCoin(Args&&... args)
+static void CheckAddCoin(Args&&... args)
{
for (CAmount base_value : {ABSENT, PRUNED, VALUE1})
CheckAddCoinBase(base_value, std::forward<Args>(args)...);
diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp
index de0d72614b..518cb849bb 100644
--- a/src/test/crypto_tests.cpp
+++ b/src/test/crypto_tests.cpp
@@ -23,7 +23,7 @@
BOOST_FIXTURE_TEST_SUITE(crypto_tests, BasicTestingSetup)
template<typename Hasher, typename In, typename Out>
-void TestVector(const Hasher &h, const In &in, const Out &out) {
+static void TestVector(const Hasher &h, const In &in, const Out &out) {
Out hash;
BOOST_CHECK(out.size() == h.OUTPUT_SIZE);
hash.resize(out.size());
@@ -51,22 +51,22 @@ void TestVector(const Hasher &h, const In &in, const Out &out) {
}
}
-void TestSHA1(const std::string &in, const std::string &hexout) { TestVector(CSHA1(), in, ParseHex(hexout));}
-void TestSHA256(const std::string &in, const std::string &hexout) { TestVector(CSHA256(), in, ParseHex(hexout));}
-void TestSHA512(const std::string &in, const std::string &hexout) { TestVector(CSHA512(), in, ParseHex(hexout));}
-void TestRIPEMD160(const std::string &in, const std::string &hexout) { TestVector(CRIPEMD160(), in, ParseHex(hexout));}
+static void TestSHA1(const std::string &in, const std::string &hexout) { TestVector(CSHA1(), in, ParseHex(hexout));}
+static void TestSHA256(const std::string &in, const std::string &hexout) { TestVector(CSHA256(), in, ParseHex(hexout));}
+static void TestSHA512(const std::string &in, const std::string &hexout) { TestVector(CSHA512(), in, ParseHex(hexout));}
+static void TestRIPEMD160(const std::string &in, const std::string &hexout) { TestVector(CRIPEMD160(), in, ParseHex(hexout));}
-void TestHMACSHA256(const std::string &hexkey, const std::string &hexin, const std::string &hexout) {
+static void TestHMACSHA256(const std::string &hexkey, const std::string &hexin, const std::string &hexout) {
std::vector<unsigned char> key = ParseHex(hexkey);
TestVector(CHMAC_SHA256(key.data(), key.size()), ParseHex(hexin), ParseHex(hexout));
}
-void TestHMACSHA512(const std::string &hexkey, const std::string &hexin, const std::string &hexout) {
+static void TestHMACSHA512(const std::string &hexkey, const std::string &hexin, const std::string &hexout) {
std::vector<unsigned char> key = ParseHex(hexkey);
TestVector(CHMAC_SHA512(key.data(), key.size()), ParseHex(hexin), ParseHex(hexout));
}
-void TestAES128(const std::string &hexkey, const std::string &hexin, const std::string &hexout)
+static void TestAES128(const std::string &hexkey, const std::string &hexin, const std::string &hexout)
{
std::vector<unsigned char> key = ParseHex(hexkey);
std::vector<unsigned char> in = ParseHex(hexin);
@@ -86,7 +86,7 @@ void TestAES128(const std::string &hexkey, const std::string &hexin, const std::
BOOST_CHECK_EQUAL(HexStr(buf2), HexStr(in));
}
-void TestAES256(const std::string &hexkey, const std::string &hexin, const std::string &hexout)
+static void TestAES256(const std::string &hexkey, const std::string &hexin, const std::string &hexout)
{
std::vector<unsigned char> key = ParseHex(hexkey);
std::vector<unsigned char> in = ParseHex(hexin);
@@ -105,7 +105,7 @@ void TestAES256(const std::string &hexkey, const std::string &hexin, const std::
BOOST_CHECK(buf == in);
}
-void TestAES128CBC(const std::string &hexkey, const std::string &hexiv, bool pad, const std::string &hexin, const std::string &hexout)
+static void TestAES128CBC(const std::string &hexkey, const std::string &hexiv, bool pad, const std::string &hexin, const std::string &hexout)
{
std::vector<unsigned char> key = ParseHex(hexkey);
std::vector<unsigned char> iv = ParseHex(hexiv);
@@ -146,7 +146,7 @@ void TestAES128CBC(const std::string &hexkey, const std::string &hexiv, bool pad
}
}
-void TestAES256CBC(const std::string &hexkey, const std::string &hexiv, bool pad, const std::string &hexin, const std::string &hexout)
+static void TestAES256CBC(const std::string &hexkey, const std::string &hexiv, bool pad, const std::string &hexin, const std::string &hexout)
{
std::vector<unsigned char> key = ParseHex(hexkey);
std::vector<unsigned char> iv = ParseHex(hexiv);
@@ -187,7 +187,7 @@ void TestAES256CBC(const std::string &hexkey, const std::string &hexiv, bool pad
}
}
-void TestChaCha20(const std::string &hexkey, uint64_t nonce, uint64_t seek, const std::string& hexout)
+static void TestChaCha20(const std::string &hexkey, uint64_t nonce, uint64_t seek, const std::string& hexout)
{
std::vector<unsigned char> key = ParseHex(hexkey);
ChaCha20 rng(key.data(), key.size());
@@ -200,7 +200,7 @@ void TestChaCha20(const std::string &hexkey, uint64_t nonce, uint64_t seek, cons
BOOST_CHECK(out == outres);
}
-std::string LongTestString(void) {
+static std::string LongTestString(void) {
std::string ret;
for (int i=0; i<200000; i++) {
ret += (unsigned char)(i);
diff --git a/src/test/cuckoocache_tests.cpp b/src/test/cuckoocache_tests.cpp
index ccd5caacd5..857ab8a1b7 100644
--- a/src/test/cuckoocache_tests.cpp
+++ b/src/test/cuckoocache_tests.cpp
@@ -28,7 +28,7 @@ BOOST_AUTO_TEST_SUITE(cuckoocache_tests);
/** insecure_GetRandHash fills in a uint256 from local_rand_ctx
*/
-void insecure_GetRandHash(uint256& t)
+static void insecure_GetRandHash(uint256& t)
{
uint32_t* ptr = (uint32_t*)t.begin();
for (uint8_t j = 0; j < 8; ++j)
@@ -62,7 +62,7 @@ BOOST_AUTO_TEST_CASE(test_cuckoocache_no_fakes)
* inserted into a megabytes sized cache
*/
template <typename Cache>
-double test_cache(size_t megabytes, double load)
+static double test_cache(size_t megabytes, double load)
{
local_rand_ctx = FastRandomContext(true);
std::vector<uint256> hashes;
@@ -109,7 +109,7 @@ double test_cache(size_t megabytes, double load)
* how you measure around load 1.0 as after load 1.0 your normalized hit rate
* becomes effectively perfect, ignoring freshness.
*/
-double normalize_hit_rate(double hits, double load)
+static double normalize_hit_rate(double hits, double load)
{
return hits * std::max(load, 1.0);
}
@@ -132,7 +132,7 @@ BOOST_AUTO_TEST_CASE(cuckoocache_hit_rate_ok)
/** This helper checks that erased elements are preferentially inserted onto and
* that the hit rate of "fresher" keys is reasonable*/
template <typename Cache>
-void test_cache_erase(size_t megabytes)
+static void test_cache_erase(size_t megabytes)
{
double load = 1;
local_rand_ctx = FastRandomContext(true);
@@ -195,7 +195,7 @@ BOOST_AUTO_TEST_CASE(cuckoocache_erase_ok)
}
template <typename Cache>
-void test_cache_erase_parallel(size_t megabytes)
+static void test_cache_erase_parallel(size_t megabytes)
{
double load = 1;
local_rand_ctx = FastRandomContext(true);
@@ -283,7 +283,7 @@ BOOST_AUTO_TEST_CASE(cuckoocache_erase_parallel_ok)
template <typename Cache>
-void test_cache_generations()
+static void test_cache_generations()
{
// This test checks that for a simulation of network activity, the fresh hit
// rate is never below 99%, and the number of times that it is worse than
diff --git a/src/test/data/tx_invalid.json b/src/test/data/tx_invalid.json
index abb46fe533..918df6d8d9 100644
--- a/src/test/data/tx_invalid.json
+++ b/src/test/data/tx_invalid.json
@@ -340,5 +340,53 @@
[[["9628667ad48219a169b41b020800162287d2c0f713c04157e95c484a8dcb7592", 7500, "0x00 0x20 0x9b66c15b4e0b4eb49fa877982cafded24859fe5b0e2dbfbe4f0df1de7743fd52", 200000]],
"010000000001019275cb8d4a485ce95741c013f7c0d28722160008021bb469a11982d47a6628964c1d000000ffffffff0101000000000000000007004830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c03959601010221023cb6055f4b57a1580c5a753e19610cafaedf7e0ff377731c77837fd666eae1712102c1b1db303ac232ffa8e5e7cc2cf5f96c6e40d3e6914061204c0541cb2043a0969552af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c039596017500000000", "P2SH,WITNESS"],
+["SCRIPT_VERIFY_CONST_SCRIPTCODE tests"],
+["All transactions are copied from OP_CODESEPARATOR tests in tx_valid.json"],
+
+[[["bc7fd132fcf817918334822ee6d9bd95c889099c96e07ca2c1eb2cc70db63224", 0, "CODESEPARATOR 0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIG"]],
+ "01000000012432b60dc72cebc1a27ce0969c0989c895bdd9e62e8234839117f8fc32d17fbc000000004a493046022100a576b52051962c25e642c0fd3d77ee6c92487048e5d90818bcf5b51abaccd7900221008204f8fb121be4ec3b24483b1f92d89b1b0548513a134e345c5442e86e8617a501ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"],
+[[["83e194f90b6ef21fa2e3a365b63794fb5daa844bdc9b25de30899fcfe7b01047", 0, "CODESEPARATOR CODESEPARATOR 0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIG"]],
+ "01000000014710b0e7cf9f8930de259bdc4b84aa5dfb9437b665a3e3a21ff26e0bf994e183000000004a493046022100a166121a61b4eeb19d8f922b978ff6ab58ead8a5a5552bf9be73dc9c156873ea02210092ad9bc43ee647da4f6652c320800debcf08ec20a094a0aaf085f63ecb37a17201ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"],
+
+[[["326882a7f22b5191f1a0cc9962ca4b878cd969cf3b3a70887aece4d801a0ba5e", 0, "0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CODESEPARATOR CHECKSIG"]],
+ "01000000015ebaa001d8e4ec7a88703a3bcf69d98c874bca6299cca0f191512bf2a7826832000000004948304502203bf754d1c6732fbf87c5dcd81258aefd30f2060d7bd8ac4a5696f7927091dad1022100f5bcb726c4cf5ed0ed34cc13dadeedf628ae1045b7cb34421bc60b89f4cecae701ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"],
+
+[[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIGVERIFY CODESEPARATOR 0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIGVERIFY CODESEPARATOR 1"]],
+ "010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a900000000924830450221009c0a27f886a1d8cb87f6f595fbc3163d28f7a81ec3c4b252ee7f3ac77fd13ffa02203caa8dfa09713c8c4d7ef575c75ed97812072405d932bd11e6a1593a98b679370148304502201e3861ef39a526406bad1e20ecad06be7375ad40ddb582c9be42d26c3a0d7b240221009d0a3985e96522e59635d19cc4448547477396ce0ef17a58e7d74c3ef464292301ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"],
+
+["CODESEPARATOR in an unexecuted IF block is still invalid"],
+[[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "IF CODESEPARATOR ENDIF 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 CHECKSIGVERIFY CODESEPARATOR 1"]],
+ "010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a9000000004a48304502207a6974a77c591fa13dff60cabbb85a0de9e025c09c65a4b2285e47ce8e22f761022100f0efaac9ff8ac36b10721e0aae1fb975c90500b50c56e8a0cc52b0403f0425dd0100ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"],
+
+["CODESEPARATOR in an executed IF block is invalid"],
+[[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "IF CODESEPARATOR ENDIF 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 CHECKSIGVERIFY CODESEPARATOR 1"]],
+ "010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a9000000004a483045022100fa4a74ba9fd59c59f46c3960cf90cbe0d2b743c471d24a3d5d6db6002af5eebb02204d70ec490fd0f7055a7c45f86514336e3a7f03503dacecabb247fc23f15c83510151ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"],
+
+
+["Using CHECKSIG with singatures in scriptSigs will trigger FindAndDelete, which is invalid"],
+[[["ccf7f4053a02e653c36ac75c891b7496d0dc5ce5214f6c913d9cf8f1329ebee0", 0, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]],
+ "0100000001e0be9e32f1f89c3d916c4f21e55cdcd096741b895cc76ac353e6023a05f4f7cc00000000d86149304602210086e5f736a2c3622ebb62bd9d93d8e5d76508b98be922b97160edc3dcca6d8c47022100b23c312ac232a4473f19d2aeb95ab7bdf2b65518911a0d72d50e38b5dd31dc820121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac4730440220508fa761865c8abd81244a168392876ee1d94e8ed83897066b5e2df2400dad24022043f5ee7538e87e9c6aef7ef55133d3e51da7cc522830a9c4d736977a76ef755c0121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"],
+
+["OP_CODESEPARATOR in scriptSig is invalid"],
+[[["10c9f0effe83e97f80f067de2b11c6a00c3088a4bce42c5ae761519af9306f3c", 1, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]],
+ "01000000013c6f30f99a5161e75a2ce4bca488300ca0c6112bde67f0807fe983feeff0c91001000000e608646561646265656675ab61493046022100ce18d384221a731c993939015e3d1bcebafb16e8c0b5b5d14097ec8177ae6f28022100bcab227af90bab33c3fe0a9abfee03ba976ee25dc6ce542526e9b2e56e14b7f10121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac493046022100c3b93edcc0fd6250eb32f2dd8a0bba1754b0f6c3be8ed4100ed582f3db73eba2022100bf75b5bd2eff4d6bf2bda2e34a40fcc07d4aa3cf862ceaa77b47b81eff829f9a01ab21038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"],
+
+["Again, FindAndDelete() in scriptSig"],
+[[["6056ebd549003b10cbbd915cea0d82209fe40b8617104be917a26fa92cbe3d6f", 0, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]],
+ "01000000016f3dbe2ca96fa217e94b1017860be49f20820dea5c91bdcb103b0049d5eb566000000000fd1d0147304402203989ac8f9ad36b5d0919d97fa0a7f70c5272abee3b14477dc646288a8b976df5022027d19da84a066af9053ad3d1d7459d171b7e3a80bc6c4ef7a330677a6be548140147304402203989ac8f9ad36b5d0919d97fa0a7f70c5272abee3b14477dc646288a8b976df5022027d19da84a066af9053ad3d1d7459d171b7e3a80bc6c4ef7a330677a6be548140121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac47304402203757e937ba807e4a5da8534c17f9d121176056406a6465054bdd260457515c1a02200f02eccf1bec0f3a0d65df37889143c2e88ab7acec61a7b6f5aa264139141a2b0121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"],
+
+[[["5a6b0021a6042a686b6b94abc36b387bef9109847774e8b1e51eb8cc55c53921", 1, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]],
+ "01000000012139c555ccb81ee5b1e87477840991ef7b386bc3ab946b6b682a04a621006b5a01000000fdb40148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f2204148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390175ac4830450220646b72c35beeec51f4d5bc1cbae01863825750d7f490864af354e6ea4f625e9c022100f04b98432df3a9641719dbced53393022e7249fb59db993af1118539830aab870148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a580039017521038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"],
+
+["FindAndDelete() in redeemScript"],
+[[["b5b598de91787439afd5938116654e0b16b7a0d0f82742ba37564219c5afcbf9", 0, "DUP HASH160 0x14 0xf6f365c40f0739b61de827a44751e5e99032ed8f EQUALVERIFY CHECKSIG"],
+ ["ab9805c6d57d7070d9a42c5176e47bb705023e6b67249fb6760880548298e742", 0, "HASH160 0x14 0xd8dacdadb7462ae15cd906f1878706d0da8660e6 EQUAL"]],
+ "0100000002f9cbafc519425637ba4227f8d0a0b7160b4e65168193d5af39747891de98b5b5000000006b4830450221008dd619c563e527c47d9bd53534a770b102e40faa87f61433580e04e271ef2f960220029886434e18122b53d5decd25f1f4acb2480659fea20aabd856987ba3c3907e0121022b78b756e2258af13779c1a1f37ea6800259716ca4b7f0b87610e0bf3ab52a01ffffffff42e7988254800876b69f24676b3e0205b77be476512ca4d970707dd5c60598ab00000000fd260100483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a53034930460221008431bdfa72bc67f9d41fe72e94c88fb8f359ffa30b33c72c121c5a877d922e1002210089ef5fc22dd8bfc6bf9ffdb01a9862d27687d424d1fefbab9e9c7176844a187a014c9052483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303210378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71210378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c7153aeffffffff01a08601000000000017a914d8dacdadb7462ae15cd906f1878706d0da8660e68700000000", "P2SH,CONST_SCRIPTCODE"],
+
+["FindAndDelete() in bare CHECKMULTISIG"],
+[[["ceafe58e0f6e7d67c0409fbbf673c84c166e3c5d3c24af58f7175b18df3bb3db", 0, "DUP HASH160 0x14 0xf6f365c40f0739b61de827a44751e5e99032ed8f EQUALVERIFY CHECKSIG"],
+ ["ceafe58e0f6e7d67c0409fbbf673c84c166e3c5d3c24af58f7175b18df3bb3db", 1, "2 0x48 0x3045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 3 CHECKMULTISIG"]],
+ "0100000002dbb33bdf185b17f758af243c5d3c6e164cc873f6bb9f40c0677d6e0f8ee5afce000000006b4830450221009627444320dc5ef8d7f68f35010b4c050a6ed0d96b67a84db99fda9c9de58b1e02203e4b4aaa019e012e65d69b487fdf8719df72f488fa91506a80c49a33929f1fd50121022b78b756e2258af13779c1a1f37ea6800259716ca4b7f0b87610e0bf3ab52a01ffffffffdbb33bdf185b17f758af243c5d3c6e164cc873f6bb9f40c0677d6e0f8ee5afce010000009300483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303ffffffff01a0860100000000001976a9149bc0bbdd3024da4d0c38ed1aecf5c68dd1d3fa1288ac00000000", "P2SH,CONST_SCRIPTCODE"],
+
["Make diffs cleaner by leaving a comment here without comma at the end"]
]
diff --git a/src/test/data/tx_valid.json b/src/test/data/tx_valid.json
index 0bcecc58fe..4a1c77166d 100644
--- a/src/test/data/tx_valid.json
+++ b/src/test/data/tx_valid.json
@@ -477,17 +477,17 @@
["BIP143 example: P2WSH with OP_CODESEPARATOR and out-of-range SIGHASH_SINGLE."],
[[["6eb316926b1c5d567cd6f5e6a84fec606fc53d7b474526d1fff3948020c93dfe", 0, "0x21 0x036d5c20fa14fb2f635474c1dc4ef5909d4568e5569b79fc94d3448486e14685f8 CHECKSIG", 156250000],
["f825690aee1b3dc247da796cacb12687a5e802429fd291cfd63e010f02cf1508", 0, "0x00 0x20 0x5d1b56b63d714eebe542309525f484b7e9d6f686b3781b6f61ef925d66d6f6a0", 4900000000]],
-"01000000000102fe3dc9208094f3ffd12645477b3dc56f60ec4fa8e6f5d67c565d1c6b9216b36e000000004847304402200af4e47c9b9629dbecc21f73af989bdaa911f7e6f6c2e9394588a3aa68f81e9902204f3fcf6ade7e5abb1295b6774c8e0abd94ae62217367096bc02ee5e435b67da201ffffffff0815cf020f013ed6cf91d29f4202e8a58726b1ac6c79da47c23d1bee0a6925f80000000000ffffffff0100f2052a010000001976a914a30741f8145e5acadf23f751864167f32e0963f788ac000347304402200de66acf4527789bfda55fc5459e214fa6083f936b430a762c629656216805ac0220396f550692cd347171cbc1ef1f51e15282e837bb2b30860dc77c8f78bc8501e503473044022027dc95ad6b740fe5129e7e62a75dd00f291a2aeb1200b84b09d9e3789406b6c002201a9ecd315dd6a0e632ab20bbb98948bc0c6fb204f2c286963bb48517a7058e27034721026dccc749adc2a9d0d89497ac511f760f45c47dc5ed9cf352a58ac706453880aeadab210255a9626aebf5e29c0e6538428ba0d1dcf6ca98ffdf086aa8ced5e0d0215ea465ac00000000", "P2SH,WITNESS"],
+"01000000000102fe3dc9208094f3ffd12645477b3dc56f60ec4fa8e6f5d67c565d1c6b9216b36e000000004847304402200af4e47c9b9629dbecc21f73af989bdaa911f7e6f6c2e9394588a3aa68f81e9902204f3fcf6ade7e5abb1295b6774c8e0abd94ae62217367096bc02ee5e435b67da201ffffffff0815cf020f013ed6cf91d29f4202e8a58726b1ac6c79da47c23d1bee0a6925f80000000000ffffffff0100f2052a010000001976a914a30741f8145e5acadf23f751864167f32e0963f788ac000347304402200de66acf4527789bfda55fc5459e214fa6083f936b430a762c629656216805ac0220396f550692cd347171cbc1ef1f51e15282e837bb2b30860dc77c8f78bc8501e503473044022027dc95ad6b740fe5129e7e62a75dd00f291a2aeb1200b84b09d9e3789406b6c002201a9ecd315dd6a0e632ab20bbb98948bc0c6fb204f2c286963bb48517a7058e27034721026dccc749adc2a9d0d89497ac511f760f45c47dc5ed9cf352a58ac706453880aeadab210255a9626aebf5e29c0e6538428ba0d1dcf6ca98ffdf086aa8ced5e0d0215ea465ac00000000", "P2SH,WITNESS,CONST_SCRIPTCODE"],
["BIP143 example: P2WSH with unexecuted OP_CODESEPARATOR and SINGLE|ANYONECANPAY"],
[[["01c0cf7fba650638e55eb91261b183251fbb466f90dff17f10086817c542b5e9", 0, "0x00 0x20 0xba468eea561b26301e4cf69fa34bde4ad60c81e70f059f045ca9a79931004a4d", 16777215],
["1b2a9a426ba603ba357ce7773cb5805cb9c7c2b386d100d1fc9263513188e680", 0, "0x00 0x20 0xd9bbfbe56af7c4b7f960a70d7ea107156913d9e5a26b0a71429df5e097ca6537", 16777215]],
-"01000000000102e9b542c5176808107ff1df906f46bb1f2583b16112b95ee5380665ba7fcfc0010000000000ffffffff80e68831516392fcd100d186b3c2c7b95c80b53c77e77c35ba03a66b429a2a1b0000000000ffffffff0280969800000000001976a914de4b231626ef508c9a74a8517e6783c0546d6b2888ac80969800000000001976a9146648a8cd4531e1ec47f35916de8e259237294d1e88ac02483045022100f6a10b8604e6dc910194b79ccfc93e1bc0ec7c03453caaa8987f7d6c3413566002206216229ede9b4d6ec2d325be245c5b508ff0339bf1794078e20bfe0babc7ffe683270063ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac024730440220032521802a76ad7bf74d0e2c218b72cf0cbc867066e2e53db905ba37f130397e02207709e2188ed7f08f4c952d9d13986da504502b8c3be59617e043552f506c46ff83275163ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac00000000", "P2SH,WITNESS"],
+"01000000000102e9b542c5176808107ff1df906f46bb1f2583b16112b95ee5380665ba7fcfc0010000000000ffffffff80e68831516392fcd100d186b3c2c7b95c80b53c77e77c35ba03a66b429a2a1b0000000000ffffffff0280969800000000001976a914de4b231626ef508c9a74a8517e6783c0546d6b2888ac80969800000000001976a9146648a8cd4531e1ec47f35916de8e259237294d1e88ac02483045022100f6a10b8604e6dc910194b79ccfc93e1bc0ec7c03453caaa8987f7d6c3413566002206216229ede9b4d6ec2d325be245c5b508ff0339bf1794078e20bfe0babc7ffe683270063ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac024730440220032521802a76ad7bf74d0e2c218b72cf0cbc867066e2e53db905ba37f130397e02207709e2188ed7f08f4c952d9d13986da504502b8c3be59617e043552f506c46ff83275163ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac00000000", "P2SH,WITNESS,CONST_SCRIPTCODE"],
["BIP143 example: Same as the previous example with input-output pairs swapped"],
[[["1b2a9a426ba603ba357ce7773cb5805cb9c7c2b386d100d1fc9263513188e680", 0, "0x00 0x20 0xd9bbfbe56af7c4b7f960a70d7ea107156913d9e5a26b0a71429df5e097ca6537", 16777215],
["01c0cf7fba650638e55eb91261b183251fbb466f90dff17f10086817c542b5e9", 0, "0x00 0x20 0xba468eea561b26301e4cf69fa34bde4ad60c81e70f059f045ca9a79931004a4d", 16777215]],
-"0100000000010280e68831516392fcd100d186b3c2c7b95c80b53c77e77c35ba03a66b429a2a1b0000000000ffffffffe9b542c5176808107ff1df906f46bb1f2583b16112b95ee5380665ba7fcfc0010000000000ffffffff0280969800000000001976a9146648a8cd4531e1ec47f35916de8e259237294d1e88ac80969800000000001976a914de4b231626ef508c9a74a8517e6783c0546d6b2888ac024730440220032521802a76ad7bf74d0e2c218b72cf0cbc867066e2e53db905ba37f130397e02207709e2188ed7f08f4c952d9d13986da504502b8c3be59617e043552f506c46ff83275163ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac02483045022100f6a10b8604e6dc910194b79ccfc93e1bc0ec7c03453caaa8987f7d6c3413566002206216229ede9b4d6ec2d325be245c5b508ff0339bf1794078e20bfe0babc7ffe683270063ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac00000000", "P2SH,WITNESS"],
+"0100000000010280e68831516392fcd100d186b3c2c7b95c80b53c77e77c35ba03a66b429a2a1b0000000000ffffffffe9b542c5176808107ff1df906f46bb1f2583b16112b95ee5380665ba7fcfc0010000000000ffffffff0280969800000000001976a9146648a8cd4531e1ec47f35916de8e259237294d1e88ac80969800000000001976a914de4b231626ef508c9a74a8517e6783c0546d6b2888ac024730440220032521802a76ad7bf74d0e2c218b72cf0cbc867066e2e53db905ba37f130397e02207709e2188ed7f08f4c952d9d13986da504502b8c3be59617e043552f506c46ff83275163ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac02483045022100f6a10b8604e6dc910194b79ccfc93e1bc0ec7c03453caaa8987f7d6c3413566002206216229ede9b4d6ec2d325be245c5b508ff0339bf1794078e20bfe0babc7ffe683270063ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac00000000", "P2SH,WITNESS,CONST_SCRIPTCODE"],
["BIP143 example: P2SH-P2WSH 6-of-6 multisig signed with 6 different SIGHASH types"],
[[["6eb98797a21c6c10aa74edf29d618be109f48a8e94c694f3701e08ca69186436", 1, "HASH160 0x14 0x9993a429037b5d912407a71c252019287b8d27a5 EQUAL", 987654321]],
@@ -504,7 +504,7 @@
"010000000169c12106097dc2e0526493ef67f21269fe888ef05c7a3a5dacab38e1ac8387f1581b0000b64830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0121037a3fb04bcdb09eba90f69961ba1692a3528e45e67c85b200df820212d7594d334aad4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e01ffffffff0101000000000000000000000000", "P2SH,WITNESS"],
["BIP143: correct sighash (without FindAndDelete) = 71c9cd9b2869b9c70b01b1f0360c148f42dee72297db312638df136f43311f23"],
[[["f18783ace138abac5d3a7a5cf08e88fe6912f267ef936452e0c27d090621c169", 7500, "0x00 0x20 0x9e1be07558ea5cc8e02ed1d80c0911048afad949affa36d5c3951e3159dbea19", 200000]],
-"0100000000010169c12106097dc2e0526493ef67f21269fe888ef05c7a3a5dacab38e1ac8387f14c1d000000ffffffff01010000000000000000034830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e012102a9781d66b61fb5a7ef00ac5ad5bc6ffc78be7b44a566e3c87870e1079368df4c4aad4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0100000000", "P2SH,WITNESS"],
+"0100000000010169c12106097dc2e0526493ef67f21269fe888ef05c7a3a5dacab38e1ac8387f14c1d000000ffffffff01010000000000000000034830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e012102a9781d66b61fb5a7ef00ac5ad5bc6ffc78be7b44a566e3c87870e1079368df4c4aad4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0100000000", "P2SH,WITNESS,CONST_SCRIPTCODE"],
["This is multisig version of the FindAndDelete tests"],
["Script is 2 CHECKMULTISIGVERIFY <sig1> <sig2> DROP"],
["52af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960175"],
@@ -514,7 +514,7 @@
"01000000019275cb8d4a485ce95741c013f7c0d28722160008021bb469a11982d47a662896581b0000fd6f01004830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c03959601522102cd74a2809ffeeed0092bc124fd79836706e41f048db3f6ae9df8708cefb83a1c2102e615999372426e46fd107b76eaf007156a507584aa2cc21de9eee3bdbd26d36c4c9552af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960175ffffffff0101000000000000000000000000", "P2SH,WITNESS"],
["BIP143: correct sighash (without FindAndDelete) = c1628a1e7c67f14ca0c27c06e4fdeec2e6d1a73c7a91d7c046ff83e835aebb72"],
[[["9628667ad48219a169b41b020800162287d2c0f713c04157e95c484a8dcb7592", 7500, "0x00 0x20 0x9b66c15b4e0b4eb49fa877982cafded24859fe5b0e2dbfbe4f0df1de7743fd52", 200000]],
-"010000000001019275cb8d4a485ce95741c013f7c0d28722160008021bb469a11982d47a6628964c1d000000ffffffff0101000000000000000007004830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960101022102966f109c54e85d3aee8321301136cedeb9fc710fdef58a9de8a73942f8e567c021034ffc99dd9a79dd3cb31e2ab3e0b09e0e67db41ac068c625cd1f491576016c84e9552af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c039596017500000000", "P2SH,WITNESS"],
+"010000000001019275cb8d4a485ce95741c013f7c0d28722160008021bb469a11982d47a6628964c1d000000ffffffff0101000000000000000007004830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960101022102966f109c54e85d3aee8321301136cedeb9fc710fdef58a9de8a73942f8e567c021034ffc99dd9a79dd3cb31e2ab3e0b09e0e67db41ac068c625cd1f491576016c84e9552af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c039596017500000000", "P2SH,WITNESS,CONST_SCRIPTCODE"],
["Test long outputs, which are streamed using length-prefixed bitcoin strings. This might be surprising."],
[[["1111111111111111111111111111111111111111111111111111111111111111", 0, "0x00 0x14 0x751e76e8199196d454941c45d1b3a323f1433bd6", 5000000]],
diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp
index edc41ec42c..6df5aec9c9 100644
--- a/src/test/dbwrapper_tests.cpp
+++ b/src/test/dbwrapper_tests.cpp
@@ -12,7 +12,7 @@
#include <boost/test/unit_test.hpp>
// Test if a string consists entirely of null characters
-bool is_null_key(const std::vector<unsigned char>& key) {
+static bool is_null_key(const std::vector<unsigned char>& key) {
bool isnull = true;
for (unsigned int i = 0; i < key.size(); i++)
diff --git a/src/test/main_tests.cpp b/src/test/main_tests.cpp
index 570c205731..8676a099da 100644
--- a/src/test/main_tests.cpp
+++ b/src/test/main_tests.cpp
@@ -58,8 +58,8 @@ BOOST_AUTO_TEST_CASE(subsidy_limit_test)
BOOST_CHECK_EQUAL(nSum, CAmount{2099999997690000});
}
-bool ReturnFalse() { return false; }
-bool ReturnTrue() { return true; }
+static bool ReturnFalse() { return false; }
+static bool ReturnTrue() { return true; }
BOOST_AUTO_TEST_CASE(test_combiner_all)
{
diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp
index 37615d08b3..c4b18151a7 100644
--- a/src/test/mempool_tests.cpp
+++ b/src/test/mempool_tests.cpp
@@ -106,7 +106,7 @@ BOOST_AUTO_TEST_CASE(MempoolRemoveTest)
}
template<typename name>
-void CheckSort(CTxMemPool &pool, std::vector<std::string> &sortedOrder)
+static void CheckSort(CTxMemPool &pool, std::vector<std::string> &sortedOrder)
{
BOOST_CHECK_EQUAL(pool.size(), sortedOrder.size());
typename CTxMemPool::indexed_transaction_set::index<name>::type::iterator it = pool.mapTx.get<name>().begin();
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index c98566f9ca..9a325f5f4c 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -82,7 +82,7 @@ struct {
{2, 0xbbbeb305}, {2, 0xfe1c810a},
};
-CBlockIndex CreateBlockIndex(int nHeight)
+static CBlockIndex CreateBlockIndex(int nHeight)
{
CBlockIndex index;
index.nHeight = nHeight;
@@ -90,7 +90,7 @@ CBlockIndex CreateBlockIndex(int nHeight)
return index;
}
-bool TestSequenceLocks(const CTransaction &tx, int flags)
+static bool TestSequenceLocks(const CTransaction &tx, int flags)
{
LOCK(mempool.cs);
return CheckSequenceLocks(tx, flags);
@@ -99,7 +99,7 @@ bool TestSequenceLocks(const CTransaction &tx, int flags)
// Test suite for ancestor feerate transaction selection.
// Implemented as an additional function, rather than a separate test case,
// to allow reusing the blockchain created in CreateNewBlock_validity.
-void TestPackageSelection(const CChainParams& chainparams, CScript scriptPubKey, std::vector<CTransactionRef>& txFirst)
+static void TestPackageSelection(const CChainParams& chainparams, CScript scriptPubKey, std::vector<CTransactionRef>& txFirst)
{
// Test the ancestor feerate transaction selection.
TestMemPoolEntryHelper entry;
diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp
index b593f9633c..77db9f5c57 100644
--- a/src/test/multisig_tests.cpp
+++ b/src/test/multisig_tests.cpp
@@ -18,8 +18,8 @@
BOOST_FIXTURE_TEST_SUITE(multisig_tests, BasicTestingSetup)
-CScript
-sign_multisig(CScript scriptPubKey, std::vector<CKey> keys, CTransaction transaction, int whichIn)
+static CScript
+sign_multisig(const CScript& scriptPubKey, const std::vector<CKey>& keys, const CTransaction& transaction, int whichIn)
{
uint256 hash = SignatureHash(scriptPubKey, transaction, whichIn, SIGHASH_ALL, 0, SigVersion::BASE);
diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp
index 436ae696d1..42e615ab0c 100644
--- a/src/test/net_tests.cpp
+++ b/src/test/net_tests.cpp
@@ -63,7 +63,7 @@ public:
}
};
-CDataStream AddrmanToStream(CAddrManSerializationMock& _addrman)
+static CDataStream AddrmanToStream(CAddrManSerializationMock& _addrman)
{
CDataStream ssPeersIn(SER_DISK, CLIENT_VERSION);
ssPeersIn << Params().MessageStart();
diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp
index 242e1fb7ff..da591547d7 100644
--- a/src/test/rpc_tests.cpp
+++ b/src/test/rpc_tests.cpp
@@ -247,14 +247,14 @@ BOOST_AUTO_TEST_CASE(rpc_ban)
ar = r.get_array();
BOOST_CHECK_EQUAL(ar.size(), 0U);
- BOOST_CHECK_NO_THROW(r = CallRPC(std::string("setban 127.0.0.0/24 add 1607731200 true")));
+ BOOST_CHECK_NO_THROW(r = CallRPC(std::string("setban 127.0.0.0/24 add 9907731200 true")));
BOOST_CHECK_NO_THROW(r = CallRPC(std::string("listbanned")));
ar = r.get_array();
o1 = ar[0].get_obj();
adr = find_value(o1, "address");
UniValue banned_until = find_value(o1, "banned_until");
BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/24");
- BOOST_CHECK_EQUAL(banned_until.get_int64(), 1607731200); // absolute time check
+ BOOST_CHECK_EQUAL(banned_until.get_int64(), 9907731200); // absolute time check
BOOST_CHECK_NO_THROW(CallRPC(std::string("clearbanned")));
diff --git a/src/test/script_standard_tests.cpp b/src/test/script_standard_tests.cpp
index 767c5fdbd2..ff0bf6c66d 100644
--- a/src/test/script_standard_tests.cpp
+++ b/src/test/script_standard_tests.cpp
@@ -561,7 +561,14 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
keystore.AddKey(keys[1]);
result = IsMine(keystore, scriptPubKey, isInvalid);
- BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
+ BOOST_CHECK(!isInvalid);
+
+ // Keystore has 2/2 keys and the script
+ keystore.AddCScript(scriptPubKey);
+
+ result = IsMine(keystore, scriptPubKey, isInvalid);
+ BOOST_CHECK_EQUAL(result, ISMINE_NO);
BOOST_CHECK(!isInvalid);
}
diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp
index 46a2d13745..33b31565a6 100644
--- a/src/test/script_tests.cpp
+++ b/src/test/script_tests.cpp
@@ -97,9 +97,11 @@ static ScriptErrorDesc script_errors[]={
{SCRIPT_ERR_WITNESS_MALLEATED_P2SH, "WITNESS_MALLEATED_P2SH"},
{SCRIPT_ERR_WITNESS_UNEXPECTED, "WITNESS_UNEXPECTED"},
{SCRIPT_ERR_WITNESS_PUBKEYTYPE, "WITNESS_PUBKEYTYPE"},
+ {SCRIPT_ERR_OP_CODESEPARATOR, "OP_CODESEPARATOR"},
+ {SCRIPT_ERR_SIG_FINDANDDELETE, "SIG_FINDANDDELETE"},
};
-const char *FormatScriptError(ScriptError_t err)
+static const char *FormatScriptError(ScriptError_t err)
{
for (unsigned int i=0; i<ARRAYLEN(script_errors); ++i)
if (script_errors[i].err == err)
@@ -108,7 +110,7 @@ const char *FormatScriptError(ScriptError_t err)
return "";
}
-ScriptError_t ParseScriptError(const std::string &name)
+static ScriptError_t ParseScriptError(const std::string &name)
{
for (unsigned int i=0; i<ARRAYLEN(script_errors); ++i)
if (script_errors[i].name == name)
@@ -1028,8 +1030,8 @@ BOOST_AUTO_TEST_CASE(script_PushData)
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
}
-CScript
-sign_multisig(CScript scriptPubKey, std::vector<CKey> keys, CTransaction transaction)
+static CScript
+sign_multisig(const CScript& scriptPubKey, const std::vector<CKey>& keys, const CTransaction& transaction)
{
uint256 hash = SignatureHash(scriptPubKey, transaction, 0, SIGHASH_ALL, 0, SigVersion::BASE);
@@ -1052,8 +1054,8 @@ sign_multisig(CScript scriptPubKey, std::vector<CKey> keys, CTransaction transac
}
return result;
}
-CScript
-sign_multisig(CScript scriptPubKey, const CKey &key, CTransaction transaction)
+static CScript
+sign_multisig(const CScript& scriptPubKey, const CKey& key, const CTransaction& transaction)
{
std::vector<CKey> keys;
keys.push_back(key);
@@ -1349,43 +1351,43 @@ BOOST_AUTO_TEST_CASE(script_FindAndDelete)
s = CScript() << OP_1 << OP_2;
d = CScript(); // delete nothing should be a no-op
expect = s;
- BOOST_CHECK_EQUAL(s.FindAndDelete(d), 0);
+ BOOST_CHECK_EQUAL(FindAndDelete(s, d), 0);
BOOST_CHECK(s == expect);
s = CScript() << OP_1 << OP_2 << OP_3;
d = CScript() << OP_2;
expect = CScript() << OP_1 << OP_3;
- BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1);
+ BOOST_CHECK_EQUAL(FindAndDelete(s, d), 1);
BOOST_CHECK(s == expect);
s = CScript() << OP_3 << OP_1 << OP_3 << OP_3 << OP_4 << OP_3;
d = CScript() << OP_3;
expect = CScript() << OP_1 << OP_4;
- BOOST_CHECK_EQUAL(s.FindAndDelete(d), 4);
+ BOOST_CHECK_EQUAL(FindAndDelete(s, d), 4);
BOOST_CHECK(s == expect);
s = ScriptFromHex("0302ff03"); // PUSH 0x02ff03 onto stack
d = ScriptFromHex("0302ff03");
expect = CScript();
- BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1);
+ BOOST_CHECK_EQUAL(FindAndDelete(s, d), 1);
BOOST_CHECK(s == expect);
s = ScriptFromHex("0302ff030302ff03"); // PUSH 0x2ff03 PUSH 0x2ff03
d = ScriptFromHex("0302ff03");
expect = CScript();
- BOOST_CHECK_EQUAL(s.FindAndDelete(d), 2);
+ BOOST_CHECK_EQUAL(FindAndDelete(s, d), 2);
BOOST_CHECK(s == expect);
s = ScriptFromHex("0302ff030302ff03");
d = ScriptFromHex("02");
expect = s; // FindAndDelete matches entire opcodes
- BOOST_CHECK_EQUAL(s.FindAndDelete(d), 0);
+ BOOST_CHECK_EQUAL(FindAndDelete(s, d), 0);
BOOST_CHECK(s == expect);
s = ScriptFromHex("0302ff030302ff03");
d = ScriptFromHex("ff");
expect = s;
- BOOST_CHECK_EQUAL(s.FindAndDelete(d), 0);
+ BOOST_CHECK_EQUAL(FindAndDelete(s, d), 0);
BOOST_CHECK(s == expect);
// This is an odd edge case: strip of the push-three-bytes
@@ -1393,44 +1395,44 @@ BOOST_AUTO_TEST_CASE(script_FindAndDelete)
s = ScriptFromHex("0302ff030302ff03");
d = ScriptFromHex("03");
expect = CScript() << ParseHex("ff03") << ParseHex("ff03");
- BOOST_CHECK_EQUAL(s.FindAndDelete(d), 2);
+ BOOST_CHECK_EQUAL(FindAndDelete(s, d), 2);
BOOST_CHECK(s == expect);
// Byte sequence that spans multiple opcodes:
s = ScriptFromHex("02feed5169"); // PUSH(0xfeed) OP_1 OP_VERIFY
d = ScriptFromHex("feed51");
expect = s;
- BOOST_CHECK_EQUAL(s.FindAndDelete(d), 0); // doesn't match 'inside' opcodes
+ BOOST_CHECK_EQUAL(FindAndDelete(s, d), 0); // doesn't match 'inside' opcodes
BOOST_CHECK(s == expect);
s = ScriptFromHex("02feed5169"); // PUSH(0xfeed) OP_1 OP_VERIFY
d = ScriptFromHex("02feed51");
expect = ScriptFromHex("69");
- BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1);
+ BOOST_CHECK_EQUAL(FindAndDelete(s, d), 1);
BOOST_CHECK(s == expect);
s = ScriptFromHex("516902feed5169");
d = ScriptFromHex("feed51");
expect = s;
- BOOST_CHECK_EQUAL(s.FindAndDelete(d), 0);
+ BOOST_CHECK_EQUAL(FindAndDelete(s, d), 0);
BOOST_CHECK(s == expect);
s = ScriptFromHex("516902feed5169");
d = ScriptFromHex("02feed51");
expect = ScriptFromHex("516969");
- BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1);
+ BOOST_CHECK_EQUAL(FindAndDelete(s, d), 1);
BOOST_CHECK(s == expect);
s = CScript() << OP_0 << OP_0 << OP_1 << OP_1;
d = CScript() << OP_0 << OP_1;
expect = CScript() << OP_0 << OP_1; // FindAndDelete is single-pass
- BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1);
+ BOOST_CHECK_EQUAL(FindAndDelete(s, d), 1);
BOOST_CHECK(s == expect);
s = CScript() << OP_0 << OP_0 << OP_1 << OP_0 << OP_1 << OP_1;
d = CScript() << OP_0 << OP_1;
expect = CScript() << OP_0 << OP_1; // FindAndDelete is single-pass
- BOOST_CHECK_EQUAL(s.FindAndDelete(d), 2);
+ BOOST_CHECK_EQUAL(FindAndDelete(s, d), 2);
BOOST_CHECK(s == expect);
// Another weird edge case:
@@ -1438,13 +1440,13 @@ BOOST_AUTO_TEST_CASE(script_FindAndDelete)
s = ScriptFromHex("0003feed");
d = ScriptFromHex("03feed"); // ... can remove the invalid push
expect = ScriptFromHex("00");
- BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1);
+ BOOST_CHECK_EQUAL(FindAndDelete(s, d), 1);
BOOST_CHECK(s == expect);
s = ScriptFromHex("0003feed");
d = ScriptFromHex("00");
expect = ScriptFromHex("03feed");
- BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1);
+ BOOST_CHECK_EQUAL(FindAndDelete(s, d), 1);
BOOST_CHECK(s == expect);
}
diff --git a/src/test/serialize_tests.cpp b/src/test/serialize_tests.cpp
index 94164346f3..eba58e0042 100644
--- a/src/test/serialize_tests.cpp
+++ b/src/test/serialize_tests.cpp
@@ -23,7 +23,7 @@ protected:
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)), txval(MakeTransactionRef(txvalin))
+ CSerializeMethodsTestSingle(int intvalin, bool boolvalin, std::string stringvalin, const char* charstrvalin, const CTransactionRef& txvalin) : intval(intvalin), boolval(boolvalin), stringval(std::move(stringvalin)), txval(txvalin)
{
memcpy(charstrval, charstrvalin, sizeof(charstrval));
}
@@ -350,8 +350,9 @@ BOOST_AUTO_TEST_CASE(class_methods)
std::string stringval("testing");
const char charstrval[16] = "testing charstr";
CMutableTransaction txval;
- CSerializeMethodsTestSingle methodtest1(intval, boolval, stringval, charstrval, txval);
- CSerializeMethodsTestMany methodtest2(intval, boolval, stringval, charstrval, txval);
+ CTransactionRef tx_ref{MakeTransactionRef(txval)};
+ CSerializeMethodsTestSingle methodtest1(intval, boolval, stringval, charstrval, tx_ref);
+ CSerializeMethodsTestMany methodtest2(intval, boolval, stringval, charstrval, tx_ref);
CSerializeMethodsTestSingle methodtest3;
CSerializeMethodsTestMany methodtest4;
CDataStream ss(SER_DISK, PROTOCOL_VERSION);
diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp
index a2bd8998b1..6b8856ef47 100644
--- a/src/test/sighash_tests.cpp
+++ b/src/test/sighash_tests.cpp
@@ -35,7 +35,7 @@ uint256 static SignatureHashOld(CScript scriptCode, const CTransaction& txTo, un
// In case concatenating two scripts ends up with two codeseparators,
// or an extra one at the end, this prevents all those possible incompatibilities.
- scriptCode.FindAndDelete(CScript(OP_CODESEPARATOR));
+ FindAndDelete(scriptCode, CScript(OP_CODESEPARATOR));
// Blank out other inputs' signatures
for (unsigned int i = 0; i < txTmp.vin.size(); i++)
diff --git a/src/test/sigopcount_tests.cpp b/src/test/sigopcount_tests.cpp
index b4d8a2419e..86647e72eb 100644
--- a/src/test/sigopcount_tests.cpp
+++ b/src/test/sigopcount_tests.cpp
@@ -67,7 +67,7 @@ BOOST_AUTO_TEST_CASE(GetSigOpCount)
* Verifies script execution of the zeroth scriptPubKey of tx output and
* zeroth scriptSig and witness of tx input.
*/
-ScriptError VerifyWithFlag(const CTransaction& output, const CMutableTransaction& input, int flags)
+static ScriptError VerifyWithFlag(const CTransaction& output, const CMutableTransaction& input, int flags)
{
ScriptError error;
CTransaction inputi(input);
@@ -82,7 +82,7 @@ ScriptError VerifyWithFlag(const CTransaction& output, const CMutableTransaction
* and witness such that spendingTx spends output zero of creationTx.
* Also inserts creationTx's output into the coins view.
*/
-void BuildTxs(CMutableTransaction& spendingTx, CCoinsViewCache& coins, CMutableTransaction& creationTx, const CScript& scriptPubKey, const CScript& scriptSig, const CScriptWitness& witness)
+static void BuildTxs(CMutableTransaction& spendingTx, CCoinsViewCache& coins, CMutableTransaction& creationTx, const CScript& scriptPubKey, const CScript& scriptSig, const CScriptWitness& witness)
{
creationTx.nVersion = 1;
creationTx.vin.resize(1);
diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp
index ff20d4b3d7..fe816a6f79 100644
--- a/src/test/test_bitcoin.cpp
+++ b/src/test/test_bitcoin.cpp
@@ -47,7 +47,6 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName)
SetupNetworking();
InitSignatureCache();
InitScriptExecutionCache();
- fPrintToDebugLog = false; // don't want to write to debug.log file
fCheckBlockIndex = true;
SelectParams(chainName);
noui_connect();
@@ -85,7 +84,7 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
{
CValidationState state;
if (!ActivateBestChain(state, chainparams)) {
- throw std::runtime_error("ActivateBestChain failed.");
+ throw std::runtime_error(strprintf("ActivateBestChain failed. (%s)", FormatStateMessage(state)));
}
}
nScriptCheckThreads = 3;
@@ -123,7 +122,7 @@ TestChain100Setup::TestChain100Setup() : TestingSetup(CBaseChainParams::REGTEST)
{
std::vector<CMutableTransaction> noTxns;
CBlock b = CreateAndProcessBlock(noTxns, scriptPubKey);
- coinbaseTxns.push_back(*b.vtx[0]);
+ m_coinbase_txns.push_back(b.vtx[0]);
}
}
@@ -164,12 +163,12 @@ TestChain100Setup::~TestChain100Setup()
CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CMutableTransaction &tx) {
- CTransaction txn(tx);
- return FromTx(txn);
+ return FromTx(MakeTransactionRef(tx));
}
-CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CTransaction &txn) {
- return CTxMemPoolEntry(MakeTransactionRef(txn), nFee, nTime, nHeight,
+CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CTransactionRef& tx)
+{
+ return CTxMemPoolEntry(tx, nFee, nTime, nHeight,
spendsCoinbase, sigOpCost, lp);
}
diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h
index 8136da3aa9..1f91eb622c 100644
--- a/src/test/test_bitcoin.h
+++ b/src/test/test_bitcoin.h
@@ -87,7 +87,7 @@ struct TestChain100Setup : public TestingSetup {
~TestChain100Setup();
- std::vector<CTransaction> coinbaseTxns; // For convenience, coinbase transactions
+ std::vector<CTransactionRef> m_coinbase_txns; // For convenience, coinbase transactions
CKey coinbaseKey; // private/public key needed to spend coinbase transactions
};
@@ -107,8 +107,8 @@ struct TestMemPoolEntryHelper
nFee(0), nTime(0), nHeight(1),
spendsCoinbase(false), sigOpCost(4) { }
- CTxMemPoolEntry FromTx(const CMutableTransaction &tx);
- CTxMemPoolEntry FromTx(const CTransaction &tx);
+ CTxMemPoolEntry FromTx(const CMutableTransaction& tx);
+ CTxMemPoolEntry FromTx(const CTransactionRef& tx);
// Change the default value
TestMemPoolEntryHelper &Fee(CAmount _fee) { nFee = _fee; return *this; }
diff --git a/src/test/test_bitcoin_fuzzy.cpp b/src/test/test_bitcoin_fuzzy.cpp
index 69e9804c2f..b2daa2adb5 100644
--- a/src/test/test_bitcoin_fuzzy.cpp
+++ b/src/test/test_bitcoin_fuzzy.cpp
@@ -52,7 +52,7 @@ enum TEST_ID {
TEST_ID_END
};
-bool read_stdin(std::vector<uint8_t> &data) {
+static bool read_stdin(std::vector<uint8_t> &data) {
uint8_t buffer[1024];
ssize_t length=0;
while((length = read(STDIN_FILENO, buffer, 1024)) > 0) {
@@ -63,7 +63,7 @@ bool read_stdin(std::vector<uint8_t> &data) {
return length==0;
}
-int test_one_input(std::vector<uint8_t> buffer) {
+static int test_one_input(std::vector<uint8_t> buffer) {
if (buffer.size() < sizeof(uint32_t)) return 0;
uint32_t test_id = 0xffffffff;
diff --git a/src/test/torcontrol_tests.cpp b/src/test/torcontrol_tests.cpp
index 9ece9e70c2..8bd5ce1222 100644
--- a/src/test/torcontrol_tests.cpp
+++ b/src/test/torcontrol_tests.cpp
@@ -10,7 +10,7 @@
BOOST_FIXTURE_TEST_SUITE(torcontrol_tests, BasicTestingSetup)
-void CheckSplitTorReplyLine(std::string input, std::string command, std::string args)
+static void CheckSplitTorReplyLine(std::string input, std::string command, std::string args)
{
BOOST_TEST_MESSAGE(std::string("CheckSplitTorReplyLine(") + input + ")");
auto ret = SplitTorReplyLine(input);
@@ -51,7 +51,7 @@ BOOST_AUTO_TEST_CASE(util_SplitTorReplyLine)
CheckSplitTorReplyLine("COMMAND EVEN+more ARGS", "COMMAND", " EVEN+more ARGS");
}
-void CheckParseTorReplyMapping(std::string input, std::map<std::string,std::string> expected)
+static void CheckParseTorReplyMapping(std::string input, std::map<std::string,std::string> expected)
{
BOOST_TEST_MESSAGE(std::string("CheckParseTorReplyMapping(") + input + ")");
auto ret = ParseTorReplyMapping(input);
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index b222392ee5..cc72e96eb1 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -53,6 +53,7 @@ static std::map<std::string, unsigned int> mapFlagNames = {
{std::string("WITNESS"), (unsigned int)SCRIPT_VERIFY_WITNESS},
{std::string("DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM},
{std::string("WITNESS_PUBKEYTYPE"), (unsigned int)SCRIPT_VERIFY_WITNESS_PUBKEYTYPE},
+ {std::string("CONST_SCRIPTCODE"), (unsigned int)SCRIPT_VERIFY_CONST_SCRIPTCODE},
};
unsigned int ParseScriptFlags(std::string strFlags)
@@ -343,7 +344,7 @@ BOOST_AUTO_TEST_CASE(test_Get)
BOOST_CHECK_EQUAL(coins.GetValueIn(t1), (50+21+22)*CENT);
}
-void CreateCreditAndSpend(const CKeyStore& keystore, const CScript& outscript, CTransactionRef& output, CMutableTransaction& input, bool success = true)
+static void CreateCreditAndSpend(const CKeyStore& keystore, const CScript& outscript, CTransactionRef& output, CMutableTransaction& input, bool success = true)
{
CMutableTransaction outputm;
outputm.nVersion = 1;
@@ -381,7 +382,7 @@ void CreateCreditAndSpend(const CKeyStore& keystore, const CScript& outscript, C
assert(input.vin[0].scriptWitness.stack == inputm.vin[0].scriptWitness.stack);
}
-void CheckWithFlag(const CTransactionRef& output, const CMutableTransaction& input, int flags, bool success)
+static void CheckWithFlag(const CTransactionRef& output, const CMutableTransaction& input, int flags, bool success)
{
ScriptError error;
CTransaction inputi(input);
@@ -404,7 +405,7 @@ static CScript PushAll(const std::vector<valtype>& values)
return result;
}
-void ReplaceRedeemScript(CScript& script, const CScript& redeemScript)
+static void ReplaceRedeemScript(CScript& script, const CScript& redeemScript)
{
std::vector<valtype> stack;
EvalScript(stack, script, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker(), SigVersion::BASE);
diff --git a/src/test/txindex_tests.cpp b/src/test/txindex_tests.cpp
new file mode 100644
index 0000000000..14158f2875
--- /dev/null
+++ b/src/test/txindex_tests.cpp
@@ -0,0 +1,66 @@
+// Copyright (c) 2017-2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <index/txindex.h>
+#include <script/standard.h>
+#include <test/test_bitcoin.h>
+#include <util.h>
+#include <utiltime.h>
+#include <validation.h>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_SUITE(txindex_tests)
+
+BOOST_FIXTURE_TEST_CASE(txindex_initial_sync, TestChain100Setup)
+{
+ TxIndex txindex(MakeUnique<TxIndexDB>(1 << 20, true));
+
+ CTransactionRef tx_disk;
+ uint256 block_hash;
+
+ // Transaction should not be found in the index before it is started.
+ for (const auto& txn : m_coinbase_txns) {
+ BOOST_CHECK(!txindex.FindTx(txn->GetHash(), block_hash, tx_disk));
+ }
+
+ // BlockUntilSyncedToCurrentChain should return false before txindex is started.
+ BOOST_CHECK(!txindex.BlockUntilSyncedToCurrentChain());
+
+ txindex.Start();
+
+ // Allow tx index to catch up with the block index.
+ constexpr int64_t timeout_ms = 10 * 1000;
+ int64_t time_start = GetTimeMillis();
+ while (!txindex.BlockUntilSyncedToCurrentChain()) {
+ BOOST_REQUIRE(time_start + timeout_ms > GetTimeMillis());
+ MilliSleep(100);
+ }
+
+ // Check that txindex has all txs that were in the chain before it started.
+ for (const auto& txn : m_coinbase_txns) {
+ if (!txindex.FindTx(txn->GetHash(), block_hash, tx_disk)) {
+ BOOST_ERROR("FindTx failed");
+ } else if (tx_disk->GetHash() != txn->GetHash()) {
+ BOOST_ERROR("Read incorrect tx");
+ }
+ }
+
+ // Check that new transactions in new blocks make it into the index.
+ for (int i = 0; i < 10; i++) {
+ CScript coinbase_script_pub_key = GetScriptForDestination(coinbaseKey.GetPubKey().GetID());
+ std::vector<CMutableTransaction> no_txns;
+ const CBlock& block = CreateAndProcessBlock(no_txns, coinbase_script_pub_key);
+ const CTransaction& txn = *block.vtx[0];
+
+ BOOST_CHECK(txindex.BlockUntilSyncedToCurrentChain());
+ if (!txindex.FindTx(txn.GetHash(), block_hash, tx_disk)) {
+ BOOST_ERROR("FindTx failed");
+ } else if (tx_disk->GetHash() != txn.GetHash()) {
+ BOOST_ERROR("Read incorrect tx");
+ }
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp
index 71f3d727b1..eb23ba5ad2 100644
--- a/src/test/txvalidationcache_tests.cpp
+++ b/src/test/txvalidationcache_tests.cpp
@@ -48,7 +48,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
{
spends[i].nVersion = 1;
spends[i].vin.resize(1);
- spends[i].vin[0].prevout.hash = coinbaseTxns[0].GetHash();
+ spends[i].vin[0].prevout.hash = m_coinbase_txns[0]->GetHash();
spends[i].vin[0].prevout.n = 0;
spends[i].vout.resize(1);
spends[i].vout[0].nValue = 11*CENT;
@@ -102,7 +102,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
// should fail.
// Capture this interaction with the upgraded_nop argument: set it when evaluating
// any script flag that is implemented as an upgraded NOP code.
-void ValidateCheckInputsForAllFlags(CMutableTransaction &tx, uint32_t failing_flags, bool add_to_cache)
+static void ValidateCheckInputsForAllFlags(CMutableTransaction &tx, uint32_t failing_flags, bool add_to_cache)
{
PrecomputedTransactionData txdata(tx);
// If we add many more flags, this loop can get too expensive, but we can
@@ -167,7 +167,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
spend_tx.nVersion = 1;
spend_tx.vin.resize(1);
- spend_tx.vin[0].prevout.hash = coinbaseTxns[0].GetHash();
+ spend_tx.vin[0].prevout.hash = m_coinbase_txns[0]->GetHash();
spend_tx.vin[0].prevout.n = 0;
spend_tx.vout.resize(4);
spend_tx.vout[0].nValue = 11*CENT;
diff --git a/src/test/uint256_tests.cpp b/src/test/uint256_tests.cpp
index 20ed29f59b..79217fa430 100644
--- a/src/test/uint256_tests.cpp
+++ b/src/test/uint256_tests.cpp
@@ -48,7 +48,7 @@ const unsigned char MaxArray[] =
const uint256 MaxL = uint256(std::vector<unsigned char>(MaxArray,MaxArray+32));
const uint160 MaxS = uint160(std::vector<unsigned char>(MaxArray,MaxArray+20));
-std::string ArrayToString(const unsigned char A[], unsigned int width)
+static std::string ArrayToString(const unsigned char A[], unsigned int width)
{
std::stringstream Stream;
Stream << std::hex;
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index 344113b60c..1c3acfb1a5 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -572,7 +572,7 @@ BOOST_AUTO_TEST_CASE(util_GetChainName)
BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error);
// check setting the network to test (and thus making
- // [test] regtest=1 potentially relevent) doesn't break things
+ // [test] regtest=1 potentially relevant) doesn't break things
test_args.SelectConfigNetwork("test");
test_args.ParseParameters(0, (char**)argv_testnet);
diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp
index 92ef58e517..7442825300 100644
--- a/src/test/versionbits_tests.cpp
+++ b/src/test/versionbits_tests.cpp
@@ -12,7 +12,7 @@
#include <boost/test/unit_test.hpp>
/* Define a virtual block time, one block per 10 minutes after Nov 14 2014, 0:55:36am */
-int32_t TestTime(int nHeight) { return 1415926536 + 600 * nHeight; }
+static int32_t TestTime(int nHeight) { return 1415926536 + 600 * nHeight; }
static const Consensus::Params paramsDummy = Consensus::Params();
diff --git a/src/threadinterrupt.cpp b/src/threadinterrupt.cpp
index 5d932091cb..7da4e136ef 100644
--- a/src/threadinterrupt.cpp
+++ b/src/threadinterrupt.cpp
@@ -5,6 +5,8 @@
#include <threadinterrupt.h>
+CThreadInterrupt::CThreadInterrupt() : flag(false) {}
+
CThreadInterrupt::operator bool() const
{
return flag.load(std::memory_order_acquire);
diff --git a/src/threadinterrupt.h b/src/threadinterrupt.h
index 54e3102808..d373e3c371 100644
--- a/src/threadinterrupt.h
+++ b/src/threadinterrupt.h
@@ -18,6 +18,7 @@
class CThreadInterrupt
{
public:
+ CThreadInterrupt();
explicit operator bool() const;
void operator()();
void reset();
diff --git a/src/timedata.cpp b/src/timedata.cpp
index a803b2fc87..dfb8fe55af 100644
--- a/src/timedata.cpp
+++ b/src/timedata.cpp
@@ -17,7 +17,7 @@
static CCriticalSection cs_nTimeOffset;
-static int64_t nTimeOffset = 0;
+static int64_t nTimeOffset GUARDED_BY(cs_nTimeOffset) = 0;
/**
* "Never go to sea with two chronometers; take one or three."
@@ -110,9 +110,9 @@ void AddTimeData(const CNetAddr& ip, int64_t nOffsetSample)
if (LogAcceptCategory(BCLog::NET)) {
for (int64_t n : vSorted) {
- LogPrint(BCLog::NET, "%+d ", n);
+ LogPrint(BCLog::NET, "%+d ", n); /* Continued */
}
- LogPrint(BCLog::NET, "| ");
+ LogPrint(BCLog::NET, "| "); /* Continued */
LogPrint(BCLog::NET, "nTimeOffset = %+d (%+d minutes)\n", nTimeOffset, nTimeOffset/60);
}
diff --git a/src/txdb.cpp b/src/txdb.cpp
index 45ce94ae42..333d3596c1 100644
--- a/src/txdb.cpp
+++ b/src/txdb.cpp
@@ -22,6 +22,7 @@ static const char DB_COIN = 'C';
static const char DB_COINS = 'c';
static const char DB_BLOCK_FILES = 'f';
static const char DB_TXINDEX = 't';
+static const char DB_TXINDEX_BLOCK = 'T';
static const char DB_BLOCK_INDEX = 'b';
static const char DB_BEST_BLOCK = 'B';
@@ -424,3 +425,173 @@ bool CCoinsViewDB::Upgrade() {
LogPrintf("[%s].\n", ShutdownRequested() ? "CANCELLED" : "DONE");
return !ShutdownRequested();
}
+
+TxIndexDB::TxIndexDB(size_t n_cache_size, bool f_memory, bool f_wipe) :
+ CDBWrapper(GetDataDir() / "indexes" / "txindex", n_cache_size, f_memory, f_wipe)
+{}
+
+bool TxIndexDB::ReadTxPos(const uint256 &txid, CDiskTxPos& pos) const
+{
+ return Read(std::make_pair(DB_TXINDEX, txid), pos);
+}
+
+bool TxIndexDB::WriteTxs(const std::vector<std::pair<uint256, CDiskTxPos>>& v_pos)
+{
+ CDBBatch batch(*this);
+ for (const auto& tuple : v_pos) {
+ batch.Write(std::make_pair(DB_TXINDEX, tuple.first), tuple.second);
+ }
+ return WriteBatch(batch);
+}
+
+bool TxIndexDB::ReadBestBlock(CBlockLocator& locator) const
+{
+ bool success = Read(DB_BEST_BLOCK, locator);
+ if (!success) {
+ locator.SetNull();
+ }
+ return success;
+}
+
+bool TxIndexDB::WriteBestBlock(const CBlockLocator& locator)
+{
+ return Write(DB_BEST_BLOCK, locator);
+}
+
+/*
+ * Safely persist a transfer of data from the old txindex database to the new one, and compact the
+ * range of keys updated. This is used internally by MigrateData.
+ */
+static void WriteTxIndexMigrationBatches(TxIndexDB& newdb, CBlockTreeDB& olddb,
+ CDBBatch& batch_newdb, CDBBatch& batch_olddb,
+ const std::pair<unsigned char, uint256>& begin_key,
+ const std::pair<unsigned char, uint256>& end_key)
+{
+ // Sync new DB changes to disk before deleting from old DB.
+ newdb.WriteBatch(batch_newdb, /*fSync=*/ true);
+ olddb.WriteBatch(batch_olddb);
+ olddb.CompactRange(begin_key, end_key);
+
+ batch_newdb.Clear();
+ batch_olddb.Clear();
+}
+
+bool TxIndexDB::MigrateData(CBlockTreeDB& block_tree_db, const CBlockLocator& best_locator)
+{
+ // The prior implementation of txindex was always in sync with block index
+ // and presence was indicated with a boolean DB flag. If the flag is set,
+ // this means the txindex from a previous version is valid and in sync with
+ // the chain tip. The first step of the migration is to unset the flag and
+ // write the chain hash to a separate key, DB_TXINDEX_BLOCK. After that, the
+ // index entries are copied over in batches to the new database. Finally,
+ // DB_TXINDEX_BLOCK is erased from the old database and the block hash is
+ // written to the new database.
+ //
+ // Unsetting the boolean flag ensures that if the node is downgraded to a
+ // previous version, it will not see a corrupted, partially migrated index
+ // -- it will see that the txindex is disabled. When the node is upgraded
+ // again, the migration will pick up where it left off and sync to the block
+ // with hash DB_TXINDEX_BLOCK.
+ bool f_legacy_flag = false;
+ block_tree_db.ReadFlag("txindex", f_legacy_flag);
+ if (f_legacy_flag) {
+ if (!block_tree_db.Write(DB_TXINDEX_BLOCK, best_locator)) {
+ return error("%s: cannot write block indicator", __func__);
+ }
+ if (!block_tree_db.WriteFlag("txindex", false)) {
+ return error("%s: cannot write block index db flag", __func__);
+ }
+ }
+
+ CBlockLocator locator;
+ if (!block_tree_db.Read(DB_TXINDEX_BLOCK, locator)) {
+ return true;
+ }
+
+ int64_t count = 0;
+ LogPrintf("Upgrading txindex database... [0%%]\n");
+ uiInterface.ShowProgress(_("Upgrading txindex database"), 0, true);
+ int report_done = 0;
+ const size_t batch_size = 1 << 24; // 16 MiB
+
+ CDBBatch batch_newdb(*this);
+ CDBBatch batch_olddb(block_tree_db);
+
+ std::pair<unsigned char, uint256> key;
+ std::pair<unsigned char, uint256> begin_key{DB_TXINDEX, uint256()};
+ std::pair<unsigned char, uint256> prev_key = begin_key;
+
+ bool interrupted = false;
+ std::unique_ptr<CDBIterator> cursor(block_tree_db.NewIterator());
+ for (cursor->Seek(begin_key); cursor->Valid(); cursor->Next()) {
+ boost::this_thread::interruption_point();
+ if (ShutdownRequested()) {
+ interrupted = true;
+ break;
+ }
+
+ if (!cursor->GetKey(key)) {
+ return error("%s: cannot get key from valid cursor", __func__);
+ }
+ if (key.first != DB_TXINDEX) {
+ break;
+ }
+
+ // Log progress every 10%.
+ if (++count % 256 == 0) {
+ // Since txids are uniformly random and traversed in increasing order, the high 16 bits
+ // of the hash can be used to estimate the current progress.
+ const uint256& txid = key.second;
+ uint32_t high_nibble =
+ (static_cast<uint32_t>(*(txid.begin() + 0)) << 8) +
+ (static_cast<uint32_t>(*(txid.begin() + 1)) << 0);
+ int percentage_done = (int)(high_nibble * 100.0 / 65536.0 + 0.5);
+
+ uiInterface.ShowProgress(_("Upgrading txindex database"), percentage_done, true);
+ if (report_done < percentage_done/10) {
+ LogPrintf("Upgrading txindex database... [%d%%]\n", percentage_done);
+ report_done = percentage_done/10;
+ }
+ }
+
+ CDiskTxPos value;
+ if (!cursor->GetValue(value)) {
+ return error("%s: cannot parse txindex record", __func__);
+ }
+ batch_newdb.Write(key, value);
+ batch_olddb.Erase(key);
+
+ if (batch_newdb.SizeEstimate() > batch_size || batch_olddb.SizeEstimate() > batch_size) {
+ // NOTE: it's OK to delete the key pointed at by the current DB cursor while iterating
+ // because LevelDB iterators are guaranteed to provide a consistent view of the
+ // underlying data, like a lightweight snapshot.
+ WriteTxIndexMigrationBatches(*this, block_tree_db,
+ batch_newdb, batch_olddb,
+ prev_key, key);
+ prev_key = key;
+ }
+ }
+
+ // If these final DB batches complete the migration, write the best block
+ // hash marker to the new database and delete from the old one. This signals
+ // that the former is fully caught up to that point in the blockchain and
+ // that all txindex entries have been removed from the latter.
+ if (!interrupted) {
+ batch_olddb.Erase(DB_TXINDEX_BLOCK);
+ batch_newdb.Write(DB_BEST_BLOCK, locator);
+ }
+
+ WriteTxIndexMigrationBatches(*this, block_tree_db,
+ batch_newdb, batch_olddb,
+ begin_key, key);
+
+ if (interrupted) {
+ LogPrintf("[CANCELLED].\n");
+ return false;
+ }
+
+ uiInterface.ShowProgress("", 100, false);
+
+ LogPrintf("[DONE].\n");
+ return true;
+}
diff --git a/src/txdb.h b/src/txdb.h
index f3454e7d09..4193f98de1 100644
--- a/src/txdb.h
+++ b/src/txdb.h
@@ -9,6 +9,7 @@
#include <coins.h>
#include <dbwrapper.h>
#include <chain.h>
+#include <primitives/block.h>
#include <map>
#include <memory>
@@ -35,7 +36,7 @@ static const int64_t nMaxBlockDBCache = 2;
//! Max memory allocated to block tree DB specific cache, if -txindex (MiB)
// Unlike for the UTXO database, for the txindex scenario the leveldb cache make
// a meaningful difference: https://github.com/bitcoin/bitcoin/pull/8273#issuecomment-229601991
-static const int64_t nMaxBlockDBAndTxIndexCache = 1024;
+static const int64_t nMaxTxIndexCache = 1024;
//! Max memory allocated to coin DB specific cache (MiB)
static const int64_t nMaxCoinsDBCache = 8;
@@ -112,9 +113,6 @@ class CBlockTreeDB : public CDBWrapper
public:
explicit CBlockTreeDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false);
- CBlockTreeDB(const CBlockTreeDB&) = delete;
- CBlockTreeDB& operator=(const CBlockTreeDB&) = delete;
-
bool WriteBatchSync(const std::vector<std::pair<int, const CBlockFileInfo*> >& fileInfo, int nLastFile, const std::vector<const CBlockIndex*>& blockinfo);
bool ReadBlockFileInfo(int nFile, CBlockFileInfo &info);
bool ReadLastBlockFile(int &nFile);
@@ -127,4 +125,36 @@ public:
bool LoadBlockIndexGuts(const Consensus::Params& consensusParams, std::function<CBlockIndex*(const uint256&)> insertBlockIndex);
};
+/**
+ * Access to the txindex database (indexes/txindex/)
+ *
+ * The database stores a block locator of the chain the database is synced to
+ * so that the TxIndex can efficiently determine the point it last stopped at.
+ * A locator is used instead of a simple hash of the chain tip because blocks
+ * and block index entries may not be flushed to disk until after this database
+ * is updated.
+ */
+class TxIndexDB : public CDBWrapper
+{
+public:
+ explicit TxIndexDB(size_t n_cache_size, bool f_memory = false, bool f_wipe = false);
+
+ /// Read the disk location of the transaction data with the given hash. Returns false if the
+ /// transaction hash is not indexed.
+ bool ReadTxPos(const uint256& txid, CDiskTxPos& pos) const;
+
+ /// Write a batch of transaction positions to the DB.
+ bool WriteTxs(const std::vector<std::pair<uint256, CDiskTxPos>>& v_pos);
+
+ /// Read block locator of the chain that the txindex is in sync with.
+ bool ReadBestBlock(CBlockLocator& locator) const;
+
+ /// Write block locator of the chain that the txindex is in sync with.
+ bool WriteBestBlock(const CBlockLocator& locator);
+
+ /// Migrate txindex data from the block tree DB, where it may be for older nodes that have not
+ /// been upgraded yet to the new database.
+ bool MigrateData(CBlockTreeDB& block_tree_db, const CBlockLocator& best_locator);
+};
+
#endif // BITCOIN_TXDB_H
diff --git a/src/txmempool.h b/src/txmempool.h
index a1cde6f779..3f9fb4850c 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -451,7 +451,7 @@ private:
mutable bool blockSinceLastRollingFeeBump;
mutable double rollingMinimumFeeRate; //!< minimum fee to get into the pool, decreases exponentially
- void trackPackageRemoved(const CFeeRate& rate);
+ void trackPackageRemoved(const CFeeRate& rate) EXCLUSIVE_LOCKS_REQUIRED(cs);
public:
@@ -512,7 +512,7 @@ private:
void UpdateParent(txiter entry, txiter parent, bool add);
void UpdateChild(txiter entry, txiter child, bool add);
- std::vector<indexed_transaction_set::const_iterator> GetSortedDepthAndScore() const;
+ std::vector<indexed_transaction_set::const_iterator> GetSortedDepthAndScore() const EXCLUSIVE_LOCKS_REQUIRED(cs);
public:
indirectmap<COutPoint, const CTransaction*> mapNextTx;
@@ -572,7 +572,7 @@ public:
* Set updateDescendants to true when removing a tx that was in a block, so
* that any in-mempool descendants have their ancestor state updated.
*/
- void RemoveStaged(setEntries &stage, bool updateDescendants, MemPoolRemovalReason reason = MemPoolRemovalReason::UNKNOWN);
+ void RemoveStaged(setEntries &stage, bool updateDescendants, MemPoolRemovalReason reason = MemPoolRemovalReason::UNKNOWN) EXCLUSIVE_LOCKS_REQUIRED(cs);
/** When adding transactions from a disconnected block back to the mempool,
* new mempool entries may have children in the mempool (which is generally
diff --git a/src/util.cpp b/src/util.cpp
index 1fb40ae7a1..7dd6884f75 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -84,21 +84,11 @@ const int64_t nStartupTime = GetTime();
const char * const BITCOIN_CONF_FILENAME = "bitcoin.conf";
const char * const BITCOIN_PID_FILENAME = "bitcoind.pid";
-const char * const DEFAULT_DEBUGLOGFILE = "debug.log";
ArgsManager gArgs;
-bool fPrintToConsole = false;
-bool fPrintToDebugLog = true;
-bool fLogTimestamps = DEFAULT_LOGTIMESTAMPS;
-bool fLogTimeMicros = DEFAULT_LOGTIMEMICROS;
-bool fLogIPs = DEFAULT_LOGIPS;
-std::atomic<bool> fReopenDebugLog(false);
CTranslationInterface translationInterface;
-/** Log categories bitfield. */
-std::atomic<uint32_t> logCategories(0);
-
/** Init OpenSSL library multithreading support */
static std::unique_ptr<CCriticalSection[]> ppmutexOpenSSL;
void locking_callback(int mode, int i, const char* file, int line) NO_THREAD_SAFETY_ANALYSIS
@@ -147,233 +137,6 @@ public:
}
instance_of_cinit;
-/**
- * LogPrintf() has been broken a couple of times now
- * by well-meaning people adding mutexes in the most straightforward way.
- * It breaks because it may be called by global destructors during shutdown.
- * Since the order of destruction of static/global objects is undefined,
- * defining a mutex as a global object doesn't work (the mutex gets
- * destroyed, and then some later destructor calls OutputDebugStringF,
- * maybe indirectly, and you get a core dump at shutdown trying to lock
- * the mutex).
- */
-
-static std::once_flag debugPrintInitFlag;
-
-/**
- * We use std::call_once() to make sure mutexDebugLog and
- * vMsgsBeforeOpenLog are initialized in a thread-safe manner.
- *
- * NOTE: fileout, mutexDebugLog and sometimes vMsgsBeforeOpenLog
- * are leaked on exit. This is ugly, but will be cleaned up by
- * the OS/libc. When the shutdown sequence is fully audited and
- * tested, explicit destruction of these objects can be implemented.
- */
-static FILE* fileout = nullptr;
-static std::mutex* mutexDebugLog = nullptr;
-static std::list<std::string>* vMsgsBeforeOpenLog;
-
-static int FileWriteStr(const std::string &str, FILE *fp)
-{
- return fwrite(str.data(), 1, str.size(), fp);
-}
-
-static void DebugPrintInit()
-{
- assert(mutexDebugLog == nullptr);
- mutexDebugLog = new std::mutex();
- vMsgsBeforeOpenLog = new std::list<std::string>;
-}
-
-fs::path GetDebugLogPath()
-{
- fs::path logfile(gArgs.GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE));
- return AbsPathForConfigVal(logfile);
-}
-
-bool OpenDebugLog()
-{
- std::call_once(debugPrintInitFlag, &DebugPrintInit);
- std::lock_guard<std::mutex> scoped_lock(*mutexDebugLog);
-
- assert(fileout == nullptr);
- assert(vMsgsBeforeOpenLog);
- fs::path pathDebug = GetDebugLogPath();
-
- fileout = fsbridge::fopen(pathDebug, "a");
- if (!fileout) {
- return false;
- }
-
- setbuf(fileout, nullptr); // unbuffered
- // dump buffered messages from before we opened the log
- while (!vMsgsBeforeOpenLog->empty()) {
- FileWriteStr(vMsgsBeforeOpenLog->front(), fileout);
- vMsgsBeforeOpenLog->pop_front();
- }
-
- delete vMsgsBeforeOpenLog;
- vMsgsBeforeOpenLog = nullptr;
- return true;
-}
-
-struct CLogCategoryDesc
-{
- uint32_t flag;
- std::string category;
-};
-
-const CLogCategoryDesc LogCategories[] =
-{
- {BCLog::NONE, "0"},
- {BCLog::NONE, "none"},
- {BCLog::NET, "net"},
- {BCLog::TOR, "tor"},
- {BCLog::MEMPOOL, "mempool"},
- {BCLog::HTTP, "http"},
- {BCLog::BENCH, "bench"},
- {BCLog::ZMQ, "zmq"},
- {BCLog::DB, "db"},
- {BCLog::RPC, "rpc"},
- {BCLog::ESTIMATEFEE, "estimatefee"},
- {BCLog::ADDRMAN, "addrman"},
- {BCLog::SELECTCOINS, "selectcoins"},
- {BCLog::REINDEX, "reindex"},
- {BCLog::CMPCTBLOCK, "cmpctblock"},
- {BCLog::RAND, "rand"},
- {BCLog::PRUNE, "prune"},
- {BCLog::PROXY, "proxy"},
- {BCLog::MEMPOOLREJ, "mempoolrej"},
- {BCLog::LIBEVENT, "libevent"},
- {BCLog::COINDB, "coindb"},
- {BCLog::QT, "qt"},
- {BCLog::LEVELDB, "leveldb"},
- {BCLog::ALL, "1"},
- {BCLog::ALL, "all"},
-};
-
-bool GetLogCategory(uint32_t *f, const std::string *str)
-{
- if (f && str) {
- if (*str == "") {
- *f = BCLog::ALL;
- return true;
- }
- for (unsigned int i = 0; i < ARRAYLEN(LogCategories); i++) {
- if (LogCategories[i].category == *str) {
- *f = LogCategories[i].flag;
- return true;
- }
- }
- }
- return false;
-}
-
-std::string ListLogCategories()
-{
- std::string ret;
- int outcount = 0;
- for (unsigned int i = 0; i < ARRAYLEN(LogCategories); i++) {
- // Omit the special cases.
- if (LogCategories[i].flag != BCLog::NONE && LogCategories[i].flag != BCLog::ALL) {
- if (outcount != 0) ret += ", ";
- ret += LogCategories[i].category;
- outcount++;
- }
- }
- return ret;
-}
-
-std::vector<CLogCategoryActive> ListActiveLogCategories()
-{
- std::vector<CLogCategoryActive> ret;
- for (unsigned int i = 0; i < ARRAYLEN(LogCategories); i++) {
- // Omit the special cases.
- if (LogCategories[i].flag != BCLog::NONE && LogCategories[i].flag != BCLog::ALL) {
- CLogCategoryActive catActive;
- catActive.category = LogCategories[i].category;
- catActive.active = LogAcceptCategory(LogCategories[i].flag);
- ret.push_back(catActive);
- }
- }
- return ret;
-}
-
-/**
- * fStartedNewLine is a state variable held by the calling context that will
- * suppress printing of the timestamp when multiple calls are made that don't
- * end in a newline. Initialize it to true, and hold it, in the calling context.
- */
-static std::string LogTimestampStr(const std::string &str, std::atomic_bool *fStartedNewLine)
-{
- std::string strStamped;
-
- if (!fLogTimestamps)
- return str;
-
- if (*fStartedNewLine) {
- int64_t nTimeMicros = GetTimeMicros();
- strStamped = FormatISO8601DateTime(nTimeMicros/1000000);
- if (fLogTimeMicros) {
- strStamped.pop_back();
- strStamped += strprintf(".%06dZ", nTimeMicros%1000000);
- }
- int64_t mocktime = GetMockTime();
- if (mocktime) {
- strStamped += " (mocktime: " + FormatISO8601DateTime(mocktime) + ")";
- }
- strStamped += ' ' + str;
- } else
- strStamped = str;
-
- if (!str.empty() && str[str.size()-1] == '\n')
- *fStartedNewLine = true;
- else
- *fStartedNewLine = false;
-
- return strStamped;
-}
-
-int LogPrintStr(const std::string &str)
-{
- int ret = 0; // Returns total number of characters written
- static std::atomic_bool fStartedNewLine(true);
-
- std::string strTimestamped = LogTimestampStr(str, &fStartedNewLine);
-
- if (fPrintToConsole)
- {
- // print to console
- ret = fwrite(strTimestamped.data(), 1, strTimestamped.size(), stdout);
- fflush(stdout);
- }
- else if (fPrintToDebugLog)
- {
- std::call_once(debugPrintInitFlag, &DebugPrintInit);
- std::lock_guard<std::mutex> scoped_lock(*mutexDebugLog);
-
- // buffer if we haven't opened the log yet
- if (fileout == nullptr) {
- assert(vMsgsBeforeOpenLog);
- ret = strTimestamped.length();
- vMsgsBeforeOpenLog->push_back(strTimestamped);
- }
- else
- {
- // reopen the log file, if requested
- if (fReopenDebugLog) {
- fReopenDebugLog = false;
- fs::path pathDebug = GetDebugLogPath();
- if (fsbridge::freopen(pathDebug,"a",fileout) != nullptr)
- setbuf(fileout, nullptr); // unbuffered
- }
-
- ret = FileWriteStr(strTimestamped, fileout);
- }
- }
- return ret;
-}
-
/** A map that contains all the currently held directory locks. After
* successful locking, these will be held here until the global destructor
* cleans them up and thus automatically unlocks them, or ReleaseDirectoryLocks
@@ -682,6 +445,17 @@ void ArgsManager::ParseParameters(int argc, const char* const argv[])
m_override_args[key].push_back(val);
}
}
+
+ // we do not allow -includeconf from command line, so we clear it here
+ auto it = m_override_args.find("-includeconf");
+ if (it != m_override_args.end()) {
+ if (it->second.size() > 0) {
+ for (const auto& ic : it->second) {
+ fprintf(stderr, "warning: -includeconf cannot be used from commandline; ignoring -includeconf=%s\n", ic.c_str());
+ }
+ m_override_args.erase(it);
+ }
+ }
}
std::vector<std::string> ArgsManager::GetArgs(const std::string& strArg) const
@@ -773,6 +547,55 @@ void ArgsManager::ForceSetArg(const std::string& strArg, const std::string& strV
m_override_args[strArg] = {strValue};
}
+void ArgsManager::AddArg(const std::string& name, const std::string& help, const bool debug_only, const OptionsCategory& cat)
+{
+ std::pair<OptionsCategory, std::string> key(cat, name);
+ assert(m_available_args.count(key) == 0);
+ m_available_args.emplace(key, std::pair<std::string, bool>(help, debug_only));
+}
+
+std::string ArgsManager::GetHelpMessage()
+{
+ const bool show_debug = gArgs.GetBoolArg("-help-debug", false);
+
+ std::string usage = HelpMessageGroup(_("Options:"));
+
+ OptionsCategory last_cat = OptionsCategory::OPTIONS;
+ for (auto& arg : m_available_args) {
+ if (arg.first.first != last_cat) {
+ last_cat = arg.first.first;
+ if (last_cat == OptionsCategory::CONNECTION)
+ usage += HelpMessageGroup(_("Connection options:"));
+ else if (last_cat == OptionsCategory::ZMQ)
+ usage += HelpMessageGroup(_("ZeroMQ notification options:"));
+ else if (last_cat == OptionsCategory::DEBUG_TEST)
+ usage += HelpMessageGroup(_("Debugging/Testing options:"));
+ else if (last_cat == OptionsCategory::NODE_RELAY)
+ usage += HelpMessageGroup(_("Node relay options:"));
+ else if (last_cat == OptionsCategory::BLOCK_CREATION)
+ usage += HelpMessageGroup(_("Block creation options:"));
+ else if (last_cat == OptionsCategory::RPC)
+ usage += HelpMessageGroup(_("RPC server options:"));
+ else if (last_cat == OptionsCategory::WALLET)
+ usage += HelpMessageGroup(_("Wallet options:"));
+ else if (last_cat == OptionsCategory::WALLET_DEBUG_TEST && show_debug)
+ usage += HelpMessageGroup(_("Wallet debugging/testing options:"));
+ else if (last_cat == OptionsCategory::CHAINPARAMS)
+ usage += HelpMessageGroup(_("Chain selection options:"));
+ else if (last_cat == OptionsCategory::GUI)
+ usage += HelpMessageGroup(_("UI Options:"));
+ else if (last_cat == OptionsCategory::COMMANDS)
+ usage += HelpMessageGroup(_("Commands:"));
+ else if (last_cat == OptionsCategory::REGISTER_COMMANDS)
+ usage += HelpMessageGroup(_("Register Commands:"));
+ }
+ if (show_debug || !arg.second.second) {
+ usage += HelpMessageOpt(arg.first.second, arg.second.first);
+ }
+ }
+ return usage;
+}
+
bool HelpRequested(const ArgsManager& args)
{
return args.IsArgSet("-?") || args.IsArgSet("-h") || args.IsArgSet("-help");
@@ -943,18 +766,40 @@ void ArgsManager::ReadConfigStream(std::istream& stream)
}
}
-void ArgsManager::ReadConfigFile(const std::string& confPath)
+void ArgsManager::ReadConfigFiles()
{
{
LOCK(cs_args);
m_config_args.clear();
}
+ const std::string confPath = GetArg("-conf", BITCOIN_CONF_FILENAME);
fs::ifstream stream(GetConfigFile(confPath));
// ok to not have a config file
if (stream.good()) {
ReadConfigStream(stream);
+ // if there is an -includeconf in the override args, but it is empty, that means the user
+ // passed '-noincludeconf' on the command line, in which case we should not include anything
+ if (m_override_args.count("-includeconf") == 0) {
+ std::vector<std::string> includeconf(GetArgs("-includeconf"));
+ {
+ // We haven't set m_network yet (that happens in SelectParams()), so manually check
+ // for network.includeconf args.
+ std::vector<std::string> includeconf_net(GetArgs(std::string("-") + GetChainName() + ".includeconf"));
+ includeconf.insert(includeconf.end(), includeconf_net.begin(), includeconf_net.end());
+ }
+
+ for (const std::string& to_include : includeconf) {
+ fs::ifstream include_config(GetConfigFile(to_include));
+ if (include_config.good()) {
+ ReadConfigStream(include_config);
+ LogPrintf("Included configuration file %s\n", to_include.c_str());
+ } else {
+ fprintf(stderr, "Failed to include configuration file %s\n", to_include.c_str());
+ }
+ }
+ }
}
// If datadir is changed in .conf file:
@@ -1025,21 +870,37 @@ bool TryCreateDirectories(const fs::path& p)
return false;
}
-void FileCommit(FILE *file)
+bool FileCommit(FILE *file)
{
- fflush(file); // harmless if redundantly called
+ if (fflush(file) != 0) { // harmless if redundantly called
+ LogPrintf("%s: fflush failed: %d\n", __func__, errno);
+ return false;
+ }
#ifdef WIN32
HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file));
- FlushFileBuffers(hFile);
+ if (FlushFileBuffers(hFile) == 0) {
+ LogPrintf("%s: FlushFileBuffers failed: %d\n", __func__, GetLastError());
+ return false;
+ }
#else
#if defined(__linux__) || defined(__NetBSD__)
- fdatasync(fileno(file));
+ if (fdatasync(fileno(file)) != 0 && errno != EINVAL) { // Ignore EINVAL for filesystems that don't support sync
+ LogPrintf("%s: fdatasync failed: %d\n", __func__, errno);
+ return false;
+ }
#elif defined(__APPLE__) && defined(F_FULLFSYNC)
- fcntl(fileno(file), F_FULLFSYNC, 0);
+ if (fcntl(fileno(file), F_FULLFSYNC, 0) == -1) { // Manpage says "value other than -1" is returned on success
+ LogPrintf("%s: fcntl F_FULLFSYNC failed: %d\n", __func__, errno);
+ return false;
+ }
#else
- fsync(fileno(file));
+ if (fsync(fileno(file)) != 0 && errno != EINVAL) {
+ LogPrintf("%s: fsync failed: %d\n", __func__, errno);
+ return false;
+ }
#endif
#endif
+ return true;
}
bool TruncateFile(FILE *file, unsigned int length) {
@@ -1108,7 +969,9 @@ void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length) {
// Fallback version
// TODO: just write one byte per block
static const char buf[65536] = {};
- fseek(file, offset, SEEK_SET);
+ if (fseek(file, offset, SEEK_SET)) {
+ return;
+ }
while (length > 0) {
unsigned int now = 65536;
if (length < now)
@@ -1119,34 +982,6 @@ void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length) {
#endif
}
-void ShrinkDebugFile()
-{
- // Amount of debug.log to save at end when shrinking (must fit in memory)
- constexpr size_t RECENT_DEBUG_HISTORY_SIZE = 10 * 1000000;
- // Scroll debug.log if it's getting too big
- fs::path pathLog = GetDebugLogPath();
- FILE* file = fsbridge::fopen(pathLog, "r");
- // If debug.log file is more than 10% bigger the RECENT_DEBUG_HISTORY_SIZE
- // trim it down by saving only the last RECENT_DEBUG_HISTORY_SIZE bytes
- if (file && fs::file_size(pathLog) > 11 * (RECENT_DEBUG_HISTORY_SIZE / 10))
- {
- // Restart the file with some of the end
- std::vector<char> vch(RECENT_DEBUG_HISTORY_SIZE, 0);
- fseek(file, -((long)vch.size()), SEEK_END);
- int nBytes = fread(vch.data(), 1, vch.size(), file);
- fclose(file);
-
- file = fsbridge::fopen(pathLog, "w");
- if (file)
- {
- fwrite(vch.data(), 1, nBytes, file);
- fclose(file);
- }
- }
- else if (file != nullptr)
- fclose(file);
-}
-
#ifdef WIN32
fs::path GetSpecialFolderPath(int nFolder, bool fCreate)
{
diff --git a/src/util.h b/src/util.h
index ffdee99d27..17dd5c0683 100644
--- a/src/util.h
+++ b/src/util.h
@@ -5,7 +5,7 @@
/**
* Server/client environment: argument handling, config file parsing,
- * logging, thread wrappers, startup time
+ * thread wrappers, startup time
*/
#ifndef BITCOIN_UTIL_H
#define BITCOIN_UTIL_H
@@ -16,6 +16,7 @@
#include <compat.h>
#include <fs.h>
+#include <logging.h>
#include <sync.h>
#include <tinyformat.h>
#include <utiltime.h>
@@ -36,11 +37,6 @@
// Application startup time (used for uptime calculation)
int64_t GetStartupTime();
-static const bool DEFAULT_LOGTIMEMICROS = false;
-static const bool DEFAULT_LOGIPS = false;
-static const bool DEFAULT_LOGTIMESTAMPS = true;
-extern const char * const DEFAULT_DEBUGLOGFILE;
-
/** Signals for translation. */
class CTranslationInterface
{
@@ -49,20 +45,11 @@ public:
boost::signals2::signal<std::string (const char* psz)> Translate;
};
-extern bool fPrintToConsole;
-extern bool fPrintToDebugLog;
-
-extern bool fLogTimestamps;
-extern bool fLogTimeMicros;
-extern bool fLogIPs;
-extern std::atomic<bool> fReopenDebugLog;
extern CTranslationInterface translationInterface;
extern const char * const BITCOIN_CONF_FILENAME;
extern const char * const BITCOIN_PID_FILENAME;
-extern std::atomic<uint32_t> logCategories;
-
/**
* Translation function: Call Translate signal on UI interface, which returns a boost::optional result.
* If no translation slot is registered, nothing is returned, and simply return the input.
@@ -76,104 +63,15 @@ inline std::string _(const char* psz)
void SetupEnvironment();
bool SetupNetworking();
-struct CLogCategoryActive
-{
- std::string category;
- bool active;
-};
-
-namespace BCLog {
- enum LogFlags : uint32_t {
- NONE = 0,
- NET = (1 << 0),
- TOR = (1 << 1),
- MEMPOOL = (1 << 2),
- HTTP = (1 << 3),
- BENCH = (1 << 4),
- ZMQ = (1 << 5),
- DB = (1 << 6),
- RPC = (1 << 7),
- ESTIMATEFEE = (1 << 8),
- ADDRMAN = (1 << 9),
- SELECTCOINS = (1 << 10),
- REINDEX = (1 << 11),
- CMPCTBLOCK = (1 << 12),
- RAND = (1 << 13),
- PRUNE = (1 << 14),
- PROXY = (1 << 15),
- MEMPOOLREJ = (1 << 16),
- LIBEVENT = (1 << 17),
- COINDB = (1 << 18),
- QT = (1 << 19),
- LEVELDB = (1 << 20),
- ALL = ~(uint32_t)0,
- };
-}
-/** Return true if log accepts specified category */
-static inline bool LogAcceptCategory(uint32_t category)
-{
- return (logCategories.load(std::memory_order_relaxed) & category) != 0;
-}
-
-/** Returns a string with the log categories. */
-std::string ListLogCategories();
-
-/** Returns a vector of the active log categories. */
-std::vector<CLogCategoryActive> ListActiveLogCategories();
-
-/** Return true if str parses as a log category and set the flags in f */
-bool GetLogCategory(uint32_t *f, const std::string *str);
-
-/** Send a string to the log output */
-int LogPrintStr(const std::string &str);
-
-/** Get format string from VA_ARGS for error reporting */
-template<typename... Args> std::string FormatStringFromLogArgs(const char *fmt, const Args&... args) { return fmt; }
-
-static inline void MarkUsed() {}
-template<typename T, typename... Args> static inline void MarkUsed(const T& t, const Args&... args)
-{
- (void)t;
- MarkUsed(args...);
-}
-
-// Be conservative when using LogPrintf/error or other things which
-// unconditionally log to debug.log! It should not be the case that an inbound
-// peer can fill up a user's disk with debug.log entries.
-
-#ifdef USE_COVERAGE
-#define LogPrintf(...) do { MarkUsed(__VA_ARGS__); } while(0)
-#define LogPrint(category, ...) do { MarkUsed(__VA_ARGS__); } while(0)
-#else
-#define LogPrintf(...) do { \
- if (fPrintToConsole || fPrintToDebugLog) { \
- std::string _log_msg_; /* Unlikely name to avoid shadowing variables */ \
- try { \
- _log_msg_ = tfm::format(__VA_ARGS__); \
- } catch (tinyformat::format_error &fmterr) { \
- /* Original format string will have newline so don't add one here */ \
- _log_msg_ = "Error \"" + std::string(fmterr.what()) + "\" while formatting log message: " + FormatStringFromLogArgs(__VA_ARGS__); \
- } \
- LogPrintStr(_log_msg_); \
- } \
-} while(0)
-
-#define LogPrint(category, ...) do { \
- if (LogAcceptCategory((category))) { \
- LogPrintf(__VA_ARGS__); \
- } \
-} while(0)
-#endif
-
template<typename... Args>
bool error(const char* fmt, const Args&... args)
{
- LogPrintStr("ERROR: " + tfm::format(fmt, args...) + "\n");
+ LogPrintf("ERROR: %s\n", tfm::format(fmt, args...));
return false;
}
void PrintExceptionContinue(const std::exception *pex, const char* pszThread);
-void FileCommit(FILE *file);
+bool FileCommit(FILE *file);
bool TruncateFile(FILE *file, unsigned int length);
int RaiseFileDescriptorLimit(int nMinFD);
void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length);
@@ -199,9 +97,6 @@ void CreatePidFile(const fs::path &path, pid_t pid);
#ifdef WIN32
fs::path GetSpecialFolderPath(int nFolder, bool fCreate = true);
#endif
-fs::path GetDebugLogPath();
-bool OpenDebugLog();
-void ShrinkDebugFile();
void runCommand(const std::string& strCommand);
/**
@@ -223,6 +118,23 @@ inline bool IsSwitchChar(char c)
#endif
}
+enum class OptionsCategory
+{
+ OPTIONS,
+ CONNECTION,
+ WALLET,
+ WALLET_DEBUG_TEST,
+ ZMQ,
+ DEBUG_TEST,
+ CHAINPARAMS,
+ NODE_RELAY,
+ BLOCK_CREATION,
+ RPC,
+ GUI,
+ COMMANDS,
+ REGISTER_COMMANDS
+};
+
class ArgsManager
{
protected:
@@ -233,6 +145,7 @@ protected:
std::map<std::string, std::vector<std::string>> m_config_args;
std::string m_network;
std::set<std::string> m_network_only_args;
+ std::map<std::pair<OptionsCategory, std::string>, std::pair<std::string, bool>> m_available_args;
void ReadConfigStream(std::istream& stream);
@@ -245,7 +158,7 @@ public:
void SelectConfigNetwork(const std::string& network);
void ParseParameters(int argc, const char*const argv[]);
- void ReadConfigFile(const std::string& confPath);
+ void ReadConfigFiles();
/**
* Log warnings for options in m_section_only_args when
@@ -334,6 +247,16 @@ public:
* @return CBaseChainParams::MAIN by default; raises runtime error if an invalid combination is given.
*/
std::string GetChainName() const;
+
+ /**
+ * Add argument
+ */
+ void AddArg(const std::string& name, const std::string& help, const bool debug_only, const OptionsCategory& cat);
+
+ /**
+ * Get the help string
+ */
+ std::string GetHelpMessage();
};
extern ArgsManager gArgs;
diff --git a/src/utiltime.cpp b/src/utiltime.cpp
index 34800c7b6d..e60996efe1 100644
--- a/src/utiltime.cpp
+++ b/src/utiltime.cpp
@@ -79,20 +79,32 @@ void MilliSleep(int64_t n)
std::string FormatISO8601DateTime(int64_t nTime) {
struct tm ts;
time_t time_val = nTime;
+#ifdef _MSC_VER
+ gmtime_s(&ts, &time_val);
+#else
gmtime_r(&time_val, &ts);
+#endif
return strprintf("%04i-%02i-%02iT%02i:%02i:%02iZ", ts.tm_year + 1900, ts.tm_mon + 1, ts.tm_mday, ts.tm_hour, ts.tm_min, ts.tm_sec);
}
std::string FormatISO8601Date(int64_t nTime) {
struct tm ts;
time_t time_val = nTime;
+#ifdef _MSC_VER
+ gmtime_s(&ts, &time_val);
+#else
gmtime_r(&time_val, &ts);
+#endif
return strprintf("%04i-%02i-%02i", ts.tm_year + 1900, ts.tm_mon + 1, ts.tm_mday);
}
std::string FormatISO8601Time(int64_t nTime) {
struct tm ts;
time_t time_val = nTime;
+#ifdef _MSC_VER
+ gmtime_s(&ts, &time_val);
+#else
gmtime_r(&time_val, &ts);
+#endif
return strprintf("%02i:%02i:%02iZ", ts.tm_hour, ts.tm_min, ts.tm_sec);
}
diff --git a/src/validation.cpp b/src/validation.cpp
index 958c187220..f22ae7f9be 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -16,6 +16,7 @@
#include <consensus/validation.h>
#include <cuckoocache.h>
#include <hash.h>
+#include <index/txindex.h>
#include <init.h>
#include <policy/fees.h>
#include <policy/policy.h>
@@ -142,7 +143,7 @@ private:
* ahead and mark descendants of invalid blocks as FAILED_CHILD at that time,
* instead of putting things in this set.
*/
- std::set<CBlockIndex*> g_failed_blocks;
+ std::set<CBlockIndex*> m_failed_blocks;
public:
CChain chainActive;
@@ -154,6 +155,10 @@ public:
bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock);
+ /**
+ * If a block header hasn't already been seen, call CheckBlockHeader on it, ensure
+ * that it doesn't descend from an invalid block, and then add it to mapBlockIndex.
+ */
bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex);
bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const CDiskBlockPos* dbp, bool* fNewBlock);
@@ -185,6 +190,11 @@ private:
CBlockIndex* AddToBlockIndex(const CBlockHeader& block);
/** Create a new block index entry for a given block hash */
CBlockIndex * InsertBlockIndex(const uint256& hash);
+ /**
+ * Make various assertions about the state of the block index.
+ *
+ * By default this only executes fully when using the Regtest chain; see: fCheckBlockIndex.
+ */
void CheckBlockIndex(const Consensus::Params& consensusParams);
void InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state);
@@ -208,7 +218,6 @@ uint256 g_best_block;
int nScriptCheckThreads = 0;
std::atomic_bool fImporting(false);
std::atomic_bool fReindex(false);
-bool fTxIndex = false;
bool fHavePruned = false;
bool fPruneMode = false;
bool fIsBareMultisigStd = DEFAULT_PERMIT_BAREMULTISIG;
@@ -354,7 +363,7 @@ bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp, bool
CBlockIndex* tip = chainActive.Tip();
assert(tip != nullptr);
-
+
CBlockIndex index;
index.pprev = tip;
// CheckSequenceLocks() uses chainActive.Height()+1 to evaluate
@@ -468,7 +477,7 @@ static bool IsCurrentForFeeEstimation()
* and instead just erase from the mempool as needed.
*/
-void UpdateMempoolForReorg(DisconnectedBlockTransactions &disconnectpool, bool fAddToMempool)
+static void UpdateMempoolForReorg(DisconnectedBlockTransactions &disconnectpool, bool fAddToMempool)
{
AssertLockHeld(cs_main);
std::vector<uint256> vHashUpdate;
@@ -573,6 +582,12 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
if (fRequireStandard && !IsStandardTx(tx, reason, witnessEnabled))
return state.DoS(0, false, REJECT_NONSTANDARD, reason);
+ // Do not work on transactions that are too small.
+ // A transaction with 1 segwit input and 1 P2WPHK output has non-witness size of 82 bytes.
+ // Transactions smaller than this are not relayed to reduce unnecessary malloc overhead.
+ if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) < MIN_STANDARD_TX_NONWITNESS_SIZE)
+ return state.DoS(0, false, REJECT_NONSTANDARD, "tx-size-small");
+
// Only accept nLockTime-using transactions that can be mined in the next
// block; we don't want our mempool filled up with transactions that can't
// be mined yet.
@@ -720,7 +735,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
// No transactions are allowed below minRelayTxFee except from disconnected blocks
if (!bypass_limits && nModifiedFees < ::minRelayTxFee.GetFee(nSize)) {
- return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "min relay fee not met");
+ return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "min relay fee not met", false, strprintf("%d < %d", nModifiedFees, ::minRelayTxFee.GetFee(nSize)));
}
if (nAbsurdFee && nFees > nAbsurdFee)
@@ -1019,28 +1034,8 @@ bool GetTransaction(const uint256& hash, CTransactionRef& txOut, const Consensus
return true;
}
- if (fTxIndex) {
- CDiskTxPos postx;
- if (pblocktree->ReadTxIndex(hash, postx)) {
- CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION);
- if (file.IsNull())
- return error("%s: OpenBlockFile failed", __func__);
- CBlockHeader header;
- try {
- file >> header;
- fseek(file.Get(), postx.nTxOffset, SEEK_CUR);
- file >> txOut;
- } catch (const std::exception& e) {
- return error("%s: Deserialize or I/O error - %s", __func__, e.what());
- }
- hashBlock = header.GetHash();
- if (txOut->GetHash() != hash)
- return error("%s: txid mismatch", __func__);
- return true;
- }
-
- // transaction not found in index, nothing more can be done
- return false;
+ if (g_txindex) {
+ return g_txindex->FindTx(hash, hashBlock, txOut);
}
if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it
@@ -1286,7 +1281,7 @@ void static InvalidChainFound(CBlockIndex* pindexNew)
void CChainState::InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) {
if (!state.CorruptionPossible()) {
pindex->nStatus |= BLOCK_FAILED_VALID;
- g_failed_blocks.insert(pindex);
+ m_failed_blocks.insert(pindex);
setDirtyBlockIndex.insert(pindex);
setBlockIndexCandidates.erase(pindex);
InvalidChainFound(pindex);
@@ -1498,7 +1493,7 @@ static bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex *pindex)
}
/** Abort with a message */
-bool AbortNode(const std::string& strMessage, const std::string& userMessage="")
+static bool AbortNode(const std::string& strMessage, const std::string& userMessage="")
{
SetMiscWarning(strMessage);
LogPrintf("*** %s\n", strMessage);
@@ -1509,7 +1504,7 @@ bool AbortNode(const std::string& strMessage, const std::string& userMessage="")
return false;
}
-bool AbortNode(CValidationState& state, const std::string& strMessage, const std::string& userMessage="")
+static bool AbortNode(CValidationState& state, const std::string& strMessage, const std::string& userMessage="")
{
AbortNode(strMessage, userMessage);
return state.Error(strMessage);
@@ -1615,22 +1610,27 @@ void static FlushBlockFile(bool fFinalize = false)
LOCK(cs_LastBlockFile);
CDiskBlockPos posOld(nLastBlockFile, 0);
+ bool status = true;
FILE *fileOld = OpenBlockFile(posOld);
if (fileOld) {
if (fFinalize)
- TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nSize);
- FileCommit(fileOld);
+ status &= TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nSize);
+ status &= FileCommit(fileOld);
fclose(fileOld);
}
fileOld = OpenUndoFile(posOld);
if (fileOld) {
if (fFinalize)
- TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nUndoSize);
- FileCommit(fileOld);
+ status &= TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nUndoSize);
+ status &= FileCommit(fileOld);
fclose(fileOld);
}
+
+ if (!status) {
+ AbortNode("Flushing block file to disk failed. This is likely the result of an I/O error.");
+ }
}
static bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize);
@@ -1654,26 +1654,6 @@ static bool WriteUndoDataForBlock(const CBlockUndo& blockundo, CValidationState&
return true;
}
-static bool WriteTxIndexDataForBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex)
-{
- if (!fTxIndex) return true;
-
- CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size()));
- std::vector<std::pair<uint256, CDiskTxPos> > vPos;
- vPos.reserve(block.vtx.size());
- for (const CTransactionRef& tx : block.vtx)
- {
- vPos.push_back(std::make_pair(tx->GetHash(), pos));
- pos.nTxOffset += ::GetSerializeSize(*tx, SER_DISK, CLIENT_VERSION);
- }
-
- if (!pblocktree->WriteTxIndex(vPos)) {
- return AbortNode(state, "Failed to write transaction index");
- }
-
- return true;
-}
-
static CCheckQueue<CScriptCheck> scriptcheckqueue(128);
void ThreadScriptCheck() {
@@ -1726,16 +1706,38 @@ public:
// Protected by cs_main
static ThresholdConditionCache warningcache[VERSIONBITS_NUM_BITS];
+// 0.13.0 was shipped with a segwit deployment defined for testnet, but not for
+// mainnet. We no longer need to support disabling the segwit deployment
+// except for testing purposes, due to limitations of the functional test
+// environment. See test/functional/p2p-segwit.py.
+static bool IsScriptWitnessEnabled(const Consensus::Params& params)
+{
+ return params.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout != 0;
+}
+
static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consensus::Params& consensusparams) {
AssertLockHeld(cs_main);
unsigned int flags = SCRIPT_VERIFY_NONE;
- // Start enforcing P2SH (BIP16)
- if (pindex->nHeight >= consensusparams.BIP16Height) {
+ // BIP16 didn't become active until Apr 1 2012 (on mainnet, and
+ // retroactively applied to testnet)
+ // However, only one historical block violated the P2SH rules (on both
+ // mainnet and testnet), so for simplicity, always leave P2SH
+ // on except for the one violating block.
+ if (consensusparams.BIP16Exception.IsNull() || // no bip16 exception on this chain
+ pindex->phashBlock == nullptr || // this is a new candidate block, eg from TestBlockValidity()
+ *pindex->phashBlock != consensusparams.BIP16Exception) // this block isn't the historical exception
+ {
flags |= SCRIPT_VERIFY_P2SH;
}
+ // Enforce WITNESS rules whenever P2SH is in effect (and the segwit
+ // deployment is defined).
+ if (flags & SCRIPT_VERIFY_P2SH && IsScriptWitnessEnabled(consensusparams)) {
+ flags |= SCRIPT_VERIFY_WITNESS;
+ }
+
// Start enforcing the DERSIG (BIP66) rule
if (pindex->nHeight >= consensusparams.BIP66Height) {
flags |= SCRIPT_VERIFY_DERSIG;
@@ -1751,9 +1753,7 @@ static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consens
flags |= SCRIPT_VERIFY_CHECKSEQUENCEVERIFY;
}
- // Start enforcing WITNESS rules using versionbits logic.
- if (IsWitnessEnabled(pindex->pprev, consensusparams)) {
- flags |= SCRIPT_VERIFY_WITNESS;
+ if (IsNullDummyEnabled(pindex->pprev, consensusparams)) {
flags |= SCRIPT_VERIFY_NULLDUMMY;
}
@@ -2045,9 +2045,6 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
setDirtyBlockIndex.insert(pindex);
}
- if (!WriteTxIndexDataForBlock(block, state, pindex))
- return false;
-
assert(pindex->phashBlock);
// add this block to the view's block chain
view.SetBestBlock(pindex->GetBlockHash());
@@ -2075,13 +2072,12 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &
LOCK(cs_main);
static int64_t nLastWrite = 0;
static int64_t nLastFlush = 0;
- static int64_t nLastSetChain = 0;
std::set<int> setFilesToPrune;
- bool fFlushForPrune = false;
- bool fDoFullFlush = false;
- int64_t nNow = 0;
+ bool full_flush_completed = false;
try {
{
+ bool fFlushForPrune = false;
+ bool fDoFullFlush = false;
LOCK(cs_LastBlockFile);
if (fPruneMode && (fCheckForPruning || nManualPruneHeight > 0) && !fReindex) {
if (nManualPruneHeight > 0) {
@@ -2098,7 +2094,7 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &
}
}
}
- nNow = GetTimeMicros();
+ int64_t nNow = GetTimeMicros();
// Avoid writing/flushing immediately after startup.
if (nLastWrite == 0) {
nLastWrite = nNow;
@@ -2106,9 +2102,6 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &
if (nLastFlush == 0) {
nLastFlush = nNow;
}
- if (nLastSetChain == 0) {
- nLastSetChain = nNow;
- }
int64_t nMempoolSizeMax = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
int64_t cacheSize = pcoinsTip->DynamicMemoryUsage();
int64_t nTotalSpace = nCoinCacheUsage + std::max<int64_t>(nMempoolSizeMax - nMempoolUsage, 0);
@@ -2165,12 +2158,12 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &
if (!pcoinsTip->Flush())
return AbortNode(state, "Failed to write to coin database");
nLastFlush = nNow;
+ full_flush_completed = true;
}
}
- if (fDoFullFlush || ((mode == FlushStateMode::ALWAYS || mode == FlushStateMode::PERIODIC) && nNow > nLastSetChain + (int64_t)DATABASE_WRITE_INTERVAL * 1000000)) {
+ if (full_flush_completed) {
// Update best block in wallet (so we can detect restored wallets).
- GetMainSignals().SetBestChain(chainActive.GetLocator());
- nLastSetChain = nNow;
+ GetMainSignals().ChainStateFlushed(chainActive.GetLocator());
}
} catch (const std::runtime_error& e) {
return AbortNode(state, std::string("System error while flushing: ") + e.what());
@@ -2181,14 +2174,18 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &
void FlushStateToDisk() {
CValidationState state;
const CChainParams& chainparams = Params();
- FlushStateToDisk(chainparams, state, FlushStateMode::ALWAYS);
+ if (!FlushStateToDisk(chainparams, state, FlushStateMode::ALWAYS)) {
+ LogPrintf("%s: failed to flush state (%s)\n", __func__, FormatStateMessage(state));
+ }
}
void PruneAndFlush() {
CValidationState state;
fCheckForPruning = true;
const CChainParams& chainparams = Params();
- FlushStateToDisk(chainparams, state, FlushStateMode::NONE);
+ if (!FlushStateToDisk(chainparams, state, FlushStateMode::NONE)) {
+ LogPrintf("%s: failed to flush state (%s)\n", __func__, FormatStateMessage(state));
+ }
}
static void DoWarning(const std::string& strWarning)
@@ -2560,8 +2557,9 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar
if (!ConnectTip(state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : std::shared_ptr<const CBlock>(), connectTrace, disconnectpool)) {
if (state.IsInvalid()) {
// The block violates a consensus rule.
- if (!state.CorruptionPossible())
- InvalidChainFound(vpindexToConnect.back());
+ if (!state.CorruptionPossible()) {
+ InvalidChainFound(vpindexToConnect.front());
+ }
state = CValidationState();
fInvalidFound = true;
fContinue = false;
@@ -2625,6 +2623,10 @@ static void NotifyHeaderTip() {
* Make the best chain active, in multiple steps. The result is either failure
* or an activated best chain. pblock is either nullptr or a pointer to a block
* that is already loaded (to avoid loading it again from disk).
+ *
+ * ActivateBestChain is split into steps (see ActivateBestChainStep) so that
+ * we avoid holding cs_main for an extended period of time; the length of this
+ * call may be quite long during reindexing or a substantial reorg.
*/
bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock) {
// Note that while we're often called here from ProcessNewBlock, this is
@@ -2678,18 +2680,17 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams&
assert(trace.pblock && trace.pindex);
GetMainSignals().BlockConnected(trace.pblock, trace.pindex, trace.conflictedTxs);
}
- }
- // When we reach this point, we switched to a new tip (stored in pindexNewTip).
- // Notifications/callbacks that can run without cs_main
+ // Notify external listeners about the new tip.
+ // Enqueue while holding cs_main to ensure that UpdatedBlockTip is called in the order in which blocks are connected
+ GetMainSignals().UpdatedBlockTip(pindexNewTip, pindexFork, fInitialDownload);
- // Notify external listeners about the new tip.
- GetMainSignals().UpdatedBlockTip(pindexNewTip, pindexFork, fInitialDownload);
-
- // Always notify the UI if a new block tip was connected
- if (pindexFork != pindexNewTip) {
- uiInterface.NotifyBlockTip(fInitialDownload, pindexNewTip);
+ // Always notify the UI if a new block tip was connected
+ if (pindexFork != pindexNewTip) {
+ uiInterface.NotifyBlockTip(fInitialDownload, pindexNewTip);
+ }
}
+ // When we reach this point, we switched to a new tip (stored in pindexNewTip).
if (nStopAtHeight && pindexNewTip && pindexNewTip->nHeight >= nStopAtHeight) StartShutdown();
@@ -2784,7 +2785,7 @@ bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& c
pindex->nStatus |= BLOCK_FAILED_VALID;
setDirtyBlockIndex.insert(pindex);
setBlockIndexCandidates.erase(pindex);
- g_failed_blocks.insert(pindex);
+ m_failed_blocks.insert(pindex);
// DisconnectTip will add transactions to disconnectpool; try to add these
// back to the mempool.
@@ -2830,7 +2831,7 @@ bool CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
// Reset invalid block marker if it was pointing to one of those.
pindexBestInvalid = nullptr;
}
- g_failed_blocks.erase(it->second);
+ m_failed_blocks.erase(it->second);
}
it++;
}
@@ -2840,6 +2841,7 @@ bool CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
if (pindex->nStatus & BLOCK_FAILED_MASK) {
pindex->nStatus &= ~BLOCK_FAILED_MASK;
setDirtyBlockIndex.insert(pindex);
+ m_failed_blocks.erase(pindex);
}
pindex = pindex->pprev;
}
@@ -3100,6 +3102,12 @@ bool IsWitnessEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& pa
return (VersionBitsState(pindexPrev, params, Consensus::DEPLOYMENT_SEGWIT, versionbitscache) == ThresholdState::ACTIVE);
}
+bool IsNullDummyEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& params)
+{
+ LOCK(cs_main);
+ return (VersionBitsState(pindexPrev, params, Consensus::DEPLOYMENT_SEGWIT, versionbitscache) == ThresholdState::ACTIVE);
+}
+
// Compute at which vout of the block's coinbase transaction the witness
// commitment occurs, or -1 if not found.
static int GetWitnessCommitmentIndex(const CBlock& block)
@@ -3323,8 +3331,11 @@ bool CChainState::AcceptBlockHeader(const CBlockHeader& block, CValidationState&
if (!ContextualCheckBlockHeader(block, state, chainparams, pindexPrev, GetAdjustedTime()))
return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state));
+ // If the previous block index isn't valid, determine if it descends from any block which
+ // has been found invalid (g_failed_blocks), then mark pindexPrev and any blocks
+ // between them as failed.
if (!pindexPrev->IsValid(BLOCK_VALID_SCRIPTS)) {
- for (const CBlockIndex* failedit : g_failed_blocks) {
+ for (const CBlockIndex* failedit : m_failed_blocks) {
if (pindexPrev->GetAncestor(failedit->nHeight) == failedit) {
assert(failedit->nStatus & BLOCK_FAILED_VALID);
CBlockIndex* invalid_walk = pindexPrev;
@@ -3490,7 +3501,7 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons
}
if (!ret) {
GetMainSignals().BlockChecked(*pblock, state);
- return error("%s: AcceptBlock FAILED (%s)", __func__, state.GetDebugMessage());
+ return error("%s: AcceptBlock FAILED (%s)", __func__, FormatStateMessage(state));
}
}
@@ -3498,7 +3509,7 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons
CValidationState state; // Only used to report errors, not invalidity - ignore it
if (!g_chainstate.ActivateBestChain(state, chainparams, pblock))
- return error("%s: ActivateBestChain failed", __func__);
+ return error("%s: ActivateBestChain failed (%s)", __func__, FormatStateMessage(state));
return true;
}
@@ -3616,7 +3627,9 @@ void PruneBlockFilesManual(int nManualPruneHeight)
{
CValidationState state;
const CChainParams& chainparams = Params();
- FlushStateToDisk(chainparams, state, FlushStateMode::NONE, nManualPruneHeight);
+ if (!FlushStateToDisk(chainparams, state, FlushStateMode::NONE, nManualPruneHeight)) {
+ LogPrintf("%s: failed to flush state (%s)\n", __func__, FormatStateMessage(state));
+ }
}
/**
@@ -3851,10 +3864,6 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams)
pblocktree->ReadReindexing(fReindexing);
if(fReindexing) fReindex = true;
- // Check whether we have a transaction index
- pblocktree->ReadFlag("txindex", fTxIndex);
- LogPrintf("%s: transaction index %s\n", __func__, fTxIndex ? "enabled" : "disabled");
-
return true;
}
@@ -3870,6 +3879,7 @@ bool LoadChainTip(const CChainParams& chainparams)
LogPrintf("%s: Connecting genesis block...\n", __func__);
CValidationState state;
if (!ActivateBestChain(state, chainparams)) {
+ LogPrintf("%s: failed to activate chain (%s)\n", __func__, FormatStateMessage(state));
return false;
}
}
@@ -3984,7 +3994,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
if (!ReadBlockFromDisk(block, pindex, chainparams.GetConsensus()))
return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
if (!g_chainstate.ConnectBlock(block, state, pindex, coins, chainparams))
- return error("VerifyDB(): *** found unconnectable block at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
+ return error("VerifyDB(): *** found unconnectable block at %d, hash=%s (%s)", pindex->nHeight, pindex->GetBlockHash().ToString(), FormatStateMessage(state));
}
}
@@ -4092,6 +4102,9 @@ bool CChainState::RewindBlockIndex(const CChainParams& params)
int nHeight = 1;
while (nHeight <= chainActive.Height()) {
+ // Although SCRIPT_VERIFY_WITNESS is now generally enforced on all
+ // blocks in ConnectBlock, we don't need to go back and
+ // re-download/re-verify blocks from before segwit actually activated.
if (IsWitnessEnabled(chainActive[nHeight - 1], params.GetConsensus()) && !(chainActive[nHeight]->nStatus & BLOCK_OPT_WITNESS)) {
break;
}
@@ -4111,11 +4124,13 @@ bool CChainState::RewindBlockIndex(const CChainParams& params)
break;
}
if (!DisconnectTip(state, params, nullptr)) {
- return error("RewindBlockIndex: unable to disconnect block at height %i", pindex->nHeight);
+ return error("RewindBlockIndex: unable to disconnect block at height %i (%s)", pindex->nHeight, FormatStateMessage(state));
}
// Occasionally flush state to disk.
- if (!FlushStateToDisk(params, state, FlushStateMode::PERIODIC))
+ if (!FlushStateToDisk(params, state, FlushStateMode::PERIODIC)) {
+ LogPrintf("RewindBlockIndex: unable to flush state to disk (%s)\n", FormatStateMessage(state));
return false;
+ }
}
// Reduce validity flag and have-data flags.
@@ -4181,6 +4196,7 @@ bool RewindBlockIndex(const CChainParams& params) {
// it'll get called a bunch real soon.
CValidationState state;
if (!FlushStateToDisk(params, state, FlushStateMode::ALWAYS)) {
+ LogPrintf("RewindBlockIndex: unable to flush state to disk (%s)\n", FormatStateMessage(state));
return false;
}
}
@@ -4190,7 +4206,7 @@ bool RewindBlockIndex(const CChainParams& params) {
void CChainState::UnloadBlockIndex() {
nBlockSequenceId = 1;
- g_failed_blocks.clear();
+ m_failed_blocks.clear();
setBlockIndexCandidates.clear();
}
@@ -4241,9 +4257,6 @@ bool LoadBlockIndex(const CChainParams& chainparams)
// needs_init.
LogPrintf("Initializing databases...\n");
- // Use the provided setting for -txindex in the new database
- fTxIndex = gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX);
- pblocktree->WriteFlag("txindex", fTxIndex);
}
return true;
}
@@ -4267,7 +4280,7 @@ bool CChainState::LoadGenesisBlock(const CChainParams& chainparams)
CBlockIndex *pindex = AddToBlockIndex(block);
CValidationState state;
if (!ReceivedBlockTransactions(block, state, pindex, blockPos, chainparams.GetConsensus()))
- return error("%s: genesis block not accepted", __func__);
+ return error("%s: genesis block not accepted (%s)", __func__, FormatStateMessage(state));
} catch (const std::runtime_error& e) {
return error("%s: failed to write genesis block: %s", __func__, e.what());
}
@@ -4732,7 +4745,8 @@ bool DumpMempool(void)
}
file << mapDeltas;
- FileCommit(file.Get());
+ if (!FileCommit(file.Get()))
+ throw std::runtime_error("FileCommit failed");
file.fclose();
RenameOver(GetDataDir() / "mempool.dat.new", GetDataDir() / "mempool.dat");
int64_t last = GetTimeMicros();
diff --git a/src/validation.h b/src/validation.h
index 3668484696..9b40100765 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -171,7 +171,6 @@ extern uint256 g_best_block;
extern std::atomic_bool fImporting;
extern std::atomic_bool fReindex;
extern int nScriptCheckThreads;
-extern bool fTxIndex;
extern bool fIsBareMultisigStd;
extern bool fRequireStandard;
extern bool fCheckBlockIndex;
@@ -411,6 +410,9 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams,
/** Check whether witness commitments are required for block. */
bool IsWitnessEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& params);
+/** Check whether NULLDUMMY (BIP 147) has activated. */
+bool IsNullDummyEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& params);
+
/** 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);
diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp
index 928df4fa65..f328d2d14b 100644
--- a/src/validationinterface.cpp
+++ b/src/validationinterface.cpp
@@ -25,7 +25,7 @@ struct MainSignalsInstance {
boost::signals2::signal<void (const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex, const std::vector<CTransactionRef>&)> BlockConnected;
boost::signals2::signal<void (const std::shared_ptr<const CBlock> &)> BlockDisconnected;
boost::signals2::signal<void (const CTransactionRef &)> TransactionRemovedFromMempool;
- boost::signals2::signal<void (const CBlockLocator &)> SetBestChain;
+ boost::signals2::signal<void (const CBlockLocator &)> ChainStateFlushed;
boost::signals2::signal<void (const uint256 &)> Inventory;
boost::signals2::signal<void (int64_t nBestBlockTime, CConnman* connman)> Broadcast;
boost::signals2::signal<void (const CBlock&, const CValidationState&)> BlockChecked;
@@ -80,7 +80,7 @@ void RegisterValidationInterface(CValidationInterface* pwalletIn) {
g_signals.m_internals->BlockConnected.connect(boost::bind(&CValidationInterface::BlockConnected, pwalletIn, _1, _2, _3));
g_signals.m_internals->BlockDisconnected.connect(boost::bind(&CValidationInterface::BlockDisconnected, pwalletIn, _1));
g_signals.m_internals->TransactionRemovedFromMempool.connect(boost::bind(&CValidationInterface::TransactionRemovedFromMempool, pwalletIn, _1));
- g_signals.m_internals->SetBestChain.connect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1));
+ g_signals.m_internals->ChainStateFlushed.connect(boost::bind(&CValidationInterface::ChainStateFlushed, pwalletIn, _1));
g_signals.m_internals->Inventory.connect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1));
g_signals.m_internals->Broadcast.connect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1, _2));
g_signals.m_internals->BlockChecked.connect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2));
@@ -91,7 +91,7 @@ void UnregisterValidationInterface(CValidationInterface* pwalletIn) {
g_signals.m_internals->BlockChecked.disconnect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2));
g_signals.m_internals->Broadcast.disconnect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1, _2));
g_signals.m_internals->Inventory.disconnect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1));
- g_signals.m_internals->SetBestChain.disconnect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1));
+ g_signals.m_internals->ChainStateFlushed.disconnect(boost::bind(&CValidationInterface::ChainStateFlushed, pwalletIn, _1));
g_signals.m_internals->TransactionAddedToMempool.disconnect(boost::bind(&CValidationInterface::TransactionAddedToMempool, pwalletIn, _1));
g_signals.m_internals->BlockConnected.disconnect(boost::bind(&CValidationInterface::BlockConnected, pwalletIn, _1, _2, _3));
g_signals.m_internals->BlockDisconnected.disconnect(boost::bind(&CValidationInterface::BlockDisconnected, pwalletIn, _1));
@@ -107,7 +107,7 @@ void UnregisterAllValidationInterfaces() {
g_signals.m_internals->BlockChecked.disconnect_all_slots();
g_signals.m_internals->Broadcast.disconnect_all_slots();
g_signals.m_internals->Inventory.disconnect_all_slots();
- g_signals.m_internals->SetBestChain.disconnect_all_slots();
+ g_signals.m_internals->ChainStateFlushed.disconnect_all_slots();
g_signals.m_internals->TransactionAddedToMempool.disconnect_all_slots();
g_signals.m_internals->BlockConnected.disconnect_all_slots();
g_signals.m_internals->BlockDisconnected.disconnect_all_slots();
@@ -139,6 +139,10 @@ void CMainSignals::MempoolEntryRemoved(CTransactionRef ptx, MemPoolRemovalReason
}
void CMainSignals::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {
+ // Dependencies exist that require UpdatedBlockTip events to be delivered in the order in which
+ // the chain actually updates. One way to ensure this is for the caller to invoke this signal
+ // in the same critical section where the chain is updated
+
m_internals->m_schedulerClient.AddToProcessQueue([pindexNew, pindexFork, fInitialDownload, this] {
m_internals->UpdatedBlockTip(pindexNew, pindexFork, fInitialDownload);
});
@@ -162,9 +166,9 @@ void CMainSignals::BlockDisconnected(const std::shared_ptr<const CBlock> &pblock
});
}
-void CMainSignals::SetBestChain(const CBlockLocator &locator) {
+void CMainSignals::ChainStateFlushed(const CBlockLocator &locator) {
m_internals->m_schedulerClient.AddToProcessQueue([locator, this] {
- m_internals->SetBestChain(locator);
+ m_internals->ChainStateFlushed(locator);
});
}
diff --git a/src/validationinterface.h b/src/validationinterface.h
index 63097166af..3a5fed0106 100644
--- a/src/validationinterface.h
+++ b/src/validationinterface.h
@@ -99,9 +99,20 @@ protected:
/**
* Notifies listeners of the new active block chain on-disk.
*
+ * Prior to this callback, any updates are not guaranteed to persist on disk
+ * (ie clients need to handle shutdown/restart safety by being able to
+ * understand when some updates were lost due to unclean shutdown).
+ *
+ * When this callback is invoked, the validation changes done by any prior
+ * callback are guaranteed to exist on disk and survive a restart, including
+ * an unclean shutdown.
+ *
+ * Provides a locator describing the best chain, which is likely useful for
+ * storing current state on disk in client DBs.
+ *
* Called on a background thread.
*/
- virtual void SetBestChain(const CBlockLocator &locator) {}
+ virtual void ChainStateFlushed(const CBlockLocator &locator) {}
/**
* Notifies listeners about an inventory item being seen on the network.
*
@@ -157,7 +168,7 @@ public:
void TransactionAddedToMempool(const CTransactionRef &);
void BlockConnected(const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex, const std::shared_ptr<const std::vector<CTransactionRef>> &);
void BlockDisconnected(const std::shared_ptr<const CBlock> &);
- void SetBestChain(const CBlockLocator &);
+ void ChainStateFlushed(const CBlockLocator &);
void Inventory(const uint256 &);
void Broadcast(int64_t nBestBlockTime, CConnman* connman);
void BlockChecked(const CBlock&, const CValidationState&);
diff --git a/src/wallet/coincontrol.h b/src/wallet/coincontrol.h
index 52d6a291c9..2f08162ee4 100644
--- a/src/wallet/coincontrol.h
+++ b/src/wallet/coincontrol.h
@@ -26,12 +26,12 @@ public:
bool fAllowWatchOnly;
//! Override automatic min/max checks on fee, m_feerate must be set if true
bool fOverrideFeeRate;
- //! Override the default payTxFee if set
+ //! Override the wallet's m_pay_tx_fee if set
boost::optional<CFeeRate> m_feerate;
//! Override the default confirmation target if set
boost::optional<unsigned int> m_confirm_target;
- //! Signal BIP-125 replace by fee.
- bool signalRbf;
+ //! Override the wallet's m_signal_rbf if set
+ boost::optional<bool> m_signal_bip125_rbf;
//! Fee estimation mode to control arguments to estimateSmartFee
FeeEstimateMode m_fee_mode;
@@ -50,7 +50,7 @@ public:
m_feerate.reset();
fOverrideFeeRate = false;
m_confirm_target.reset();
- signalRbf = fWalletRbf;
+ m_signal_bip125_rbf.reset();
m_fee_mode = FeeEstimateMode::UNSET;
}
diff --git a/src/wallet/coinselection.cpp b/src/wallet/coinselection.cpp
index 8596ad2adc..a403411e5b 100644
--- a/src/wallet/coinselection.cpp
+++ b/src/wallet/coinselection.cpp
@@ -286,10 +286,10 @@ bool KnapsackSolver(const CAmount& nTargetValue, std::vector<CInputCoin>& vCoins
}
if (LogAcceptCategory(BCLog::SELECTCOINS)) {
- LogPrint(BCLog::SELECTCOINS, "SelectCoins() best subset: ");
+ LogPrint(BCLog::SELECTCOINS, "SelectCoins() best subset: "); /* Continued */
for (unsigned int i = 0; i < vValue.size(); i++) {
if (vfBest[i]) {
- LogPrint(BCLog::SELECTCOINS, "%s ", FormatMoney(vValue[i].txout.nValue));
+ LogPrint(BCLog::SELECTCOINS, "%s ", FormatMoney(vValue[i].txout.nValue)); /* Continued */
}
}
LogPrint(BCLog::SELECTCOINS, "total %s\n", FormatMoney(nBest));
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index 10a06e4b9a..7e75ee7ad1 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -102,7 +102,7 @@ void BerkeleyEnvironment::Close()
int ret = dbenv->close(0);
if (ret != 0)
- LogPrintf("BerkeleyEnvironment::EnvShutdown: Error %d shutting down database environment: %s\n", ret, DbEnv::strerror(ret));
+ LogPrintf("BerkeleyEnvironment::Close: Error %d closing database environment: %s\n", ret, DbEnv::strerror(ret));
if (!fMockDb)
DbEnv((u_int32_t)0).remove(strPath.c_str(), 0);
}
@@ -168,8 +168,12 @@ bool BerkeleyEnvironment::Open(bool retry)
nEnvFlags,
S_IRUSR | S_IWUSR);
if (ret != 0) {
- dbenv->close(0);
LogPrintf("BerkeleyEnvironment::Open: Error %d opening database environment: %s\n", ret, DbEnv::strerror(ret));
+ int ret2 = dbenv->close(0);
+ if (ret2 != 0) {
+ LogPrintf("BerkeleyEnvironment::Open: Error %d closing failed database environment: %s\n", ret2, DbEnv::strerror(ret2));
+ }
+ Reset();
if (retry) {
// try moving the database env out of the way
fs::path pathDatabaseBak = pathIn / strprintf("database.%d.bak", GetTime());
diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp
index 5c34b39ec8..7742d5cec4 100644
--- a/src/wallet/feebumper.cpp
+++ b/src/wallet/feebumper.cpp
@@ -18,7 +18,7 @@
//! Check whether transaction has descendant in wallet or mempool, or has been
//! mined, or conflicts with a mined transaction. Return a feebumper::Result.
-static feebumper::Result PreconditionChecks(const CWallet* wallet, const CWalletTx& wtx, std::vector<std::string>& errors)
+static feebumper::Result PreconditionChecks(const CWallet* wallet, const CWalletTx& wtx, std::vector<std::string>& errors) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet)
{
if (wallet->HasWalletSpend(wtx.GetHash())) {
errors.push_back("Transaction has descendants in the wallet");
@@ -134,7 +134,7 @@ Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoin
FormatMoney(minTotalFee), FormatMoney(nOldFeeRate.GetFee(maxNewTxSize)), FormatMoney(::incrementalRelayFee.GetFee(maxNewTxSize))));
return Result::INVALID_PARAMETER;
}
- CAmount requiredFee = GetRequiredFee(maxNewTxSize);
+ CAmount requiredFee = GetRequiredFee(*wallet, maxNewTxSize);
if (total_fee < requiredFee) {
errors.push_back(strprintf("Insufficient totalFee (cannot be less than required fee %s)",
FormatMoney(requiredFee)));
@@ -143,7 +143,7 @@ Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoin
new_fee = total_fee;
nNewFeeRate = CFeeRate(total_fee, maxNewTxSize);
} else {
- new_fee = GetMinimumFee(maxNewTxSize, coin_control, mempool, ::feeEstimator, nullptr /* FeeCalculation */);
+ new_fee = GetMinimumFee(*wallet, maxNewTxSize, coin_control, mempool, ::feeEstimator, nullptr /* FeeCalculation */);
nNewFeeRate = CFeeRate(new_fee, maxNewTxSize);
// New fee rate must be at least old rate + minimum incremental relay rate
@@ -194,14 +194,14 @@ Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoin
// If the output would become dust, discard it (converting the dust to fee)
poutput->nValue -= nDelta;
- if (poutput->nValue <= GetDustThreshold(*poutput, GetDiscardRate(::feeEstimator))) {
+ if (poutput->nValue <= GetDustThreshold(*poutput, GetDiscardRate(*wallet, ::feeEstimator))) {
LogPrint(BCLog::RPC, "Bumping fee and discarding dust output\n");
new_fee += poutput->nValue;
mtx.vout.erase(mtx.vout.begin() + nOutput);
}
// Mark new tx not replaceable, if requested.
- if (!coin_control.signalRbf) {
+ if (!coin_control.m_signal_bip125_rbf.get_value_or(wallet->m_signal_rbf)) {
for (auto& input : mtx.vin) {
if (input.nSequence < 0xfffffffe) input.nSequence = 0xfffffffe;
}
diff --git a/src/wallet/fees.cpp b/src/wallet/fees.cpp
index 03c32d3b97..8d576a6689 100644
--- a/src/wallet/fees.cpp
+++ b/src/wallet/fees.cpp
@@ -13,15 +13,15 @@
#include <wallet/wallet.h>
-CAmount GetRequiredFee(unsigned int nTxBytes)
+CAmount GetRequiredFee(const CWallet& wallet, unsigned int nTxBytes)
{
- return std::max(CWallet::minTxFee.GetFee(nTxBytes), ::minRelayTxFee.GetFee(nTxBytes));
+ return GetRequiredFeeRate(wallet).GetFee(nTxBytes);
}
-CAmount GetMinimumFee(unsigned int nTxBytes, const CCoinControl& coin_control, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation *feeCalc)
+CAmount GetMinimumFee(const CWallet& wallet, unsigned int nTxBytes, const CCoinControl& coin_control, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation* feeCalc)
{
- CAmount fee_needed = GetMinimumFeeRate(coin_control, pool, estimator, feeCalc).GetFee(nTxBytes);
+ CAmount fee_needed = GetMinimumFeeRate(wallet, coin_control, pool, estimator, feeCalc).GetFee(nTxBytes);
// Always obey the maximum
if (fee_needed > maxTxFee) {
fee_needed = maxTxFee;
@@ -30,48 +30,48 @@ CAmount GetMinimumFee(unsigned int nTxBytes, const CCoinControl& coin_control, c
return fee_needed;
}
-CFeeRate GetRequiredFeeRate()
+CFeeRate GetRequiredFeeRate(const CWallet& wallet)
{
- return std::max(CWallet::minTxFee, ::minRelayTxFee);
+ return std::max(wallet.m_min_fee, ::minRelayTxFee);
}
-CFeeRate GetMinimumFeeRate(const CCoinControl& coin_control, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation *feeCalc)
+CFeeRate GetMinimumFeeRate(const CWallet& wallet, const CCoinControl& coin_control, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation* feeCalc)
{
/* User control of how to calculate fee uses the following parameter precedence:
1. coin_control.m_feerate
2. coin_control.m_confirm_target
- 3. payTxFee (user-set global variable)
- 4. nTxConfirmTarget (user-set global variable)
+ 3. m_pay_tx_fee (user-set member variable of wallet)
+ 4. m_confirm_target (user-set member variable of wallet)
The first parameter that is set is used.
*/
- CFeeRate feerate_needed ;
+ CFeeRate feerate_needed;
if (coin_control.m_feerate) { // 1.
feerate_needed = *(coin_control.m_feerate);
if (feeCalc) feeCalc->reason = FeeReason::PAYTXFEE;
// Allow to override automatic min/max check over coin control instance
if (coin_control.fOverrideFeeRate) return feerate_needed;
}
- else if (!coin_control.m_confirm_target && ::payTxFee != CFeeRate(0)) { // 3. TODO: remove magic value of 0 for global payTxFee
- feerate_needed = ::payTxFee;
+ else if (!coin_control.m_confirm_target && wallet.m_pay_tx_fee != CFeeRate(0)) { // 3. TODO: remove magic value of 0 for wallet member m_pay_tx_fee
+ feerate_needed = wallet.m_pay_tx_fee;
if (feeCalc) feeCalc->reason = FeeReason::PAYTXFEE;
}
else { // 2. or 4.
// We will use smart fee estimation
- unsigned int target = coin_control.m_confirm_target ? *coin_control.m_confirm_target : ::nTxConfirmTarget;
+ unsigned int target = coin_control.m_confirm_target ? *coin_control.m_confirm_target : wallet.m_confirm_target;
// By default estimates are economical iff we are signaling opt-in-RBF
- bool conservative_estimate = !coin_control.signalRbf;
+ bool conservative_estimate = !coin_control.m_signal_bip125_rbf.get_value_or(wallet.m_signal_rbf);
// Allow to override the default fee estimate mode over the CoinControl instance
if (coin_control.m_fee_mode == FeeEstimateMode::CONSERVATIVE) conservative_estimate = true;
else if (coin_control.m_fee_mode == FeeEstimateMode::ECONOMICAL) conservative_estimate = false;
feerate_needed = estimator.estimateSmartFee(target, feeCalc, conservative_estimate);
if (feerate_needed == CFeeRate(0)) {
- // if we don't have enough data for estimateSmartFee, then use fallbackFee
- feerate_needed = CWallet::fallbackFee;
+ // if we don't have enough data for estimateSmartFee, then use fallback fee
+ feerate_needed = wallet.m_fallback_fee;
if (feeCalc) feeCalc->reason = FeeReason::FALLBACK;
// directly return if fallback fee is disabled (feerate 0 == disabled)
- if (CWallet::fallbackFee == CFeeRate(0)) return feerate_needed;
+ if (wallet.m_fallback_fee == CFeeRate(0)) return feerate_needed;
}
// Obey mempool min fee when using smart fee estimation
CFeeRate min_mempool_feerate = pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
@@ -81,8 +81,8 @@ CFeeRate GetMinimumFeeRate(const CCoinControl& coin_control, const CTxMemPool& p
}
}
- // prevent user from paying a fee below minRelayTxFee or minTxFee
- CFeeRate required_feerate = GetRequiredFeeRate();
+ // prevent user from paying a fee below the required fee rate
+ CFeeRate required_feerate = GetRequiredFeeRate(wallet);
if (required_feerate > feerate_needed) {
feerate_needed = required_feerate;
if (feeCalc) feeCalc->reason = FeeReason::REQUIRED;
@@ -90,12 +90,12 @@ CFeeRate GetMinimumFeeRate(const CCoinControl& coin_control, const CTxMemPool& p
return feerate_needed;
}
-CFeeRate GetDiscardRate(const CBlockPolicyEstimator& estimator)
+CFeeRate GetDiscardRate(const CWallet& wallet, const CBlockPolicyEstimator& estimator)
{
unsigned int highest_target = estimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);
CFeeRate discard_rate = estimator.estimateSmartFee(highest_target, nullptr /* FeeCalculation */, false /* conservative */);
// Don't let discard_rate be greater than longest possible fee estimate if we get a valid fee estimate
- discard_rate = (discard_rate == CFeeRate(0)) ? CWallet::m_discard_rate : std::min(discard_rate, CWallet::m_discard_rate);
+ discard_rate = (discard_rate == CFeeRate(0)) ? wallet.m_discard_rate : std::min(discard_rate, wallet.m_discard_rate);
// Discard rate must be at least dustRelayFee
discard_rate = std::max(discard_rate, ::dustRelayFee);
return discard_rate;
diff --git a/src/wallet/fees.h b/src/wallet/fees.h
index a627af70b0..b3cd064abc 100644
--- a/src/wallet/fees.h
+++ b/src/wallet/fees.h
@@ -12,35 +12,36 @@ class CBlockPolicyEstimator;
class CCoinControl;
class CFeeRate;
class CTxMemPool;
+class CWallet;
struct FeeCalculation;
/**
- * Return the minimum required fee taking into account the
- * floating relay fee and user set minimum transaction fee
+ * Return the minimum required absolute fee for this size
+ * based on the required fee rate
*/
-CAmount GetRequiredFee(unsigned int nTxBytes);
+CAmount GetRequiredFee(const CWallet& wallet, unsigned int nTxBytes);
/**
* Estimate the minimum fee considering user set parameters
* and the required fee
*/
-CAmount GetMinimumFee(unsigned int nTxBytes, const CCoinControl& coin_control, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation *feeCalc);
+CAmount GetMinimumFee(const CWallet& wallet, unsigned int nTxBytes, const CCoinControl& coin_control, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation* feeCalc);
/**
* Return the minimum required feerate taking into account the
- * floating relay feerate and user set minimum transaction feerate
+ * minimum relay feerate and user set minimum transaction feerate
*/
-CFeeRate GetRequiredFeeRate();
+CFeeRate GetRequiredFeeRate(const CWallet& wallet);
/**
* Estimate the minimum fee rate considering user set parameters
* and the required fee
*/
-CFeeRate GetMinimumFeeRate(const CCoinControl& coin_control, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation *feeCalc);
+CFeeRate GetMinimumFeeRate(const CWallet& wallet, const CCoinControl& coin_control, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation* feeCalc);
/**
* Return the maximum feerate for discarding change.
*/
-CFeeRate GetDiscardRate(const CBlockPolicyEstimator& estimator);
+CFeeRate GetDiscardRate(const CWallet& wallet, const CBlockPolicyEstimator& estimator);
#endif // BITCOIN_WALLET_FEES_H
diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp
index b6f4a0e1e1..6c5522e4bc 100644
--- a/src/wallet/init.cpp
+++ b/src/wallet/init.cpp
@@ -18,81 +18,72 @@ class WalletInit : public WalletInitInterface {
public:
//! Return the wallets help message.
- std::string GetHelpString(bool showDebug) override;
+ void AddWalletOptions() const override;
//! Wallets parameter interaction
- bool ParameterInteraction() override;
+ bool ParameterInteraction() const override;
//! Register wallet RPCs.
- void RegisterRPC(CRPCTable &tableRPC) override;
+ void RegisterRPC(CRPCTable &tableRPC) const 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;
+ bool Verify() const override;
//! Load wallet databases.
- bool Open() override;
+ bool Open() const override;
//! Complete startup of wallets.
- void Start(CScheduler& scheduler) override;
+ void Start(CScheduler& scheduler) const override;
//! Flush all wallets in preparation for shutdown.
- void Flush() override;
+ void Flush() const override;
//! Stop all wallets. Wallets will be flushed first.
- void Stop() override;
+ void Stop() const override;
//! Close all wallets.
- void Close() override;
+ void Close() const override;
};
-static WalletInit g_wallet_init;
-WalletInitInterface* const g_wallet_init_interface = &g_wallet_init;
+const WalletInitInterface& g_wallet_init_interface = WalletInit();
-std::string WalletInit::GetHelpString(bool showDebug)
+void WalletInit::AddWalletOptions() const
{
- 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)));
- strUsage += HelpMessageOpt("-changetype", "What type of change to use (\"legacy\", \"p2sh-segwit\", or \"bech32\"). Default is same as -addresstype, except when -addresstype=p2sh-segwit a native segwit output is used when sending to a native segwit address)");
- strUsage += HelpMessageOpt("-disablewallet", _("Do not load the wallet and disable wallet RPC calls"));
- strUsage += HelpMessageOpt("-discardfee=<amt>", strprintf(_("The fee rate (in %s/kB) that indicates your tolerance for discarding change by adding it to the fee (default: %s). "
+ gArgs.AddArg("-addresstype", strprintf("What type of addresses to use (\"legacy\", \"p2sh-segwit\", or \"bech32\", default: \"%s\")", FormatOutputType(DEFAULT_ADDRESS_TYPE)), false, OptionsCategory::WALLET);
+ gArgs.AddArg("-changetype", "What type of change to use (\"legacy\", \"p2sh-segwit\", or \"bech32\"). Default is same as -addresstype, except when -addresstype=p2sh-segwit a native segwit output is used when sending to a native segwit address)", false, OptionsCategory::WALLET);
+ gArgs.AddArg("-disablewallet", _("Do not load the wallet and disable wallet RPC calls"), false, OptionsCategory::WALLET);
+ gArgs.AddArg("-discardfee=<amt>", strprintf(_("The fee rate (in %s/kB) that indicates your tolerance for discarding change by adding it to the fee (default: %s). "
"Note: An output is discarded if it is dust at this rate, but we will always discard up to the dust relay fee and a discard fee above that is limited by the fee estimate for the longest target"),
- CURRENCY_UNIT, FormatMoney(DEFAULT_DISCARD_FEE)));
- strUsage += HelpMessageOpt("-fallbackfee=<amt>", strprintf(_("A fee rate (in %s/kB) that will be used when fee estimation has insufficient data (default: %s)"),
- CURRENCY_UNIT, FormatMoney(DEFAULT_FALLBACK_FEE)));
- strUsage += HelpMessageOpt("-keypool=<n>", strprintf(_("Set key pool size to <n> (default: %u)"), DEFAULT_KEYPOOL_SIZE));
- strUsage += HelpMessageOpt("-mintxfee=<amt>", strprintf(_("Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)"),
- CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MINFEE)));
- strUsage += HelpMessageOpt("-paytxfee=<amt>", strprintf(_("Fee (in %s/kB) to add to transactions you send (default: %s)"),
- CURRENCY_UNIT, FormatMoney(payTxFee.GetFeePerK())));
- strUsage += HelpMessageOpt("-rescan", _("Rescan the block chain for missing wallet transactions on startup"));
- strUsage += HelpMessageOpt("-salvagewallet", _("Attempt to recover private keys from a corrupt wallet on startup"));
- strUsage += HelpMessageOpt("-spendzeroconfchange", strprintf(_("Spend unconfirmed change when sending transactions (default: %u)"), DEFAULT_SPEND_ZEROCONF_CHANGE));
- strUsage += HelpMessageOpt("-txconfirmtarget=<n>", strprintf(_("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)"), DEFAULT_TX_CONFIRM_TARGET));
- strUsage += HelpMessageOpt("-upgradewallet", _("Upgrade wallet to latest format on startup"));
- strUsage += HelpMessageOpt("-wallet=<path>", _("Specify wallet database path. Can be specified multiple times to load multiple wallets. Path is interpreted relative to <walletdir> if it is not absolute, and will be created if it does not exist (as a directory containing a wallet.dat file and log files). For backwards compatibility this will also accept names of existing data files in <walletdir>.)"));
- strUsage += HelpMessageOpt("-walletbroadcast", _("Make the wallet broadcast transactions") + " " + strprintf(_("(default: %u)"), DEFAULT_WALLETBROADCAST));
- strUsage += HelpMessageOpt("-walletdir=<dir>", _("Specify directory to hold wallets (default: <datadir>/wallets if it exists, otherwise <datadir>)"));
- strUsage += HelpMessageOpt("-walletnotify=<cmd>", _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)"));
- strUsage += HelpMessageOpt("-walletrbf", strprintf(_("Send transactions with full-RBF opt-in enabled (RPC only, default: %u)"), DEFAULT_WALLET_RBF));
- strUsage += HelpMessageOpt("-zapwallettxes=<mode>", _("Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup") +
- " " + _("(1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data)"));
-
- if (showDebug)
- {
- strUsage += HelpMessageGroup(_("Wallet debugging/testing options:"));
-
- strUsage += HelpMessageOpt("-dblogsize=<n>", strprintf("Flush wallet database activity from memory to disk log every <n> megabytes (default: %u)", DEFAULT_WALLET_DBLOGSIZE));
- strUsage += HelpMessageOpt("-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %u)", DEFAULT_FLUSHWALLET));
- strUsage += HelpMessageOpt("-privdb", strprintf("Sets the DB_PRIVATE flag in the wallet db environment (default: %u)", DEFAULT_WALLET_PRIVDB));
- strUsage += HelpMessageOpt("-walletrejectlongchains", strprintf(_("Wallet will not create transactions that violate mempool chain limits (default: %u)"), DEFAULT_WALLET_REJECT_LONG_CHAINS));
- }
-
- return strUsage;
+ CURRENCY_UNIT, FormatMoney(DEFAULT_DISCARD_FEE)), false, OptionsCategory::WALLET);
+ gArgs.AddArg("-fallbackfee=<amt>", strprintf(_("A fee rate (in %s/kB) that will be used when fee estimation has insufficient data (default: %s)"),
+ CURRENCY_UNIT, FormatMoney(DEFAULT_FALLBACK_FEE)), false, OptionsCategory::WALLET);
+ gArgs.AddArg("-keypool=<n>", strprintf(_("Set key pool size to <n> (default: %u)"), DEFAULT_KEYPOOL_SIZE), false, OptionsCategory::WALLET);
+ gArgs.AddArg("-mintxfee=<amt>", strprintf(_("Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)"),
+ CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MINFEE)), false, OptionsCategory::WALLET);
+ gArgs.AddArg("-paytxfee=<amt>", strprintf(_("Fee (in %s/kB) to add to transactions you send (default: %s)"),
+ CURRENCY_UNIT, FormatMoney(CFeeRate{DEFAULT_PAY_TX_FEE}.GetFeePerK())), false, OptionsCategory::WALLET);
+ gArgs.AddArg("-rescan", _("Rescan the block chain for missing wallet transactions on startup"), false, OptionsCategory::WALLET);
+ gArgs.AddArg("-salvagewallet", _("Attempt to recover private keys from a corrupt wallet on startup"), false, OptionsCategory::WALLET);
+ gArgs.AddArg("-spendzeroconfchange", strprintf(_("Spend unconfirmed change when sending transactions (default: %u)"), DEFAULT_SPEND_ZEROCONF_CHANGE), false, OptionsCategory::WALLET);
+ gArgs.AddArg("-txconfirmtarget=<n>", strprintf(_("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)"), DEFAULT_TX_CONFIRM_TARGET), false, OptionsCategory::WALLET);
+ gArgs.AddArg("-upgradewallet", _("Upgrade wallet to latest format on startup"), false, OptionsCategory::WALLET);
+ gArgs.AddArg("-wallet=<path>", _("Specify wallet database path. Can be specified multiple times to load multiple wallets. Path is interpreted relative to <walletdir> if it is not absolute, and will be created if it does not exist (as a directory containing a wallet.dat file and log files). For backwards compatibility this will also accept names of existing data files in <walletdir>.)"), false, OptionsCategory::WALLET);
+ gArgs.AddArg("-walletbroadcast", _("Make the wallet broadcast transactions") + " " + strprintf(_("(default: %u)"), DEFAULT_WALLETBROADCAST), false, OptionsCategory::WALLET);
+ gArgs.AddArg("-walletdir=<dir>", _("Specify directory to hold wallets (default: <datadir>/wallets if it exists, otherwise <datadir>)"), false, OptionsCategory::WALLET);
+ gArgs.AddArg("-walletnotify=<cmd>", _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)"), false, OptionsCategory::WALLET);
+ gArgs.AddArg("-walletrbf", strprintf(_("Send transactions with full-RBF opt-in enabled (RPC only, default: %u)"), DEFAULT_WALLET_RBF), false, OptionsCategory::WALLET);
+ gArgs.AddArg("-zapwallettxes=<mode>", _("Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup") +
+ " " + _("(1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data)"), false, OptionsCategory::WALLET);
+
+ gArgs.AddArg("-dblogsize=<n>", strprintf("Flush wallet database activity from memory to disk log every <n> megabytes (default: %u)", DEFAULT_WALLET_DBLOGSIZE), true, OptionsCategory::WALLET_DEBUG_TEST);
+ gArgs.AddArg("-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %u)", DEFAULT_FLUSHWALLET), true, OptionsCategory::WALLET_DEBUG_TEST);
+ gArgs.AddArg("-privdb", strprintf("Sets the DB_PRIVATE flag in the wallet db environment (default: %u)", DEFAULT_WALLET_PRIVDB), true, OptionsCategory::WALLET_DEBUG_TEST);
+ gArgs.AddArg("-walletrejectlongchains", strprintf(_("Wallet will not create transactions that violate mempool chain limits (default: %u)"), DEFAULT_WALLET_REJECT_LONG_CHAINS), true, OptionsCategory::WALLET_DEBUG_TEST);
}
-bool WalletInit::ParameterInteraction()
+bool WalletInit::ParameterInteraction() const
{
if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
for (const std::string& wallet : gArgs.GetArgs("-wallet")) {
@@ -119,19 +110,19 @@ bool WalletInit::ParameterInteraction()
}
}
- int zapwallettxes = gArgs.GetArg("-zapwallettxes", 0);
+ bool zapwallettxes = gArgs.GetBoolArg("-zapwallettxes", false);
// -zapwallettxes implies dropping the mempool on startup
- if (zapwallettxes != 0 && gArgs.SoftSetBoolArg("-persistmempool", false)) {
- LogPrintf("%s: parameter interaction: -zapwallettxes=%s -> setting -persistmempool=0\n", __func__, zapwallettxes);
+ if (zapwallettxes && gArgs.SoftSetBoolArg("-persistmempool", false)) {
+ LogPrintf("%s: parameter interaction: -zapwallettxes enabled -> setting -persistmempool=0\n", __func__);
}
// -zapwallettxes implies a rescan
- if (zapwallettxes != 0) {
+ if (zapwallettxes) {
if (is_multiwallet) {
return InitError(strprintf("%s is only allowed with a single wallet file", "-zapwallettxes"));
}
if (gArgs.SoftSetBoolArg("-rescan", true)) {
- LogPrintf("%s: parameter interaction: -zapwallettxes=%s -> setting -rescan=1\n", __func__, zapwallettxes);
+ LogPrintf("%s: parameter interaction: -zapwallettxes enabled -> setting -rescan=1\n", __func__);
}
}
@@ -150,55 +141,6 @@ bool WalletInit::ParameterInteraction()
InitWarning(AmountHighWarn("-minrelaytxfee") + " " +
_("The wallet will avoid paying less than the minimum relay fee."));
- if (gArgs.IsArgSet("-mintxfee"))
- {
- CAmount n = 0;
- if (!ParseMoney(gArgs.GetArg("-mintxfee", ""), n) || 0 == n)
- return InitError(AmountErrMsg("mintxfee", gArgs.GetArg("-mintxfee", "")));
- if (n > HIGH_TX_FEE_PER_KB)
- InitWarning(AmountHighWarn("-mintxfee") + " " +
- _("This is the minimum transaction fee you pay on every transaction."));
- CWallet::minTxFee = CFeeRate(n);
- }
-
- g_wallet_allow_fallback_fee = Params().IsFallbackFeeEnabled();
- if (gArgs.IsArgSet("-fallbackfee"))
- {
- CAmount nFeePerK = 0;
- if (!ParseMoney(gArgs.GetArg("-fallbackfee", ""), nFeePerK))
- return InitError(strprintf(_("Invalid amount for -fallbackfee=<amount>: '%s'"), gArgs.GetArg("-fallbackfee", "")));
- if (nFeePerK > HIGH_TX_FEE_PER_KB)
- InitWarning(AmountHighWarn("-fallbackfee") + " " +
- _("This is the transaction fee you may pay when fee estimates are not available."));
- CWallet::fallbackFee = CFeeRate(nFeePerK);
- g_wallet_allow_fallback_fee = nFeePerK != 0; //disable fallback fee in case value was set to 0, enable if non-null value
- }
- if (gArgs.IsArgSet("-discardfee"))
- {
- CAmount nFeePerK = 0;
- if (!ParseMoney(gArgs.GetArg("-discardfee", ""), nFeePerK))
- return InitError(strprintf(_("Invalid amount for -discardfee=<amount>: '%s'"), gArgs.GetArg("-discardfee", "")));
- if (nFeePerK > HIGH_TX_FEE_PER_KB)
- InitWarning(AmountHighWarn("-discardfee") + " " +
- _("This is the transaction fee you may discard if change is smaller than dust at this level"));
- CWallet::m_discard_rate = CFeeRate(nFeePerK);
- }
- if (gArgs.IsArgSet("-paytxfee"))
- {
- CAmount nFeePerK = 0;
- if (!ParseMoney(gArgs.GetArg("-paytxfee", ""), nFeePerK))
- return InitError(AmountErrMsg("paytxfee", gArgs.GetArg("-paytxfee", "")));
- if (nFeePerK > HIGH_TX_FEE_PER_KB)
- InitWarning(AmountHighWarn("-paytxfee") + " " +
- _("This is the transaction fee you will pay if you send a transaction."));
-
- payTxFee = CFeeRate(nFeePerK, 1000);
- if (payTxFee < ::minRelayTxFee)
- {
- return InitError(strprintf(_("Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s)"),
- gArgs.GetArg("-paytxfee", ""), ::minRelayTxFee.ToString()));
- }
- }
if (gArgs.IsArgSet("-maxtxfee"))
{
CAmount nMaxFee = 0;
@@ -213,14 +155,11 @@ bool WalletInit::ParameterInteraction()
gArgs.GetArg("-maxtxfee", ""), ::minRelayTxFee.ToString()));
}
}
- nTxConfirmTarget = gArgs.GetArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET);
- bSpendZeroConfChange = gArgs.GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE);
- fWalletRbf = gArgs.GetBoolArg("-walletrbf", DEFAULT_WALLET_RBF);
return true;
}
-void WalletInit::RegisterRPC(CRPCTable &t)
+void WalletInit::RegisterRPC(CRPCTable &t) const
{
if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
return;
@@ -229,7 +168,7 @@ void WalletInit::RegisterRPC(CRPCTable &t)
RegisterWalletRPCCommands(t);
}
-bool WalletInit::Verify()
+bool WalletInit::Verify() const
{
if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
return true;
@@ -304,7 +243,7 @@ bool WalletInit::Verify()
return true;
}
-bool WalletInit::Open()
+bool WalletInit::Open() const
{
if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
LogPrintf("Wallet disabled!\n");
@@ -316,37 +255,37 @@ bool WalletInit::Open()
if (!pwallet) {
return false;
}
- vpwallets.push_back(pwallet);
+ AddWallet(pwallet);
}
return true;
}
-void WalletInit::Start(CScheduler& scheduler)
+void WalletInit::Start(CScheduler& scheduler) const
{
- for (CWalletRef pwallet : vpwallets) {
+ for (CWallet* pwallet : GetWallets()) {
pwallet->postInitProcess(scheduler);
}
}
-void WalletInit::Flush()
+void WalletInit::Flush() const
{
- for (CWalletRef pwallet : vpwallets) {
+ for (CWallet* pwallet : GetWallets()) {
pwallet->Flush(false);
}
}
-void WalletInit::Stop()
+void WalletInit::Stop() const
{
- for (CWalletRef pwallet : vpwallets) {
+ for (CWallet* pwallet : GetWallets()) {
pwallet->Flush(true);
}
}
-void WalletInit::Close()
+void WalletInit::Close() const
{
- for (CWalletRef pwallet : vpwallets) {
+ for (CWallet* pwallet : GetWallets()) {
+ RemoveWallet(pwallet);
delete pwallet;
}
- vpwallets.clear();
}
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index b8533839a0..4fe630b660 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -4,7 +4,6 @@
#include <chain.h>
#include <key_io.h>
-#include <rpc/safemode.h>
#include <rpc/server.h>
#include <validation.h>
#include <script/script.h>
@@ -52,7 +51,7 @@ std::string static EncodeDumpString(const std::string &str) {
return ret.str();
}
-std::string DecodeDumpString(const std::string &str) {
+static std::string DecodeDumpString(const std::string &str) {
std::stringstream ret;
for (unsigned int pos = 0; pos < str.length(); pos++) {
unsigned char c = str[pos];
@@ -66,7 +65,7 @@ std::string DecodeDumpString(const std::string &str) {
return ret.str();
}
-bool GetWalletAddressesForKey(CWallet * const pwallet, const CKeyID &keyid, std::string &strAddr, std::string &strLabel)
+static bool GetWalletAddressesForKey(CWallet * const pwallet, const CKeyID &keyid, std::string &strAddr, std::string &strLabel)
{
bool fLabelFound = false;
CKey key;
@@ -204,14 +203,13 @@ UniValue abortrescan(const JSONRPCRequest& request)
+ HelpExampleRpc("abortrescan", "")
);
- ObserveSafeMode();
if (!pwallet->IsScanning() || pwallet->IsAbortingRescan()) return false;
pwallet->AbortRescan();
return true;
}
-void ImportAddress(CWallet*, const CTxDestination& dest, const std::string& strLabel);
-void ImportScript(CWallet* const pwallet, const CScript& script, const std::string& strLabel, bool isRedeemScript)
+static void ImportAddress(CWallet*, const CTxDestination& dest, const std::string& strLabel);
+static void ImportScript(CWallet* const pwallet, const CScript& script, const std::string& strLabel, bool isRedeemScript) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
{
if (!isRedeemScript && ::IsMine(*pwallet, script) == ISMINE_SPENDABLE) {
throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
@@ -224,10 +222,11 @@ void ImportScript(CWallet* const pwallet, const CScript& script, const std::stri
}
if (isRedeemScript) {
- if (!pwallet->HaveCScript(script) && !pwallet->AddCScript(script)) {
+ const CScriptID id(script);
+ if (!pwallet->HaveCScript(id) && !pwallet->AddCScript(script)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2sh redeemScript to wallet");
}
- ImportAddress(pwallet, CScriptID(script), strLabel);
+ ImportAddress(pwallet, id, strLabel);
} else {
CTxDestination destination;
if (ExtractDestination(script, destination)) {
@@ -236,7 +235,7 @@ void ImportScript(CWallet* const pwallet, const CScript& script, const std::stri
}
}
-void ImportAddress(CWallet* const pwallet, const CTxDestination& dest, const std::string& strLabel)
+static void ImportAddress(CWallet* const pwallet, const CTxDestination& dest, const std::string& strLabel) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
{
CScript script = GetScriptForDestination(dest);
ImportScript(pwallet, script, strLabel, false);
@@ -602,7 +601,8 @@ UniValue importwallet(const JSONRPCRequest& request)
} else if(IsHex(vstr[0])) {
std::vector<unsigned char> vData(ParseHex(vstr[0]));
CScript script = CScript(vData.begin(), vData.end());
- if (pwallet->HaveCScript(script)) {
+ CScriptID id(script);
+ if (pwallet->HaveCScript(id)) {
LogPrintf("Skipping import of %s (script already present)\n", vstr[0]);
continue;
}
@@ -613,7 +613,7 @@ UniValue importwallet(const JSONRPCRequest& request)
}
int64_t birth_time = DecodeDumpTime(vstr[1]);
if (birth_time > 0) {
- pwallet->m_script_metadata[CScriptID(script)].nCreateTime = birth_time;
+ pwallet->m_script_metadata[id].nCreateTime = birth_time;
nTimeBegin = std::min(nTimeBegin, birth_time);
}
}
@@ -811,7 +811,7 @@ UniValue dumpwallet(const JSONRPCRequest& request)
}
-UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int64_t timestamp)
+static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
{
try {
bool success = false;
@@ -899,12 +899,12 @@ UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int6
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
}
- if (!pwallet->HaveCScript(redeemScript) && !pwallet->AddCScript(redeemScript)) {
+ CScriptID redeem_id(redeemScript);
+ if (!pwallet->HaveCScript(redeem_id) && !pwallet->AddCScript(redeemScript)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2sh redeemScript to wallet");
}
- CTxDestination redeem_dest = CScriptID(redeemScript);
- CScript redeemDestination = GetScriptForDestination(redeem_dest);
+ CScript redeemDestination = GetScriptForDestination(redeem_id);
if (::IsMine(*pwallet, redeemDestination) == ISMINE_SPENDABLE) {
throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
@@ -1111,7 +1111,7 @@ UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int6
}
}
-int64_t GetImportTimestamp(const UniValue& data, int64_t now)
+static int64_t GetImportTimestamp(const UniValue& data, int64_t now)
{
if (data.exists("timestamp")) {
const UniValue& timestamp = data["timestamp"];
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 56bdc0695c..a7b2b05d9b 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -17,7 +17,6 @@
#include <policy/rbf.h>
#include <rpc/mining.h>
#include <rpc/rawtransaction.h>
-#include <rpc/safemode.h>
#include <rpc/server.h>
#include <rpc/util.h>
#include <script/sign.h>
@@ -46,14 +45,13 @@ CWallet *GetWalletForJSONRPCRequest(const JSONRPCRequest& request)
if (request.URI.substr(0, WALLET_ENDPOINT_BASE.size()) == WALLET_ENDPOINT_BASE) {
// wallet endpoint was used
std::string requestedWallet = urlDecode(request.URI.substr(WALLET_ENDPOINT_BASE.size()));
- for (CWalletRef pwallet : ::vpwallets) {
- if (pwallet->GetName() == requestedWallet) {
- return pwallet;
- }
- }
- throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded");
+ CWallet* pwallet = GetWallet(requestedWallet);
+ if (!pwallet) throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded");
+ return pwallet;
}
- return ::vpwallets.size() == 1 || (request.fHelp && ::vpwallets.size() > 0) ? ::vpwallets[0] : nullptr;
+
+ std::vector<CWallet*> wallets = GetWallets();
+ return wallets.size() == 1 || (request.fHelp && wallets.size() > 0) ? wallets[0] : nullptr;
}
std::string HelpRequiringPassphrase(CWallet * const pwallet)
@@ -67,7 +65,7 @@ bool EnsureWalletIsAvailable(CWallet * const pwallet, bool avoidException)
{
if (pwallet) return true;
if (avoidException) return false;
- if (::vpwallets.empty()) {
+ if (!HasWallets()) {
// Note: It isn't currently possible to trigger this error because
// wallet RPC methods aren't registered unless a wallet is loaded. But
// this error is being kept as a precaution, because it's possible in
@@ -87,7 +85,7 @@ void EnsureWalletIsUnlocked(CWallet * const pwallet)
}
}
-void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry)
+static void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry)
{
int confirms = wtx.GetDepthInMainChain();
entry.pushKV("confirmations", confirms);
@@ -126,7 +124,7 @@ void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry)
entry.pushKV(item.first, item.second);
}
-std::string LabelFromValue(const UniValue& value)
+static std::string LabelFromValue(const UniValue& value)
{
std::string label = value.get_str();
if (label == "*")
@@ -134,7 +132,7 @@ std::string LabelFromValue(const UniValue& value)
return label;
}
-UniValue getnewaddress(const JSONRPCRequest& request)
+static UniValue getnewaddress(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
@@ -166,8 +164,7 @@ UniValue getnewaddress(const JSONRPCRequest& request)
OutputType output_type = pwallet->m_default_address_type;
if (!request.params[1].isNull()) {
- output_type = ParseOutputType(request.params[1].get_str(), pwallet->m_default_address_type);
- if (output_type == OutputType::NONE) {
+ if (!ParseOutputType(request.params[1].get_str(), output_type)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[1].get_str()));
}
}
@@ -199,13 +196,20 @@ CTxDestination GetLabelDestination(CWallet* const pwallet, const std::string& la
return dest;
}
-UniValue getlabeladdress(const JSONRPCRequest& request)
+static UniValue getlabeladdress(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
+ if (!IsDeprecatedRPCEnabled("accounts") && request.strMethod == "getaccountaddress") {
+ if (request.fHelp) {
+ throw std::runtime_error("getaccountaddress (Deprecated, will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts)");
+ }
+ throw JSONRPCError(RPC_METHOD_DEPRECATED, "getaccountaddress is deprecated and will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts.");
+ }
+
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
"getlabeladdress \"label\" ( force ) \n"
@@ -227,7 +231,7 @@ UniValue getlabeladdress(const JSONRPCRequest& request)
// Parse the label first so we don't generate a key if there's an error
std::string label = LabelFromValue(request.params[0]);
- bool force = request.strMethod == "getaccountaddress" ? true : false;
+ bool force = request.strMethod == "getaccountaddress";
if (!request.params[1].isNull()) {
force = request.params[1].get_bool();
}
@@ -250,7 +254,7 @@ UniValue getlabeladdress(const JSONRPCRequest& request)
}
-UniValue getrawchangeaddress(const JSONRPCRequest& request)
+static UniValue getrawchangeaddress(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
@@ -277,10 +281,9 @@ UniValue getrawchangeaddress(const JSONRPCRequest& request)
pwallet->TopUpKeyPool();
}
- OutputType output_type = pwallet->m_default_change_type != OutputType::NONE ? pwallet->m_default_change_type : pwallet->m_default_address_type;
+ OutputType output_type = pwallet->m_default_change_type != OutputType::CHANGE_AUTO ? pwallet->m_default_change_type : pwallet->m_default_address_type;
if (!request.params[0].isNull()) {
- output_type = ParseOutputType(request.params[0].get_str(), output_type);
- if (output_type == OutputType::NONE) {
+ if (!ParseOutputType(request.params[0].get_str(), output_type)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[0].get_str()));
}
}
@@ -299,13 +302,20 @@ UniValue getrawchangeaddress(const JSONRPCRequest& request)
}
-UniValue setlabel(const JSONRPCRequest& request)
+static UniValue setlabel(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
+ if (!IsDeprecatedRPCEnabled("accounts") && request.strMethod == "setaccount") {
+ if (request.fHelp) {
+ throw std::runtime_error("setaccount (Deprecated, will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts)");
+ }
+ throw JSONRPCError(RPC_METHOD_DEPRECATED, "setaccount is deprecated and will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts.");
+ }
+
if (request.fHelp || request.params.size() != 2)
throw std::runtime_error(
"setlabel \"address\" \"label\"\n"
@@ -346,13 +356,20 @@ UniValue setlabel(const JSONRPCRequest& request)
}
-UniValue getaccount(const JSONRPCRequest& request)
+static UniValue getaccount(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
+ if (!IsDeprecatedRPCEnabled("accounts")) {
+ if (request.fHelp) {
+ throw std::runtime_error("getaccount (Deprecated, will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts)");
+ }
+ throw JSONRPCError(RPC_METHOD_DEPRECATED, "getaccount is deprecated and will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts.");
+ }
+
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
"getaccount \"address\"\n"
@@ -382,13 +399,20 @@ UniValue getaccount(const JSONRPCRequest& request)
}
-UniValue getaddressesbyaccount(const JSONRPCRequest& request)
+static UniValue getaddressesbyaccount(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
+ if (!IsDeprecatedRPCEnabled("accounts")) {
+ if (request.fHelp) {
+ throw std::runtime_error("getaddressbyaccount (Deprecated, will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts)");
+ }
+ throw JSONRPCError(RPC_METHOD_DEPRECATED, "getaddressesbyaccount is deprecated and will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts.");
+ }
+
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
"getaddressesbyaccount \"account\"\n"
@@ -461,7 +485,7 @@ static CTransactionRef SendMoney(CWallet * const pwallet, const CTxDestination &
return tx;
}
-UniValue sendtoaddress(const JSONRPCRequest& request)
+static UniValue sendtoaddress(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
@@ -498,8 +522,6 @@ UniValue sendtoaddress(const JSONRPCRequest& request)
+ HelpExampleRpc("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", 0.1, \"donation\", \"seans outpost\"")
);
- ObserveSafeMode();
-
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
@@ -530,7 +552,7 @@ UniValue sendtoaddress(const JSONRPCRequest& request)
CCoinControl coin_control;
if (!request.params[5].isNull()) {
- coin_control.signalRbf = request.params[5].get_bool();
+ coin_control.m_signal_bip125_rbf = request.params[5].get_bool();
}
if (!request.params[6].isNull()) {
@@ -550,7 +572,7 @@ UniValue sendtoaddress(const JSONRPCRequest& request)
return tx->GetHash().GetHex();
}
-UniValue listaddressgroupings(const JSONRPCRequest& request)
+static UniValue listaddressgroupings(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
@@ -580,8 +602,6 @@ UniValue listaddressgroupings(const JSONRPCRequest& request)
+ HelpExampleRpc("listaddressgroupings", "")
);
- ObserveSafeMode();
-
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
@@ -609,7 +629,7 @@ UniValue listaddressgroupings(const JSONRPCRequest& request)
return jsonGroupings;
}
-UniValue signmessage(const JSONRPCRequest& request)
+static UniValue signmessage(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
@@ -670,7 +690,7 @@ UniValue signmessage(const JSONRPCRequest& request)
return EncodeBase64(vchSig.data(), vchSig.size());
}
-UniValue getreceivedbyaddress(const JSONRPCRequest& request)
+static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
@@ -697,8 +717,6 @@ UniValue getreceivedbyaddress(const JSONRPCRequest& request)
+ HelpExampleRpc("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", 6")
);
- ObserveSafeMode();
-
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
@@ -737,13 +755,20 @@ UniValue getreceivedbyaddress(const JSONRPCRequest& request)
}
-UniValue getreceivedbylabel(const JSONRPCRequest& request)
+static UniValue getreceivedbylabel(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
+ if (!IsDeprecatedRPCEnabled("accounts") && request.strMethod == "getreceivedbyaccount") {
+ if (request.fHelp) {
+ throw std::runtime_error("getreceivedbyaccount (Deprecated, will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts)");
+ }
+ throw JSONRPCError(RPC_METHOD_DEPRECATED, "getreceivedbyaccount is deprecated and will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts.");
+ }
+
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
"getreceivedbylabel \"label\" ( minconf )\n"
@@ -764,8 +789,6 @@ UniValue getreceivedbylabel(const JSONRPCRequest& request)
+ HelpExampleRpc("getreceivedbylabel", "\"tabby\", 6")
);
- ObserveSafeMode();
-
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
@@ -802,14 +825,14 @@ UniValue getreceivedbylabel(const JSONRPCRequest& request)
}
-UniValue getbalance(const JSONRPCRequest& request)
+static UniValue getbalance(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
- if (request.fHelp || request.params.size() > 3)
+ if (request.fHelp || (request.params.size() > 3 && IsDeprecatedRPCEnabled("accounts")) || (request.params.size() != 0 && !IsDeprecatedRPCEnabled("accounts")))
throw std::runtime_error(
"getbalance ( \"account\" minconf include_watchonly )\n"
"\nIf account is not specified, returns the server's total available balance.\n"
@@ -819,7 +842,8 @@ UniValue getbalance(const JSONRPCRequest& request)
"Note that the account \"\" is not the same as leaving the parameter out.\n"
"The server total may be different to the balance in the default \"\" account.\n"
"\nArguments:\n"
- "1. \"account\" (string, optional) DEPRECATED. The account string may be given as a\n"
+ "1. \"account\" (string, optional) DEPRECATED. This argument will be removed in V0.18. \n"
+ " To use this deprecated argument, start bitcoind with -deprecatedrpc=accounts. The account string may be given as a\n"
" specific account name to find the balance associated with wallet keys in\n"
" a named account, or as the empty string (\"\") to find the balance\n"
" associated with wallet keys not in any named account, or as \"*\" to find\n"
@@ -831,8 +855,8 @@ UniValue getbalance(const JSONRPCRequest& request)
" balances. In general, account balance calculation is not considered\n"
" reliable and has resulted in confusing outcomes, so it is recommended to\n"
" avoid passing this argument.\n"
- "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n"
- "3. include_watchonly (bool, optional, default=false) Also include balance in watch-only addresses (see 'importaddress')\n"
+ "2. minconf (numeric, optional, default=1) DEPRECATED. Only valid when an account is specified. This argument will be removed in V0.18. To use this deprecated argument, start bitcoind with -deprecatedrpc=accounts. Only include transactions confirmed at least this many times.\n"
+ "3. include_watchonly (bool, optional, default=false) DEPRECATED. Only valid when an account is specified. This argument will be removed in V0.18. To use this deprecated argument, start bitcoind with -deprecatedrpc=accounts. Also include balance in watch-only addresses (see 'importaddress')\n"
"\nResult:\n"
"amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this account.\n"
"\nExamples:\n"
@@ -844,45 +868,47 @@ UniValue getbalance(const JSONRPCRequest& request)
+ HelpExampleRpc("getbalance", "\"*\", 6")
);
- ObserveSafeMode();
-
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
LOCK2(cs_main, pwallet->cs_wallet);
- const UniValue& account_value = request.params[0];
- const UniValue& minconf = request.params[1];
- const UniValue& include_watchonly = request.params[2];
+ if (IsDeprecatedRPCEnabled("accounts")) {
+ const UniValue& account_value = request.params[0];
+ const UniValue& minconf = request.params[1];
+ const UniValue& include_watchonly = request.params[2];
- if (account_value.isNull()) {
- if (!minconf.isNull()) {
- throw JSONRPCError(RPC_INVALID_PARAMETER,
- "getbalance minconf option is only currently supported if an account is specified");
- }
- if (!include_watchonly.isNull()) {
- throw JSONRPCError(RPC_INVALID_PARAMETER,
- "getbalance include_watchonly option is only currently supported if an account is specified");
+ if (account_value.isNull()) {
+ if (!minconf.isNull()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER,
+ "getbalance minconf option is only currently supported if an account is specified");
+ }
+ if (!include_watchonly.isNull()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER,
+ "getbalance include_watchonly option is only currently supported if an account is specified");
+ }
+ return ValueFromAmount(pwallet->GetBalance());
}
- return ValueFromAmount(pwallet->GetBalance());
- }
- const std::string& account_param = account_value.get_str();
- const std::string* account = account_param != "*" ? &account_param : nullptr;
+ const std::string& account_param = account_value.get_str();
+ const std::string* account = account_param != "*" ? &account_param : nullptr;
- int nMinDepth = 1;
- if (!minconf.isNull())
- nMinDepth = minconf.get_int();
- isminefilter filter = ISMINE_SPENDABLE;
- if(!include_watchonly.isNull())
- if(include_watchonly.get_bool())
- filter = filter | ISMINE_WATCH_ONLY;
+ int nMinDepth = 1;
+ if (!minconf.isNull())
+ nMinDepth = minconf.get_int();
+ isminefilter filter = ISMINE_SPENDABLE;
+ if(!include_watchonly.isNull())
+ if(include_watchonly.get_bool())
+ filter = filter | ISMINE_WATCH_ONLY;
+
+ return ValueFromAmount(pwallet->GetLegacyBalance(filter, nMinDepth, account));
+ }
- return ValueFromAmount(pwallet->GetLegacyBalance(filter, nMinDepth, account));
+ return ValueFromAmount(pwallet->GetBalance());
}
-UniValue getunconfirmedbalance(const JSONRPCRequest &request)
+static UniValue getunconfirmedbalance(const JSONRPCRequest &request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
@@ -894,8 +920,6 @@ UniValue getunconfirmedbalance(const JSONRPCRequest &request)
"getunconfirmedbalance\n"
"Returns the server's total unconfirmed balance\n");
- ObserveSafeMode();
-
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
@@ -906,13 +930,20 @@ UniValue getunconfirmedbalance(const JSONRPCRequest &request)
}
-UniValue movecmd(const JSONRPCRequest& request)
+static UniValue movecmd(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
+ if (!IsDeprecatedRPCEnabled("accounts")) {
+ if (request.fHelp) {
+ throw std::runtime_error("move (Deprecated, will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts)");
+ }
+ throw JSONRPCError(RPC_METHOD_DEPRECATED, "move is deprecated and will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts.");
+ }
+
if (request.fHelp || request.params.size() < 3 || request.params.size() > 5)
throw std::runtime_error(
"move \"fromaccount\" \"toaccount\" amount ( minconf \"comment\" )\n"
@@ -934,7 +965,6 @@ UniValue movecmd(const JSONRPCRequest& request)
+ HelpExampleRpc("move", "\"timotei\", \"akiko\", 0.01, 6, \"happy birthday!\"")
);
- ObserveSafeMode();
LOCK2(cs_main, pwallet->cs_wallet);
std::string strFrom = LabelFromValue(request.params[0]);
@@ -957,7 +987,7 @@ UniValue movecmd(const JSONRPCRequest& request)
}
-UniValue sendfrom(const JSONRPCRequest& request)
+static UniValue sendfrom(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
@@ -993,8 +1023,6 @@ UniValue sendfrom(const JSONRPCRequest& request)
+ HelpExampleRpc("sendfrom", "\"tabby\", \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", 0.01, 6, \"donation\", \"seans outpost\"")
);
- ObserveSafeMode();
-
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
@@ -1032,16 +1060,57 @@ UniValue sendfrom(const JSONRPCRequest& request)
}
-UniValue sendmany(const JSONRPCRequest& request)
+static UniValue sendmany(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
- if (request.fHelp || request.params.size() < 2 || request.params.size() > 8)
- throw std::runtime_error(
- "sendmany \"fromaccount\" {\"address\":amount,...} ( minconf \"comment\" [\"address\",...] replaceable conf_target \"estimate_mode\")\n"
+ std::string help_text;
+ if (!IsDeprecatedRPCEnabled("accounts")) {
+ help_text = "sendmany \"\" {\"address\":amount,...} ( minconf \"comment\" [\"address\",...] replaceable conf_target \"estimate_mode\")\n"
+ "\nSend multiple times. Amounts are double-precision floating point numbers.\n"
+ "Note that the \"fromaccount\" argument has been removed in V0.17. To use this RPC with a \"fromaccount\" argument, restart\n"
+ "bitcoind with -deprecatedrpc=accounts\n"
+ + HelpRequiringPassphrase(pwallet) + "\n"
+ "\nArguments:\n"
+ "1. \"dummy\" (string, required) Must be set to \"\" for backwards compatibility.\n"
+ "2. \"amounts\" (string, required) A json object with addresses and amounts\n"
+ " {\n"
+ " \"address\":amount (numeric or string) The bitcoin address is the key, the numeric amount (can be string) in " + CURRENCY_UNIT + " is the value\n"
+ " ,...\n"
+ " }\n"
+ "3. minconf (numeric, optional, default=1) Only use the balance confirmed at least this many times.\n"
+ "4. \"comment\" (string, optional) A comment\n"
+ "5. subtractfeefrom (array, optional) A json array with addresses.\n"
+ " The fee will be equally deducted from the amount of each selected address.\n"
+ " Those recipients will receive less bitcoins than you enter in their corresponding amount field.\n"
+ " If no addresses are specified here, the sender pays the fee.\n"
+ " [\n"
+ " \"address\" (string) Subtract fee from this address\n"
+ " ,...\n"
+ " ]\n"
+ "6. replaceable (boolean, optional) Allow this transaction to be replaced by a transaction with higher fees via BIP 125\n"
+ "7. conf_target (numeric, optional) Confirmation target (in blocks)\n"
+ "8. \"estimate_mode\" (string, optional, default=UNSET) The fee estimate mode, must be one of:\n"
+ " \"UNSET\"\n"
+ " \"ECONOMICAL\"\n"
+ " \"CONSERVATIVE\"\n"
+ "\nResult:\n"
+ "\"txid\" (string) The transaction id for the send. Only 1 transaction is created regardless of \n"
+ " the number of addresses.\n"
+ "\nExamples:\n"
+ "\nSend two amounts to two different addresses:\n"
+ + HelpExampleCli("sendmany", "\"\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\"") +
+ "\nSend two amounts to two different addresses setting the confirmation and comment:\n"
+ + HelpExampleCli("sendmany", "\"\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" 6 \"testing\"") +
+ "\nSend two amounts to two different addresses, subtract fee from amount:\n"
+ + HelpExampleCli("sendmany", "\"\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" 1 \"\" \"[\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\",\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\"]\"") +
+ "\nAs a json rpc call\n"
+ + HelpExampleRpc("sendmany", "\"\", {\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\":0.01,\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\":0.02}, 6, \"testing\"");
+ } else {
+ help_text = "sendmany \"\" \"fromaccount\" {\"address\":amount,...} ( minconf \"comment\" [\"address\",...] replaceable conf_target \"estimate_mode\")\n"
"\nSend multiple times. Amounts are double-precision floating point numbers."
+ HelpRequiringPassphrase(pwallet) + "\n"
"\nArguments:\n"
@@ -1078,10 +1147,10 @@ UniValue sendmany(const JSONRPCRequest& request)
"\nSend two amounts to two different addresses, subtract fee from amount:\n"
+ HelpExampleCli("sendmany", "\"\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" 1 \"\" \"[\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\",\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\"]\"") +
"\nAs a json rpc call\n"
- + HelpExampleRpc("sendmany", "\"\", {\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\":0.01,\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\":0.02}, 6, \"testing\"")
- );
+ + HelpExampleRpc("sendmany", "\"\", {\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\":0.01,\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\":0.02}, 6, \"testing\"");
+ }
- ObserveSafeMode();
+ if (request.fHelp || request.params.size() < 2 || request.params.size() > 8) throw std::runtime_error(help_text);
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
@@ -1093,6 +1162,9 @@ UniValue sendmany(const JSONRPCRequest& request)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
}
+ if (!IsDeprecatedRPCEnabled("accounts") && !request.params[0].get_str().empty()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Dummy value must be set to \"\"");
+ }
std::string strAccount = LabelFromValue(request.params[0]);
UniValue sendTo = request.params[1].get_obj();
int nMinDepth = 1;
@@ -1109,7 +1181,7 @@ UniValue sendmany(const JSONRPCRequest& request)
CCoinControl coin_control;
if (!request.params[5].isNull()) {
- coin_control.signalRbf = request.params[5].get_bool();
+ coin_control.m_signal_bip125_rbf = request.params[5].get_bool();
}
if (!request.params[6].isNull()) {
@@ -1183,7 +1255,7 @@ UniValue sendmany(const JSONRPCRequest& request)
return tx->GetHash().GetHex();
}
-UniValue addmultisigaddress(const JSONRPCRequest& request)
+static UniValue addmultisigaddress(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
@@ -1243,8 +1315,7 @@ UniValue addmultisigaddress(const JSONRPCRequest& request)
OutputType output_type = pwallet->m_default_address_type;
if (!request.params[3].isNull()) {
- output_type = ParseOutputType(request.params[3].get_str(), output_type);
- if (output_type == OutputType::NONE) {
+ if (!ParseOutputType(request.params[3].get_str(), output_type)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[3].get_str()));
}
}
@@ -1319,7 +1390,7 @@ public:
bool operator()(const T& dest) { return false; }
};
-UniValue addwitnessaddress(const JSONRPCRequest& request)
+static UniValue addwitnessaddress(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
@@ -1405,7 +1476,7 @@ struct tallyitem
}
};
-UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool by_label)
+static UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool by_label)
{
// Minimum confirmations
int nMinDepth = 1;
@@ -1551,7 +1622,7 @@ UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool by_l
return ret;
}
-UniValue listreceivedbyaddress(const JSONRPCRequest& request)
+static UniValue listreceivedbyaddress(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
@@ -1591,8 +1662,6 @@ UniValue listreceivedbyaddress(const JSONRPCRequest& request)
+ HelpExampleRpc("listreceivedbyaddress", "6, true, true, \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\"")
);
- ObserveSafeMode();
-
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
@@ -1602,13 +1671,20 @@ UniValue listreceivedbyaddress(const JSONRPCRequest& request)
return ListReceived(pwallet, request.params, false);
}
-UniValue listreceivedbylabel(const JSONRPCRequest& request)
+static UniValue listreceivedbylabel(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
+ if (!IsDeprecatedRPCEnabled("accounts") && request.strMethod == "listreceivedbyaccount") {
+ if (request.fHelp) {
+ throw std::runtime_error("listreceivedbyaccount (Deprecated, will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts)");
+ }
+ throw JSONRPCError(RPC_METHOD_DEPRECATED, "listreceivedbyaccount is deprecated and will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts.");
+ }
+
if (request.fHelp || request.params.size() > 3)
throw std::runtime_error(
"listreceivedbylabel ( minconf include_empty include_watchonly)\n"
@@ -1636,8 +1712,6 @@ UniValue listreceivedbylabel(const JSONRPCRequest& request)
+ HelpExampleRpc("listreceivedbylabel", "6, true, true")
);
- ObserveSafeMode();
-
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
@@ -1665,7 +1739,7 @@ static void MaybePushAddress(UniValue & entry, const CTxDestination &dest)
* @param ret The UniValue into which the result is stored.
* @param filter The "is mine" filter bool.
*/
-void ListTransactions(CWallet* const pwallet, const CWalletTx& wtx, const std::string& strAccount, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter)
+static void ListTransactions(CWallet* const pwallet, const CWalletTx& wtx, const std::string& strAccount, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter)
{
CAmount nFee;
std::string strSentAccount;
@@ -1686,7 +1760,7 @@ void ListTransactions(CWallet* const pwallet, const CWalletTx& wtx, const std::s
if (involvesWatchonly || (::IsMine(*pwallet, s.destination) & ISMINE_WATCH_ONLY)) {
entry.pushKV("involvesWatchonly", true);
}
- entry.pushKV("account", strSentAccount);
+ if (IsDeprecatedRPCEnabled("accounts")) entry.pushKV("account", strSentAccount);
MaybePushAddress(entry, s.destination);
entry.pushKV("category", "send");
entry.pushKV("amount", ValueFromAmount(-s.amount));
@@ -1717,7 +1791,7 @@ void ListTransactions(CWallet* const pwallet, const CWalletTx& wtx, const std::s
if (involvesWatchonly || (::IsMine(*pwallet, r.destination) & ISMINE_WATCH_ONLY)) {
entry.pushKV("involvesWatchonly", true);
}
- entry.pushKV("account", account);
+ if (IsDeprecatedRPCEnabled("accounts")) entry.pushKV("account", account);
MaybePushAddress(entry, r.destination);
if (wtx.IsCoinBase())
{
@@ -1745,7 +1819,7 @@ void ListTransactions(CWallet* const pwallet, const CWalletTx& wtx, const std::s
}
}
-void AcentryToJSON(const CAccountingEntry& acentry, const std::string& strAccount, UniValue& ret)
+static void AcentryToJSON(const CAccountingEntry& acentry, const std::string& strAccount, UniValue& ret)
{
bool fAllAccounts = (strAccount == std::string("*"));
@@ -1756,7 +1830,7 @@ void AcentryToJSON(const CAccountingEntry& acentry, const std::string& strAccoun
entry.pushKV("category", "move");
entry.pushKV("time", acentry.nTime);
entry.pushKV("amount", ValueFromAmount(acentry.nCreditDebit));
- entry.pushKV("otheraccount", acentry.strOtherAccount);
+ if (IsDeprecatedRPCEnabled("accounts")) entry.pushKV("otheraccount", acentry.strOtherAccount);
entry.pushKV("comment", acentry.strComment);
ret.push_back(entry);
}
@@ -1769,19 +1843,64 @@ UniValue listtransactions(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() > 4)
- throw std::runtime_error(
- "listtransactions ( \"account\" count skip include_watchonly)\n"
+ std::string help_text {};
+ if (!IsDeprecatedRPCEnabled("accounts")) {
+ help_text = "listtransactions (dummy count skip include_watchonly)\n"
"\nReturns up to 'count' most recent transactions skipping the first 'from' transactions for account 'account'.\n"
+ "Note that the \"account\" argument and \"otheraccount\" return value have been removed in V0.17. To use this RPC with an \"account\" argument, restart\n"
+ "bitcoind with -deprecatedrpc=accounts\n"
"\nArguments:\n"
- "1. \"account\" (string, optional) DEPRECATED. The account name. Should be \"*\".\n"
+ "1. \"dummy\" (string, optional) If set, should be \"*\" for backwards compatibility.\n"
"2. count (numeric, optional, default=10) The number of transactions to return\n"
"3. skip (numeric, optional, default=0) The number of transactions to skip\n"
"4. include_watchonly (bool, optional, default=false) Include transactions to watch-only addresses (see 'importaddress')\n"
"\nResult:\n"
"[\n"
" {\n"
- " \"account\":\"accountname\", (string) DEPRECATED. The account name associated with the transaction. \n"
+ " \"address\":\"address\", (string) The bitcoin address of the transaction.\n"
+ " \"category\":\"send|receive\", (string) The transaction category.\n"
+ " \"amount\": x.xxx, (numeric) The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and is positive\n"
+ " for the 'receive' category,\n"
+ " \"label\": \"label\", (string) A comment for the address/transaction, if any\n"
+ " \"vout\": n, (numeric) the vout value\n"
+ " \"fee\": x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n"
+ " 'send' category of transactions.\n"
+ " \"confirmations\": n, (numeric) The number of confirmations for the transaction. Negative confirmations indicate the\n"
+ " transaction conflicts with the block chain\n"
+ " \"trusted\": xxx, (bool) Whether we consider the outputs of this unconfirmed transaction safe to spend.\n"
+ " \"blockhash\": \"hashvalue\", (string) The block hash containing the transaction.\n"
+ " \"blockindex\": n, (numeric) The index of the transaction in the block that includes it.\n"
+ " \"blocktime\": xxx, (numeric) The block time in seconds since epoch (1 Jan 1970 GMT).\n"
+ " \"txid\": \"transactionid\", (string) The transaction id.\n"
+ " \"time\": xxx, (numeric) The transaction time in seconds since epoch (midnight Jan 1 1970 GMT).\n"
+ " \"timereceived\": xxx, (numeric) The time received in seconds since epoch (midnight Jan 1 1970 GMT).\n"
+ " \"comment\": \"...\", (string) If a comment is associated with the transaction.\n"
+ " \"bip125-replaceable\": \"yes|no|unknown\", (string) Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n"
+ " may be unknown for unconfirmed transactions not in the mempool\n"
+ " \"abandoned\": xxx (bool) 'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n"
+ " 'send' category of transactions.\n"
+ " }\n"
+ "]\n"
+
+ "\nExamples:\n"
+ "\nList the most recent 10 transactions in the systems\n"
+ + HelpExampleCli("listtransactions", "") +
+ "\nList transactions 100 to 120\n"
+ + HelpExampleCli("listtransactions", "\"*\" 20 100") +
+ "\nAs a json rpc call\n"
+ + HelpExampleRpc("listtransactions", "\"*\", 20, 100");
+ } else {
+ help_text = "listtransactions ( \"account\" count skip include_watchonly)\n"
+ "\nReturns up to 'count' most recent transactions skipping the first 'from' transactions for account 'account'.\n"
+ "\nArguments:\n"
+ "1. \"account\" (string, optional) DEPRECATED. This argument will be removed in V0.18. The account name. Should be \"*\".\n"
+ "2. count (numeric, optional, default=10) The number of transactions to return\n"
+ "3. skip (numeric, optional, default=0) The number of transactions to skip\n"
+ "4. include_watchonly (bool, optional, default=false) Include transactions to watch-only addresses (see 'importaddress')\n"
+ "\nResult:\n"
+ "[\n"
+ " {\n"
+ " \"account\":\"accountname\", (string) DEPRECATED. This field will be removed in V0.18. The account name associated with the transaction. \n"
" It will be \"\" for the default account.\n"
" \"address\":\"address\", (string) The bitcoin address of the transaction. Not present for \n"
" move transactions (category = move).\n"
@@ -1810,7 +1929,7 @@ UniValue listtransactions(const JSONRPCRequest& request)
" \"timereceived\": xxx, (numeric) The time received in seconds since epoch (midnight Jan 1 1970 GMT). Available \n"
" for 'send' and 'receive' category of transactions.\n"
" \"comment\": \"...\", (string) If a comment is associated with the transaction.\n"
- " \"otheraccount\": \"accountname\", (string) DEPRECATED. For the 'move' category of transactions, the account the funds came \n"
+ " \"otheraccount\": \"accountname\", (string) DEPRECATED. This field will be removed in V0.18. For the 'move' category of transactions, the account the funds came \n"
" from (for receiving funds, positive amounts), or went to (for sending funds,\n"
" negative amounts).\n"
" \"bip125-replaceable\": \"yes|no|unknown\", (string) Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n"
@@ -1826,18 +1945,21 @@ UniValue listtransactions(const JSONRPCRequest& request)
"\nList transactions 100 to 120\n"
+ HelpExampleCli("listtransactions", "\"*\" 20 100") +
"\nAs a json rpc call\n"
- + HelpExampleRpc("listtransactions", "\"*\", 20, 100")
- );
-
- ObserveSafeMode();
+ + HelpExampleRpc("listtransactions", "\"*\", 20, 100");
+ }
+ if (request.fHelp || request.params.size() > 4) throw std::runtime_error(help_text);
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
std::string strAccount = "*";
- if (!request.params[0].isNull())
+ if (!request.params[0].isNull()) {
strAccount = request.params[0].get_str();
+ if (!IsDeprecatedRPCEnabled("accounts") && strAccount != "*") {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Dummy value must be set to \"*\"");
+ }
+ }
int nCount = 10;
if (!request.params[1].isNull())
nCount = request.params[1].get_int();
@@ -1867,9 +1989,10 @@ UniValue listtransactions(const JSONRPCRequest& request)
CWalletTx *const pwtx = (*it).second.first;
if (pwtx != nullptr)
ListTransactions(pwallet, *pwtx, strAccount, 0, true, ret, filter);
- CAccountingEntry *const pacentry = (*it).second.second;
- if (pacentry != nullptr)
- AcentryToJSON(*pacentry, strAccount, ret);
+ if (IsDeprecatedRPCEnabled("accounts")) {
+ CAccountingEntry *const pacentry = (*it).second.second;
+ if (pacentry != nullptr) AcentryToJSON(*pacentry, strAccount, ret);
+ }
if ((int)ret.size() >= (nCount+nFrom)) break;
}
@@ -1901,13 +2024,20 @@ UniValue listtransactions(const JSONRPCRequest& request)
return ret;
}
-UniValue listaccounts(const JSONRPCRequest& request)
+static UniValue listaccounts(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
+ if (!IsDeprecatedRPCEnabled("accounts")) {
+ if (request.fHelp) {
+ throw std::runtime_error("listaccounts (Deprecated, will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts)");
+ }
+ throw JSONRPCError(RPC_METHOD_DEPRECATED, "listaccounts is deprecated and will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts.");
+ }
+
if (request.fHelp || request.params.size() > 2)
throw std::runtime_error(
"listaccounts ( minconf include_watchonly)\n"
@@ -1931,8 +2061,6 @@ UniValue listaccounts(const JSONRPCRequest& request)
+ HelpExampleRpc("listaccounts", "6")
);
- ObserveSafeMode();
-
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
@@ -1989,7 +2117,7 @@ UniValue listaccounts(const JSONRPCRequest& request)
return ret;
}
-UniValue listsinceblock(const JSONRPCRequest& request)
+static UniValue listsinceblock(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
@@ -2011,7 +2139,7 @@ UniValue listsinceblock(const JSONRPCRequest& request)
"\nResult:\n"
"{\n"
" \"transactions\": [\n"
- " \"account\":\"accountname\", (string) DEPRECATED. The account name associated with the transaction. Will be \"\" for the default account.\n"
+ " \"account\":\"accountname\", (string) DEPRECATED. This field will be removed in V0.18. To see this deprecated field, start bitcoind with -deprecatedrpc=accounts. The account name associated with the transaction. Will be \"\" for the default account.\n"
" \"address\":\"address\", (string) The bitcoin address of the transaction. Not present for move transactions (category = move).\n"
" \"category\":\"send|receive\", (string) The transaction category. 'send' has negative amounts, 'receive' has positive amounts.\n"
" \"amount\": x.xxx, (numeric) The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and for the 'move' category for moves \n"
@@ -2045,8 +2173,6 @@ UniValue listsinceblock(const JSONRPCRequest& request)
+ HelpExampleRpc("listsinceblock", "\"000000000000000bacf66f7497b7dc45ef753ee9a7d38571037cdb1a57f663ad\", 6")
);
- ObserveSafeMode();
-
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
@@ -2130,7 +2256,7 @@ UniValue listsinceblock(const JSONRPCRequest& request)
return ret;
}
-UniValue gettransaction(const JSONRPCRequest& request)
+static UniValue gettransaction(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
@@ -2160,7 +2286,7 @@ UniValue gettransaction(const JSONRPCRequest& request)
" may be unknown for unconfirmed transactions not in the mempool\n"
" \"details\" : [\n"
" {\n"
- " \"account\" : \"accountname\", (string) DEPRECATED. The account name involved in the transaction, can be \"\" for the default account.\n"
+ " \"account\" : \"accountname\", (string) DEPRECATED. This field will be removed in a V0.18. To see this deprecated field, start bitcoind with -deprecatedrpc=accounts. The account name involved in the transaction, can be \"\" for the default account.\n"
" \"address\" : \"address\", (string) The bitcoin address involved in the transaction\n"
" \"category\" : \"send|receive\", (string) The category, either 'send' or 'receive'\n"
" \"amount\" : x.xxx, (numeric) The amount in " + CURRENCY_UNIT + "\n"
@@ -2182,8 +2308,6 @@ UniValue gettransaction(const JSONRPCRequest& request)
+ HelpExampleRpc("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
);
- ObserveSafeMode();
-
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
@@ -2226,7 +2350,7 @@ UniValue gettransaction(const JSONRPCRequest& request)
return entry;
}
-UniValue abandontransaction(const JSONRPCRequest& request)
+static UniValue abandontransaction(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
@@ -2250,8 +2374,6 @@ UniValue abandontransaction(const JSONRPCRequest& request)
);
}
- ObserveSafeMode();
-
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
@@ -2272,7 +2394,7 @@ UniValue abandontransaction(const JSONRPCRequest& request)
}
-UniValue backupwallet(const JSONRPCRequest& request)
+static UniValue backupwallet(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
@@ -2305,7 +2427,7 @@ UniValue backupwallet(const JSONRPCRequest& request)
}
-UniValue keypoolrefill(const JSONRPCRequest& request)
+static UniValue keypoolrefill(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
@@ -2352,7 +2474,7 @@ static void LockWallet(CWallet* pWallet)
pWallet->Lock();
}
-UniValue walletpassphrase(const JSONRPCRequest& request)
+static UniValue walletpassphrase(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
@@ -2425,7 +2547,7 @@ UniValue walletpassphrase(const JSONRPCRequest& request)
}
-UniValue walletpassphrasechange(const JSONRPCRequest& request)
+static UniValue walletpassphrasechange(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
@@ -2474,7 +2596,7 @@ UniValue walletpassphrasechange(const JSONRPCRequest& request)
}
-UniValue walletlock(const JSONRPCRequest& request)
+static UniValue walletlock(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
@@ -2512,7 +2634,7 @@ UniValue walletlock(const JSONRPCRequest& request)
}
-UniValue encryptwallet(const JSONRPCRequest& request)
+static UniValue encryptwallet(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
@@ -2572,7 +2694,7 @@ UniValue encryptwallet(const JSONRPCRequest& request)
return "wallet encrypted; Bitcoin server stopping, restart to run with encrypted wallet. The keypool has been flushed and a new HD seed was generated (if you are using HD). You need to make a new backup.";
}
-UniValue lockunspent(const JSONRPCRequest& request)
+static UniValue lockunspent(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
@@ -2699,7 +2821,7 @@ UniValue lockunspent(const JSONRPCRequest& request)
return true;
}
-UniValue listlockunspent(const JSONRPCRequest& request)
+static UniValue listlockunspent(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
@@ -2732,7 +2854,6 @@ UniValue listlockunspent(const JSONRPCRequest& request)
+ HelpExampleRpc("listlockunspent", "")
);
- ObserveSafeMode();
LOCK2(cs_main, pwallet->cs_wallet);
std::vector<COutPoint> vOutpts;
@@ -2751,17 +2872,17 @@ UniValue listlockunspent(const JSONRPCRequest& request)
return ret;
}
-UniValue settxfee(const JSONRPCRequest& request)
+static UniValue settxfee(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 1)
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 1) {
throw std::runtime_error(
"settxfee amount\n"
- "\nSet the transaction fee per kB. Overwrites the paytxfee parameter.\n"
+ "\nSet the transaction fee per kB for this wallet. Overrides the global -paytxfee command line parameter.\n"
"\nArguments:\n"
"1. amount (numeric or string, required) The transaction fee in " + CURRENCY_UNIT + "/kB\n"
"\nResult\n"
@@ -2770,17 +2891,17 @@ UniValue settxfee(const JSONRPCRequest& request)
+ HelpExampleCli("settxfee", "0.00001")
+ HelpExampleRpc("settxfee", "0.00001")
);
+ }
LOCK2(cs_main, pwallet->cs_wallet);
- // Amount
CAmount nAmount = AmountFromValue(request.params[0]);
- payTxFee = CFeeRate(nAmount, 1000);
+ pwallet->m_pay_tx_fee = CFeeRate(nAmount, 1000);
return true;
}
-UniValue getwalletinfo(const JSONRPCRequest& request)
+static UniValue getwalletinfo(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
@@ -2811,8 +2932,6 @@ UniValue getwalletinfo(const JSONRPCRequest& request)
+ HelpExampleRpc("getwalletinfo", "")
);
- ObserveSafeMode();
-
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
@@ -2837,13 +2956,13 @@ UniValue getwalletinfo(const JSONRPCRequest& request)
if (pwallet->IsCrypted()) {
obj.pushKV("unlocked_until", pwallet->nRelockTime);
}
- obj.pushKV("paytxfee", ValueFromAmount(payTxFee.GetFeePerK()));
+ obj.pushKV("paytxfee", ValueFromAmount(pwallet->m_pay_tx_fee.GetFeePerK()));
if (!masterKeyID.IsNull())
- obj.pushKV("hdmasterkeyid", masterKeyID.GetHex());
+ obj.pushKV("hdmasterkeyid", masterKeyID.GetHex());
return obj;
}
-UniValue listwallets(const JSONRPCRequest& request)
+static UniValue listwallets(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
@@ -2862,8 +2981,7 @@ UniValue listwallets(const JSONRPCRequest& request)
UniValue obj(UniValue::VARR);
- for (CWalletRef pwallet : vpwallets) {
-
+ for (CWallet* pwallet : GetWallets()) {
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
@@ -2876,7 +2994,7 @@ UniValue listwallets(const JSONRPCRequest& request)
return obj;
}
-UniValue resendwallettransactions(const JSONRPCRequest& request)
+static UniValue resendwallettransactions(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
@@ -2911,7 +3029,7 @@ UniValue resendwallettransactions(const JSONRPCRequest& request)
return result;
}
-UniValue listunspent(const JSONRPCRequest& request)
+static UniValue listunspent(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
@@ -2948,7 +3066,7 @@ UniValue listunspent(const JSONRPCRequest& request)
" \"vout\" : n, (numeric) the vout value\n"
" \"address\" : \"address\", (string) the bitcoin address\n"
" \"label\" : \"label\", (string) The associated label, or \"\" for the default label\n"
- " \"account\" : \"account\", (string) DEPRECATED. Backwards compatible alias for label.\n"
+ " \"account\" : \"account\", (string) DEPRECATED. This field will be removed in V0.18. To see this deprecated field, start bitcoind with -deprecatedrpc=accounts. The associated account, or \"\" for the default account\n"
" \"scriptPubKey\" : \"key\", (string) the script key\n"
" \"amount\" : x.xxx, (numeric) the transaction output amount in " + CURRENCY_UNIT + "\n"
" \"confirmations\" : n, (numeric) The number of confirmations\n"
@@ -2970,8 +3088,6 @@ UniValue listunspent(const JSONRPCRequest& request)
+ HelpExampleRpc("listunspent", "6, 9999999, [] , true, { \"minimumAmount\": 0.005 } ")
);
- ObserveSafeMode();
-
int nMinDepth = 1;
if (!request.params[0].isNull()) {
RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
@@ -3033,9 +3149,13 @@ UniValue listunspent(const JSONRPCRequest& request)
UniValue results(UniValue::VARR);
std::vector<COutput> vecOutputs;
- LOCK2(cs_main, pwallet->cs_wallet);
+ {
+ LOCK2(cs_main, pwallet->cs_wallet);
+ pwallet->AvailableCoins(vecOutputs, !include_unsafe, nullptr, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount, nMinDepth, nMaxDepth);
+ }
+
+ LOCK(pwallet->cs_wallet);
- pwallet->AvailableCoins(vecOutputs, !include_unsafe, nullptr, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount, nMinDepth, nMaxDepth);
for (const COutput& out : vecOutputs) {
CTxDestination address;
const CScript& scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey;
@@ -3051,9 +3171,12 @@ UniValue listunspent(const JSONRPCRequest& request)
if (fValidAddress) {
entry.pushKV("address", EncodeDestination(address));
- if (pwallet->mapAddressBook.count(address)) {
- entry.pushKV("label", pwallet->mapAddressBook[address].name);
- entry.pushKV("account", pwallet->mapAddressBook[address].name);
+ auto i = pwallet->mapAddressBook.find(address);
+ if (i != pwallet->mapAddressBook.end()) {
+ entry.pushKV("label", i->second.name);
+ if (IsDeprecatedRPCEnabled("accounts")) {
+ entry.pushKV("account", i->second.name);
+ }
}
if (scriptPubKey.IsPayToScriptHash()) {
@@ -3077,7 +3200,7 @@ UniValue listunspent(const JSONRPCRequest& request)
return results;
}
-UniValue fundrawtransaction(const JSONRPCRequest& request)
+static UniValue fundrawtransaction(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
@@ -3142,7 +3265,6 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
+ HelpExampleCli("sendrawtransaction", "\"signedtransactionhex\"")
);
- ObserveSafeMode();
RPCTypeCheck(request.params, {UniValue::VSTR});
// Make sure the results are valid at least up to the most recent block
@@ -3197,8 +3319,8 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
if (options.exists("changeAddress")) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both changeAddress and address_type options");
}
- coinControl.m_change_type = ParseOutputType(options["change_type"].get_str(), pwallet->m_default_change_type);
- if (coinControl.m_change_type == OutputType::NONE) {
+ coinControl.m_change_type = pwallet->m_default_change_type;
+ if (!ParseOutputType(options["change_type"].get_str(), *coinControl.m_change_type)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown change type '%s'", options["change_type"].get_str()));
}
}
@@ -3219,7 +3341,7 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
subtractFeeFromOutputs = options["subtractFeeFromOutputs"].get_array();
if (options.exists("replaceable")) {
- coinControl.signalRbf = options["replaceable"].get_bool();
+ coinControl.m_signal_bip125_rbf = options["replaceable"].get_bool();
}
if (options.exists("conf_target")) {
if (options.exists("feeRate")) {
@@ -3347,7 +3469,7 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
return SignTransaction(mtx, request.params[1], pwallet, false, request.params[2]);
}
-UniValue bumpfee(const JSONRPCRequest& request)
+static UniValue bumpfee(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
@@ -3408,7 +3530,7 @@ UniValue bumpfee(const JSONRPCRequest& request)
// optional parameters
CAmount totalFee = 0;
CCoinControl coin_control;
- coin_control.signalRbf = true;
+ coin_control.m_signal_bip125_rbf = true;
if (!request.params[1].isNull()) {
UniValue options = request.params[1];
RPCTypeCheckObj(options,
@@ -3432,7 +3554,7 @@ UniValue bumpfee(const JSONRPCRequest& request)
}
if (options.exists("replaceable")) {
- coin_control.signalRbf = options["replaceable"].get_bool();
+ coin_control.m_signal_bip125_rbf = options["replaceable"].get_bool();
}
if (options.exists("estimate_mode")) {
if (!FeeModeFromString(options["estimate_mode"].get_str(), coin_control.m_fee_mode)) {
@@ -3726,7 +3848,7 @@ public:
UniValue operator()(const WitnessUnknown& id) const { return UniValue(UniValue::VOBJ); }
};
-UniValue DescribeWalletAddress(CWallet* pwallet, const CTxDestination& dest)
+static UniValue DescribeWalletAddress(CWallet* pwallet, const CTxDestination& dest)
{
UniValue ret(UniValue::VOBJ);
UniValue detail = DescribeAddress(dest);
@@ -3781,7 +3903,8 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
" \"pubkey\" : \"publickeyhex\", (string, optional) The hex value of the raw public key, for single-key addresses (possibly embedded in P2SH or P2WSH)\n"
" \"embedded\" : {...}, (object, optional) Information about the address embedded in P2SH or P2WSH, if relevant and known. It includes all getaddressinfo output fields for the embedded address, excluding metadata (\"timestamp\", \"hdkeypath\", \"hdmasterkeyid\") and relation to the wallet (\"ismine\", \"iswatchonly\", \"account\").\n"
" \"iscompressed\" : true|false, (boolean) If the address is compressed\n"
- " \"account\" : \"account\" (string) The account associated with the address, \"\" is the default account\n"
+ " \"label\" : \"label\" (string) The label associated with the address, \"\" is the default account\n"
+ " \"account\" : \"account\" (string) DEPRECATED. This field will be removed in V0.18. To see this deprecated field, start bitcoind with -deprecatedrpc=accounts. The account associated with the address, \"\" is the default account\n"
" \"timestamp\" : timestamp, (number, optional) The creation time of the key if available in seconds since epoch (Jan 1 1970 GMT)\n"
" \"hdkeypath\" : \"keypath\" (string, optional) The HD keypath if the key is HD and available\n"
" \"hdmasterkeyid\" : \"<hash160>\" (string, optional) The Hash160 of the HD master pubkey\n"
@@ -3821,7 +3944,10 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
UniValue detail = DescribeWalletAddress(pwallet, dest);
ret.pushKVs(detail);
if (pwallet->mapAddressBook.count(dest)) {
- ret.pushKV("account", pwallet->mapAddressBook[dest].name);
+ ret.pushKV("label", pwallet->mapAddressBook[dest].name);
+ if (IsDeprecatedRPCEnabled("accounts")) {
+ ret.pushKV("account", pwallet->mapAddressBook[dest].name);
+ }
}
const CKeyMetadata* meta = nullptr;
CKeyID key_id = GetKeyForDestination(*pwallet, dest);
@@ -3858,7 +3984,7 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
return ret;
}
-UniValue getaddressesbylabel(const JSONRPCRequest& request)
+static UniValue getaddressesbylabel(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
@@ -3901,7 +4027,7 @@ UniValue getaddressesbylabel(const JSONRPCRequest& request)
return ret;
}
-UniValue listlabels(const JSONRPCRequest& request)
+static UniValue listlabels(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
@@ -3953,6 +4079,76 @@ UniValue listlabels(const JSONRPCRequest& request)
return ret;
}
+UniValue sethdseed(const JSONRPCRequest& request)
+{
+ CWallet* const pwallet = GetWalletForJSONRPCRequest(request);
+
+ if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
+ return NullUniValue;
+ }
+
+ if (request.fHelp || request.params.size() > 2) {
+ throw std::runtime_error(
+ "sethdseed ( \"newkeypool\" \"seed\" )\n"
+ "\nSet or generate a new HD wallet seed. Non-HD wallets will not be upgraded to being a HD wallet. Wallets that are already\n"
+ "HD will have a new HD seed set so that new keys added to the keypool will be derived from this new seed.\n"
+ "\nNote that you will need to MAKE A NEW BACKUP of your wallet after setting the HD wallet seed.\n"
+ + HelpRequiringPassphrase(pwallet) +
+ "\nArguments:\n"
+ "1. \"newkeypool\" (boolean, optional, default=true) Whether to flush old unused addresses, including change addresses, from the keypool and regenerate it.\n"
+ " If true, the next address from getnewaddress and change address from getrawchangeaddress will be from this new seed.\n"
+ " If false, addresses (including change addresses if the wallet already had HD Chain Split enabled) from the existing\n"
+ " keypool will be used until it has been depleted.\n"
+ "2. \"seed\" (string, optional) The WIF private key to use as the new HD seed; if not provided a random seed will be used.\n"
+ " The seed value can be retrieved using the dumpwallet command. It is the private key marked hdmaster=1\n"
+ "\nExamples:\n"
+ + HelpExampleCli("sethdseed", "")
+ + HelpExampleCli("sethdseed", "false")
+ + HelpExampleCli("sethdseed", "true \"wifkey\"")
+ + HelpExampleRpc("sethdseed", "true, \"wifkey\"")
+ );
+ }
+
+ if (IsInitialBlockDownload()) {
+ throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Cannot set a new HD seed while still in Initial Block Download");
+ }
+
+ LOCK2(cs_main, pwallet->cs_wallet);
+
+ // Do not do anything to non-HD wallets
+ if (!pwallet->IsHDEnabled()) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Cannot set a HD seed on a non-HD wallet. Start with -upgradewallet in order to upgrade a non-HD wallet to HD");
+ }
+
+ EnsureWalletIsUnlocked(pwallet);
+
+ bool flush_key_pool = true;
+ if (!request.params[0].isNull()) {
+ flush_key_pool = request.params[0].get_bool();
+ }
+
+ CPubKey master_pub_key;
+ if (request.params[1].isNull()) {
+ master_pub_key = pwallet->GenerateNewHDMasterKey();
+ } else {
+ CKey key = DecodeSecret(request.params[1].get_str());
+ if (!key.IsValid()) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key");
+ }
+
+ if (HaveKey(*pwallet, key)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Already have this key (either as an HD seed or as a loose private key)");
+ }
+
+ master_pub_key = pwallet->DeriveNewMasterHDKey(key);
+ }
+
+ pwallet->SetHDMasterKey(master_pub_key);
+ if (flush_key_pool) pwallet->NewKeyPool();
+
+ return NullUniValue;
+}
+
extern UniValue abortrescan(const JSONRPCRequest& request); // in rpcdump.cpp
extern UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp
extern UniValue importprivkey(const JSONRPCRequest& request);
@@ -3996,16 +4192,14 @@ static const CRPCCommand commands[] =
{ "wallet", "keypoolrefill", &keypoolrefill, {"newsize"} },
{ "wallet", "listaddressgroupings", &listaddressgroupings, {} },
{ "wallet", "listlockunspent", &listlockunspent, {} },
- { "wallet", "listreceivedbylabel", &listreceivedbylabel, {"minconf","include_empty","include_watchonly"} },
- { "wallet", "listreceivedbyaccount", &listreceivedbylabel, {"minconf","include_empty","include_watchonly"} },
{ "wallet", "listreceivedbyaddress", &listreceivedbyaddress, {"minconf","include_empty","include_watchonly","address_filter"} },
{ "wallet", "listsinceblock", &listsinceblock, {"blockhash","target_confirmations","include_watchonly","include_removed"} },
- { "wallet", "listtransactions", &listtransactions, {"account","count","skip","include_watchonly"} },
+ { "wallet", "listtransactions", &listtransactions, {"account|dummy","count","skip","include_watchonly"} },
{ "wallet", "listunspent", &listunspent, {"minconf","maxconf","addresses","include_unsafe","query_options"} },
{ "wallet", "listwallets", &listwallets, {} },
{ "wallet", "lockunspent", &lockunspent, {"unlock","transactions"} },
{ "wallet", "sendfrom", &sendfrom, {"fromaccount","toaddress","amount","minconf","comment","comment_to"} },
- { "wallet", "sendmany", &sendmany, {"fromaccount","amounts","minconf","comment","subtractfeefrom","replaceable","conf_target","estimate_mode"} },
+ { "wallet", "sendmany", &sendmany, {"fromaccount|dummy","amounts","minconf","comment","subtractfeefrom","replaceable","conf_target","estimate_mode"} },
{ "wallet", "sendtoaddress", &sendtoaddress, {"address","amount","comment","comment_to","subtractfeefromamount","replaceable","conf_target","estimate_mode"} },
{ "wallet", "settxfee", &settxfee, {"amount"} },
{ "wallet", "signmessage", &signmessage, {"address","message"} },
@@ -4015,6 +4209,7 @@ static const CRPCCommand commands[] =
{ "wallet", "walletpassphrase", &walletpassphrase, {"passphrase","timeout"} },
{ "wallet", "removeprunedfunds", &removeprunedfunds, {"txid"} },
{ "wallet", "rescanblockchain", &rescanblockchain, {"start_height", "stop_height"} },
+ { "wallet", "sethdseed", &sethdseed, {"newkeypool","seed"} },
/** Account functions (deprecated) */
{ "wallet", "getaccountaddress", &getlabeladdress, {"account"} },
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 14c5ad7214..99c963a348 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -74,7 +74,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
// after.
{
CWallet wallet("dummy", WalletDatabase::CreateDummy());
- vpwallets.insert(vpwallets.begin(), &wallet);
+ AddWallet(&wallet);
UniValue keys;
keys.setArray();
UniValue key;
@@ -105,7 +105,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
"downloading and rescanning the relevant blocks (see -reindex and -rescan "
"options).\"}},{\"success\":true}]",
0, oldTip->GetBlockTimeMax(), TIMESTAMP_WINDOW));
- vpwallets.erase(vpwallets.begin());
+ RemoveWallet(&wallet);
}
}
@@ -119,14 +119,14 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
// will pick up both blocks, not just the first.
const int64_t BLOCK_TIME = chainActive.Tip()->GetBlockTimeMax() + 5;
SetMockTime(BLOCK_TIME);
- coinbaseTxns.emplace_back(*CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
- coinbaseTxns.emplace_back(*CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
+ m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
+ m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
// Set key birthday to block time increased by the timestamp window, so
// rescan will start at the block time.
const int64_t KEY_TIME = BLOCK_TIME + TIMESTAMP_WINDOW;
SetMockTime(KEY_TIME);
- coinbaseTxns.emplace_back(*CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
+ m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
LOCK(cs_main);
@@ -140,8 +140,9 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
JSONRPCRequest request;
request.params.setArray();
request.params.push_back((pathTemp / "wallet.backup").string());
- vpwallets.insert(vpwallets.begin(), &wallet);
+ AddWallet(&wallet);
::dumpwallet(request);
+ RemoveWallet(&wallet);
}
// Call importwallet RPC and verify all blocks with timestamps >= BLOCK_TIME
@@ -152,21 +153,21 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
JSONRPCRequest request;
request.params.setArray();
request.params.push_back((pathTemp / "wallet.backup").string());
- vpwallets[0] = &wallet;
+ AddWallet(&wallet);
::importwallet(request);
+ RemoveWallet(&wallet);
LOCK(wallet.cs_wallet);
BOOST_CHECK_EQUAL(wallet.mapWallet.size(), 3U);
- BOOST_CHECK_EQUAL(coinbaseTxns.size(), 103U);
- for (size_t i = 0; i < coinbaseTxns.size(); ++i) {
- bool found = wallet.GetWalletTx(coinbaseTxns[i].GetHash());
+ BOOST_CHECK_EQUAL(m_coinbase_txns.size(), 103U);
+ for (size_t i = 0; i < m_coinbase_txns.size(); ++i) {
+ bool found = wallet.GetWalletTx(m_coinbase_txns[i]->GetHash());
bool expected = i >= 100;
BOOST_CHECK_EQUAL(found, expected);
}
}
SetMockTime(0);
- vpwallets.erase(vpwallets.begin());
}
// Check that GetImmatureCredit() returns a newly calculated value instead of
@@ -178,7 +179,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
{
CWallet wallet("dummy", WalletDatabase::CreateDummy());
- CWalletTx wtx(&wallet, MakeTransactionRef(coinbaseTxns.back()));
+ CWalletTx wtx(&wallet, m_coinbase_txns.back());
LOCK2(cs_main, wallet.cs_wallet);
wtx.hashBlock = chainActive.Tip()->GetBlockHash();
wtx.nIndex = 0;
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 45c85a7912..5be9f4a290 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -11,6 +11,7 @@
#include <consensus/consensus.h>
#include <consensus/validation.h>
#include <fs.h>
+#include <init.h>
#include <key.h>
#include <key_io.h>
#include <keystore.h>
@@ -28,34 +29,57 @@
#include <utilmoneystr.h>
#include <wallet/fees.h>
+#include <algorithm>
#include <assert.h>
#include <future>
#include <boost/algorithm/string/replace.hpp>
-std::vector<CWalletRef> vpwallets;
-/** Transaction fee set by the user */
-CFeeRate payTxFee(DEFAULT_TRANSACTION_FEE);
-unsigned int nTxConfirmTarget = DEFAULT_TX_CONFIRM_TARGET;
-bool bSpendZeroConfChange = DEFAULT_SPEND_ZEROCONF_CHANGE;
-bool fWalletRbf = DEFAULT_WALLET_RBF;
-bool g_wallet_allow_fallback_fee = true; //<! will be defined via chainparams
+static CCriticalSection cs_wallets;
+static std::vector<CWallet*> vpwallets GUARDED_BY(cs_wallets);
-const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000;
+bool AddWallet(CWallet* wallet)
+{
+ LOCK(cs_wallets);
+ assert(wallet);
+ std::vector<CWallet*>::const_iterator i = std::find(vpwallets.begin(), vpwallets.end(), wallet);
+ if (i != vpwallets.end()) return false;
+ vpwallets.push_back(wallet);
+ return true;
+}
-/**
- * Fees smaller than this (in satoshi) are considered zero fee (for transaction creation)
- * Override with -mintxfee
- */
-CFeeRate CWallet::minTxFee = CFeeRate(DEFAULT_TRANSACTION_MINFEE);
-/**
- * If fee estimation does not have enough data to provide estimates, use this fee instead.
- * Has no effect if not using fee estimation
- * Override with -fallbackfee
- */
-CFeeRate CWallet::fallbackFee = CFeeRate(DEFAULT_FALLBACK_FEE);
+bool RemoveWallet(CWallet* wallet)
+{
+ LOCK(cs_wallets);
+ assert(wallet);
+ std::vector<CWallet*>::iterator i = std::find(vpwallets.begin(), vpwallets.end(), wallet);
+ if (i == vpwallets.end()) return false;
+ vpwallets.erase(i);
+ return true;
+}
+
+bool HasWallets()
+{
+ LOCK(cs_wallets);
+ return !vpwallets.empty();
+}
+
+std::vector<CWallet*> GetWallets()
+{
+ LOCK(cs_wallets);
+ return vpwallets;
+}
+
+CWallet* GetWallet(const std::string& name)
+{
+ LOCK(cs_wallets);
+ for (CWallet* wallet : vpwallets) {
+ if (wallet->GetName() == name) return wallet;
+ }
+ return nullptr;
+}
-CFeeRate CWallet::m_discard_rate = CFeeRate(DEFAULT_DISCARD_FEE);
+const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000;
const uint256 CMerkleTx::ABANDON_HASH(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
@@ -430,7 +454,7 @@ bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase,
return false;
}
-void CWallet::SetBestChain(const CBlockLocator& loc)
+void CWallet::ChainStateFlushed(const CBlockLocator& loc)
{
WalletBatch batch(*database);
batch.WriteBestBlock(loc);
@@ -1428,7 +1452,11 @@ CPubKey CWallet::GenerateNewHDMasterKey()
{
CKey key;
key.MakeNewKey(true);
+ return DeriveNewMasterHDKey(key);
+}
+CPubKey CWallet::DeriveNewMasterHDKey(const CKey& key)
+{
int64_t nCreationTime = GetTime();
CKeyMetadata metadata(nCreationTime);
@@ -1721,23 +1749,27 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock
fAbortRescan = false;
ShowProgress(_("Rescanning..."), 0); // show rescan progress in GUI as dialog or on splashscreen, if -rescan on startup
CBlockIndex* tip = nullptr;
- double dProgressStart;
- double dProgressTip;
+ double progress_begin;
+ double progress_end;
{
LOCK(cs_main);
- tip = chainActive.Tip();
- dProgressStart = GuessVerificationProgress(chainParams.TxData(), pindex);
- dProgressTip = GuessVerificationProgress(chainParams.TxData(), tip);
+ progress_begin = GuessVerificationProgress(chainParams.TxData(), pindex);
+ if (pindexStop == nullptr) {
+ tip = chainActive.Tip();
+ progress_end = GuessVerificationProgress(chainParams.TxData(), tip);
+ } else {
+ progress_end = GuessVerificationProgress(chainParams.TxData(), pindexStop);
+ }
}
- double gvp = dProgressStart;
- while (pindex && !fAbortRescan)
+ double progress_current = progress_begin;
+ while (pindex && !fAbortRescan && !ShutdownRequested())
{
- if (pindex->nHeight % 100 == 0 && dProgressTip - dProgressStart > 0.0) {
- ShowProgress(_("Rescanning..."), std::max(1, std::min(99, (int)((gvp - dProgressStart) / (dProgressTip - dProgressStart) * 100))));
+ if (pindex->nHeight % 100 == 0 && progress_end - progress_begin > 0.0) {
+ ShowProgress(_("Rescanning..."), std::max(1, std::min(99, (int)((progress_current - progress_begin) / (progress_end - progress_begin) * 100))));
}
if (GetTime() >= nNow + 60) {
nNow = GetTime();
- LogPrintf("Still rescanning. At block %d. Progress=%f\n", pindex->nHeight, gvp);
+ LogPrintf("Still rescanning. At block %d. Progress=%f\n", pindex->nHeight, progress_current);
}
CBlock block;
@@ -1761,16 +1793,18 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock
{
LOCK(cs_main);
pindex = chainActive.Next(pindex);
- gvp = GuessVerificationProgress(chainParams.TxData(), pindex);
- if (tip != chainActive.Tip()) {
+ progress_current = GuessVerificationProgress(chainParams.TxData(), pindex);
+ if (pindexStop == nullptr && tip != chainActive.Tip()) {
tip = chainActive.Tip();
// in case the tip has changed, update progress max
- dProgressTip = GuessVerificationProgress(chainParams.TxData(), tip);
+ progress_end = GuessVerificationProgress(chainParams.TxData(), tip);
}
}
}
if (pindex && fAbortRescan) {
- LogPrintf("Rescan aborted at block %d. Progress=%f\n", pindex->nHeight, gvp);
+ LogPrintf("Rescan aborted at block %d. Progress=%f\n", pindex->nHeight, progress_current);
+ } else if (pindex && ShutdownRequested()) {
+ LogPrintf("Rescan interrupted by shutdown request at block %d. Progress=%f\n", pindex->nHeight, progress_current);
}
ShowProgress(_("Rescanning..."), 100); // hide progress dialog in GUI
}
@@ -2016,7 +2050,7 @@ bool CWalletTx::IsTrusted() const
return true;
if (nDepth < 0)
return false;
- if (!bSpendZeroConfChange || !IsFromMe(ISMINE_ALL)) // using wtx's cached debit
+ if (!pwallet->m_spend_zero_conf_change || !IsFromMe(ISMINE_ALL)) // using wtx's cached debit
return false;
// Don't trust unconfirmed transactions from us unless they are in the mempool.
@@ -2452,10 +2486,10 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibil
FeeCalculation feeCalc;
CCoinControl temp;
temp.m_confirm_target = 1008;
- CFeeRate long_term_feerate = GetMinimumFeeRate(temp, ::mempool, ::feeEstimator, &feeCalc);
+ CFeeRate long_term_feerate = GetMinimumFeeRate(*this, temp, ::mempool, ::feeEstimator, &feeCalc);
// Calculate cost of change
- CAmount cost_of_change = GetDiscardRate(::feeEstimator).GetFee(coin_selection_params.change_spend_size) + coin_selection_params.effective_fee.GetFee(coin_selection_params.change_output_size);
+ CAmount cost_of_change = GetDiscardRate(*this, ::feeEstimator).GetFee(coin_selection_params.change_spend_size) + coin_selection_params.effective_fee.GetFee(coin_selection_params.change_output_size);
// Filter by the min conf specs and add to utxo_pool and calculate effective value
for (const COutput &output : vCoins)
@@ -2552,11 +2586,11 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
bool res = nTargetValue <= nValueFromPresetInputs ||
SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(1, 6, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used) ||
SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(1, 1, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used) ||
- (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, 2), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
- (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, std::min((size_t)4, nMaxChainLength/3)), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
- (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, nMaxChainLength/2), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
- (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, nMaxChainLength), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
- (bSpendZeroConfChange && !fRejectLongChains && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, std::numeric_limits<uint64_t>::max()), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ (m_spend_zero_conf_change && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, 2), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
+ (m_spend_zero_conf_change && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, std::min((size_t)4, nMaxChainLength/3)), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
+ (m_spend_zero_conf_change && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, nMaxChainLength/2), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
+ (m_spend_zero_conf_change && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, nMaxChainLength), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
+ (m_spend_zero_conf_change && !fRejectLongChains && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, std::numeric_limits<uint64_t>::max()), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
// because SelectCoinsMinConf clears the setCoinsRet, we now add the possible inputs to the coinset
setCoinsRet.insert(setPresetCoins.begin(), setPresetCoins.end());
@@ -2648,7 +2682,7 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC
OutputType CWallet::TransactionChangeType(OutputType change_type, const std::vector<CRecipient>& vecSend)
{
// If -changetype is specified, always use that change type.
- if (change_type != OutputType::NONE) {
+ if (change_type != OutputType::CHANGE_AUTO) {
return change_type;
}
@@ -2775,10 +2809,10 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransac
CTxOut change_prototype_txout(0, scriptChange);
coin_selection_params.change_output_size = GetSerializeSize(change_prototype_txout, SER_DISK, 0);
- CFeeRate discard_rate = GetDiscardRate(::feeEstimator);
+ CFeeRate discard_rate = GetDiscardRate(*this, ::feeEstimator);
// Get the fee rate to use effective values in coin selection
- CFeeRate nFeeRateNeeded = GetMinimumFeeRate(coin_control, ::mempool, ::feeEstimator, &feeCalc);
+ CFeeRate nFeeRateNeeded = GetMinimumFeeRate(*this, coin_control, ::mempool, ::feeEstimator, &feeCalc);
nFeeRet = 0;
bool pick_new_inputs = true;
@@ -2902,8 +2936,8 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransac
return false;
}
- nFeeNeeded = GetMinimumFee(nBytes, coin_control, ::mempool, ::feeEstimator, &feeCalc);
- if (feeCalc.reason == FeeReason::FALLBACK && !g_wallet_allow_fallback_fee) {
+ nFeeNeeded = GetMinimumFee(*this, nBytes, coin_control, ::mempool, ::feeEstimator, &feeCalc);
+ if (feeCalc.reason == FeeReason::FALLBACK && !m_allow_fallback_fee) {
// eventually allow a fallback fee
strFailReason = _("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.");
return false;
@@ -2930,7 +2964,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransac
// change output. Only try this once.
if (nChangePosInOut == -1 && nSubtractFeeFromAmount == 0 && pick_new_inputs) {
unsigned int tx_size_with_change = nBytes + coin_selection_params.change_output_size + 2; // Add 2 as a buffer in case increasing # of outputs changes compact size
- CAmount fee_needed_with_change = GetMinimumFee(tx_size_with_change, coin_control, ::mempool, ::feeEstimator, nullptr);
+ CAmount fee_needed_with_change = GetMinimumFee(*this, tx_size_with_change, coin_control, ::mempool, ::feeEstimator, nullptr);
CAmount minimum_value_for_change = GetDustThreshold(change_prototype_txout, discard_rate);
if (nFeeRet >= fee_needed_with_change + minimum_value_for_change) {
pick_new_inputs = false;
@@ -2997,7 +3031,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransac
// to avoid conflicting with other possible uses of nSequence,
// and in the spirit of "smallest possible change from prior
// behavior."
- const uint32_t nSequence = coin_control.signalRbf ? MAX_BIP125_RBF_SEQUENCE : (CTxIn::SEQUENCE_FINAL - 1);
+ const uint32_t nSequence = coin_control.m_signal_bip125_rbf.get_value_or(m_signal_rbf) ? MAX_BIP125_RBF_SEQUENCE : (CTxIn::SEQUENCE_FINAL - 1);
for (const auto& coin : selected_coins) {
txNew.vin.push_back(CTxIn(coin.outpoint, CScript(), nSequence));
}
@@ -3296,6 +3330,11 @@ bool CWallet::NewKeyPool()
}
setExternalKeyPool.clear();
+ for (int64_t nIndex : set_pre_split_keypool) {
+ batch.ErasePool(nIndex);
+ }
+ set_pre_split_keypool.clear();
+
m_pool_key_to_index.clear();
if (!TopUpKeyPool()) {
@@ -3309,13 +3348,15 @@ bool CWallet::NewKeyPool()
size_t CWallet::KeypoolCountExternalKeys()
{
AssertLockHeld(cs_wallet); // setExternalKeyPool
- return setExternalKeyPool.size();
+ return setExternalKeyPool.size() + set_pre_split_keypool.size();
}
void CWallet::LoadKeyPool(int64_t nIndex, const CKeyPool &keypool)
{
AssertLockHeld(cs_wallet);
- if (keypool.fInternal) {
+ if (keypool.m_pre_split) {
+ set_pre_split_keypool.insert(nIndex);
+ } else if (keypool.fInternal) {
setInternalKeyPool.insert(nIndex);
} else {
setExternalKeyPool.insert(nIndex);
@@ -3380,7 +3421,7 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize)
m_pool_key_to_index[pubkey.GetID()] = index;
}
if (missingInternal + missingExternal > 0) {
- LogPrintf("keypool added %d keys (%d internal), size=%u (%u internal)\n", missingInternal + missingExternal, missingInternal, setInternalKeyPool.size() + setExternalKeyPool.size(), setInternalKeyPool.size());
+ LogPrintf("keypool added %d keys (%d internal), size=%u (%u internal)\n", missingInternal + missingExternal, missingInternal, setInternalKeyPool.size() + setExternalKeyPool.size() + set_pre_split_keypool.size(), setInternalKeyPool.size());
}
}
return true;
@@ -3397,7 +3438,7 @@ void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRe
TopUpKeyPool();
bool fReturningInternal = IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT) && fRequestedInternal;
- std::set<int64_t>& setKeyPool = fReturningInternal ? setInternalKeyPool : setExternalKeyPool;
+ std::set<int64_t>& setKeyPool = set_pre_split_keypool.empty() ? (fReturningInternal ? setInternalKeyPool : setExternalKeyPool) : set_pre_split_keypool;
// Get the oldest key
if(setKeyPool.empty())
@@ -3414,7 +3455,8 @@ void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRe
if (!HaveKey(keypool.vchPubKey.GetID())) {
throw std::runtime_error(std::string(__func__) + ": unknown key in key pool");
}
- if (keypool.fInternal != fReturningInternal) {
+ // If the key was pre-split keypool, we don't care about what type it is
+ if (set_pre_split_keypool.size() == 0 && keypool.fInternal != fReturningInternal) {
throw std::runtime_error(std::string(__func__) + ": keypool entry misclassified");
}
@@ -3439,6 +3481,8 @@ void CWallet::ReturnKey(int64_t nIndex, bool fInternal, const CPubKey& pubkey)
LOCK(cs_wallet);
if (fInternal) {
setInternalKeyPool.insert(nIndex);
+ } else if (!set_pre_split_keypool.empty()) {
+ set_pre_split_keypool.insert(nIndex);
} else {
setExternalKeyPool.insert(nIndex);
}
@@ -3491,6 +3535,9 @@ int64_t CWallet::GetOldestKeyPoolTime()
int64_t oldestKey = GetOldestKeyTimeInPool(setExternalKeyPool, batch);
if (IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT)) {
oldestKey = std::max(GetOldestKeyTimeInPool(setInternalKeyPool, batch), oldestKey);
+ if (!set_pre_split_keypool.empty()) {
+ oldestKey = std::max(GetOldestKeyTimeInPool(set_pre_split_keypool, batch), oldestKey);
+ }
}
return oldestKey;
@@ -3688,8 +3735,8 @@ void CWallet::MarkReserveKeysAsUsed(int64_t keypool_id)
{
AssertLockHeld(cs_wallet);
bool internal = setInternalKeyPool.count(keypool_id);
- if (!internal) assert(setExternalKeyPool.count(keypool_id));
- std::set<int64_t> *setKeyPool = internal ? &setInternalKeyPool : &setExternalKeyPool;
+ if (!internal) assert(setExternalKeyPool.count(keypool_id) || set_pre_split_keypool.count(keypool_id));
+ std::set<int64_t> *setKeyPool = internal ? &setInternalKeyPool : (set_pre_split_keypool.empty() ? &setExternalKeyPool : &set_pre_split_keypool);
auto it = setKeyPool->begin();
WalletBatch batch(*database);
@@ -3925,6 +3972,24 @@ std::vector<std::string> CWallet::GetDestValues(const std::string& prefix) const
return values;
}
+void CWallet::MarkPreSplitKeys()
+{
+ WalletBatch batch(*database);
+ for (auto it = setExternalKeyPool.begin(); it != setExternalKeyPool.end();) {
+ int64_t index = *it;
+ CKeyPool keypool;
+ if (!batch.ReadPool(index, keypool)) {
+ throw std::runtime_error(std::string(__func__) + ": read keypool entry failed");
+ }
+ keypool.m_pre_split = true;
+ if (!batch.WritePool(index, keypool)) {
+ throw std::runtime_error(std::string(__func__) + ": writing modified keypool entry failed");
+ }
+ set_pre_split_keypool.insert(index);
+ it = setExternalKeyPool.erase(it);
+ }
+}
+
CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path& path)
{
const std::string& walletFile = name;
@@ -3976,6 +4041,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path&
}
}
+ int prev_version = walletInstance->nWalletVersion;
if (gArgs.GetBoolArg("-upgradewallet", fFirstRun))
{
int nMaxVersion = gArgs.GetArg("-upgradewallet", 0);
@@ -3995,6 +4061,49 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path&
walletInstance->SetMaxVersion(nMaxVersion);
}
+ // Upgrade to HD if explicit upgrade
+ if (gArgs.GetBoolArg("-upgradewallet", false)) {
+ LOCK(walletInstance->cs_wallet);
+
+ // Do not upgrade versions to any version between HD_SPLIT and FEATURE_PRE_SPLIT_KEYPOOL unless already supporting HD_SPLIT
+ int max_version = walletInstance->nWalletVersion;
+ if (!walletInstance->CanSupportFeature(FEATURE_HD_SPLIT) && max_version >=FEATURE_HD_SPLIT && max_version < FEATURE_PRE_SPLIT_KEYPOOL) {
+ InitError(_("Cannot upgrade a non HD split wallet without upgrading to support pre split keypool. Please use -upgradewallet=169900 or -upgradewallet with no version specified."));
+ return nullptr;
+ }
+
+ bool hd_upgrade = false;
+ bool split_upgrade = false;
+ if (walletInstance->CanSupportFeature(FEATURE_HD) && !walletInstance->IsHDEnabled()) {
+ LogPrintf("Upgrading wallet to HD\n");
+ walletInstance->SetMinVersion(FEATURE_HD);
+
+ // generate a new master key
+ CPubKey masterPubKey = walletInstance->GenerateNewHDMasterKey();
+ if (!walletInstance->SetHDMasterKey(masterPubKey)) {
+ throw std::runtime_error(std::string(__func__) + ": Storing master key failed");
+ }
+ hd_upgrade = true;
+ }
+ // Upgrade to HD chain split if necessary
+ if (walletInstance->CanSupportFeature(FEATURE_HD_SPLIT)) {
+ LogPrintf("Upgrading wallet to use HD chain split\n");
+ walletInstance->SetMinVersion(FEATURE_PRE_SPLIT_KEYPOOL);
+ split_upgrade = FEATURE_HD_SPLIT > prev_version;
+ }
+ // Mark all keys currently in the keypool as pre-split
+ if (split_upgrade) {
+ walletInstance->MarkPreSplitKeys();
+ }
+ // Regenerate the keypool if upgraded to HD
+ if (hd_upgrade) {
+ if (!walletInstance->TopUpKeyPool()) {
+ InitError(_("Unable to generate keys") += "\n");
+ return nullptr;
+ }
+ }
+ }
+
if (fFirstRun)
{
// ensure this wallet.dat can only be opened by clients supporting HD with chain split and expects no default key
@@ -4002,7 +4111,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path&
InitError(strprintf(_("Error creating %s: You can't create non-HD wallets with this version."), walletFile));
return nullptr;
}
- walletInstance->SetMinVersion(FEATURE_NO_DEFAULT_KEY);
+ walletInstance->SetMinVersion(FEATURE_LATEST);
// generate a new master key
CPubKey masterPubKey = walletInstance->GenerateNewHDMasterKey();
@@ -4015,7 +4124,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path&
return nullptr;
}
- walletInstance->SetBestChain(chainActive.GetLocator());
+ walletInstance->ChainStateFlushed(chainActive.GetLocator());
} else if (gArgs.IsArgSet("-usehd")) {
bool useHD = gArgs.GetBoolArg("-usehd", true);
if (walletInstance->IsHDEnabled() && !useHD) {
@@ -4028,20 +4137,76 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path&
}
}
- walletInstance->m_default_address_type = ParseOutputType(gArgs.GetArg("-addresstype", ""), DEFAULT_ADDRESS_TYPE);
- if (walletInstance->m_default_address_type == OutputType::NONE) {
+ if (!gArgs.GetArg("-addresstype", "").empty() && !ParseOutputType(gArgs.GetArg("-addresstype", ""), walletInstance->m_default_address_type)) {
InitError(strprintf("Unknown address type '%s'", gArgs.GetArg("-addresstype", "")));
return nullptr;
}
- // If changetype is set in config file or parameter, check that it's valid.
- // Default to OutputType::NONE if not set.
- walletInstance->m_default_change_type = ParseOutputType(gArgs.GetArg("-changetype", ""), OutputType::NONE);
- if (walletInstance->m_default_change_type == OutputType::NONE && !gArgs.GetArg("-changetype", "").empty()) {
+ if (!gArgs.GetArg("-changetype", "").empty() && !ParseOutputType(gArgs.GetArg("-changetype", ""), walletInstance->m_default_change_type)) {
InitError(strprintf("Unknown change type '%s'", gArgs.GetArg("-changetype", "")));
return nullptr;
}
+ if (gArgs.IsArgSet("-mintxfee")) {
+ CAmount n = 0;
+ if (!ParseMoney(gArgs.GetArg("-mintxfee", ""), n) || 0 == n) {
+ InitError(AmountErrMsg("mintxfee", gArgs.GetArg("-mintxfee", "")));
+ return nullptr;
+ }
+ if (n > HIGH_TX_FEE_PER_KB) {
+ InitWarning(AmountHighWarn("-mintxfee") + " " +
+ _("This is the minimum transaction fee you pay on every transaction."));
+ }
+ walletInstance->m_min_fee = CFeeRate(n);
+ }
+
+ walletInstance->m_allow_fallback_fee = Params().IsFallbackFeeEnabled();
+ if (gArgs.IsArgSet("-fallbackfee")) {
+ CAmount nFeePerK = 0;
+ if (!ParseMoney(gArgs.GetArg("-fallbackfee", ""), nFeePerK)) {
+ InitError(strprintf(_("Invalid amount for -fallbackfee=<amount>: '%s'"), gArgs.GetArg("-fallbackfee", "")));
+ return nullptr;
+ }
+ if (nFeePerK > HIGH_TX_FEE_PER_KB) {
+ InitWarning(AmountHighWarn("-fallbackfee") + " " +
+ _("This is the transaction fee you may pay when fee estimates are not available."));
+ }
+ walletInstance->m_fallback_fee = CFeeRate(nFeePerK);
+ walletInstance->m_allow_fallback_fee = nFeePerK != 0; //disable fallback fee in case value was set to 0, enable if non-null value
+ }
+ if (gArgs.IsArgSet("-discardfee")) {
+ CAmount nFeePerK = 0;
+ if (!ParseMoney(gArgs.GetArg("-discardfee", ""), nFeePerK)) {
+ InitError(strprintf(_("Invalid amount for -discardfee=<amount>: '%s'"), gArgs.GetArg("-discardfee", "")));
+ return nullptr;
+ }
+ if (nFeePerK > HIGH_TX_FEE_PER_KB) {
+ InitWarning(AmountHighWarn("-discardfee") + " " +
+ _("This is the transaction fee you may discard if change is smaller than dust at this level"));
+ }
+ walletInstance->m_discard_rate = CFeeRate(nFeePerK);
+ }
+ if (gArgs.IsArgSet("-paytxfee")) {
+ CAmount nFeePerK = 0;
+ if (!ParseMoney(gArgs.GetArg("-paytxfee", ""), nFeePerK)) {
+ InitError(AmountErrMsg("paytxfee", gArgs.GetArg("-paytxfee", "")));
+ return nullptr;
+ }
+ if (nFeePerK > HIGH_TX_FEE_PER_KB) {
+ InitWarning(AmountHighWarn("-paytxfee") + " " +
+ _("This is the transaction fee you will pay if you send a transaction."));
+ }
+ walletInstance->m_pay_tx_fee = CFeeRate(nFeePerK, 1000);
+ if (walletInstance->m_pay_tx_fee < ::minRelayTxFee) {
+ InitError(strprintf(_("Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s)"),
+ gArgs.GetArg("-paytxfee", ""), ::minRelayTxFee.ToString()));
+ return nullptr;
+ }
+ }
+ walletInstance->m_confirm_target = gArgs.GetArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET);
+ walletInstance->m_spend_zero_conf_change = gArgs.GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE);
+ walletInstance->m_signal_rbf = gArgs.GetBoolArg("-walletrbf", DEFAULT_WALLET_RBF);
+
LogPrintf(" wallet %15dms\n", GetTimeMillis() - nStart);
// Try to top up keypool. No-op if the wallet is locked.
@@ -4097,7 +4262,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path&
walletInstance->ScanForWalletTransactions(pindexRescan, nullptr, reserver, true);
}
LogPrintf(" rescan %15dms\n", GetTimeMillis() - nStart);
- walletInstance->SetBestChain(chainActive.GetLocator());
+ walletInstance->ChainStateFlushed(chainActive.GetLocator());
walletInstance->database->IncrementUpdateCounter();
// Restore wallet transaction metadata after -zapwallettxes=1
@@ -4160,6 +4325,7 @@ CKeyPool::CKeyPool()
{
nTime = GetTime();
fInternal = false;
+ m_pre_split = false;
}
CKeyPool::CKeyPool(const CPubKey& vchPubKeyIn, bool internalIn)
@@ -4167,6 +4333,7 @@ CKeyPool::CKeyPool(const CPubKey& vchPubKeyIn, bool internalIn)
nTime = GetTime();
vchPubKey = vchPubKeyIn;
fInternal = internalIn;
+ m_pre_split = false;
}
CWalletKey::CWalletKey(int64_t nExpires)
@@ -4225,19 +4392,19 @@ static const std::string OUTPUT_TYPE_STRING_LEGACY = "legacy";
static const std::string OUTPUT_TYPE_STRING_P2SH_SEGWIT = "p2sh-segwit";
static const std::string OUTPUT_TYPE_STRING_BECH32 = "bech32";
-OutputType ParseOutputType(const std::string& type, OutputType default_type)
+bool ParseOutputType(const std::string& type, OutputType& output_type)
{
- if (type.empty()) {
- return default_type;
- } else if (type == OUTPUT_TYPE_STRING_LEGACY) {
- return OutputType::LEGACY;
+ if (type == OUTPUT_TYPE_STRING_LEGACY) {
+ output_type = OutputType::LEGACY;
+ return true;
} else if (type == OUTPUT_TYPE_STRING_P2SH_SEGWIT) {
- return OutputType::P2SH_SEGWIT;
+ output_type = OutputType::P2SH_SEGWIT;
+ return true;
} else if (type == OUTPUT_TYPE_STRING_BECH32) {
- return OutputType::BECH32;
- } else {
- return OutputType::NONE;
+ output_type = OutputType::BECH32;
+ return true;
}
+ return false;
}
const std::string& FormatOutputType(OutputType type)
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index b85f374a06..1e23b44eae 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -32,25 +32,19 @@
#include <utility>
#include <vector>
-typedef CWallet* CWalletRef;
-extern std::vector<CWalletRef> vpwallets;
-
-/**
- * Settings
- */
-extern CFeeRate payTxFee;
-extern unsigned int nTxConfirmTarget;
-extern bool bSpendZeroConfChange;
-extern bool fWalletRbf;
-extern bool g_wallet_allow_fallback_fee;
+bool AddWallet(CWallet* wallet);
+bool RemoveWallet(CWallet* wallet);
+bool HasWallets();
+std::vector<CWallet*> GetWallets();
+CWallet* GetWallet(const std::string& name);
//! Default for -keypool
static const unsigned int DEFAULT_KEYPOOL_SIZE = 1000;
//! -paytxfee default
-static const CAmount DEFAULT_TRANSACTION_FEE = 0;
+constexpr CAmount DEFAULT_PAY_TX_FEE = 0;
//! -fallbackfee default
static const CAmount DEFAULT_FALLBACK_FEE = 20000;
-//! -m_discard_rate default
+//! -discardfee default
static const CAmount DEFAULT_DISCARD_FEE = 10000;
//! -mintxfee default
static const CAmount DEFAULT_TRANSACTION_MINFEE = 1000;
@@ -95,19 +89,30 @@ enum WalletFeature
FEATURE_NO_DEFAULT_KEY = 159900, // Wallet without a default key written
- FEATURE_LATEST = FEATURE_COMPRPUBKEY // HD is optional, use FEATURE_COMPRPUBKEY as latest version
+ FEATURE_PRE_SPLIT_KEYPOOL = 169900, // Upgraded to HD SPLIT and can have a pre-split keypool
+
+ FEATURE_LATEST = FEATURE_PRE_SPLIT_KEYPOOL
};
enum class OutputType {
- NONE,
LEGACY,
P2SH_SEGWIT,
BECH32,
+
+ /**
+ * Special output type for change outputs only. Automatically choose type
+ * based on address type setting and the types other of non-change outputs
+ * (see -changetype option documentation and implementation in
+ * CWallet::TransactionChangeType for details).
+ */
+ CHANGE_AUTO,
};
//! Default for -addresstype
constexpr OutputType DEFAULT_ADDRESS_TYPE{OutputType::P2SH_SEGWIT};
+//! Default for -changetype
+constexpr OutputType DEFAULT_CHANGE_TYPE{OutputType::CHANGE_AUTO};
/** A key pool entry */
class CKeyPool
@@ -116,6 +121,7 @@ public:
int64_t nTime;
CPubKey vchPubKey;
bool fInternal; // for change outputs
+ bool m_pre_split; // For keys generated before keypool split upgrade
CKeyPool();
CKeyPool(const CPubKey& vchPubKeyIn, bool internalIn);
@@ -138,9 +144,18 @@ public:
(this will be the case for any wallet before the HD chain split version) */
fInternal = false;
}
+ try {
+ READWRITE(m_pre_split);
+ }
+ catch (std::ios_base::failure&) {
+ /* flag as postsplit address if we can't read the m_pre_split boolean
+ (this will be the case for any wallet that upgrades to HD chain split)*/
+ m_pre_split = false;
+ }
}
else {
READWRITE(fInternal);
+ READWRITE(m_pre_split);
}
}
};
@@ -268,7 +283,7 @@ public:
//Get the marginal bytes of spending the specified output
int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* pwallet);
-/**
+/**
* A transaction with a bunch of additional info that only the owner cares about.
* It includes any unrecorded transactions needed to link it back to the block chain.
*/
@@ -653,7 +668,7 @@ struct CoinEligibilityFilter
};
class WalletRescanReserver; //forward declarations for ScanForWalletTransactions/RescanFromTime
-/**
+/**
* A CWallet is an extension of a keystore, which also maintains a set of transactions and balances,
* and provides the ability to create new transactions.
*/
@@ -695,16 +710,17 @@ private:
/* Used by TransactionAddedToMemorypool/BlockConnected/Disconnected.
* Should be called with pindexBlock and posInBlock if this is for a transaction that is included in a block. */
- void SyncTransaction(const CTransactionRef& tx, const CBlockIndex *pindex = nullptr, int posInBlock = 0);
+ void SyncTransaction(const CTransactionRef& tx, const CBlockIndex *pindex = nullptr, int posInBlock = 0) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/* the HD chain data model (external chain counters) */
CHDChain hdChain;
/* HD derive new child key (on internal or external chain) */
- void DeriveNewChildKey(WalletBatch &batch, CKeyMetadata& metadata, CKey& secret, bool internal = false);
+ void DeriveNewChildKey(WalletBatch &batch, CKeyMetadata& metadata, CKey& secret, bool internal = false) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
std::set<int64_t> setInternalKeyPool;
std::set<int64_t> setExternalKeyPool;
+ std::set<int64_t> set_pre_split_keypool;
int64_t m_max_keypool_index = 0;
std::map<CKeyID, int64_t> m_pool_key_to_index;
@@ -719,7 +735,7 @@ private:
* of the other AddWatchOnly which accepts a timestamp and sets
* nTimeFirstKey more intelligently for more efficient rescans.
*/
- bool AddWatchOnly(const CScript& dest) override;
+ bool AddWatchOnly(const CScript& dest) override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/**
* Wallet filename from wallet=<path> command line or config option.
@@ -770,7 +786,8 @@ public:
*/
const std::string& GetName() const { return m_name; }
- void LoadKeyPool(int64_t nIndex, const CKeyPool &keypool);
+ void LoadKeyPool(int64_t nIndex, const CKeyPool &keypool) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ void MarkPreSplitKeys();
// Map from Key ID to key metadata.
std::map<CKeyID, CKeyMetadata> mapKeyMetadata;
@@ -811,12 +828,12 @@ public:
const CWalletTx* GetWalletTx(const uint256& hash) const;
//! check whether we are allowed to upgrade (or already support) to the named feature
- bool CanSupportFeature(enum WalletFeature wf) const { AssertLockHeld(cs_wallet); return nWalletMaxVersion >= wf; }
+ bool CanSupportFeature(enum WalletFeature wf) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); return nWalletMaxVersion >= wf; }
/**
* populate vCoins with vector of available COutputs.
*/
- void AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe=true, const CCoinControl *coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0, const int nMinDepth = 0, const int nMaxDepth = 9999999) const;
+ void AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe=true, const CCoinControl *coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0, const int nMinDepth = 0, const int nMaxDepth = 9999999) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/**
* Return list of available coins and locked coins grouped by non-change output address.
@@ -839,11 +856,11 @@ public:
bool IsSpent(const uint256& hash, unsigned int n) const;
- bool IsLockedCoin(uint256 hash, unsigned int n) const;
- void LockCoin(const COutPoint& output);
- void UnlockCoin(const COutPoint& output);
- void UnlockAllCoins();
- void ListLockedCoins(std::vector<COutPoint>& vOutpts) const;
+ bool IsLockedCoin(uint256 hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ void LockCoin(const COutPoint& output) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ void UnlockCoin(const COutPoint& output) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ void UnlockAllCoins() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ void ListLockedCoins(std::vector<COutPoint>& vOutpts) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/*
* Rescan abort properties
@@ -856,18 +873,18 @@ public:
* keystore implementation
* Generate a new key
*/
- CPubKey GenerateNewKey(WalletBatch& batch, bool internal = false);
+ CPubKey GenerateNewKey(WalletBatch& batch, bool internal = false) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Adds a key to the store, and saves it to disk.
- bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override;
- bool AddKeyPubKeyWithDB(WalletBatch &batch,const CKey& key, const CPubKey &pubkey);
+ bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool AddKeyPubKeyWithDB(WalletBatch &batch,const CKey& key, const CPubKey &pubkey) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Adds a key to the store, without saving it to disk (used by LoadWallet)
bool LoadKey(const CKey& key, const CPubKey &pubkey) { return CCryptoKeyStore::AddKeyPubKey(key, pubkey); }
//! Load metadata (used by LoadWallet)
- bool LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata &metadata);
- bool LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata &metadata);
+ bool LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata &metadata) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata &metadata) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- bool LoadMinVersion(int nVersion) { AssertLockHeld(cs_wallet); nWalletVersion = nVersion; nWalletMaxVersion = std::max(nWalletMaxVersion, nVersion); return true; }
- void UpdateTimeFirstKey(int64_t nCreateTime);
+ bool LoadMinVersion(int nVersion) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); nWalletVersion = nVersion; nWalletMaxVersion = std::max(nWalletMaxVersion, nVersion); return true; }
+ void UpdateTimeFirstKey(int64_t nCreateTime) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Adds an encrypted key to the store, and saves it to disk.
bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret) override;
@@ -888,8 +905,8 @@ public:
std::vector<std::string> GetDestValues(const std::string& prefix) const;
//! Adds a watch-only address to the store, and saves it to disk.
- bool AddWatchOnly(const CScript& dest, int64_t nCreateTime);
- bool RemoveWatchOnly(const CScript &dest) override;
+ bool AddWatchOnly(const CScript& dest, int64_t nCreateTime) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool RemoveWatchOnly(const CScript &dest) override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Adds a watch-only address to the store, without saving it to disk (used by LoadWallet)
bool LoadWatchOnly(const CScript &dest);
@@ -900,16 +917,16 @@ public:
bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase);
bool EncryptWallet(const SecureString& strWalletPassphrase);
- void GetKeyBirthTimes(std::map<CTxDestination, int64_t> &mapKeyBirth) const;
+ void GetKeyBirthTimes(std::map<CTxDestination, int64_t> &mapKeyBirth) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
unsigned int ComputeTimeSmart(const CWalletTx& wtx) const;
- /**
+ /**
* Increment the next transaction order id
* @return next transaction order id
*/
- int64_t IncOrderPosNext(WalletBatch *batch = nullptr);
+ int64_t IncOrderPosNext(WalletBatch *batch = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
DBErrors ReorderTransactions();
- bool AccountMove(std::string strFrom, std::string strTo, CAmount nAmount, std::string strComment = "");
+ bool AccountMove(std::string strFrom, std::string strTo, CAmount nAmount, std::string strComment = "") EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool GetLabelDestination(CTxDestination &dest, const std::string& label, bool bForceNew = false);
void MarkDirty();
@@ -918,7 +935,7 @@ public:
void TransactionAddedToMempool(const CTransactionRef& tx) override;
void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex *pindex, const std::vector<CTransactionRef>& vtxConflicted) override;
void BlockDisconnected(const std::shared_ptr<const CBlock>& pblock) override;
- bool AddToWalletIfInvolvingMe(const CTransactionRef& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate);
+ bool AddToWalletIfInvolvingMe(const CTransactionRef& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
int64_t RescanFromTime(int64_t startTime, const WalletRescanReserver& reserver, bool update);
CBlockIndex* ScanForWalletTransactions(CBlockIndex* pindexStart, CBlockIndex* pindexStop, const WalletRescanReserver& reserver, bool fUpdate = false);
void TransactionRemovedFromMempool(const CTransactionRef &ptx) override;
@@ -942,7 +959,7 @@ public:
* calling CreateTransaction();
*/
bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, std::string& strFailReason, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl);
- bool SignTransaction(CMutableTransaction& tx);
+ bool SignTransaction(CMutableTransaction& tx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/**
* Create a new transaction paying the recipients with a set of coins
@@ -965,14 +982,24 @@ public:
bool DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut> &txouts) const;
bool DummySignInput(CTxIn &tx_in, const CTxOut &txout) const;
- static CFeeRate minTxFee;
- static CFeeRate fallbackFee;
- static CFeeRate m_discard_rate;
+ CFeeRate m_pay_tx_fee{DEFAULT_PAY_TX_FEE};
+ unsigned int m_confirm_target{DEFAULT_TX_CONFIRM_TARGET};
+ bool m_spend_zero_conf_change{DEFAULT_SPEND_ZEROCONF_CHANGE};
+ bool m_signal_rbf{DEFAULT_WALLET_RBF};
+ bool m_allow_fallback_fee{true}; //<! will be defined via chainparams
+ CFeeRate m_min_fee{DEFAULT_TRANSACTION_MINFEE}; //!< Override with -mintxfee
+ /**
+ * If fee estimation does not have enough data to provide estimates, use this fee instead.
+ * Has no effect if not using fee estimation
+ * Override with -fallbackfee
+ */
+ CFeeRate m_fallback_fee{DEFAULT_FALLBACK_FEE};
+ CFeeRate m_discard_rate{DEFAULT_DISCARD_FEE};
OutputType m_default_address_type{DEFAULT_ADDRESS_TYPE};
- OutputType m_default_change_type{OutputType::NONE}; // Default to OutputType::NONE if not set by -changetype
+ OutputType m_default_change_type{DEFAULT_CHANGE_TYPE};
bool NewKeyPool();
- size_t KeypoolCountExternalKeys();
+ size_t KeypoolCountExternalKeys() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool TopUpKeyPool(unsigned int kpSize = 0);
void ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRequestedInternal);
void KeepKey(int64_t nIndex);
@@ -982,10 +1009,10 @@ public:
/**
* Marks all keys in the keypool up to and including reserve_key as used.
*/
- void MarkReserveKeysAsUsed(int64_t keypool_id);
+ void MarkReserveKeysAsUsed(int64_t keypool_id) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
const std::map<CKeyID, int64_t>& GetAllReserveKeys() const { return m_pool_key_to_index; }
- std::set< std::set<CTxDestination> > GetAddressGroupings();
+ std::set<std::set<CTxDestination>> GetAddressGroupings() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
std::map<CTxDestination, CAmount> GetAddressBalances();
std::set<CTxDestination> GetLabelAddresses(const std::string& label) const;
@@ -1009,11 +1036,11 @@ public:
bool IsAllFromMe(const CTransaction& tx, const isminefilter& filter) const;
CAmount GetCredit(const CTransaction& tx, const isminefilter& filter) const;
CAmount GetChange(const CTransaction& tx) const;
- void SetBestChain(const CBlockLocator& loc) override;
+ void ChainStateFlushed(const CBlockLocator& loc) override;
DBErrors LoadWallet(bool& fFirstRunRet);
DBErrors ZapWalletTx(std::vector<CWalletTx>& vWtx);
- DBErrors ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut);
+ DBErrors ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool SetAddressBook(const CTxDestination& address, const std::string& strName, const std::string& purpose);
@@ -1032,8 +1059,8 @@ public:
}
void GetScriptForMining(std::shared_ptr<CReserveScript> &script);
-
- unsigned int GetKeyPoolSize()
+
+ unsigned int GetKeyPoolSize() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet)
{
AssertLockHeld(cs_wallet); // set{Ex,In}ternalKeyPool
return setInternalKeyPool.size() + setExternalKeyPool.size();
@@ -1052,12 +1079,12 @@ public:
std::set<uint256> GetConflicts(const uint256& txid) const;
//! Check if a given transaction has any of its outputs spent by another transaction in the wallet
- bool HasWalletSpend(const uint256& txid) const;
+ bool HasWalletSpend(const uint256& txid) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Flush wallet (bitdb flush)
void Flush(bool shutdown=false);
- /**
+ /**
* Address book entry changed.
* @note called with lock cs_wallet held.
*/
@@ -1066,7 +1093,7 @@ public:
const std::string &purpose,
ChangeType status)> NotifyAddressBookChanged;
- /**
+ /**
* Wallet transaction added, removed or updated.
* @note called with lock cs_wallet held.
*/
@@ -1113,7 +1140,10 @@ public:
/* Generates a new HD master key (will not be activated) */
CPubKey GenerateNewHDMasterKey();
-
+
+ /* Derives a new HD master key (will not be activated) */
+ CPubKey DeriveNewMasterHDKey(const CKey& key);
+
/* Set the current HD master key (will reset the chain child index counters)
Sets the master key's version based on the current wallet version (so the
caller must ensure the current wallet version is correct before calling
@@ -1126,7 +1156,7 @@ public:
* Obviously holding cs_main/cs_wallet when going into this call may cause
* deadlock
*/
- void BlockUntilSyncedToCurrentChain();
+ void BlockUntilSyncedToCurrentChain() LOCKS_EXCLUDED(cs_wallet);
/**
* Explicitly make the wallet learn the related scripts for outputs to the
@@ -1184,7 +1214,7 @@ public:
};
-/**
+/**
* DEPRECATED Account information.
* Stored in wallet with key "acc"+string account name.
*/
@@ -1214,7 +1244,7 @@ public:
}
};
-OutputType ParseOutputType(const std::string& str, OutputType default_type);
+bool ParseOutputType(const std::string& str, OutputType& output_type);
const std::string& FormatOutputType(OutputType type);
/**
@@ -1230,10 +1260,10 @@ std::vector<CTxDestination> GetAllDestinationsForKey(const CPubKey& key);
class WalletRescanReserver
{
private:
- CWalletRef m_wallet;
+ CWallet* m_wallet;
bool m_could_reserve;
public:
- explicit WalletRescanReserver(CWalletRef w) : m_wallet(w), m_could_reserve(false) {}
+ explicit WalletRescanReserver(CWallet* w) : m_wallet(w), m_could_reserve(false) {}
bool reserve()
{
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index bcc7cf877d..66a50db15d 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -246,9 +246,9 @@ public:
}
};
-bool
+static bool
ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
- CWalletScanState &wss, std::string& strType, std::string& strErr)
+ CWalletScanState &wss, std::string& strType, std::string& strErr) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
{
try {
// Unserialize
@@ -756,7 +756,7 @@ void MaybeCompactWalletDB()
return;
}
- for (CWalletRef pwallet : vpwallets) {
+ for (CWallet* pwallet : GetWallets()) {
WalletDatabase& dbh = pwallet->GetDBHandle();
unsigned int nUpdateCounter = dbh.nUpdateCounter;
diff --git a/src/walletinitinterface.h b/src/walletinitinterface.h
index c7eee37ce5..020d8971cb 100644
--- a/src/walletinitinterface.h
+++ b/src/walletinitinterface.h
@@ -13,23 +13,23 @@ class CRPCTable;
class WalletInitInterface {
public:
/** Get wallet help string */
- virtual std::string GetHelpString(bool showDebug) = 0;
+ virtual void AddWalletOptions() const = 0;
/** Check wallet parameter interaction */
- virtual bool ParameterInteraction() = 0;
+ virtual bool ParameterInteraction() const = 0;
/** Register wallet RPC*/
- virtual void RegisterRPC(CRPCTable &) = 0;
+ virtual void RegisterRPC(CRPCTable &) const = 0;
/** Verify wallets */
- virtual bool Verify() = 0;
+ virtual bool Verify() const = 0;
/** Open wallets*/
- virtual bool Open() = 0;
+ virtual bool Open() const = 0;
/** Start wallets*/
- virtual void Start(CScheduler& scheduler) = 0;
+ virtual void Start(CScheduler& scheduler) const = 0;
/** Flush Wallets*/
- virtual void Flush() = 0;
+ virtual void Flush() const = 0;
/** Stop Wallets*/
- virtual void Stop() = 0;
+ virtual void Stop() const = 0;
/** Close wallets */
- virtual void Close() = 0;
+ virtual void Close() const = 0;
virtual ~WalletInitInterface() {}
};
diff --git a/src/warnings.cpp b/src/warnings.cpp
index 572c766600..534745f998 100644
--- a/src/warnings.cpp
+++ b/src/warnings.cpp
@@ -9,9 +9,9 @@
#include <warnings.h>
CCriticalSection cs_warnings;
-std::string strMiscWarning;
-bool fLargeWorkForkFound = false;
-bool fLargeWorkInvalidChainFound = false;
+std::string strMiscWarning GUARDED_BY(cs_warnings);
+bool fLargeWorkForkFound GUARDED_BY(cs_warnings) = false;
+bool fLargeWorkInvalidChainFound GUARDED_BY(cs_warnings) = false;
void SetMiscWarning(const std::string& strWarning)
{
@@ -40,7 +40,6 @@ void SetfLargeWorkInvalidChainFound(bool flag)
std::string GetWarnings(const std::string& strFor)
{
std::string strStatusBar;
- std::string strRPC;
std::string strGUI;
const std::string uiAlertSeperator = "<hr />";
@@ -51,9 +50,6 @@ std::string GetWarnings(const std::string& strFor)
strGUI = _("This is a pre-release test build - use at your own risk - do not use for mining or merchant applications");
}
- if (gArgs.GetBoolArg("-testsafemode", DEFAULT_TESTSAFEMODE))
- strStatusBar = strRPC = strGUI = "testsafemode enabled";
-
// Misc warnings like out of disk space and clock is wrong
if (strMiscWarning != "")
{
@@ -63,12 +59,12 @@ std::string GetWarnings(const std::string& strFor)
if (fLargeWorkForkFound)
{
- strStatusBar = strRPC = "Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.";
+ strStatusBar = "Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.";
strGUI += (strGUI.empty() ? "" : uiAlertSeperator) + _("Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.");
}
else if (fLargeWorkInvalidChainFound)
{
- strStatusBar = strRPC = "Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.";
+ strStatusBar = "Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.";
strGUI += (strGUI.empty() ? "" : uiAlertSeperator) + _("Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.");
}
@@ -76,8 +72,6 @@ std::string GetWarnings(const std::string& strFor)
return strGUI;
else if (strFor == "statusbar")
return strStatusBar;
- else if (strFor == "rpc")
- return strRPC;
assert(!"GetWarnings(): invalid parameter");
return "error";
}
diff --git a/src/warnings.h b/src/warnings.h
index 3d7ac5aab4..904e8c0440 100644
--- a/src/warnings.h
+++ b/src/warnings.h
@@ -15,13 +15,10 @@ bool GetfLargeWorkForkFound();
void SetfLargeWorkInvalidChainFound(bool flag);
/** Format a string that describes several potential problems detected by the core.
* strFor can have three values:
- * - "rpc": get critical warnings, which should put the client in safe mode if non-empty
* - "statusbar": get all warnings
* - "gui": get all warnings, translated (where possible) for GUI
* This function only returns the highest priority warning of the set selected by strFor.
*/
std::string GetWarnings(const std::string& strFor);
-static const bool DEFAULT_TESTSAFEMODE = false;
-
#endif // BITCOIN_WARNINGS_H
diff --git a/test/config.ini.in b/test/config.ini.in
index 35ee092be4..a1119dc739 100644
--- a/test/config.ini.in
+++ b/test/config.ini.in
@@ -9,6 +9,7 @@
SRCDIR=@abs_top_srcdir@
BUILDDIR=@abs_top_builddir@
EXEEXT=@EXEEXT@
+RPCAUTH=@abs_top_srcdir@/share/rpcauth/rpcauth.py
[components]
# Which components are enabled. These are commented out by `configure` if they were disabled when running config.
diff --git a/test/functional/feature_bip68_sequence.py b/test/functional/feature_bip68_sequence.py
index eee38ce648..d8521550b5 100755
--- a/test/functional/feature_bip68_sequence.py
+++ b/test/functional/feature_bip68_sequence.py
@@ -67,7 +67,7 @@ class BIP68Test(BitcoinTestFramework):
# If sequence locks were used, this would require 1 block for the
# input to mature.
sequence_value = SEQUENCE_LOCKTIME_DISABLE_FLAG | 1
- tx1.vin = [CTxIn(COutPoint(int(utxo["txid"], 16), utxo["vout"]), nSequence=sequence_value)]
+ tx1.vin = [CTxIn(COutPoint(int(utxo["txid"], 16), utxo["vout"]), nSequence=sequence_value)]
tx1.vout = [CTxOut(value, CScript([b'a']))]
tx1_signed = self.nodes[0].signrawtransactionwithwallet(ToHex(tx1))["hex"]
@@ -80,7 +80,7 @@ class BIP68Test(BitcoinTestFramework):
tx2.nVersion = 2
sequence_value = sequence_value & 0x7fffffff
tx2.vin = [CTxIn(COutPoint(tx1_id, 0), nSequence=sequence_value)]
- tx2.vout = [CTxOut(int(value-self.relayfee*COIN), CScript([b'a']))]
+ tx2.vout = [CTxOut(int(value - self.relayfee * COIN), CScript([b'a' * 35]))]
tx2.rehash()
assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, ToHex(tx2))
@@ -222,7 +222,7 @@ class BIP68Test(BitcoinTestFramework):
tx = CTransaction()
tx.nVersion = 2
tx.vin = [CTxIn(COutPoint(orig_tx.sha256, 0), nSequence=sequence_value)]
- tx.vout = [CTxOut(int(orig_tx.vout[0].nValue - relayfee*COIN), CScript([b'a']))]
+ tx.vout = [CTxOut(int(orig_tx.vout[0].nValue - relayfee * COIN), CScript([b'a' * 35]))]
tx.rehash()
if (orig_tx.hash in node.getrawmempool()):
@@ -350,7 +350,7 @@ class BIP68Test(BitcoinTestFramework):
tx3 = CTransaction()
tx3.nVersion = 2
tx3.vin = [CTxIn(COutPoint(tx2.sha256, 0), nSequence=sequence_value)]
- tx3.vout = [CTxOut(int(tx2.vout[0].nValue - self.relayfee*COIN), CScript([b'a']))]
+ tx3.vout = [CTxOut(int(tx2.vout[0].nValue - self.relayfee * COIN), CScript([b'a' * 35]))]
tx3.rehash()
assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, ToHex(tx3))
diff --git a/test/functional/feature_block.py b/test/functional/feature_block.py
index 181c7f3369..f943fdf176 100755
--- a/test/functional/feature_block.py
+++ b/test/functional/feature_block.py
@@ -32,6 +32,7 @@ from test_framework.script import (
OP_ELSE,
OP_ENDIF,
OP_EQUAL,
+ OP_DROP,
OP_FALSE,
OP_HASH160,
OP_IF,
@@ -82,10 +83,7 @@ class FullBlockTest(BitcoinTestFramework):
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.bootstrap_p2p() # Add one p2p connection to the node
self.block_heights = {}
self.coinbase_key = CECKey()
@@ -1218,7 +1216,7 @@ class FullBlockTest(BitcoinTestFramework):
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])):
+ def create_tx(self, spend_tx, n, value, script=CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE])):
return create_transaction(spend_tx, n, b"", value, script)
# sign a transaction, using the key we know about
@@ -1296,17 +1294,28 @@ class FullBlockTest(BitcoinTestFramework):
self.blocks[block_number] = block
return block
- def reconnect_p2p(self):
+ def bootstrap_p2p(self):
"""Add a P2P connection to the node.
+ Helper to connect and wait for version handshake."""
+ self.nodes[0].add_p2p_connection(P2PDataStore())
+ network_thread_start()
+ # We need to wait for the initial getheaders from the peer before we
+ # start populating our blockstore. If we don't, then we may run ahead
+ # to the next subtest before we receive the getheaders. We'd then send
+ # an INV for the next block and receive two getheaders - one for the
+ # IBD and one for the INV. We'd respond to both and could get
+ # unexpectedly disconnected if the DoS score for that error is 50.
+ self.nodes[0].p2p.wait_for_getheaders(timeout=5)
+
+ def reconnect_p2p(self):
+ """Tear down and bootstrap the 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()
+ network_thread_join()
+ self.bootstrap_p2p()
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.
diff --git a/test/functional/feature_includeconf.py b/test/functional/feature_includeconf.py
new file mode 100755
index 0000000000..1ead2fcb02
--- /dev/null
+++ b/test/functional/feature_includeconf.py
@@ -0,0 +1,75 @@
+#!/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.
+"""Tests the includeconf argument
+
+Verify that:
+
+1. adding includeconf to the configuration file causes the includeconf
+ file to be loaded in the correct order.
+2. includeconf cannot be used as a command line argument.
+3. includeconf cannot be used recursively (ie includeconf can only
+ be used from the base config file).
+4. multiple includeconf arguments can be specified in the main config
+ file.
+"""
+import os
+
+from test_framework.test_framework import BitcoinTestFramework
+
+class IncludeConfTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = False
+ self.num_nodes = 1
+
+ def setup_chain(self):
+ super().setup_chain()
+ # Create additional config files
+ # - tmpdir/node0/relative.conf
+ with open(os.path.join(self.options.tmpdir, "node0", "relative.conf"), "w", encoding="utf8") as f:
+ f.write("uacomment=relative\n")
+ # - tmpdir/node0/relative2.conf
+ with open(os.path.join(self.options.tmpdir, "node0", "relative2.conf"), "w", encoding="utf8") as f:
+ f.write("uacomment=relative2\n")
+ with open(os.path.join(self.options.tmpdir, "node0", "bitcoin.conf"), "a", encoding='utf8') as f:
+ f.write("uacomment=main\nincludeconf=relative.conf\n")
+
+ def run_test(self):
+ self.log.info("-includeconf works from config file. subversion should end with 'main; relative)/'")
+
+ subversion = self.nodes[0].getnetworkinfo()["subversion"]
+ assert subversion.endswith("main; relative)/")
+
+ self.log.info("-includeconf cannot be used as command-line arg. subversion should still end with 'main; relative)/'")
+ self.stop_node(0)
+
+ self.start_node(0, extra_args=["-includeconf=relative2.conf"])
+
+ subversion = self.nodes[0].getnetworkinfo()["subversion"]
+ assert subversion.endswith("main; relative)/")
+ self.stop_node(0, expected_stderr="warning: -includeconf cannot be used from commandline; ignoring -includeconf=relative2.conf")
+
+ self.log.info("-includeconf cannot be used recursively. subversion should end with 'main; relative)/'")
+ with open(os.path.join(self.options.tmpdir, "node0", "relative.conf"), "a", encoding="utf8") as f:
+ f.write("includeconf=relative2.conf\n")
+
+ self.start_node(0)
+
+ subversion = self.nodes[0].getnetworkinfo()["subversion"]
+ assert subversion.endswith("main; relative)/")
+
+ self.log.info("multiple -includeconf args can be used from the base config file. subversion should end with 'main; relative; relative2)/'")
+ with open(os.path.join(self.options.tmpdir, "node0", "relative.conf"), "w", encoding="utf8") as f:
+ f.write("uacomment=relative\n")
+
+ with open(os.path.join(self.options.tmpdir, "node0", "bitcoin.conf"), "a", encoding='utf8') as f:
+ f.write("includeconf=relative2.conf\n")
+
+ self.restart_node(0)
+
+ subversion = self.nodes[0].getnetworkinfo()["subversion"]
+ assert subversion.endswith("main; relative; relative2)/")
+
+if __name__ == '__main__':
+ IncludeConfTest().main()
diff --git a/test/functional/feature_logging.py b/test/functional/feature_logging.py
index 3c7aecf10a..166f8f8694 100755
--- a/test/functional/feature_logging.py
+++ b/test/functional/feature_logging.py
@@ -15,13 +15,17 @@ class LoggingTest(BitcoinTestFramework):
self.num_nodes = 1
self.setup_clean_chain = True
+ def relative_log_path(self, name):
+ return os.path.join(self.nodes[0].datadir, "regtest", name)
+
def run_test(self):
# test default log file name
- assert os.path.isfile(os.path.join(self.nodes[0].datadir, "regtest", "debug.log"))
+ default_log_path = self.relative_log_path("debug.log")
+ assert os.path.isfile(default_log_path)
# test alternative log file name in datadir
self.restart_node(0, ["-debuglogfile=foo.log"])
- assert os.path.isfile(os.path.join(self.nodes[0].datadir, "regtest", "foo.log"))
+ assert os.path.isfile(self.relative_log_path("foo.log"))
# test alternative log file name outside datadir
tempname = os.path.join(self.options.tmpdir, "foo.log")
@@ -29,7 +33,7 @@ class LoggingTest(BitcoinTestFramework):
assert os.path.isfile(tempname)
# check that invalid log (relative) will cause error
- invdir = os.path.join(self.nodes[0].datadir, "regtest", "foo")
+ invdir = self.relative_log_path("foo")
invalidname = os.path.join("foo", "foo.log")
self.stop_node(0)
exp_stderr = "Error: Could not open debug log file \S+$"
@@ -55,6 +59,17 @@ class LoggingTest(BitcoinTestFramework):
self.start_node(0, ["-debuglogfile=%s" % (invalidname)])
assert os.path.isfile(os.path.join(invdir, "foo.log"))
+ # check that -nodebuglogfile disables logging
+ self.stop_node(0)
+ os.unlink(default_log_path)
+ assert not os.path.isfile(default_log_path)
+ self.start_node(0, ["-nodebuglogfile"])
+ assert not os.path.isfile(default_log_path)
+
+ # just sanity check no crash here
+ self.stop_node(0)
+ self.start_node(0, ["-debuglogfile=%s" % os.devnull])
+
if __name__ == '__main__':
LoggingTest().main()
diff --git a/test/functional/feature_maxuploadtarget.py b/test/functional/feature_maxuploadtarget.py
index 072ba6c7c7..0946f27b90 100755
--- a/test/functional/feature_maxuploadtarget.py
+++ b/test/functional/feature_maxuploadtarget.py
@@ -100,7 +100,7 @@ class MaxUploadTest(BitcoinTestFramework):
assert_equal(p2p_conns[0].block_receive_map[big_old_block], i+1)
assert_equal(len(self.nodes[0].getpeerinfo()), 3)
- # At most a couple more tries should succeed (depending on how long
+ # At most a couple more tries should succeed (depending on how long
# the test has been running so far).
for i in range(3):
p2p_conns[0].send_message(getdata_request)
diff --git a/test/functional/feature_proxy.py b/test/functional/feature_proxy.py
index 60859de7a5..2d10c547e2 100755
--- a/test/functional/feature_proxy.py
+++ b/test/functional/feature_proxy.py
@@ -79,9 +79,9 @@ class ProxyTest(BitcoinTestFramework):
# Note: proxies are not used to connect to local nodes
# this is because the proxy to use is based on CService.GetNetwork(), which return NET_UNROUTABLE for localhost
args = [
- ['-listen', '-proxy=%s:%i' % (self.conf1.addr),'-proxyrandomize=1'],
- ['-listen', '-proxy=%s:%i' % (self.conf1.addr),'-onion=%s:%i' % (self.conf2.addr),'-proxyrandomize=0'],
- ['-listen', '-proxy=%s:%i' % (self.conf2.addr),'-proxyrandomize=1'],
+ ['-listen', '-proxy=%s:%i' % (self.conf1.addr),'-proxyrandomize=1'],
+ ['-listen', '-proxy=%s:%i' % (self.conf1.addr),'-onion=%s:%i' % (self.conf2.addr),'-proxyrandomize=0'],
+ ['-listen', '-proxy=%s:%i' % (self.conf2.addr),'-proxyrandomize=1'],
[]
]
if self.have_ipv6:
diff --git a/test/functional/feature_pruning.py b/test/functional/feature_pruning.py
index 3adde8dd73..11a52b9ee2 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","-blockmaxweight=20000", "-checkblocks=5", "-disablesafemode"])
+ self.start_node(1, extra_args=["-maxreceivebuffer=20000","-blockmaxweight=20000", "-checkblocks=5"])
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","-blockmaxweight=20000", "-checkblocks=5", "-disablesafemode"])
+ self.start_node(1, extra_args=["-maxreceivebuffer=20000","-blockmaxweight=20000", "-checkblocks=5"])
self.log.info("Generating new longer chain of 300 more blocks")
self.nodes[1].generate(300)
diff --git a/test/functional/feature_rbf.py b/test/functional/feature_rbf.py
index d6ab5ecc37..c087960782 100755
--- a/test/functional/feature_rbf.py
+++ b/test/functional/feature_rbf.py
@@ -123,7 +123,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx1a = CTransaction()
tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0)]
- tx1a.vout = [CTxOut(1*COIN, CScript([b'a']))]
+ tx1a.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))]
tx1a_hex = txToHex(tx1a)
tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True)
@@ -132,7 +132,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
# Should fail because we haven't changed the fee
tx1b = CTransaction()
tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)]
- tx1b.vout = [CTxOut(1*COIN, CScript([b'b']))]
+ tx1b.vout = [CTxOut(1 * COIN, CScript([b'b' * 35]))]
tx1b_hex = txToHex(tx1b)
# This will raise an exception due to insufficient fee
@@ -143,7 +143,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
# Extra 0.1 BTC fee
tx1b = CTransaction()
tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)]
- tx1b.vout = [CTxOut(int(0.9*COIN), CScript([b'b']))]
+ tx1b.vout = [CTxOut(int(0.9 * COIN), CScript([b'b' * 35]))]
tx1b_hex = txToHex(tx1b)
# Replacement still disabled even with "enough fee"
assert_raises_rpc_error(-26, "txn-mempool-conflict", self.nodes[1].sendrawtransaction, tx1b_hex, True)
@@ -175,7 +175,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
remaining_value -= 1*COIN
tx = CTransaction()
tx.vin = [CTxIn(prevout, nSequence=0)]
- tx.vout = [CTxOut(remaining_value, CScript([1]))]
+ tx.vout = [CTxOut(remaining_value, CScript([1, OP_DROP] * 15 + [1]))]
tx_hex = txToHex(tx)
txid = self.nodes[0].sendrawtransaction(tx_hex, True)
chain_txids.append(txid)
@@ -185,7 +185,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
# child fees - 40 BTC - so this attempt is rejected.
dbl_tx = CTransaction()
dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)]
- dbl_tx.vout = [CTxOut(initial_nValue - 30*COIN, CScript([1]))]
+ dbl_tx.vout = [CTxOut(initial_nValue - 30 * COIN, CScript([1] * 35))]
dbl_tx_hex = txToHex(dbl_tx)
# This will raise an exception due to insufficient fee
@@ -194,7 +194,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
# Accepted with sufficient fee
dbl_tx = CTransaction()
dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)]
- dbl_tx.vout = [CTxOut(1*COIN, CScript([1]))]
+ dbl_tx.vout = [CTxOut(1 * COIN, CScript([1] * 35))]
dbl_tx_hex = txToHex(dbl_tx)
self.nodes[0].sendrawtransaction(dbl_tx_hex, True)
@@ -247,7 +247,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
# Attempt double-spend, will fail because too little fee paid
dbl_tx = CTransaction()
dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)]
- dbl_tx.vout = [CTxOut(initial_nValue - fee*n, CScript([1]))]
+ dbl_tx.vout = [CTxOut(initial_nValue - fee * n, CScript([1] * 35))]
dbl_tx_hex = txToHex(dbl_tx)
# This will raise an exception due to insufficient fee
assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, dbl_tx_hex, True)
@@ -255,7 +255,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
# 1 BTC fee is enough
dbl_tx = CTransaction()
dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)]
- dbl_tx.vout = [CTxOut(initial_nValue - fee*n - 1*COIN, CScript([1]))]
+ dbl_tx.vout = [CTxOut(initial_nValue - fee * n - 1 * COIN, CScript([1] * 35))]
dbl_tx_hex = txToHex(dbl_tx)
self.nodes[0].sendrawtransaction(dbl_tx_hex, True)
@@ -275,7 +275,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
dbl_tx = CTransaction()
dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)]
- dbl_tx.vout = [CTxOut(initial_nValue - 2*fee*n, CScript([1]))]
+ dbl_tx.vout = [CTxOut(initial_nValue - 2 * fee * n, CScript([1] * 35))]
dbl_tx_hex = txToHex(dbl_tx)
# This will raise an exception
assert_raises_rpc_error(-26, "too many potential replacements", self.nodes[0].sendrawtransaction, dbl_tx_hex, True)
@@ -290,7 +290,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx1a = CTransaction()
tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0)]
- tx1a.vout = [CTxOut(1*COIN, CScript([b'a']))]
+ tx1a.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))]
tx1a_hex = txToHex(tx1a)
self.nodes[0].sendrawtransaction(tx1a_hex, True)
@@ -311,7 +311,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx1a = CTransaction()
tx1a.vin = [CTxIn(utxo1, nSequence=0)]
- tx1a.vout = [CTxOut(int(1.1*COIN), CScript([b'a']))]
+ tx1a.vout = [CTxOut(int(1.1 * COIN), CScript([b'a' * 35]))]
tx1a_hex = txToHex(tx1a)
tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True)
@@ -330,7 +330,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
# Spend tx1a's output to test the indirect case.
tx1b = CTransaction()
tx1b.vin = [CTxIn(COutPoint(tx1a_txid, 0), nSequence=0)]
- tx1b.vout = [CTxOut(1*COIN, CScript([b'a']))]
+ tx1b.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))]
tx1b_hex = txToHex(tx1b)
tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True)
tx1b_txid = int(tx1b_txid, 16)
@@ -351,7 +351,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx1 = CTransaction()
tx1.vin = [CTxIn(confirmed_utxo)]
- tx1.vout = [CTxOut(1*COIN, CScript([b'a']))]
+ tx1.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))]
tx1_hex = txToHex(tx1)
self.nodes[0].sendrawtransaction(tx1_hex, True)
@@ -390,7 +390,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
for i in range(MAX_REPLACEMENT_LIMIT+1):
tx_i = CTransaction()
tx_i.vin = [CTxIn(COutPoint(txid, i), nSequence=0)]
- tx_i.vout = [CTxOut(split_value-fee, CScript([b'a']))]
+ tx_i.vout = [CTxOut(split_value - fee, CScript([b'a' * 35]))]
tx_i_hex = txToHex(tx_i)
self.nodes[0].sendrawtransaction(tx_i_hex, True)
@@ -423,14 +423,14 @@ class ReplaceByFeeTest(BitcoinTestFramework):
# Create a non-opting in transaction
tx1a = CTransaction()
tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0xffffffff)]
- tx1a.vout = [CTxOut(1*COIN, CScript([b'a']))]
+ tx1a.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))]
tx1a_hex = txToHex(tx1a)
tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True)
# Shouldn't be able to double-spend
tx1b = CTransaction()
tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)]
- tx1b.vout = [CTxOut(int(0.9*COIN), CScript([b'b']))]
+ tx1b.vout = [CTxOut(int(0.9 * COIN), CScript([b'b' * 35]))]
tx1b_hex = txToHex(tx1b)
# This will raise an exception
@@ -441,14 +441,14 @@ class ReplaceByFeeTest(BitcoinTestFramework):
# Create a different non-opting in transaction
tx2a = CTransaction()
tx2a.vin = [CTxIn(tx1_outpoint, nSequence=0xfffffffe)]
- tx2a.vout = [CTxOut(1*COIN, CScript([b'a']))]
+ tx2a.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))]
tx2a_hex = txToHex(tx2a)
tx2a_txid = self.nodes[0].sendrawtransaction(tx2a_hex, True)
# Still shouldn't be able to double-spend
tx2b = CTransaction()
tx2b.vin = [CTxIn(tx1_outpoint, nSequence=0)]
- tx2b.vout = [CTxOut(int(0.9*COIN), CScript([b'b']))]
+ tx2b.vout = [CTxOut(int(0.9 * COIN), CScript([b'b' * 35]))]
tx2b_hex = txToHex(tx2b)
# This will raise an exception
@@ -471,12 +471,12 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx3b = CTransaction()
tx3b.vin = [CTxIn(COutPoint(tx1a_txid, 0), nSequence=0)]
- tx3b.vout = [CTxOut(int(0.5*COIN), CScript([b'e']))]
+ tx3b.vout = [CTxOut(int(0.5 * COIN), CScript([b'e' * 35]))]
tx3b_hex = txToHex(tx3b)
tx3c = CTransaction()
tx3c.vin = [CTxIn(COutPoint(tx2a_txid, 0), nSequence=0)]
- tx3c.vout = [CTxOut(int(0.5*COIN), CScript([b'f']))]
+ tx3c.vout = [CTxOut(int(0.5 * COIN), CScript([b'f' * 35]))]
tx3c_hex = txToHex(tx3c)
self.nodes[0].sendrawtransaction(tx3b_hex, True)
@@ -493,7 +493,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx1a = CTransaction()
tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0)]
- tx1a.vout = [CTxOut(1*COIN, CScript([b'a']))]
+ tx1a.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))]
tx1a_hex = txToHex(tx1a)
tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True)
@@ -519,14 +519,14 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx2a = CTransaction()
tx2a.vin = [CTxIn(tx1_outpoint, nSequence=0)]
- tx2a.vout = [CTxOut(1*COIN, CScript([b'a']))]
+ tx2a.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))]
tx2a_hex = txToHex(tx2a)
self.nodes[0].sendrawtransaction(tx2a_hex, True)
# Lower fee, but we'll prioritise it
tx2b = CTransaction()
tx2b.vin = [CTxIn(tx1_outpoint, nSequence=0)]
- tx2b.vout = [CTxOut(int(1.01*COIN), CScript([b'a']))]
+ tx2b.vout = [CTxOut(int(1.01 * COIN), CScript([b'a' * 35]))]
tx2b.rehash()
tx2b_hex = txToHex(tx2b)
diff --git a/test/functional/feature_segwit.py b/test/functional/feature_segwit.py
index fa1732c4c5..3622e1834a 100755
--- a/test/functional/feature_segwit.py
+++ b/test/functional/feature_segwit.py
@@ -16,7 +16,7 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
from test_framework.mininode import sha256, CTransaction, CTxIn, COutPoint, CTxOut, COIN, ToHex, FromHex
from test_framework.address import script_to_p2sh, key_to_p2pkh
-from test_framework.script import CScript, OP_HASH160, OP_CHECKSIG, OP_0, hash160, OP_EQUAL, OP_DUP, OP_EQUALVERIFY, OP_1, OP_2, OP_CHECKMULTISIG, OP_TRUE
+from test_framework.script import CScript, OP_HASH160, OP_CHECKSIG, OP_0, hash160, OP_EQUAL, OP_DUP, OP_EQUALVERIFY, OP_1, OP_2, OP_CHECKMULTISIG, OP_TRUE, OP_DROP
from io import BytesIO
NODE_0 = 0
@@ -150,19 +150,11 @@ class SegWitTest(BitcoinTestFramework):
self.skip_mine(self.nodes[2], p2sh_ids[NODE_2][WIT_V0][0], True) #block 426
self.skip_mine(self.nodes[2], p2sh_ids[NODE_2][WIT_V1][0], True) #block 427
- # TODO: An old node would see these txs without witnesses and be able to mine them
-
- self.log.info("Verify unsigned bare witness txs in versionbits-setting blocks are valid before the fork")
- self.success_mine(self.nodes[2], wit_ids[NODE_2][WIT_V0][1], False) #block 428
- self.success_mine(self.nodes[2], wit_ids[NODE_2][WIT_V1][1], False) #block 429
-
self.log.info("Verify unsigned p2sh witness txs without a redeem script are invalid")
self.fail_accept(self.nodes[2], "mandatory-script-verify-flag", p2sh_ids[NODE_2][WIT_V0][1], False)
self.fail_accept(self.nodes[2], "mandatory-script-verify-flag", p2sh_ids[NODE_2][WIT_V1][1], False)
- self.log.info("Verify unsigned p2sh witness txs with a redeem script in versionbits-settings blocks are valid before the fork")
- self.success_mine(self.nodes[2], p2sh_ids[NODE_2][WIT_V0][1], False, witness_script(False, self.pubkey[2])) #block 430
- self.success_mine(self.nodes[2], p2sh_ids[NODE_2][WIT_V1][1], False, witness_script(True, self.pubkey[2])) #block 431
+ self.nodes[2].generate(4) # blocks 428-431
self.log.info("Verify previous witness txs skipped for mining can now be mined")
assert_equal(len(self.nodes[2].getrawmempool()), 4)
@@ -220,7 +212,7 @@ class SegWitTest(BitcoinTestFramework):
# Now create tx2, which will spend from txid1.
tx = CTransaction()
tx.vin.append(CTxIn(COutPoint(int(txid1, 16), 0), b''))
- tx.vout.append(CTxOut(int(49.99*COIN), CScript([OP_TRUE])))
+ tx.vout.append(CTxOut(int(49.99 * COIN), CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE])))
tx2_hex = self.nodes[0].signrawtransactionwithwallet(ToHex(tx))['hex']
txid2 = self.nodes[0].sendrawtransaction(tx2_hex)
tx = FromHex(CTransaction(), tx2_hex)
@@ -229,7 +221,7 @@ class SegWitTest(BitcoinTestFramework):
# Now create tx3, which will spend from txid2
tx = CTransaction()
tx.vin.append(CTxIn(COutPoint(int(txid2, 16), 0), b""))
- tx.vout.append(CTxOut(int(49.95*COIN), CScript([OP_TRUE]))) # Huge fee
+ tx.vout.append(CTxOut(int(49.95 * COIN), CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))) # Huge fee
tx.calc_sha256()
txid3 = self.nodes[0].sendrawtransaction(ToHex(tx))
assert(tx.wit.is_null())
@@ -311,8 +303,10 @@ class SegWitTest(BitcoinTestFramework):
v = self.nodes[0].getaddressinfo(i)
if (v['isscript']):
[bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
- # bare and p2sh multisig with compressed keys should always be spendable
- spendable_anytime.extend([bare, p2sh])
+ # p2sh multisig with compressed keys should always be spendable
+ spendable_anytime.extend([p2sh])
+ # bare multisig can be watched and signed, but is not treated as ours
+ solvable_after_importaddress.extend([bare])
# P2WSH and P2SH(P2WSH) multisig with compressed keys are spendable after direct importaddress
spendable_after_importaddress.extend([p2wsh, p2sh_p2wsh])
else:
@@ -328,8 +322,10 @@ class SegWitTest(BitcoinTestFramework):
v = self.nodes[0].getaddressinfo(i)
if (v['isscript']):
[bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
- # bare and p2sh multisig with uncompressed keys should always be spendable
- spendable_anytime.extend([bare, p2sh])
+ # p2sh multisig with uncompressed keys should always be spendable
+ spendable_anytime.extend([p2sh])
+ # bare multisig can be watched and signed, but is not treated as ours
+ solvable_after_importaddress.extend([bare])
# P2WSH and P2SH(P2WSH) multisig with uncompressed keys are never seen
unseen_anytime.extend([p2wsh, p2sh_p2wsh])
else:
diff --git a/test/functional/interface_zmq.py b/test/functional/interface_zmq.py
index 86ccea4394..af2e752b7a 100755
--- a/test/functional/interface_zmq.py
+++ b/test/functional/interface_zmq.py
@@ -4,7 +4,6 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test the ZMQ notification interface."""
import configparser
-import os
import struct
from test_framework.test_framework import BitcoinTestFramework, SkipTest
@@ -47,8 +46,6 @@ class ZMQTest (BitcoinTestFramework):
# Check that bitcoin has been built with ZMQ enabled.
config = configparser.ConfigParser()
- if not self.options.configfile:
- self.options.configfile = os.path.abspath(os.path.join(os.path.dirname(__file__), "../config.ini"))
config.read_file(open(self.options.configfile))
if not config["components"].getboolean("ENABLE_ZMQ"):
diff --git a/test/functional/mempool_packages.py b/test/functional/mempool_packages.py
index 8880db8002..79cf159f43 100755
--- a/test/functional/mempool_packages.py
+++ b/test/functional/mempool_packages.py
@@ -70,7 +70,10 @@ class MempoolPackagesTest(BitcoinTestFramework):
assert_equal(mempool[x]['descendantcount'], descendant_count)
descendant_fees += mempool[x]['fee']
assert_equal(mempool[x]['modifiedfee'], mempool[x]['fee'])
+ assert_equal(mempool[x]['fees']['base'], mempool[x]['fee'])
+ assert_equal(mempool[x]['fees']['modified'], mempool[x]['modifiedfee'])
assert_equal(mempool[x]['descendantfees'], descendant_fees * COIN)
+ assert_equal(mempool[x]['fees']['descendant'], descendant_fees)
descendant_size += mempool[x]['size']
assert_equal(mempool[x]['descendantsize'], descendant_size)
descendant_count += 1
@@ -132,6 +135,7 @@ class MempoolPackagesTest(BitcoinTestFramework):
ancestor_fees = 0
for x in chain:
ancestor_fees += mempool[x]['fee']
+ assert_equal(mempool[x]['fees']['ancestor'], ancestor_fees + Decimal('0.00001'))
assert_equal(mempool[x]['ancestorfees'], ancestor_fees * COIN + 1000)
# Undo the prioritisetransaction for later tests
@@ -145,6 +149,7 @@ class MempoolPackagesTest(BitcoinTestFramework):
descendant_fees = 0
for x in reversed(chain):
descendant_fees += mempool[x]['fee']
+ assert_equal(mempool[x]['fees']['descendant'], descendant_fees + Decimal('0.00001'))
assert_equal(mempool[x]['descendantfees'], descendant_fees * COIN + 1000)
# Adding one more transaction on to the chain should fail.
@@ -170,7 +175,9 @@ class MempoolPackagesTest(BitcoinTestFramework):
descendant_fees += mempool[x]['fee']
if (x == chain[-1]):
assert_equal(mempool[x]['modifiedfee'], mempool[x]['fee']+satoshi_round(0.00002))
+ assert_equal(mempool[x]['fees']['modified'], mempool[x]['fee']+satoshi_round(0.00002))
assert_equal(mempool[x]['descendantfees'], descendant_fees * COIN + 2000)
+ assert_equal(mempool[x]['fees']['descendant'], descendant_fees+satoshi_round(0.00002))
# TODO: check that node1's mempool is as expected
diff --git a/test/functional/mining_prioritisetransaction.py b/test/functional/mining_prioritisetransaction.py
index e754dd31ad..85f1af6682 100755
--- a/test/functional/mining_prioritisetransaction.py
+++ b/test/functional/mining_prioritisetransaction.py
@@ -120,7 +120,7 @@ class PrioritiseTransactionTest(BitcoinTestFramework):
tx_id = self.nodes[0].decoderawtransaction(tx_hex)["txid"]
# This will raise an exception due to min relay fee not being met
- assert_raises_rpc_error(-26, "min relay fee not met (code 66)", self.nodes[0].sendrawtransaction, tx_hex)
+ assert_raises_rpc_error(-26, "min relay fee not met", self.nodes[0].sendrawtransaction, tx_hex)
assert(tx_id not in self.nodes[0].getrawmempool())
# This is a less than 1000-byte transaction, so just set the fee
diff --git a/test/functional/p2p_compactblocks.py b/test/functional/p2p_compactblocks.py
index 1657d97281..cb4c9867a3 100755
--- a/test/functional/p2p_compactblocks.py
+++ b/test/functional/p2p_compactblocks.py
@@ -12,7 +12,8 @@ from test_framework.mininode import *
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
from test_framework.blocktools import create_block, create_coinbase, add_witness_commitment
-from test_framework.script import CScript, OP_TRUE
+from test_framework.script import CScript, OP_TRUE, OP_DROP
+
# TestP2PConn: A peer we use to send messages to bitcoind, and store responses.
class TestP2PConn(P2PInterface):
@@ -423,7 +424,7 @@ class CompactBlocksTest(BitcoinTestFramework):
for i in range(num_transactions):
tx = CTransaction()
tx.vin.append(CTxIn(COutPoint(utxo[0], utxo[1]), b''))
- tx.vout.append(CTxOut(utxo[2] - 1000, CScript([OP_TRUE])))
+ tx.vout.append(CTxOut(utxo[2] - 1000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE])))
tx.rehash()
utxo = [tx.sha256, 0, tx.vout[0].nValue]
block.vtx.append(tx)
diff --git a/test/functional/p2p_feefilter.py b/test/functional/p2p_feefilter.py
index 7c954cdca2..c304bbba85 100755
--- a/test/functional/p2p_feefilter.py
+++ b/test/functional/p2p_feefilter.py
@@ -69,7 +69,7 @@ class FeeFilterTest(BitcoinTestFramework):
# Change tx fee rate to 10 sat/byte and test they are no longer received
node1.settxfee(Decimal("0.00010000"))
[node1.sendtoaddress(node1.getnewaddress(), 1) for x in range(3)]
- sync_mempools(self.nodes) # must be sure node 0 has received all txs
+ sync_mempools(self.nodes) # must be sure node 0 has received all txs
# Send one transaction from node0 that should be received, so that we
# we can sync the test on receipt (if node1's txs were relayed, they'd
diff --git a/test/functional/p2p_invalid_tx.py b/test/functional/p2p_invalid_tx.py
index 69ce529ad6..a12c1d6023 100755
--- a/test/functional/p2p_invalid_tx.py
+++ b/test/functional/p2p_invalid_tx.py
@@ -6,24 +6,48 @@
In this test we connect to one node over p2p, and test tx requests."""
from test_framework.blocktools import create_block, create_coinbase, create_transaction
-from test_framework.messages import COIN
-from test_framework.mininode import network_thread_start, P2PDataStore
+from test_framework.messages import (
+ COIN,
+ COutPoint,
+ CTransaction,
+ CTxIn,
+ CTxOut,
+)
+from test_framework.mininode import network_thread_start, P2PDataStore, network_thread_join
from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+ wait_until,
+)
-class InvalidTxRequestTest(BitcoinTestFramework):
+class InvalidTxRequestTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.setup_clean_chain = True
- self.extra_args = [["-whitelist=127.0.0.1"]]
+
+ def bootstrap_p2p(self, *, num_connections=1):
+ """Add a P2P connection to the node.
+
+ Helper to connect and wait for version handshake."""
+ for _ in range(num_connections):
+ self.nodes[0].add_p2p_connection(P2PDataStore())
+ network_thread_start()
+ self.nodes[0].p2p.wait_for_verack()
+
+ def reconnect_p2p(self, **kwargs):
+ """Tear down and bootstrap the 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."""
+ self.nodes[0].disconnect_p2ps()
+ network_thread_join()
+ self.bootstrap_p2p(**kwargs)
def run_test(self):
- # Add p2p connection to node0
node = self.nodes[0] # convenience reference to the node
- node.add_p2p_connection(P2PDataStore())
- network_thread_start()
- node.p2p.wait_for_verack()
+ self.bootstrap_p2p() # Add one p2p connection to the node
best_block = self.nodes[0].getbestblockhash()
tip = int(best_block, 16)
@@ -44,12 +68,74 @@ class InvalidTxRequestTest(BitcoinTestFramework):
# b'\x64' is OP_NOTIF
# Transaction will be rejected with code 16 (REJECT_INVALID)
- tx1 = create_transaction(block1.vtx[0], 0, b'\x64', 50 * COIN - 12000)
- node.p2p.send_txs_and_test([tx1], node, success=False, reject_code=16, reject_reason=b'mandatory-script-verify-flag-failed (Invalid OP_IF construction)')
+ # and we get disconnected immediately
+ self.log.info('Test a transaction that is rejected')
+ tx1 = create_transaction(block1.vtx[0], 0, b'\x64' * 35, 50 * COIN - 12000)
+ node.p2p.send_txs_and_test([tx1], node, success=False, expect_disconnect=True)
+
+ # Make two p2p connections to provide the node with orphans
+ # * p2ps[0] will send valid orphan txs (one with low fee)
+ # * p2ps[1] will send an invalid orphan tx (and is later disconnected for that)
+ self.reconnect_p2p(num_connections=2)
+
+ self.log.info('Test orphan transaction handling ... ')
+ # Create a root transaction that we withold until all dependend transactions
+ # are sent out and in the orphan cache
+ SCRIPT_PUB_KEY_OP_TRUE = b'\x51\x75' * 15 + b'\x51'
+ tx_withhold = CTransaction()
+ tx_withhold.vin.append(CTxIn(outpoint=COutPoint(block1.vtx[0].sha256, 0)))
+ tx_withhold.vout.append(CTxOut(nValue=50 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
+ tx_withhold.calc_sha256()
+
+ # Our first orphan tx with some outputs to create further orphan txs
+ tx_orphan_1 = CTransaction()
+ tx_orphan_1.vin.append(CTxIn(outpoint=COutPoint(tx_withhold.sha256, 0)))
+ tx_orphan_1.vout = [CTxOut(nValue=10 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)] * 3
+ tx_orphan_1.calc_sha256()
+
+ # A valid transaction with low fee
+ tx_orphan_2_no_fee = CTransaction()
+ tx_orphan_2_no_fee.vin.append(CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 0)))
+ tx_orphan_2_no_fee.vout.append(CTxOut(nValue=10 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
+
+ # A valid transaction with sufficient fee
+ tx_orphan_2_valid = CTransaction()
+ tx_orphan_2_valid.vin.append(CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 1)))
+ tx_orphan_2_valid.vout.append(CTxOut(nValue=10 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
+ tx_orphan_2_valid.calc_sha256()
+
+ # An invalid transaction with negative fee
+ tx_orphan_2_invalid = CTransaction()
+ tx_orphan_2_invalid.vin.append(CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 2)))
+ tx_orphan_2_invalid.vout.append(CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
+
+ self.log.info('Send the orphans ... ')
+ # Send valid orphan txs from p2ps[0]
+ node.p2p.send_txs_and_test([tx_orphan_1, tx_orphan_2_no_fee, tx_orphan_2_valid], node, success=False)
+ # Send invalid tx from p2ps[1]
+ node.p2ps[1].send_txs_and_test([tx_orphan_2_invalid], node, success=False)
+
+ assert_equal(0, node.getmempoolinfo()['size']) # Mempool should be empty
+ assert_equal(2, len(node.getpeerinfo())) # p2ps[1] is still connected
+
+ self.log.info('Send the withhold tx ... ')
+ node.p2p.send_txs_and_test([tx_withhold], node, success=True)
+
+ # Transactions that should end up in the mempool
+ expected_mempool = {
+ t.hash
+ for t in [
+ tx_withhold, # The transaction that is the root for all orphans
+ tx_orphan_1, # The orphan transaction that splits the coins
+ tx_orphan_2_valid, # The valid transaction (with sufficient fee)
+ ]
+ }
+ # Transactions that do not end up in the mempool
+ # tx_orphan_no_fee, because it has too low fee (p2ps[0] is not disconnected for relaying that tx)
+ # tx_orphan_invaid, because it has negative fee (p2ps[1] is disconnected for relaying that tx)
- # Verify valid transaction
- tx1 = create_transaction(block1.vtx[0], 0, b'', 50 * COIN - 12000)
- node.p2p.send_txs_and_test([tx1], node, success=True)
+ wait_until(lambda: 1 == len(node.getpeerinfo()), timeout=12) # p2ps[1] is no longer connected
+ assert_equal(expected_mempool, set(node.getrawmempool()))
if __name__ == '__main__':
diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py
index 10c8475d01..e80a764477 100755
--- a/test/functional/p2p_segwit.py
+++ b/test/functional/p2p_segwit.py
@@ -10,6 +10,7 @@ from test_framework.util import *
from test_framework.script import *
from test_framework.blocktools import create_block, create_coinbase, add_witness_commitment, get_witness_script, WITNESS_COMMITMENT_HEADER
from test_framework.key import CECKey, CPubKey
+import math
import time
import random
from binascii import hexlify
@@ -47,7 +48,7 @@ def test_transaction_acceptance(rpc, p2p, tx, with_witness, accepted, reason=Non
with mininode_lock:
assert_equal(p2p.last_message["reject"].reason, reason)
-def test_witness_block(rpc, p2p, block, accepted, with_witness=True):
+def test_witness_block(rpc, p2p, block, accepted, with_witness=True, reason=None):
"""Send a block to the node and check that it's accepted
- Submit the block over the p2p interface
@@ -58,6 +59,10 @@ def test_witness_block(rpc, p2p, block, accepted, with_witness=True):
p2p.send_message(msg_block(block))
p2p.sync_with_ping()
assert_equal(rpc.getbestblockhash() == block.hash, accepted)
+ if (reason != None and not accepted):
+ # Check the rejection reason as well.
+ with mininode_lock:
+ assert_equal(p2p.last_message["reject"].reason, reason)
class TestP2PConn(P2PInterface):
def __init__(self):
@@ -68,11 +73,15 @@ class TestP2PConn(P2PInterface):
for inv in message.inv:
self.getdataset.add(inv.hash)
- def announce_tx_and_wait_for_getdata(self, tx, timeout=60):
+ def announce_tx_and_wait_for_getdata(self, tx, timeout=60, success=True):
with mininode_lock:
self.last_message.pop("getdata", None)
self.send_message(msg_inv(inv=[CInv(1, tx.sha256)]))
- self.wait_for_getdata(timeout)
+ if success:
+ self.wait_for_getdata(timeout)
+ else:
+ time.sleep(timeout)
+ assert not self.last_message.get("getdata")
def announce_block_and_wait_for_getdata(self, block, use_header, timeout=60):
with mininode_lock:
@@ -168,7 +177,7 @@ class SegWitTest(BitcoinTestFramework):
# Create a transaction that spends the coinbase
tx = CTransaction()
tx.vin.append(CTxIn(COutPoint(txid, 0), b""))
- tx.vout.append(CTxOut(49*100000000, CScript([OP_TRUE])))
+ tx.vout.append(CTxOut(49 * 100000000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE])))
tx.calc_sha256()
# Check that serializing it with or without witness is the same
@@ -259,7 +268,7 @@ class SegWitTest(BitcoinTestFramework):
# Now create a new anyone-can-spend utxo for the next test.
tx4 = CTransaction()
tx4.vin.append(CTxIn(COutPoint(tx3.sha256, 0), CScript([p2sh_program])))
- tx4.vout.append(CTxOut(tx3.vout[0].nValue-1000, CScript([OP_TRUE])))
+ tx4.vout.append(CTxOut(tx3.vout[0].nValue - 1000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE])))
tx4.rehash()
test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx3, False, True)
test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx4, False, True)
@@ -271,6 +280,80 @@ class SegWitTest(BitcoinTestFramework):
self.utxo.pop(0)
self.utxo.append(UTXO(tx4.sha256, 0, tx4.vout[0].nValue))
+ # ~6 months after segwit activation, the SCRIPT_VERIFY_WITNESS flag was
+ # backdated so that it applies to all blocks, going back to the genesis
+ # block.
+ #
+ # Consequently, version 0 witness outputs are never spendable without
+ # witness, and so can't be spent before segwit activation (the point at which
+ # blocks are permitted to contain witnesses).
+ def test_v0_outputs_arent_spendable(self):
+ self.log.info("Testing that v0 witness program outputs aren't spendable before activation")
+
+ assert len(self.utxo), "self.utxo is empty"
+
+ # Create two outputs, a p2wsh and p2sh-p2wsh
+ witness_program = CScript([OP_TRUE])
+ witness_hash = sha256(witness_program)
+ scriptPubKey = CScript([OP_0, witness_hash])
+
+ p2sh_pubkey = hash160(scriptPubKey)
+ p2sh_scriptPubKey = CScript([OP_HASH160, p2sh_pubkey, OP_EQUAL])
+
+ value = self.utxo[0].nValue // 3
+
+ tx = CTransaction()
+ tx.vin = [CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b'')]
+ tx.vout = [CTxOut(value, scriptPubKey), CTxOut(value, p2sh_scriptPubKey)]
+ tx.vout.append(CTxOut(value, CScript([OP_TRUE])))
+ tx.rehash()
+ txid = tx.sha256
+
+ # Add it to a block
+ block = self.build_next_block()
+ self.update_witness_block_with_transactions(block, [tx])
+ # Verify that segwit isn't activated. A block serialized with witness
+ # should be rejected prior to activation.
+ test_witness_block(self.nodes[0], self.test_node, block, accepted=False, with_witness=True, reason = b'unexpected-witness')
+ # Now send the block without witness. It should be accepted
+ test_witness_block(self.nodes[0], self.test_node, block, accepted=True, with_witness=False)
+
+ # Now try to spend the outputs. This should fail since SCRIPT_VERIFY_WITNESS is always enabled.
+ p2wsh_tx = CTransaction()
+ p2wsh_tx.vin = [CTxIn(COutPoint(txid, 0), b'')]
+ p2wsh_tx.vout = [CTxOut(value, CScript([OP_TRUE]))]
+ p2wsh_tx.wit.vtxinwit.append(CTxInWitness())
+ p2wsh_tx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])]
+ p2wsh_tx.rehash()
+
+ p2sh_p2wsh_tx = CTransaction()
+ p2sh_p2wsh_tx.vin = [CTxIn(COutPoint(txid, 1), CScript([scriptPubKey]))]
+ p2sh_p2wsh_tx.vout = [CTxOut(value, CScript([OP_TRUE]))]
+ p2sh_p2wsh_tx.wit.vtxinwit.append(CTxInWitness())
+ p2sh_p2wsh_tx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])]
+ p2sh_p2wsh_tx.rehash()
+
+ for tx in [p2wsh_tx, p2sh_p2wsh_tx]:
+
+ block = self.build_next_block()
+ self.update_witness_block_with_transactions(block, [tx])
+
+ # When the block is serialized with a witness, the block will be rejected because witness
+ # data isn't allowed in blocks that don't commit to witness data.
+ test_witness_block(self.nodes[0], self.test_node, block, accepted=False, with_witness=True, reason=b'unexpected-witness')
+
+ # When the block is serialized without witness, validation fails because the transaction is
+ # invalid (transactions are always validated with SCRIPT_VERIFY_WITNESS so a segwit v0 transaction
+ # without a witness is invalid).
+ # Note: The reject reason for this failure could be
+ # 'block-validation-failed' (if script check threads > 1) or
+ # 'non-mandatory-script-verify-flag (Witness program was passed an
+ # empty witness)' (otherwise).
+ # TODO: support multiple acceptable reject reasons.
+ test_witness_block(self.nodes[0], self.test_node, block, accepted=False, with_witness=False)
+
+ self.utxo.pop(0)
+ self.utxo.append(UTXO(txid, 2, value))
# Mine enough blocks for segwit's vb state to be 'started'.
def advance_to_segwit_started(self):
@@ -814,7 +897,7 @@ class SegWitTest(BitcoinTestFramework):
assert(len(self.utxo))
tx = CTransaction()
tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b""))
- tx.vout.append(CTxOut(self.utxo[0].nValue-1000, CScript([OP_TRUE])))
+ tx.vout.append(CTxOut(self.utxo[0].nValue - 1000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE])))
tx.wit.vtxinwit.append(CTxInWitness())
tx.wit.vtxinwit[0].scriptWitness.stack = [ b'a' ]
tx.rehash()
@@ -829,12 +912,7 @@ class SegWitTest(BitcoinTestFramework):
# Since we haven't delivered the tx yet, inv'ing the same tx from
# a witness transaction ought not result in a getdata.
- try:
- self.test_node.announce_tx_and_wait_for_getdata(tx, timeout=2)
- self.log.error("Error: duplicate tx getdata!")
- assert(False)
- except AssertionError:
- pass
+ self.test_node.announce_tx_and_wait_for_getdata(tx, timeout=2, success=False)
# Delivering this transaction with witness should fail (no matter who
# its from)
@@ -866,7 +944,7 @@ class SegWitTest(BitcoinTestFramework):
assert(len(self.utxo))
tx = CTransaction()
tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b""))
- tx.vout.append(CTxOut(self.utxo[0].nValue-1000, CScript([OP_TRUE])))
+ tx.vout.append(CTxOut(self.utxo[0].nValue - 1000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE])))
tx.wit.vtxinwit.append(CTxInWitness())
tx.wit.vtxinwit[0].scriptWitness.stack = [ b'a' ]
tx.rehash()
@@ -910,7 +988,7 @@ class SegWitTest(BitcoinTestFramework):
test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx3, True, False, b'tx-size')
# Remove witness stuffing, instead add extra witness push on stack
- tx3.vout[0] = CTxOut(tx2.vout[0].nValue-1000, CScript([OP_TRUE]))
+ tx3.vout[0] = CTxOut(tx2.vout[0].nValue - 1000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))
tx3.wit.vtxinwit[0].scriptWitness.stack = [CScript([CScriptNum(1)]), witness_program ]
tx3.rehash()
@@ -930,8 +1008,10 @@ class SegWitTest(BitcoinTestFramework):
raw_tx = self.nodes[0].getrawtransaction(tx3.hash, 1)
assert_equal(int(raw_tx["hash"], 16), tx3.calc_sha256(True))
assert_equal(raw_tx["size"], len(tx3.serialize_with_witness()))
- vsize = (len(tx3.serialize_with_witness()) + 3*len(tx3.serialize_without_witness()) + 3) / 4
+ weight = len(tx3.serialize_with_witness()) + 3*len(tx3.serialize_without_witness())
+ vsize = math.ceil(weight / 4)
assert_equal(raw_tx["vsize"], vsize)
+ assert_equal(raw_tx["weight"], weight)
assert_equal(len(raw_tx["vin"][0]["txinwitness"]), 1)
assert_equal(raw_tx["vin"][0]["txinwitness"][0], hexlify(witness_program).decode('ascii'))
assert(vsize != raw_tx["size"])
@@ -1096,7 +1176,7 @@ class SegWitTest(BitcoinTestFramework):
# P2PKH output; just send tx's first output back to an anyone-can-spend.
sync_mempools([self.nodes[0], self.nodes[1]])
tx3.vin = [CTxIn(COutPoint(tx.sha256, 0), b"")]
- tx3.vout = [CTxOut(tx.vout[0].nValue-1000, CScript([OP_TRUE]))]
+ tx3.vout = [CTxOut(tx.vout[0].nValue - 1000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))]
tx3.wit.vtxinwit.append(CTxInWitness())
tx3.wit.vtxinwit[0].scriptWitness.stack = [witness_program]
tx3.rehash()
@@ -1104,7 +1184,7 @@ class SegWitTest(BitcoinTestFramework):
else:
# tx and tx2 didn't go anywhere; just clean up the p2sh_tx output.
tx3.vin = [CTxIn(COutPoint(p2sh_tx.sha256, 0), CScript([witness_program]))]
- tx3.vout = [CTxOut(p2sh_tx.vout[0].nValue-1000, witness_program)]
+ tx3.vout = [CTxOut(p2sh_tx.vout[0].nValue - 1000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))]
tx3.rehash()
test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx3, with_witness=True, accepted=True)
@@ -1476,9 +1556,10 @@ class SegWitTest(BitcoinTestFramework):
block = self.build_next_block()
self.update_witness_block_with_transactions(block, [spend_tx])
- # If we're before activation, then sending this without witnesses
- # should be valid. If we're after activation, then sending this with
- # witnesses should be valid.
+ # If we're after activation, then sending this with witnesses should be valid.
+ # This no longer works before activation, because SCRIPT_VERIFY_WITNESS
+ # is always set.
+ # TODO: rewrite this test to make clear that it only works after activation.
if segwit_activated:
test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
else:
@@ -1577,7 +1658,7 @@ class SegWitTest(BitcoinTestFramework):
tx2.wit.vtxinwit.append(CTxInWitness())
tx2.wit.vtxinwit[-1].scriptWitness.stack = [ witness_program ]
total_value += tx.vout[i].nValue
- tx2.wit.vtxinwit[-1].scriptWitness.stack = [ witness_program_toomany ]
+ tx2.wit.vtxinwit[-1].scriptWitness.stack = [ witness_program_toomany ]
tx2.vout.append(CTxOut(total_value, CScript([OP_TRUE])))
tx2.rehash()
@@ -1897,6 +1978,7 @@ class SegWitTest(BitcoinTestFramework):
self.test_witness_services() # Verifies NODE_WITNESS
self.test_non_witness_transaction() # non-witness tx's are accepted
self.test_unnecessary_witness_before_segwit_activation()
+ self.test_v0_outputs_arent_spendable()
self.test_block_relay(segwit_activated=False)
# Advance to segwit being 'started'
@@ -1914,7 +1996,6 @@ class SegWitTest(BitcoinTestFramework):
self.test_unnecessary_witness_before_segwit_activation()
self.test_witness_tx_relay_before_segwit_activation()
self.test_block_relay(segwit_activated=False)
- self.test_p2sh_witness(segwit_activated=False)
self.test_standardness_v0(segwit_activated=False)
sync_blocks(self.nodes)
diff --git a/test/functional/p2p_sendheaders.py b/test/functional/p2p_sendheaders.py
index 4c7d5e65c5..095cc4b734 100755
--- a/test/functional/p2p_sendheaders.py
+++ b/test/functional/p2p_sendheaders.py
@@ -116,6 +116,7 @@ class BaseNode(P2PInterface):
self.block_announced = False
self.last_blockhash_announced = None
+ self.recent_headers_announced = []
def send_get_data(self, block_hashes):
"""Request data for a list of block hashes."""
@@ -163,40 +164,45 @@ class BaseNode(P2PInterface):
def on_headers(self, message):
if len(message.headers):
self.block_announced = True
- message.headers[-1].calc_sha256()
+ for x in message.headers:
+ x.calc_sha256()
+ # append because headers may be announced over multiple messages.
+ self.recent_headers_announced.append(x.sha256)
self.last_blockhash_announced = message.headers[-1].sha256
- def clear_last_announcement(self):
+ def clear_block_announcements(self):
with mininode_lock:
self.block_announced = False
self.last_message.pop("inv", None)
self.last_message.pop("headers", None)
+ self.recent_headers_announced = []
- def check_last_announcement(self, headers=None, inv=None):
- """Test whether the last announcement received had the right header or the right inv.
- inv and headers should be lists of block hashes."""
+ def check_last_headers_announcement(self, headers):
+ """Test whether the last headers announcements received are right.
+ Headers may be announced across more than one message."""
+ test_function = lambda: (len(self.recent_headers_announced) >= len(headers))
+ wait_until(test_function, timeout=60, lock=mininode_lock)
+ with mininode_lock:
+ assert_equal(self.recent_headers_announced, headers)
+ self.block_announced = False
+ self.last_message.pop("headers", None)
+ self.recent_headers_announced = []
+
+ def check_last_inv_announcement(self, inv):
+ """Test whether the last announcement received had the right inv.
+ inv should be a list of block hashes."""
test_function = lambda: self.block_announced
wait_until(test_function, timeout=60, lock=mininode_lock)
with mininode_lock:
- self.block_announced = False
-
compare_inv = []
if "inv" in self.last_message:
compare_inv = [x.hash for x in self.last_message["inv"].inv]
- if inv is not None:
- assert_equal(compare_inv, inv)
-
- compare_headers = []
- if "headers" in self.last_message:
- compare_headers = [x.sha256 for x in self.last_message["headers"].headers]
- if headers is not None:
- assert_equal(compare_headers, headers)
-
+ assert_equal(compare_inv, inv)
+ self.block_announced = False
self.last_message.pop("inv", None)
- self.last_message.pop("headers", None)
class SendHeadersTest(BitcoinTestFramework):
def set_test_params(self):
@@ -206,8 +212,8 @@ class SendHeadersTest(BitcoinTestFramework):
def mine_blocks(self, count):
"""Mine count blocks and return the new tip."""
- # Clear out last block announcement from each p2p listener
- [x.clear_last_announcement() for x in self.nodes[0].p2ps]
+ # Clear out block announcements from each p2p listener
+ [x.clear_block_announcements() for x in self.nodes[0].p2ps]
self.nodes[0].generate(count)
return int(self.nodes[0].getbestblockhash(), 16)
@@ -222,7 +228,7 @@ class SendHeadersTest(BitcoinTestFramework):
sync_blocks(self.nodes, wait=0.1)
for x in self.nodes[0].p2ps:
x.wait_for_block_announcement(int(self.nodes[0].getbestblockhash(), 16))
- x.clear_last_announcement()
+ x.clear_block_announcements()
tip_height = self.nodes[1].getblockcount()
hash_to_invalidate = self.nodes[1].getblockhash(tip_height - (length - 1))
@@ -255,25 +261,25 @@ class SendHeadersTest(BitcoinTestFramework):
tip = self.nodes[0].getblockheader(self.nodes[0].generate(1)[0])
tip_hash = int(tip["hash"], 16)
- inv_node.check_last_announcement(inv=[tip_hash], headers=[])
- test_node.check_last_announcement(inv=[tip_hash], headers=[])
+ inv_node.check_last_inv_announcement(inv=[tip_hash])
+ test_node.check_last_inv_announcement(inv=[tip_hash])
self.log.info("Verify getheaders with null locator and valid hashstop returns headers.")
- test_node.clear_last_announcement()
+ test_node.clear_block_announcements()
test_node.send_get_headers(locator=[], hashstop=tip_hash)
- test_node.check_last_announcement(headers=[tip_hash])
+ test_node.check_last_headers_announcement(headers=[tip_hash])
self.log.info("Verify getheaders with null locator and invalid hashstop does not return headers.")
block = create_block(int(tip["hash"], 16), create_coinbase(tip["height"] + 1), tip["mediantime"] + 1)
block.solve()
test_node.send_header_for_blocks([block])
- test_node.clear_last_announcement()
+ test_node.clear_block_announcements()
test_node.send_get_headers(locator=[], hashstop=int(block.hash, 16))
test_node.sync_with_ping()
assert_equal(test_node.block_announced, False)
- inv_node.clear_last_announcement()
+ inv_node.clear_block_announcements()
test_node.send_message(msg_block(block))
- inv_node.check_last_announcement(inv=[int(block.hash, 16)], headers=[])
+ inv_node.check_last_inv_announcement(inv=[int(block.hash, 16)])
def test_nonnull_locators(self, test_node, inv_node):
tip = int(self.nodes[0].getbestblockhash(), 16)
@@ -284,8 +290,8 @@ class SendHeadersTest(BitcoinTestFramework):
for i in range(4):
old_tip = tip
tip = self.mine_blocks(1)
- inv_node.check_last_announcement(inv=[tip], headers=[])
- test_node.check_last_announcement(inv=[tip], headers=[])
+ inv_node.check_last_inv_announcement(inv=[tip])
+ test_node.check_last_inv_announcement(inv=[tip])
# Try a few different responses; none should affect next announcement
if i == 0:
# first request the block
@@ -296,7 +302,7 @@ class SendHeadersTest(BitcoinTestFramework):
test_node.send_get_headers(locator=[old_tip], hashstop=tip)
test_node.send_get_data([tip])
test_node.wait_for_block(tip)
- test_node.clear_last_announcement() # since we requested headers...
+ test_node.clear_block_announcements() # since we requested headers...
elif i == 2:
# this time announce own block via headers
height = self.nodes[0].getblockcount()
@@ -308,8 +314,8 @@ class SendHeadersTest(BitcoinTestFramework):
test_node.wait_for_getdata([new_block.sha256])
test_node.send_message(msg_block(new_block))
test_node.sync_with_ping() # make sure this block is processed
- inv_node.clear_last_announcement()
- test_node.clear_last_announcement()
+ inv_node.clear_block_announcements()
+ test_node.clear_block_announcements()
self.log.info("Part 1: success!")
self.log.info("Part 2: announce blocks with headers after sendheaders message...")
@@ -323,8 +329,8 @@ class SendHeadersTest(BitcoinTestFramework):
# Now that we've synced headers, headers announcements should work
tip = self.mine_blocks(1)
- inv_node.check_last_announcement(inv=[tip], headers=[])
- test_node.check_last_announcement(headers=[tip])
+ inv_node.check_last_inv_announcement(inv=[tip])
+ test_node.check_last_headers_announcement(headers=[tip])
height = self.nodes[0].getblockcount() + 1
block_time += 10 # Advance far enough ahead
@@ -368,8 +374,8 @@ class SendHeadersTest(BitcoinTestFramework):
assert "inv" not in inv_node.last_message
assert "headers" not in inv_node.last_message
tip = self.mine_blocks(1)
- inv_node.check_last_announcement(inv=[tip], headers=[])
- test_node.check_last_announcement(headers=[tip])
+ inv_node.check_last_inv_announcement(inv=[tip])
+ test_node.check_last_headers_announcement(headers=[tip])
height += 1
block_time += 1
@@ -383,16 +389,16 @@ class SendHeadersTest(BitcoinTestFramework):
# First try mining a reorg that can propagate with header announcement
new_block_hashes = self.mine_reorg(length=7)
tip = new_block_hashes[-1]
- inv_node.check_last_announcement(inv=[tip], headers=[])
- test_node.check_last_announcement(headers=new_block_hashes)
+ inv_node.check_last_inv_announcement(inv=[tip])
+ test_node.check_last_headers_announcement(headers=new_block_hashes)
block_time += 8
# Mine a too-large reorg, which should be announced with a single inv
new_block_hashes = self.mine_reorg(length=8)
tip = new_block_hashes[-1]
- inv_node.check_last_announcement(inv=[tip], headers=[])
- test_node.check_last_announcement(inv=[tip], headers=[])
+ inv_node.check_last_inv_announcement(inv=[tip])
+ test_node.check_last_inv_announcement(inv=[tip])
block_time += 9
@@ -401,15 +407,15 @@ class SendHeadersTest(BitcoinTestFramework):
# Use getblocks/getdata
test_node.send_getblocks(locator=[fork_point])
- test_node.check_last_announcement(inv=new_block_hashes, headers=[])
+ test_node.check_last_inv_announcement(inv=new_block_hashes)
test_node.send_get_data(new_block_hashes)
test_node.wait_for_block(new_block_hashes[-1])
for i in range(3):
# Mine another block, still should get only an inv
tip = self.mine_blocks(1)
- inv_node.check_last_announcement(inv=[tip], headers=[])
- test_node.check_last_announcement(inv=[tip], headers=[])
+ inv_node.check_last_inv_announcement(inv=[tip])
+ test_node.check_last_inv_announcement(inv=[tip])
if i == 0:
self.log.debug("Just get the data -- shouldn't cause headers announcements to resume")
test_node.send_get_data([tip])
@@ -431,8 +437,8 @@ class SendHeadersTest(BitcoinTestFramework):
test_node.sync_with_ping()
# New blocks should now be announced with header
tip = self.mine_blocks(1)
- inv_node.check_last_announcement(inv=[tip], headers=[])
- test_node.check_last_announcement(headers=[tip])
+ inv_node.check_last_inv_announcement(inv=[tip])
+ test_node.check_last_headers_announcement(headers=[tip])
self.log.info("Part 3: success!")
diff --git a/test/functional/p2p_unrequested_blocks.py b/test/functional/p2p_unrequested_blocks.py
index 53b2856eb5..49c619a4ce 100755
--- a/test/functional/p2p_unrequested_blocks.py
+++ b/test/functional/p2p_unrequested_blocks.py
@@ -57,12 +57,8 @@ from test_framework.util import *
import time
from test_framework.blocktools import create_block, create_coinbase, create_transaction
-class AcceptBlockTest(BitcoinTestFramework):
- def add_options(self, parser):
- parser.add_option("--testbinary", dest="testbinary",
- default=os.getenv("BITCOIND", "bitcoind"),
- help="bitcoind binary to test")
+class AcceptBlockTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 2
diff --git a/test/functional/rpc_bind.py b/test/functional/rpc_bind.py
index 5b50520d3f..343e162058 100755
--- a/test/functional/rpc_bind.py
+++ b/test/functional/rpc_bind.py
@@ -4,7 +4,6 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test running bitcoind with the -rpcbind and -rpcallowip options."""
-import socket
import sys
from test_framework.test_framework import BitcoinTestFramework, SkipTest
@@ -20,6 +19,11 @@ class RPCBindTest(BitcoinTestFramework):
def setup_network(self):
self.add_nodes(self.num_nodes, None)
+ def add_options(self, parser):
+ parser.add_option("--ipv4", action='store_true', dest="run_ipv4", help="Run ipv4 tests only", default=False)
+ parser.add_option("--ipv6", action='store_true', dest="run_ipv6", help="Run ipv6 tests only", default=False)
+ parser.add_option("--nonloopback", action='store_true', dest="run_nonloopback", help="Run non-loopback tests only", default=False)
+
def run_bind_test(self, allow_ips, connect_to, addresses, expected):
'''
Start a node with requested rpcallowip and rpcbind parameters,
@@ -54,55 +58,69 @@ class RPCBindTest(BitcoinTestFramework):
def run_test(self):
# due to OS-specific network stats queries, this test works only on Linux
+ if sum([self.options.run_ipv4, self.options.run_ipv6, self.options.run_nonloopback]) > 1:
+ raise AssertionError("Only one of --ipv4, --ipv6 and --nonloopback can be set")
+
+ self.log.info("Check for linux")
if not sys.platform.startswith('linux'):
- raise SkipTest("This test can only be run on Linux.")
- # find the first non-loopback interface for testing
- non_loopback_ip = None
+ raise SkipTest("This test can only be run on linux.")
+
+ self.log.info("Check for ipv6")
+ have_ipv6 = test_ipv6_local()
+ if not have_ipv6 and not self.options.run_ipv4:
+ raise SkipTest("This test requires ipv6 support.")
+
+ self.log.info("Check for non-loopback interface")
+ self.non_loopback_ip = None
for name,ip in all_interfaces():
if ip != '127.0.0.1':
- non_loopback_ip = ip
+ self.non_loopback_ip = ip
break
- if non_loopback_ip is None:
- raise SkipTest("This test requires at least one non-loopback IPv4 interface.")
- try:
- s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
- s.connect(("::1",1))
- s.close
- except OSError:
- raise SkipTest("This test requires IPv6 support.")
-
- self.log.info("Using interface %s for testing" % non_loopback_ip)
-
- defaultport = rpc_port(0)
-
- # check default without rpcallowip (IPv4 and IPv6 localhost)
- self.run_bind_test(None, '127.0.0.1', [],
- [('127.0.0.1', defaultport), ('::1', defaultport)])
- # check default with rpcallowip (IPv6 any)
- self.run_bind_test(['127.0.0.1'], '127.0.0.1', [],
- [('::0', defaultport)])
- # check only IPv4 localhost (explicit)
- self.run_bind_test(['127.0.0.1'], '127.0.0.1', ['127.0.0.1'],
- [('127.0.0.1', defaultport)])
- # check only IPv4 localhost (explicit) with alternative port
- self.run_bind_test(['127.0.0.1'], '127.0.0.1:32171', ['127.0.0.1:32171'],
- [('127.0.0.1', 32171)])
- # check only IPv4 localhost (explicit) with multiple alternative ports on same host
- self.run_bind_test(['127.0.0.1'], '127.0.0.1:32171', ['127.0.0.1:32171', '127.0.0.1:32172'],
- [('127.0.0.1', 32171), ('127.0.0.1', 32172)])
- # check only IPv6 localhost (explicit)
- self.run_bind_test(['[::1]'], '[::1]', ['[::1]'],
- [('::1', defaultport)])
- # check both IPv4 and IPv6 localhost (explicit)
- self.run_bind_test(['127.0.0.1'], '127.0.0.1', ['127.0.0.1', '[::1]'],
- [('127.0.0.1', defaultport), ('::1', defaultport)])
+ if self.non_loopback_ip is None and self.options.run_nonloopback:
+ raise SkipTest("This test requires a non-loopback ip address.")
+
+ self.defaultport = rpc_port(0)
+
+ if not self.options.run_nonloopback:
+ self._run_loopback_tests()
+ if not self.options.run_ipv4 and not self.options.run_ipv6:
+ self._run_nonloopback_tests()
+
+ def _run_loopback_tests(self):
+ if self.options.run_ipv4:
+ # check only IPv4 localhost (explicit)
+ self.run_bind_test(['127.0.0.1'], '127.0.0.1', ['127.0.0.1'],
+ [('127.0.0.1', self.defaultport)])
+ # check only IPv4 localhost (explicit) with alternative port
+ self.run_bind_test(['127.0.0.1'], '127.0.0.1:32171', ['127.0.0.1:32171'],
+ [('127.0.0.1', 32171)])
+ # check only IPv4 localhost (explicit) with multiple alternative ports on same host
+ self.run_bind_test(['127.0.0.1'], '127.0.0.1:32171', ['127.0.0.1:32171', '127.0.0.1:32172'],
+ [('127.0.0.1', 32171), ('127.0.0.1', 32172)])
+ else:
+ # check default without rpcallowip (IPv4 and IPv6 localhost)
+ self.run_bind_test(None, '127.0.0.1', [],
+ [('127.0.0.1', self.defaultport), ('::1', self.defaultport)])
+ # check default with rpcallowip (IPv6 any)
+ self.run_bind_test(['127.0.0.1'], '127.0.0.1', [],
+ [('::0', self.defaultport)])
+ # check only IPv6 localhost (explicit)
+ self.run_bind_test(['[::1]'], '[::1]', ['[::1]'],
+ [('::1', self.defaultport)])
+ # check both IPv4 and IPv6 localhost (explicit)
+ self.run_bind_test(['127.0.0.1'], '127.0.0.1', ['127.0.0.1', '[::1]'],
+ [('127.0.0.1', self.defaultport), ('::1', self.defaultport)])
+
+ def _run_nonloopback_tests(self):
+ self.log.info("Using interface %s for testing" % self.non_loopback_ip)
+
# check only non-loopback interface
- self.run_bind_test([non_loopback_ip], non_loopback_ip, [non_loopback_ip],
- [(non_loopback_ip, defaultport)])
+ self.run_bind_test([self.non_loopback_ip], self.non_loopback_ip, [self.non_loopback_ip],
+ [(self.non_loopback_ip, self.defaultport)])
# Check that with invalid rpcallowip, we are denied
- self.run_allowip_test([non_loopback_ip], non_loopback_ip, defaultport)
- assert_raises_rpc_error(-342, "non-JSON HTTP response with '403 Forbidden' from server", self.run_allowip_test, ['1.1.1.1'], non_loopback_ip, defaultport)
+ self.run_allowip_test([self.non_loopback_ip], self.non_loopback_ip, self.defaultport)
+ assert_raises_rpc_error(-342, "non-JSON HTTP response with '403 Forbidden' from server", self.run_allowip_test, ['1.1.1.1'], self.non_loopback_ip, self.defaultport)
if __name__ == '__main__':
RPCBindTest().main()
diff --git a/test/functional/rpc_decodescript.py b/test/functional/rpc_decodescript.py
index 1ffc570437..d588151768 100755
--- a/test/functional/rpc_decodescript.py
+++ b/test/functional/rpc_decodescript.py
@@ -50,8 +50,11 @@ class DecodeScriptTest(BitcoinTestFramework):
def decodescript_script_pub_key(self):
public_key = '03b0da749730dc9b4b1f4a14d6902877a92541f5368778853d9c4a0cb7802dcfb2'
push_public_key = '21' + public_key
- public_key_hash = '11695b6cd891484c2d49ec5aa738ec2b2f897777'
+ public_key_hash = '5dd1d3a048119c27b28293056724d9522f26d945'
push_public_key_hash = '14' + public_key_hash
+ uncompressed_public_key = '04b0da749730dc9b4b1f4a14d6902877a92541f5368778853d9c4a0cb7802dcfb25e01fc8fde47c96c98a4f3a8123e33a38a50cf9025cc8c4494a518f991792bb7'
+ push_uncompressed_public_key = '41' + uncompressed_public_key
+ p2wsh_p2pk_script_hash = 'd8590cf8ea0674cf3d49fd7ca249b85ef7485dea62c138468bddeb20cd6519f7'
# below are test cases for all of the standard transaction types
@@ -59,18 +62,26 @@ class DecodeScriptTest(BitcoinTestFramework):
# <pubkey> OP_CHECKSIG
rpc_result = self.nodes[0].decodescript(push_public_key + 'ac')
assert_equal(public_key + ' OP_CHECKSIG', rpc_result['asm'])
+ # P2PK is translated to P2WPKH
+ assert_equal('0 ' + public_key_hash, rpc_result['segwit']['asm'])
# 2) P2PKH scriptPubKey
# OP_DUP OP_HASH160 <PubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
rpc_result = self.nodes[0].decodescript('76a9' + push_public_key_hash + '88ac')
assert_equal('OP_DUP OP_HASH160 ' + public_key_hash + ' OP_EQUALVERIFY OP_CHECKSIG', rpc_result['asm'])
+ # P2PKH is translated to P2WPKH
+ assert_equal('0 ' + public_key_hash, rpc_result['segwit']['asm'])
# 3) multisig scriptPubKey
# <m> <A pubkey> <B pubkey> <C pubkey> <n> OP_CHECKMULTISIG
# just imagine that the pub keys used below are different.
# for our purposes here it does not matter that they are the same even though it is unrealistic.
- rpc_result = self.nodes[0].decodescript('52' + push_public_key + push_public_key + push_public_key + '53ae')
+ multisig_script = '52' + push_public_key + push_public_key + push_public_key + '53ae'
+ rpc_result = self.nodes[0].decodescript(multisig_script)
assert_equal('2 ' + public_key + ' ' + public_key + ' ' + public_key + ' 3 OP_CHECKMULTISIG', rpc_result['asm'])
+ # multisig in P2WSH
+ multisig_script_hash = bytes_to_hex_str(sha256(hex_str_to_bytes(multisig_script)))
+ assert_equal('0 ' + multisig_script_hash, rpc_result['segwit']['asm'])
# 4) P2SH scriptPubKey
# OP_HASH160 <Hash160(redeemScript)> OP_EQUAL.
@@ -78,6 +89,8 @@ class DecodeScriptTest(BitcoinTestFramework):
# but this works the same for purposes of this test.
rpc_result = self.nodes[0].decodescript('a9' + push_public_key_hash + '87')
assert_equal('OP_HASH160 ' + public_key_hash + ' OP_EQUAL', rpc_result['asm'])
+ # P2SH does not work in segwit secripts. decodescript should not return a result for it.
+ assert 'segwit' not in rpc_result
# 5) null data scriptPubKey
# use a signature look-alike here to make sure that we do not decode random data as a signature.
@@ -101,8 +114,49 @@ class DecodeScriptTest(BitcoinTestFramework):
# <sender-pubkey> OP_CHECKSIG
#
# lock until block 500,000
- rpc_result = self.nodes[0].decodescript('63' + push_public_key + 'ad670320a107b17568' + push_public_key + 'ac')
+ cltv_script = '63' + push_public_key + 'ad670320a107b17568' + push_public_key + 'ac'
+ rpc_result = self.nodes[0].decodescript(cltv_script)
assert_equal('OP_IF ' + public_key + ' OP_CHECKSIGVERIFY OP_ELSE 500000 OP_CHECKLOCKTIMEVERIFY OP_DROP OP_ENDIF ' + public_key + ' OP_CHECKSIG', rpc_result['asm'])
+ # CLTV script in P2WSH
+ cltv_script_hash = bytes_to_hex_str(sha256(hex_str_to_bytes(cltv_script)))
+ assert_equal('0 ' + cltv_script_hash, rpc_result['segwit']['asm'])
+
+ # 7) P2PK scriptPubKey
+ # <pubkey> OP_CHECKSIG
+ rpc_result = self.nodes[0].decodescript(push_uncompressed_public_key + 'ac')
+ assert_equal(uncompressed_public_key + ' OP_CHECKSIG', rpc_result['asm'])
+ # uncompressed pubkeys are invalid for checksigs in segwit scripts.
+ # decodescript should not return a P2WPKH equivalent.
+ assert 'segwit' not in rpc_result
+
+ # 8) multisig scriptPubKey with an uncompressed pubkey
+ # <m> <A pubkey> <B pubkey> <n> OP_CHECKMULTISIG
+ # just imagine that the pub keys used below are different.
+ # the purpose of this test is to check that a segwit script is not returned for bare multisig scripts
+ # with an uncompressed pubkey in them.
+ rpc_result = self.nodes[0].decodescript('52' + push_public_key + push_uncompressed_public_key +'52ae')
+ assert_equal('2 ' + public_key + ' ' + uncompressed_public_key + ' 2 OP_CHECKMULTISIG', rpc_result['asm'])
+ # uncompressed pubkeys are invalid for checksigs in segwit scripts.
+ # decodescript should not return a P2WPKH equivalent.
+ assert 'segwit' not in rpc_result
+
+ # 9) P2WPKH scriptpubkey
+ # 0 <PubKeyHash>
+ rpc_result = self.nodes[0].decodescript('00' + push_public_key_hash)
+ assert_equal('0 ' + public_key_hash, rpc_result['asm'])
+ # segwit scripts do not work nested into each other.
+ # a nested segwit script should not be returned in the results.
+ assert 'segwit' not in rpc_result
+
+ # 10) P2WSH scriptpubkey
+ # 0 <ScriptHash>
+ # even though this hash is of a P2PK script which is better used as bare P2WPKH, it should not matter
+ # for the purpose of this test.
+ rpc_result = self.nodes[0].decodescript('0020' + p2wsh_p2pk_script_hash)
+ assert_equal('0 ' + p2wsh_p2pk_script_hash, rpc_result['asm'])
+ # segwit scripts do not work nested into each other.
+ # a nested segwit script should not be returned in the results.
+ assert 'segwit' not in rpc_result
def decoderawtransaction_asm_sighashtype(self):
"""Test decoding scripts via RPC command "decoderawtransaction".
diff --git a/test/functional/rpc_deprecated.py b/test/functional/rpc_deprecated.py
index b94b9d8fae..2e0500e7c4 100755
--- a/test/functional/rpc_deprecated.py
+++ b/test/functional/rpc_deprecated.py
@@ -4,12 +4,13 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test deprecation of RPC calls."""
from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_raises_rpc_error
class DeprecatedRpcTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
self.setup_clean_chain = True
- self.extra_args = [[], ["-deprecatedrpc=validateaddress"]]
+ self.extra_args = [[], ["-deprecatedrpc=validateaddress", "-deprecatedrpc=accounts"]]
def run_test(self):
# This test should be used to verify correct behaviour of deprecated
@@ -20,11 +21,93 @@ class DeprecatedRpcTest(BitcoinTestFramework):
# self.nodes[1].createmultisig(1, [self.nodes[1].getnewaddress()])
self.log.info("Test validateaddress deprecation")
- SOME_ADDRESS = "mnvGjUy3NMj67yJ6gkK5o9e5RS33Z2Vqcu" # This is just some random address to pass as a parameter to validateaddress
+ SOME_ADDRESS = "mnvGjUy3NMj67yJ6gkK5o9e5RS33Z2Vqcu" # This is just some random address to pass as a parameter to validateaddress
dep_validate_address = self.nodes[0].validateaddress(SOME_ADDRESS)
assert "ismine" not in dep_validate_address
not_dep_val = self.nodes[1].validateaddress(SOME_ADDRESS)
assert "ismine" in not_dep_val
+ self.log.info("Test accounts deprecation")
+ # The following account RPC methods are deprecated:
+ # - getaccount
+ # - getaccountaddress
+ # - getaddressesbyaccount
+ # - getreceivedbyaccount
+ # - listaccouts
+ # - listreceivedbyaccount
+ # - move
+ # - setaccount
+ #
+ # The following 'label' RPC methods are usable both with and without the
+ # -deprecatedrpc=accounts switch enabled.
+ # - getlabeladdress
+ # - getaddressesbylabel
+ # - getreceivedbylabel
+ # - listlabels
+ # - listreceivedbylabel
+ # - setlabel
+ #
+ address0 = self.nodes[0].getnewaddress()
+ self.nodes[0].generatetoaddress(101, address0)
+ self.sync_all()
+ address1 = self.nodes[1].getnewaddress()
+ self.nodes[1].generatetoaddress(101, address1)
+
+ self.log.info("- getaccount")
+ assert_raises_rpc_error(-32, "getaccount is deprecated", self.nodes[0].getaccount, address0)
+ self.nodes[1].getaccount(address1)
+
+ self.log.info("- setaccount")
+ assert_raises_rpc_error(-32, "setaccount is deprecated", self.nodes[0].setaccount, address0, "label0")
+ self.nodes[1].setaccount(address1, "label1")
+
+ self.log.info("- setlabel")
+ self.nodes[0].setlabel(address0, "label0")
+ self.nodes[1].setlabel(address1, "label1")
+
+ self.log.info("- getaccountaddress")
+ assert_raises_rpc_error(-32, "getaccountaddress is deprecated", self.nodes[0].getaccountaddress, "label0")
+ self.nodes[1].getaccountaddress("label1")
+
+ self.log.info("- getlabeladdress")
+ self.nodes[0].getlabeladdress("label0")
+ self.nodes[1].getlabeladdress("label1")
+
+ self.log.info("- getaddressesbyaccount")
+ assert_raises_rpc_error(-32, "getaddressesbyaccount is deprecated", self.nodes[0].getaddressesbyaccount, "label0")
+ self.nodes[1].getaddressesbyaccount("label1")
+
+ self.log.info("- getaddressesbylabel")
+ self.nodes[0].getaddressesbylabel("label0")
+ self.nodes[1].getaddressesbylabel("label1")
+
+ self.log.info("- getreceivedbyaccount")
+ assert_raises_rpc_error(-32, "getreceivedbyaccount is deprecated", self.nodes[0].getreceivedbyaccount, "label0")
+ self.nodes[1].getreceivedbyaccount("label1")
+
+ self.log.info("- getreceivedbylabel")
+ self.nodes[0].getreceivedbylabel("label0")
+ self.nodes[1].getreceivedbylabel("label1")
+
+ self.log.info("- listaccounts")
+ assert_raises_rpc_error(-32, "listaccounts is deprecated", self.nodes[0].listaccounts)
+ self.nodes[1].listaccounts()
+
+ self.log.info("- listlabels")
+ self.nodes[0].listlabels()
+ self.nodes[1].listlabels()
+
+ self.log.info("- listreceivedbyaccount")
+ assert_raises_rpc_error(-32, "listreceivedbyaccount is deprecated", self.nodes[0].listreceivedbyaccount)
+ self.nodes[1].listreceivedbyaccount()
+
+ self.log.info("- listreceivedbylabel")
+ self.nodes[0].listreceivedbylabel()
+ self.nodes[1].listreceivedbylabel()
+
+ self.log.info("- move")
+ assert_raises_rpc_error(-32, "move is deprecated", self.nodes[0].move, "label0", "label0b", 10)
+ self.nodes[1].move("label1", "label1b", 10)
+
if __name__ == '__main__':
DeprecatedRpcTest().main()
diff --git a/test/functional/rpc_fundrawtransaction.py b/test/functional/rpc_fundrawtransaction.py
index 5fb9a361d9..1e91cf598c 100755
--- a/test/functional/rpc_fundrawtransaction.py
+++ b/test/functional/rpc_fundrawtransaction.py
@@ -4,8 +4,18 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test the fundrawtransaction RPC."""
+from decimal import Decimal
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import *
+from test_framework.util import (
+ assert_equal,
+ assert_fee_amount,
+ assert_greater_than,
+ assert_greater_than_or_equal,
+ assert_raises_rpc_error,
+ connect_nodes_bi,
+ count_bytes,
+ find_vout_for_address,
+)
def get_unspent(listunspent, amount):
@@ -57,6 +67,11 @@ class RawTransactionsTest(BitcoinTestFramework):
watchonly_amount = Decimal(200)
self.nodes[3].importpubkey(watchonly_pubkey, "", True)
watchonly_txid = self.nodes[0].sendtoaddress(watchonly_address, watchonly_amount)
+
+ # Lock UTXO so nodes[0] doesn't accidentally spend it
+ watchonly_vout = find_vout_for_address(self.nodes[0], watchonly_txid, watchonly_address)
+ self.nodes[0].lockunspent(False, [{"txid": watchonly_txid, "vout": watchonly_vout}])
+
self.nodes[0].sendtoaddress(self.nodes[3].getnewaddress(), watchonly_amount / 10)
self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 1.5)
@@ -224,7 +239,7 @@ class RawTransactionsTest(BitcoinTestFramework):
outputs = { self.nodes[0].getnewaddress() : Decimal(4.0) }
rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
assert_raises_rpc_error(-1, "JSON value is not a string as expected", self.nodes[2].fundrawtransaction, rawtx, {'change_type': None})
- assert_raises_rpc_error(-5, "Unknown change type", self.nodes[2].fundrawtransaction, rawtx, {'change_type': ''})
+ assert_raises_rpc_error(-5, "Unknown change type ''", self.nodes[2].fundrawtransaction, rawtx, {'change_type': ''})
rawtx = self.nodes[2].fundrawtransaction(rawtx, {'change_type': 'bech32'})
dec_tx = self.nodes[2].decoderawtransaction(rawtx['hex'])
assert_equal('witness_v0_keyhash', dec_tx['vout'][rawtx['changepos']]['scriptPubKey']['type'])
@@ -475,6 +490,9 @@ class RawTransactionsTest(BitcoinTestFramework):
connect_nodes_bi(self.nodes,1,2)
connect_nodes_bi(self.nodes,0,2)
connect_nodes_bi(self.nodes,0,3)
+ # Again lock the watchonly UTXO or nodes[0] may spend it, because
+ # lockunspent is memory-only and thus lost on restart
+ self.nodes[0].lockunspent(False, [{"txid": watchonly_txid, "vout": watchonly_vout}])
self.sync_all()
# drain the keypool
diff --git a/test/functional/rpc_rawtransaction.py b/test/functional/rpc_rawtransaction.py
index 658782e82a..48b4a4a9db 100755
--- a/test/functional/rpc_rawtransaction.py
+++ b/test/functional/rpc_rawtransaction.py
@@ -14,13 +14,10 @@ Test the following RPCs:
from collections import OrderedDict
from io import BytesIO
+from test_framework.messages import CTransaction, ToHex
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.messages import (
- CTransaction,
-)
from test_framework.util import *
-
class multidict(dict):
"""Dictionary that allows duplicate keys.
@@ -363,5 +360,23 @@ class RawTransactionsTest(BitcoinTestFramework):
decrawtx= self.nodes[0].decoderawtransaction(rawtx)
assert_equal(decrawtx['vin'][0]['sequence'], 4294967294)
+ ####################################
+ # TRANSACTION VERSION NUMBER TESTS #
+ ####################################
+
+ # Test the minimum transaction version number that fits in a signed 32-bit integer.
+ tx = CTransaction()
+ tx.nVersion = -0x80000000
+ rawtx = ToHex(tx)
+ decrawtx = self.nodes[0].decoderawtransaction(rawtx)
+ assert_equal(decrawtx['version'], -0x80000000)
+
+ # Test the maximum transaction version number that fits in a signed 32-bit integer.
+ tx = CTransaction()
+ tx.nVersion = 0x7fffffff
+ rawtx = ToHex(tx)
+ decrawtx = self.nodes[0].decoderawtransaction(rawtx)
+ assert_equal(decrawtx['version'], 0x7fffffff)
+
if __name__ == '__main__':
RawTransactionsTest().main()
diff --git a/test/functional/rpc_users.py b/test/functional/rpc_users.py
index 0ce412f74a..1ef59da5ad 100755
--- a/test/functional/rpc_users.py
+++ b/test/functional/rpc_users.py
@@ -14,6 +14,10 @@ from test_framework.util import (
import os
import http.client
import urllib.parse
+import subprocess
+from random import SystemRandom
+import string
+import configparser
class HTTPBasicsTest(BitcoinTestFramework):
@@ -27,9 +31,20 @@ class HTTPBasicsTest(BitcoinTestFramework):
rpcauth2 = "rpcauth=rt2:f8607b1a88861fac29dfccf9b52ff9f$ff36a0c23c8c62b4846112e50fa888416e94c17bfd4c42f88fd8f55ec6a3137e"
rpcuser = "rpcuser=rpcuser💻"
rpcpassword = "rpcpassword=rpcpassword🔑"
+
+ self.user = ''.join(SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(10))
+ config = configparser.ConfigParser()
+ config.read_file(open(self.options.configfile))
+ gen_rpcauth = config['environment']['RPCAUTH']
+ p = subprocess.Popen([gen_rpcauth, self.user], stdout=subprocess.PIPE, universal_newlines=True)
+ lines = p.stdout.read().splitlines()
+ rpcauth3 = lines[1]
+ self.password = lines[3]
+
with open(os.path.join(get_datadir_path(self.options.tmpdir, 0), "bitcoin.conf"), 'a', encoding='utf8') as f:
f.write(rpcauth+"\n")
f.write(rpcauth2+"\n")
+ f.write(rpcauth3+"\n")
with open(os.path.join(get_datadir_path(self.options.tmpdir, 1), "bitcoin.conf"), 'a', encoding='utf8') as f:
f.write(rpcuser+"\n")
f.write(rpcpassword+"\n")
@@ -51,6 +66,7 @@ class HTTPBasicsTest(BitcoinTestFramework):
password2 = "8/F3uMDw4KSEbw96U3CA1C4X05dkHDN2BPFjTgZW4KI="
authpairnew = "rt:"+password
+ self.log.info('Correct...')
headers = {"Authorization": "Basic " + str_to_b64str(authpair)}
conn = http.client.HTTPConnection(url.hostname, url.port)
@@ -61,6 +77,7 @@ class HTTPBasicsTest(BitcoinTestFramework):
conn.close()
#Use new authpair to confirm both work
+ self.log.info('Correct...')
headers = {"Authorization": "Basic " + str_to_b64str(authpairnew)}
conn = http.client.HTTPConnection(url.hostname, url.port)
@@ -71,6 +88,7 @@ class HTTPBasicsTest(BitcoinTestFramework):
conn.close()
#Wrong login name with rt's password
+ self.log.info('Wrong...')
authpairnew = "rtwrong:"+password
headers = {"Authorization": "Basic " + str_to_b64str(authpairnew)}
@@ -82,6 +100,7 @@ class HTTPBasicsTest(BitcoinTestFramework):
conn.close()
#Wrong password for rt
+ self.log.info('Wrong...')
authpairnew = "rt:"+password+"wrong"
headers = {"Authorization": "Basic " + str_to_b64str(authpairnew)}
@@ -93,6 +112,7 @@ class HTTPBasicsTest(BitcoinTestFramework):
conn.close()
#Correct for rt2
+ self.log.info('Correct...')
authpairnew = "rt2:"+password2
headers = {"Authorization": "Basic " + str_to_b64str(authpairnew)}
@@ -104,6 +124,7 @@ class HTTPBasicsTest(BitcoinTestFramework):
conn.close()
#Wrong password for rt2
+ self.log.info('Wrong...')
authpairnew = "rt2:"+password2+"wrong"
headers = {"Authorization": "Basic " + str_to_b64str(authpairnew)}
@@ -114,12 +135,37 @@ class HTTPBasicsTest(BitcoinTestFramework):
assert_equal(resp.status, 401)
conn.close()
+ #Correct for randomly generated user
+ self.log.info('Correct...')
+ authpairnew = self.user+":"+self.password
+ headers = {"Authorization": "Basic " + str_to_b64str(authpairnew)}
+
+ conn = http.client.HTTPConnection(url.hostname, url.port)
+ conn.connect()
+ conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
+ resp = conn.getresponse()
+ assert_equal(resp.status, 200)
+ conn.close()
+
+ #Wrong password for randomly generated user
+ self.log.info('Wrong...')
+ authpairnew = self.user+":"+self.password+"Wrong"
+ headers = {"Authorization": "Basic " + str_to_b64str(authpairnew)}
+
+ conn = http.client.HTTPConnection(url.hostname, url.port)
+ conn.connect()
+ conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
+ resp = conn.getresponse()
+ assert_equal(resp.status, 401)
+ conn.close()
+
###############################################################
# Check correctness of the rpcuser/rpcpassword config options #
###############################################################
url = urllib.parse.urlparse(self.nodes[1].url)
# rpcuser and rpcpassword authpair
+ self.log.info('Correct...')
rpcuserauthpair = "rpcuser💻:rpcpassword🔑"
headers = {"Authorization": "Basic " + str_to_b64str(rpcuserauthpair)}
@@ -143,6 +189,7 @@ class HTTPBasicsTest(BitcoinTestFramework):
conn.close()
#Wrong password for rpcuser
+ self.log.info('Wrong...')
rpcuserauthpair = "rpcuser:rpcpasswordwrong"
headers = {"Authorization": "Basic " + str_to_b64str(rpcuserauthpair)}
diff --git a/test/functional/test_framework/blocktools.py b/test/functional/test_framework/blocktools.py
index 43982cd09a..5c2b1815e5 100644
--- a/test/functional/test_framework/blocktools.py
+++ b/test/functional/test_framework/blocktools.py
@@ -10,7 +10,24 @@ from .address import (
script_to_p2sh_p2wsh,
script_to_p2wsh,
)
-from .mininode import *
+from .messages import (
+ CBlock,
+ COIN,
+ COutPoint,
+ CTransaction,
+ CTxIn,
+ CTxInWitness,
+ CTxOut,
+ FromHex,
+ ToHex,
+ bytes_to_hex_str,
+ hash256,
+ hex_str_to_bytes,
+ ser_string,
+ ser_uint256,
+ sha256,
+ uint256_from_str,
+)
from .script import (
CScript,
OP_0,
@@ -23,34 +40,34 @@ from .script import (
)
from .util import assert_equal
-# Create a block (with regtest difficulty)
-def create_block(hashprev, coinbase, nTime=None):
+# From BIP141
+WITNESS_COMMITMENT_HEADER = b"\xaa\x21\xa9\xed"
+
+def create_block(hashprev, coinbase, ntime=None):
+ """Create a block (with regtest difficulty)."""
block = CBlock()
- if nTime is None:
+ if ntime is None:
import time
- block.nTime = int(time.time()+600)
+ block.nTime = int(time.time() + 600)
else:
- block.nTime = nTime
+ block.nTime = ntime
block.hashPrevBlock = hashprev
- block.nBits = 0x207fffff # Will break after a difficulty adjustment...
+ block.nBits = 0x207fffff # difficulty retargeting is disabled in REGTEST chainparams
block.vtx.append(coinbase)
block.hashMerkleRoot = block.calc_merkle_root()
block.calc_sha256()
return block
-# From BIP141
-WITNESS_COMMITMENT_HEADER = b"\xaa\x21\xa9\xed"
-
-
def get_witness_script(witness_root, witness_nonce):
- witness_commitment = uint256_from_str(hash256(ser_uint256(witness_root)+ser_uint256(witness_nonce)))
+ witness_commitment = uint256_from_str(hash256(ser_uint256(witness_root) + ser_uint256(witness_nonce)))
output_data = WITNESS_COMMITMENT_HEADER + ser_uint256(witness_commitment)
return CScript([OP_RETURN, output_data])
-
-# According to BIP141, blocks with witness rules active must commit to the
-# hash of all in-block transactions including witness.
def add_witness_commitment(block, nonce=0):
+ """Add a witness commitment to the block's coinbase transaction.
+
+ According to BIP141, blocks with witness rules active must commit to the
+ hash of all in-block transactions including witness."""
# First calculate the merkle root of the block's
# transactions, with witnesses.
witness_nonce = nonce
@@ -65,7 +82,6 @@ def add_witness_commitment(block, nonce=0):
block.hashMerkleRoot = block.calc_merkle_root()
block.rehash()
-
def serialize_script_num(value):
r = bytearray(0)
if value == 0:
@@ -81,55 +97,59 @@ def serialize_script_num(value):
r[-1] |= 0x80
return r
-# Create a coinbase transaction, assuming no miner fees.
-# If pubkey is passed in, the coinbase output will be a P2PK output;
-# otherwise an anyone-can-spend output.
-def create_coinbase(height, pubkey = None):
+def create_coinbase(height, pubkey=None):
+ """Create a coinbase transaction, assuming no miner fees.
+
+ If pubkey is passed in, the coinbase output will be a P2PK output;
+ otherwise an anyone-can-spend output."""
coinbase = CTransaction()
- coinbase.vin.append(CTxIn(COutPoint(0, 0xffffffff),
- ser_string(serialize_script_num(height)), 0xffffffff))
+ coinbase.vin.append(CTxIn(COutPoint(0, 0xffffffff),
+ ser_string(serialize_script_num(height)), 0xffffffff))
coinbaseoutput = CTxOut()
coinbaseoutput.nValue = 50 * COIN
- halvings = int(height/150) # regtest
+ halvings = int(height / 150) # regtest
coinbaseoutput.nValue >>= halvings
- if (pubkey != None):
+ if (pubkey is not None):
coinbaseoutput.scriptPubKey = CScript([pubkey, OP_CHECKSIG])
else:
coinbaseoutput.scriptPubKey = CScript([OP_TRUE])
- coinbase.vout = [ coinbaseoutput ]
+ coinbase.vout = [coinbaseoutput]
coinbase.calc_sha256()
return coinbase
-# Create a transaction.
-# If the scriptPubKey is not specified, make it anyone-can-spend.
-def create_transaction(prevtx, n, sig, value, scriptPubKey=CScript()):
+def create_transaction(prevtx, n, sig, value, script_pub_key=CScript()):
+ """Create a transaction.
+
+ If the script_pub_key is not specified, make it anyone-can-spend."""
tx = CTransaction()
assert(n < len(prevtx.vout))
tx.vin.append(CTxIn(COutPoint(prevtx.sha256, n), sig, 0xffffffff))
- tx.vout.append(CTxOut(value, scriptPubKey))
+ tx.vout.append(CTxOut(value, script_pub_key))
tx.calc_sha256()
return tx
-def get_legacy_sigopcount_block(block, fAccurate=True):
+def get_legacy_sigopcount_block(block, accurate=True):
count = 0
for tx in block.vtx:
- count += get_legacy_sigopcount_tx(tx, fAccurate)
+ count += get_legacy_sigopcount_tx(tx, accurate)
return count
-def get_legacy_sigopcount_tx(tx, fAccurate=True):
+def get_legacy_sigopcount_tx(tx, accurate=True):
count = 0
for i in tx.vout:
- count += i.scriptPubKey.GetSigOpCount(fAccurate)
+ count += i.scriptPubKey.GetSigOpCount(accurate)
for j in tx.vin:
# scriptSig might be of type bytes, so convert to CScript for the moment
- count += CScript(j.scriptSig).GetSigOpCount(fAccurate)
+ count += CScript(j.scriptSig).GetSigOpCount(accurate)
return count
-# Create a scriptPubKey corresponding to either a P2WPKH output for the
-# given pubkey, or a P2WSH output of a 1-of-1 multisig for the given
-# pubkey. Returns the hex encoding of the scriptPubKey.
def witness_script(use_p2wsh, pubkey):
- if (use_p2wsh == False):
+ """Create a scriptPubKey for a pay-to-wtiness TxOut.
+
+ This is either a P2WPKH output for the given pubkey, or a P2WSH output of a
+ 1-of-1 multisig for the given pubkey. Returns the hex encoding of the
+ scriptPubKey."""
+ if not use_p2wsh:
# P2WPKH instead
pubkeyhash = hash160(hex_str_to_bytes(pubkey))
pkscript = CScript([OP_0, pubkeyhash])
@@ -140,9 +160,10 @@ def witness_script(use_p2wsh, pubkey):
pkscript = CScript([OP_0, scripthash])
return bytes_to_hex_str(pkscript)
-# Return a transaction (in hex) that spends the given utxo to a segwit output,
-# optionally wrapping the segwit output using P2SH.
def create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount):
+ """Return a transaction (in hex) that spends the given utxo to a segwit output.
+
+ Optionally wrap the segwit output using P2SH."""
if use_p2wsh:
program = CScript([OP_1, hex_str_to_bytes(pubkey), OP_1, OP_CHECKMULTISIG])
addr = script_to_p2sh_p2wsh(program) if encode_p2sh else script_to_p2wsh(program)
@@ -152,12 +173,13 @@ def create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount):
assert_equal(node.getaddressinfo(addr)['scriptPubKey'], witness_script(use_p2wsh, pubkey))
return node.createrawtransaction([utxo], {addr: amount})
-# Create a transaction spending a given utxo to a segwit output corresponding
-# to the given pubkey: use_p2wsh determines whether to use P2WPKH or P2WSH;
-# encode_p2sh determines whether to wrap in P2SH.
-# sign=True will have the given node sign the transaction.
-# insert_redeem_script will be added to the scriptSig, if given.
def send_to_witness(use_p2wsh, node, utxo, pubkey, encode_p2sh, amount, sign=True, insert_redeem_script=""):
+ """Create a transaction spending a given utxo to a segwit output.
+
+ The output corresponds to the given pubkey: use_p2wsh determines whether to
+ use P2WPKH or P2WSH; encode_p2sh determines whether to wrap in P2SH.
+ sign=True will have the given node sign the transaction.
+ insert_redeem_script will be added to the scriptSig, if given."""
tx_to_witness = create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount)
if (sign):
signed = node.signrawtransactionwithwallet(tx_to_witness)
diff --git a/test/functional/test_framework/mininode.py b/test/functional/test_framework/mininode.py
index aba2841682..7c2125a177 100755
--- a/test/functional/test_framework/mininode.py
+++ b/test/functional/test_framework/mininode.py
@@ -554,13 +554,13 @@ class P2PDataStore(P2PInterface):
if reject_reason is not None:
wait_until(lambda: self.reject_reason_received == reject_reason, lock=mininode_lock)
- def send_txs_and_test(self, txs, rpc, success=True, reject_code=None, reject_reason=None):
+ def send_txs_and_test(self, txs, rpc, success=True, expect_disconnect=False, reject_code=None, reject_reason=None):
"""Send txs to test node and test whether they're accepted to the mempool.
- add all txs to our tx_store
- send tx messages for all txs
- - if success is True: assert that the tx is accepted to the mempool
- - if success is False: assert that the tx is not accepted to the mempool
+ - if success is True/False: assert that the txs are/are not accepted to the mempool
+ - if expect_disconnect is True: Skip the sync with ping
- if reject_code and reject_reason are set: assert that the correct reject message is received."""
with mininode_lock:
@@ -573,7 +573,10 @@ class P2PDataStore(P2PInterface):
for tx in txs:
self.send_message(msg_tx(tx))
- self.sync_with_ping()
+ if expect_disconnect:
+ self.wait_for_disconnect()
+ else:
+ self.sync_with_ping()
raw_mempool = rpc.getrawmempool()
if success:
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index 54ff9eb038..b842e6ef4e 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -4,6 +4,7 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Base class for RPC testing."""
+import configparser
from enum import Enum
import logging
import optparse
@@ -97,10 +98,8 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
help="Leave bitcoinds and test.* datadir on exit or error")
parser.add_option("--noshutdown", dest="noshutdown", default=False, action="store_true",
help="Don't stop bitcoinds after the test execution")
- parser.add_option("--srcdir", dest="srcdir", default=os.path.normpath(os.path.dirname(os.path.realpath(__file__)) + "/../../../src"),
- help="Source directory containing bitcoind/bitcoin-cli (default: %default)")
- parser.add_option("--cachedir", dest="cachedir", default=os.path.normpath(os.path.dirname(os.path.realpath(__file__)) + "/../../cache"),
- help="Directory for caching pregenerated datadirs")
+ parser.add_option("--cachedir", dest="cachedir", default=os.path.abspath(os.path.dirname(os.path.realpath(__file__)) + "/../../cache"),
+ help="Directory for caching pregenerated datadirs (default: %default)")
parser.add_option("--tmpdir", dest="tmpdir", help="Root directory for datadirs")
parser.add_option("-l", "--loglevel", dest="loglevel", default="INFO",
help="log events at this level and higher to the console. Can be set to DEBUG, INFO, WARNING, ERROR or CRITICAL. Passing --loglevel DEBUG will output all logs to console. Note that logs at all levels are always written to the test_framework.log file in the temporary test directory.")
@@ -111,7 +110,8 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
parser.add_option("--coveragedir", dest="coveragedir",
help="Write tested RPC commands into this directory")
parser.add_option("--configfile", dest="configfile",
- help="Location of the test framework config file")
+ default=os.path.abspath(os.path.dirname(os.path.realpath(__file__)) + "/../../config.ini"),
+ help="Location of the test framework config file (default: %default)")
parser.add_option("--pdbonfailure", dest="pdbonfailure", default=False, action="store_true",
help="Attach a python debugger if test fails")
parser.add_option("--usecli", dest="usecli", default=False, action="store_true",
@@ -121,14 +121,19 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
PortSeed.n = self.options.port_seed
- os.environ['PATH'] = self.options.srcdir + os.pathsep + \
- self.options.srcdir + os.path.sep + "qt" + os.pathsep + \
- os.environ['PATH']
-
check_json_precision()
self.options.cachedir = os.path.abspath(self.options.cachedir)
+ config = configparser.ConfigParser()
+ config.read_file(open(self.options.configfile))
+ self.options.bitcoind = os.getenv("BITCOIND", default=config["environment"]["BUILDDIR"] + '/src/bitcoind' + config["environment"]["EXEEXT"])
+ self.options.bitcoincli = os.getenv("BITCOINCLI", default=config["environment"]["BUILDDIR"] + '/src/bitcoin-cli' + config["environment"]["EXEEXT"])
+
+ os.environ['PATH'] = config['environment']['BUILDDIR'] + os.pathsep + \
+ config['environment']['BUILDDIR'] + os.path.sep + "qt" + os.pathsep + \
+ os.environ['PATH']
+
# Set up temp directory and start logging
if self.options.tmpdir:
self.options.tmpdir = os.path.abspath(self.options.tmpdir)
@@ -246,12 +251,12 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
if extra_args is None:
extra_args = [[]] * num_nodes
if binary is None:
- binary = [None] * num_nodes
+ binary = [self.options.bitcoind] * num_nodes
assert_equal(len(extra_confs), num_nodes)
assert_equal(len(extra_args), num_nodes)
assert_equal(len(binary), num_nodes)
for i in range(num_nodes):
- self.nodes.append(TestNode(i, get_datadir_path(self.options.tmpdir, i), rpchost=rpchost, timewait=timewait, binary=binary[i], stderr=None, mocktime=self.mocktime, coverage_dir=self.options.coveragedir, extra_conf=extra_confs[i], extra_args=extra_args[i], use_cli=self.options.usecli))
+ self.nodes.append(TestNode(i, get_datadir_path(self.options.tmpdir, i), rpchost=rpchost, timewait=timewait, bitcoind=binary[i], bitcoin_cli=self.options.bitcoincli, mocktime=self.mocktime, coverage_dir=self.options.coveragedir, extra_conf=extra_confs[i], extra_args=extra_args[i], use_cli=self.options.usecli))
def start_node(self, i, *args, **kwargs):
"""Start a bitcoind"""
@@ -284,9 +289,9 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
for node in self.nodes:
coverage.write_all_rpc_commands(self.options.coveragedir, node.rpc)
- def stop_node(self, i):
+ def stop_node(self, i, expected_stderr=''):
"""Stop a bitcoind test node"""
- self.nodes[i].stop_node()
+ self.nodes[i].stop_node(expected_stderr)
self.nodes[i].wait_until_stopped()
def stop_nodes(self):
@@ -399,10 +404,10 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
# Create cache directories, run bitcoinds:
for i in range(MAX_NODES):
datadir = initialize_datadir(self.options.cachedir, i)
- args = [os.getenv("BITCOIND", "bitcoind"), "-datadir=" + datadir]
+ args = [self.options.bitcoind, "-datadir=" + datadir]
if i > 0:
args.append("-connect=127.0.0.1:" + str(p2p_port(0)))
- self.nodes.append(TestNode(i, get_datadir_path(self.options.cachedir, i), extra_conf=["bind=127.0.0.1"], extra_args=[],rpchost=None, timewait=None, binary=None, stderr=None, mocktime=self.mocktime, coverage_dir=None))
+ self.nodes.append(TestNode(i, get_datadir_path(self.options.cachedir, i), extra_conf=["bind=127.0.0.1"], extra_args=[], rpchost=None, timewait=None, bitcoind=self.options.bitcoind, bitcoin_cli=self.options.bitcoincli, mocktime=self.mocktime, coverage_dir=None))
self.nodes[i].args = args
self.start_node(i)
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index 0f0d031f35..0353fc0712 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -19,7 +19,6 @@ import time
from .authproxy import JSONRPCException
from .util import (
append_config,
- assert_equal,
delete_cookie_file,
get_rpc_proxy,
rpc_url,
@@ -57,20 +56,18 @@ class TestNode():
To make things easier for the test writer, any unrecognised messages will
be dispatched to the RPC connection."""
- def __init__(self, i, datadir, rpchost, timewait, binary, stderr, mocktime, coverage_dir, extra_conf=None, extra_args=None, use_cli=False):
+ def __init__(self, i, datadir, rpchost, timewait, bitcoind, bitcoin_cli, mocktime, coverage_dir, extra_conf=None, extra_args=None, use_cli=False):
self.index = i
self.datadir = datadir
+ self.stdout_dir = os.path.join(self.datadir, "stdout")
+ self.stderr_dir = os.path.join(self.datadir, "stderr")
self.rpchost = rpchost
if timewait:
self.rpc_timeout = timewait
else:
# Wait for up to 60 seconds for the RPC server to respond
self.rpc_timeout = 60
- if binary is None:
- self.binary = os.getenv("BITCOIND", "bitcoind")
- else:
- self.binary = binary
- self.stderr = stderr
+ self.binary = bitcoind
self.coverage_dir = coverage_dir
if extra_conf != None:
append_config(datadir, extra_conf)
@@ -78,9 +75,18 @@ class TestNode():
# For those callers that need more flexibility, they can just set the args property directly.
# Note that common args are set in the config file (see initialize_datadir)
self.extra_args = extra_args
- self.args = [self.binary, "-datadir=" + self.datadir, "-logtimemicros", "-debug", "-debugexclude=libevent", "-debugexclude=leveldb", "-mocktime=" + str(mocktime), "-uacomment=testnode%d" % i]
-
- self.cli = TestNodeCLI(os.getenv("BITCOINCLI", "bitcoin-cli"), self.datadir)
+ self.args = [
+ self.binary,
+ "-datadir=" + self.datadir,
+ "-logtimemicros",
+ "-debug",
+ "-debugexclude=libevent",
+ "-debugexclude=leveldb",
+ "-mocktime=" + str(mocktime),
+ "-uacomment=testnode%d" % i
+ ]
+
+ self.cli = TestNodeCLI(bitcoin_cli, self.datadir)
self.use_cli = use_cli
self.running = False
@@ -93,6 +99,14 @@ class TestNode():
self.p2ps = []
+ def _node_msg(self, msg: str) -> str:
+ """Return a modified msg that identifies this node by its index as a debugging aid."""
+ return "[node %d] %s" % (self.index, msg)
+
+ def _raise_assertion_error(self, msg: str):
+ """Raise an AssertionError with msg modified to identify this node."""
+ raise AssertionError(self._node_msg(msg))
+
def __del__(self):
# Ensure that we don't leave any bitcoind processes lying around after
# the test ends
@@ -100,7 +114,7 @@ class TestNode():
# Should only happen on test failure
# Avoid using logger, as that may have already been shutdown when
# this destructor is called.
- print("Cleaning up leftover process")
+ print(self._node_msg("Cleaning up leftover process"))
self.process.kill()
def __getattr__(self, name):
@@ -108,20 +122,32 @@ class TestNode():
if self.use_cli:
return getattr(self.cli, name)
else:
- assert self.rpc_connected and self.rpc is not None, "Error: no RPC connection"
+ assert self.rpc_connected and self.rpc is not None, self._node_msg("Error: no RPC connection")
return getattr(self.rpc, name)
- def start(self, extra_args=None, stderr=None, *args, **kwargs):
+ def start(self, extra_args=None, stdout=None, stderr=None, *args, **kwargs):
"""Start the node."""
if extra_args is None:
extra_args = self.extra_args
+
+ # Add a new stdout and stderr file each time bitcoind is started
if stderr is None:
- stderr = self.stderr
+ stderr = tempfile.NamedTemporaryFile(dir=self.stderr_dir, delete=False)
+ if stdout is None:
+ stdout = tempfile.NamedTemporaryFile(dir=self.stdout_dir, delete=False)
+ self.stderr = stderr
+ self.stdout = stdout
+
# Delete any existing cookie file -- if such a file exists (eg due to
# unclean shutdown), it will get overwritten anyway by bitcoind, and
# potentially interfere with our attempt to authenticate
delete_cookie_file(self.datadir)
- self.process = subprocess.Popen(self.args + extra_args, stderr=stderr, *args, **kwargs)
+
+ # add environment variable LIBC_FATAL_STDERR_=1 so that libc errors are written to stderr and not the terminal
+ subp_env = dict(os.environ, LIBC_FATAL_STDERR_="1")
+
+ self.process = subprocess.Popen(self.args + extra_args, env=subp_env, stdout=stdout, stderr=stderr, *args, **kwargs)
+
self.running = True
self.log.debug("bitcoind started, waiting for RPC to come up")
@@ -131,7 +157,8 @@ class TestNode():
poll_per_s = 4
for _ in range(poll_per_s * self.rpc_timeout):
if self.process.poll() is not None:
- raise FailedToStartError('bitcoind exited with status {} during initialization'.format(self.process.returncode))
+ raise FailedToStartError(self._node_msg(
+ '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()
@@ -150,18 +177,17 @@ class TestNode():
if "No RPC credentials" not in str(e):
raise
time.sleep(1.0 / poll_per_s)
- raise AssertionError("Unable to connect to bitcoind")
+ self._raise_assertion_error("Unable to connect to bitcoind")
def get_wallet_rpc(self, wallet_name):
if self.use_cli:
return self.cli("-rpcwallet={}".format(wallet_name))
else:
- assert self.rpc_connected
- assert self.rpc
+ assert self.rpc_connected and self.rpc, self._node_msg("RPC not connected")
wallet_path = "wallet/%s" % wallet_name
return self.rpc / wallet_path
- def stop_node(self):
+ def stop_node(self, expected_stderr=''):
"""Stop the node."""
if not self.running:
return
@@ -170,6 +196,13 @@ class TestNode():
self.stop()
except http.client.CannotSendRequest:
self.log.exception("Unable to stop node.")
+
+ # Check that stderr is as expected
+ self.stderr.seek(0)
+ stderr = self.stderr.read().decode('utf-8').strip()
+ if stderr != expected_stderr:
+ raise AssertionError("Unexpected stderr {} != {}".format(stderr, expected_stderr))
+
del self.p2ps[:]
def is_node_stopped(self):
@@ -184,7 +217,8 @@ class TestNode():
return False
# process has stopped. Assert that it didn't return an error code.
- assert_equal(return_code, 0)
+ assert return_code == 0, self._node_msg(
+ "Node returned non-zero exit code (%d) when stopping" % return_code)
self.running = False
self.process = None
self.rpc_connected = False
@@ -203,9 +237,10 @@ class TestNode():
Will throw if bitcoind starts without an error.
Will throw if an expected_msg is provided and it does not match bitcoind's stdout."""
- with tempfile.SpooledTemporaryFile(max_size=2**16) as log_stderr:
+ with tempfile.NamedTemporaryFile(dir=self.stderr_dir, delete=False) as log_stderr, \
+ tempfile.NamedTemporaryFile(dir=self.stdout_dir, delete=False) as log_stdout:
try:
- self.start(extra_args, stderr=log_stderr, *args, **kwargs)
+ self.start(extra_args, stdout=log_stdout, stderr=log_stderr, *args, **kwargs)
self.wait_for_rpc_connection()
self.stop_node()
self.wait_until_stopped()
@@ -219,19 +254,22 @@ class TestNode():
stderr = log_stderr.read().decode('utf-8').strip()
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))
+ self._raise_assertion_error(
+ 'Expected message "{}" does not partially match stderr:\n"{}"'.format(expected_msg, stderr))
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))
+ self._raise_assertion_error(
+ '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))
+ self._raise_assertion_error(
+ '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"
else:
assert_msg = "bitcoind should have exited with expected error " + expected_msg
- raise AssertionError(assert_msg)
+ self._raise_assertion_error(assert_msg)
def node_encrypt_wallet(self, passphrase):
""""Encrypts the wallet.
@@ -262,7 +300,7 @@ class TestNode():
Convenience property - most tests only use a single p2p connection to each
node, so this saves having to write node.p2ps[0] many times."""
- assert self.p2ps, "No p2p connection"
+ assert self.p2ps, self._node_msg("No p2p connection")
return self.p2ps[0]
def disconnect_p2ps(self):
diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py
index 4ec3175cd6..e016148f70 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -301,6 +301,9 @@ def initialize_datadir(dirname, n):
f.write("keypool=1\n")
f.write("discover=0\n")
f.write("listenonion=0\n")
+ f.write("printtoconsole=0\n")
+ os.makedirs(os.path.join(datadir, 'stderr'), exist_ok=True)
+ os.makedirs(os.path.join(datadir, 'stdout'), exist_ok=True)
return datadir
def get_datadir_path(dirname, n):
@@ -349,7 +352,14 @@ def set_node_times(nodes, t):
def disconnect_nodes(from_connection, node_num):
for peer_id in [peer['id'] for peer in from_connection.getpeerinfo() if "testnode%d" % node_num in peer['subver']]:
- from_connection.disconnectnode(nodeid=peer_id)
+ try:
+ from_connection.disconnectnode(nodeid=peer_id)
+ except JSONRPCException as e:
+ # If this node is disconnected between calculating the peer id
+ # and issuing the disconnect, don't worry about it.
+ # This avoids a race condition if we're mass-disconnecting peers.
+ if e.error['code'] != -29: # RPC_CLIENT_NODE_NOT_CONNECTED
+ raise
# wait to disconnect
wait_until(lambda: [peer['id'] for peer in from_connection.getpeerinfo() if "testnode%d" % node_num in peer['subver']] == [], timeout=5)
@@ -556,3 +566,14 @@ def mine_large_block(node, utxos=None):
fee = 100 * node.getnetworkinfo()["relayfee"]
create_lots_of_big_transactions(node, txouts, utxos, num, fee=fee)
node.generate(1)
+
+def find_vout_for_address(node, txid, addr):
+ """
+ Locate the vout index of the given transaction sending to the
+ given address. Raises runtime error exception if not found.
+ """
+ tx = node.getrawtransaction(txid, True)
+ for i in range(len(tx["vout"])):
+ if any([addr == a for a in tx["vout"][i]["scriptPubKey"]["addresses"]]):
+ return i
+ raise RuntimeError("Vout not found for address: txid=%s, addr=%s" % (txid, addr))
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 518c16b5f1..dfa8c33728 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -70,7 +70,7 @@ BASE_SCRIPTS = [
'wallet_labels.py',
'p2p_segwit.py',
'wallet_dump.py',
- 'rpc_listtransactions.py',
+ 'wallet_listtransactions.py',
# vv Tests less than 60s vv
'p2p_sendheaders.py',
'wallet_zapwallettxes.py',
@@ -120,6 +120,9 @@ BASE_SCRIPTS = [
'feature_nulldummy.py',
'mempool_accept.py',
'wallet_import_rescan.py',
+ 'rpc_bind.py --ipv4',
+ 'rpc_bind.py --ipv6',
+ 'rpc_bind.py --nonloopback',
'mining_basic.py',
'wallet_bumpfee.py',
'rpc_named_arguments.py',
@@ -135,6 +138,7 @@ BASE_SCRIPTS = [
'p2p_fingerprint.py',
'feature_uacomment.py',
'p2p_unrequested_blocks.py',
+ 'feature_includeconf.py',
'feature_logging.py',
'p2p_node_network_limited.py',
'feature_blocksdir.py',
@@ -160,7 +164,6 @@ EXTENDED_SCRIPTS = [
'p2p_timeouts.py',
# vv Tests less than 60s vv
'p2p_feefilter.py',
- 'rpc_bind.py',
# vv Tests less than 30s vv
'feature_assumevalid.py',
'example_test.py',
@@ -199,6 +202,7 @@ def main():
parser.add_argument('--keepcache', '-k', action='store_true', help='the default behavior is to flush the cache directory on startup. --keepcache retains the cache from the previous testrun.')
parser.add_argument('--quiet', '-q', action='store_true', help='only print results summary and failure logs')
parser.add_argument('--tmpdirprefix', '-t', default=tempfile.gettempdir(), help="Root directory for datadirs")
+ parser.add_argument('--failfast', action='store_true', help='stop execution after the first test failure')
args, unknown_args = parser.parse_known_args()
# args to be passed on always start with two dashes; tests are the remaining unknown args
@@ -281,9 +285,21 @@ def main():
if not args.keepcache:
shutil.rmtree("%s/test/cache" % config["environment"]["BUILDDIR"], ignore_errors=True)
- run_tests(test_list, config["environment"]["SRCDIR"], config["environment"]["BUILDDIR"], config["environment"]["EXEEXT"], tmpdir, args.jobs, args.coverage, passon_args, args.combinedlogslen)
+ run_tests(
+ test_list,
+ config["environment"]["SRCDIR"],
+ config["environment"]["BUILDDIR"],
+ tmpdir,
+ jobs=args.jobs,
+ enable_coverage=args.coverage,
+ args=passon_args,
+ combined_logs_len=args.combinedlogslen,
+ failfast=args.failfast,
+ )
+
+def run_tests(test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage=False, args=None, combined_logs_len=0, failfast=False):
+ args = args or []
-def run_tests(test_list, src_dir, build_dir, exeext, tmpdir, jobs=1, enable_coverage=False, args=[], combined_logs_len=0):
# Warn if bitcoind is already running (unix only)
try:
if subprocess.check_output(["pidof", "bitcoind"]) is not None:
@@ -296,15 +312,9 @@ def run_tests(test_list, src_dir, build_dir, exeext, tmpdir, jobs=1, enable_cove
if os.path.isdir(cache_dir):
print("%sWARNING!%s There is a cache directory here: %s. If tests fail unexpectedly, try deleting the cache directory." % (BOLD[1], BOLD[0], cache_dir))
- #Set env vars
- if "BITCOIND" not in os.environ:
- os.environ["BITCOIND"] = build_dir + '/src/bitcoind' + exeext
- os.environ["BITCOINCLI"] = build_dir + '/src/bitcoin-cli' + exeext
-
tests_dir = src_dir + '/test/functional/'
- flags = ["--srcdir={}/src".format(build_dir)] + args
- flags.append("--cachedir=%s" % cache_dir)
+ flags = ['--cachedir={}'.format(cache_dir)] + args
if enable_coverage:
coverage = RPCCoverage()
@@ -349,6 +359,10 @@ def run_tests(test_list, src_dir, build_dir, exeext, tmpdir, jobs=1, enable_cove
combined_logs, _ = subprocess.Popen([sys.executable, os.path.join(tests_dir, 'combine_logs.py'), '-c', testdir], universal_newlines=True, stdout=subprocess.PIPE).communicate()
print("\n".join(deque(combined_logs.splitlines(), combined_logs_len)))
+ if failfast:
+ logging.debug("Early exiting after test failure")
+ break
+
print_results(test_results, max_len_name, (int(time.time() - start_time)))
if coverage:
@@ -363,6 +377,10 @@ def run_tests(test_list, src_dir, build_dir, exeext, tmpdir, jobs=1, enable_cove
all_passed = all(map(lambda test_result: test_result.was_successful, test_results))
+ # This will be a no-op unless failfast is True in which case there may be dangling
+ # processes which need to be killed.
+ job_queue.kill_and_join()
+
sys.exit(not all_passed)
def print_results(test_results, max_len_name, runtime):
@@ -453,6 +471,17 @@ class TestHandler:
return TestResult(name, status, int(time.time() - start_time)), testdir, stdout, stderr
print('.', end='', flush=True)
+ def kill_and_join(self):
+ """Send SIGKILL to all jobs and block until all have ended."""
+ procs = [i[2] for i in self.jobs]
+
+ for proc in procs:
+ proc.kill()
+
+ for proc in procs:
+ proc.wait()
+
+
class TestResult():
def __init__(self, name, status, time):
self.name = name
diff --git a/test/functional/wallet_address_types.py b/test/functional/wallet_address_types.py
index 5d2428e6ef..7658d78383 100755
--- a/test/functional/wallet_address_types.py
+++ b/test/functional/wallet_address_types.py
@@ -280,7 +280,10 @@ class AddressTypeTest(BitcoinTestFramework):
self.log.info('getrawchangeaddress defaults to addresstype if -changetype is not set and argument is absent')
self.test_address(3, self.nodes[3].getrawchangeaddress(), multisig=False, typ='bech32')
- self.log.info('getrawchangeaddress fails with invalid changetype argument')
+ self.log.info('test invalid address type arguments')
+ assert_raises_rpc_error(-5, "Unknown address type ''", self.nodes[3].addmultisigaddress, 2, [compressed_1, compressed_2], None, '')
+ assert_raises_rpc_error(-5, "Unknown address type ''", self.nodes[3].getnewaddress, None, '')
+ assert_raises_rpc_error(-5, "Unknown address type ''", self.nodes[3].getrawchangeaddress, '')
assert_raises_rpc_error(-5, "Unknown address type 'bech23'", self.nodes[3].getrawchangeaddress, 'bech23')
self.log.info("Nodes with changetype=p2sh-segwit never use a P2WPKH change output")
diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py
index 0436aca6a4..9c58b84819 100755
--- a/test/functional/wallet_basic.py
+++ b/test/functional/wallet_basic.py
@@ -3,8 +3,20 @@
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test the wallet."""
+from decimal import Decimal
+import time
+
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import *
+from test_framework.util import (
+ assert_array_result,
+ assert_equal,
+ assert_fee_amount,
+ assert_raises_rpc_error,
+ connect_nodes_bi,
+ sync_blocks,
+ sync_mempools,
+ wait_until,
+)
class WalletTest(BitcoinTestFramework):
def set_test_params(self):
@@ -16,9 +28,9 @@ class WalletTest(BitcoinTestFramework):
self.start_node(0)
self.start_node(1)
self.start_node(2)
- connect_nodes_bi(self.nodes,0,1)
- connect_nodes_bi(self.nodes,1,2)
- connect_nodes_bi(self.nodes,0,2)
+ connect_nodes_bi(self.nodes, 0, 1)
+ connect_nodes_bi(self.nodes, 1, 2)
+ connect_nodes_bi(self.nodes, 0, 2)
self.sync_all([self.nodes[0:3]])
def check_fee_amount(self, curr_balance, balance_with_fee, fee_per_byte, tx_size):
@@ -111,11 +123,11 @@ class WalletTest(BitcoinTestFramework):
self.nodes[2].lockunspent(True, [unspent_0])
assert_equal(len(self.nodes[2].listlockunspent()), 0)
assert_raises_rpc_error(-8, "Invalid parameter, unknown transaction",
- self.nodes[2].lockunspent, False,
- [{"txid": "0000000000000000000000000000000000", "vout": 0}])
+ self.nodes[2].lockunspent, False,
+ [{"txid": "0000000000000000000000000000000000", "vout": 0}])
assert_raises_rpc_error(-8, "Invalid parameter, vout index out of bounds",
- self.nodes[2].lockunspent, False,
- [{"txid": unspent_0["txid"], "vout": 999}])
+ self.nodes[2].lockunspent, False,
+ [{"txid": unspent_0["txid"], "vout": 999}])
# Have node1 generate 100 blocks (so node0 can recover the fee)
self.nodes[1].generate(100)
@@ -123,7 +135,7 @@ class WalletTest(BitcoinTestFramework):
# node0 should end up with 100 btc in block rewards plus fees, but
# minus the 21 plus fees sent to node2
- assert_equal(self.nodes[0].getbalance(), 100-21)
+ assert_equal(self.nodes[0].getbalance(), 100 - 21)
assert_equal(self.nodes[2].getbalance(), 21)
# Node0 should have two unspent outputs.
@@ -137,8 +149,8 @@ class WalletTest(BitcoinTestFramework):
for utxo in node0utxos:
inputs = []
outputs = {}
- inputs.append({ "txid" : utxo["txid"], "vout" : utxo["vout"]})
- outputs[self.nodes[2].getnewaddress("from1")] = utxo["amount"] - 3
+ inputs.append({"txid": utxo["txid"], "vout": utxo["vout"]})
+ outputs[self.nodes[2].getnewaddress()] = utxo["amount"] - 3
raw_tx = self.nodes[0].createrawtransaction(inputs, outputs)
txns_to_send.append(self.nodes[0].signrawtransactionwithwallet(raw_tx))
@@ -152,7 +164,6 @@ class WalletTest(BitcoinTestFramework):
assert_equal(self.nodes[0].getbalance(), 0)
assert_equal(self.nodes[2].getbalance(), 94)
- assert_equal(self.nodes[2].getbalance("from1"), 94-21)
# Verify that a spent output cannot be locked anymore
spent_0 = {"txid": node0utxos[0]["txid"], "vout": node0utxos[0]["vout"]}
@@ -177,7 +188,7 @@ class WalletTest(BitcoinTestFramework):
node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(), Decimal('20'), fee_per_byte, self.get_vsize(self.nodes[2].getrawtransaction(txid)))
# Sendmany 10 BTC
- txid = self.nodes[2].sendmany('from1', {address: 10}, 0, "", [])
+ txid = self.nodes[2].sendmany('', {address: 10}, 0, "", [])
self.nodes[2].generate(1)
self.sync_all([self.nodes[0:3]])
node_0_bal += Decimal('10')
@@ -185,7 +196,7 @@ class WalletTest(BitcoinTestFramework):
assert_equal(self.nodes[0].getbalance(), node_0_bal)
# Sendmany 10 BTC with subtract fee from amount
- txid = self.nodes[2].sendmany('from1', {address: 10}, 0, "", [address])
+ txid = self.nodes[2].sendmany('', {address: 10}, 0, "", [address])
self.nodes[2].generate(1)
self.sync_all([self.nodes[0:3]])
node_2_bal -= Decimal('10')
@@ -214,91 +225,90 @@ class WalletTest(BitcoinTestFramework):
assert_equal(self.nodes[0].getwalletinfo()["unconfirmed_balance"], 1)
assert_equal(self.nodes[0].getunconfirmedbalance(), 1)
- #check if we can list zero value tx as available coins
- #1. create rawtx
- #2. hex-changed one output to 0.0
- #3. sign and send
- #4. check if recipient (node0) can list the zero value tx
+ # check if we can list zero value tx as available coins
+ # 1. create raw_tx
+ # 2. hex-changed one output to 0.0
+ # 3. sign and send
+ # 4. check if recipient (node0) can list the zero value tx
usp = self.nodes[1].listunspent()
- inputs = [{"txid":usp[0]['txid'], "vout":usp[0]['vout']}]
+ inputs = [{"txid": usp[0]['txid'], "vout": usp[0]['vout']}]
outputs = {self.nodes[1].getnewaddress(): 49.998, self.nodes[0].getnewaddress(): 11.11}
- rawTx = self.nodes[1].createrawtransaction(inputs, outputs).replace("c0833842", "00000000") #replace 11.11 with 0.0 (int32)
- decRawTx = self.nodes[1].decoderawtransaction(rawTx)
- signedRawTx = self.nodes[1].signrawtransactionwithwallet(rawTx)
- decRawTx = self.nodes[1].decoderawtransaction(signedRawTx['hex'])
- zeroValueTxid= decRawTx['txid']
- self.nodes[1].sendrawtransaction(signedRawTx['hex'])
+ raw_tx = self.nodes[1].createrawtransaction(inputs, outputs).replace("c0833842", "00000000") # replace 11.11 with 0.0 (int32)
+ signed_raw_tx = self.nodes[1].signrawtransactionwithwallet(raw_tx)
+ decoded_raw_tx = self.nodes[1].decoderawtransaction(signed_raw_tx['hex'])
+ zero_value_txid = decoded_raw_tx['txid']
+ self.nodes[1].sendrawtransaction(signed_raw_tx['hex'])
self.sync_all()
- self.nodes[1].generate(1) #mine a block
+ self.nodes[1].generate(1) # mine a block
self.sync_all()
- unspentTxs = self.nodes[0].listunspent() #zero value tx must be in listunspents output
+ unspent_txs = self.nodes[0].listunspent() # zero value tx must be in listunspents output
found = False
- for uTx in unspentTxs:
- if uTx['txid'] == zeroValueTxid:
+ for uTx in unspent_txs:
+ if uTx['txid'] == zero_value_txid:
found = True
assert_equal(uTx['amount'], Decimal('0'))
assert(found)
- #do some -walletbroadcast tests
+ # do some -walletbroadcast tests
self.stop_nodes()
self.start_node(0, ["-walletbroadcast=0"])
self.start_node(1, ["-walletbroadcast=0"])
self.start_node(2, ["-walletbroadcast=0"])
- connect_nodes_bi(self.nodes,0,1)
- connect_nodes_bi(self.nodes,1,2)
- connect_nodes_bi(self.nodes,0,2)
+ connect_nodes_bi(self.nodes, 0, 1)
+ connect_nodes_bi(self.nodes, 1, 2)
+ connect_nodes_bi(self.nodes, 0, 2)
self.sync_all([self.nodes[0:3]])
- txIdNotBroadcasted = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 2)
- txObjNotBroadcasted = self.nodes[0].gettransaction(txIdNotBroadcasted)
- self.nodes[1].generate(1) #mine a block, tx should not be in there
+ txid_not_broadcast = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 2)
+ tx_obj_not_broadcast = self.nodes[0].gettransaction(txid_not_broadcast)
+ self.nodes[1].generate(1) # mine a block, tx should not be in there
self.sync_all([self.nodes[0:3]])
- assert_equal(self.nodes[2].getbalance(), node_2_bal) #should not be changed because tx was not broadcasted
+ assert_equal(self.nodes[2].getbalance(), node_2_bal) # should not be changed because tx was not broadcasted
- #now broadcast from another node, mine a block, sync, and check the balance
- self.nodes[1].sendrawtransaction(txObjNotBroadcasted['hex'])
+ # now broadcast from another node, mine a block, sync, and check the balance
+ self.nodes[1].sendrawtransaction(tx_obj_not_broadcast['hex'])
self.nodes[1].generate(1)
self.sync_all([self.nodes[0:3]])
node_2_bal += 2
- txObjNotBroadcasted = self.nodes[0].gettransaction(txIdNotBroadcasted)
+ tx_obj_not_broadcast = self.nodes[0].gettransaction(txid_not_broadcast)
assert_equal(self.nodes[2].getbalance(), node_2_bal)
- #create another tx
- txIdNotBroadcasted = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 2)
+ # create another tx
+ self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 2)
- #restart the nodes with -walletbroadcast=1
+ # restart the nodes with -walletbroadcast=1
self.stop_nodes()
self.start_node(0)
self.start_node(1)
self.start_node(2)
- connect_nodes_bi(self.nodes,0,1)
- connect_nodes_bi(self.nodes,1,2)
- connect_nodes_bi(self.nodes,0,2)
+ connect_nodes_bi(self.nodes, 0, 1)
+ connect_nodes_bi(self.nodes, 1, 2)
+ connect_nodes_bi(self.nodes, 0, 2)
sync_blocks(self.nodes[0:3])
self.nodes[0].generate(1)
sync_blocks(self.nodes[0:3])
node_2_bal += 2
- #tx should be added to balance because after restarting the nodes tx should be broadcast
+ # tx should be added to balance because after restarting the nodes tx should be broadcast
assert_equal(self.nodes[2].getbalance(), node_2_bal)
- #send a tx with value in a string (PR#6380 +)
- txId = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "2")
- txObj = self.nodes[0].gettransaction(txId)
- assert_equal(txObj['amount'], Decimal('-2'))
+ # send a tx with value in a string (PR#6380 +)
+ txid = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "2")
+ tx_obj = self.nodes[0].gettransaction(txid)
+ assert_equal(tx_obj['amount'], Decimal('-2'))
- txId = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "0.0001")
- txObj = self.nodes[0].gettransaction(txId)
- assert_equal(txObj['amount'], Decimal('-0.0001'))
+ txid = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "0.0001")
+ tx_obj = self.nodes[0].gettransaction(txid)
+ assert_equal(tx_obj['amount'], Decimal('-0.0001'))
- #check if JSON parser can handle scientific notation in strings
- txId = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "1e-4")
- txObj = self.nodes[0].gettransaction(txId)
- assert_equal(txObj['amount'], Decimal('-0.0001'))
+ # check if JSON parser can handle scientific notation in strings
+ txid = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "1e-4")
+ tx_obj = self.nodes[0].gettransaction(txid)
+ assert_equal(tx_obj['amount'], Decimal('-0.0001'))
# This will raise an exception because the amount type is wrong
assert_raises_rpc_error(-3, "Invalid amount", self.nodes[0].sendtoaddress, self.nodes[2].getnewaddress(), "1f-4")
@@ -321,8 +331,8 @@ class WalletTest(BitcoinTestFramework):
# 4. Check that the unspents after import are not spendable
assert_array_result(self.nodes[1].listunspent(),
- {"address": address_to_import},
- {"spendable": False})
+ {"address": address_to_import},
+ {"spendable": False})
# 5. Import private key of the previously imported address on node1
priv_key = self.nodes[2].dumpprivkey(address_to_import)
@@ -330,17 +340,17 @@ class WalletTest(BitcoinTestFramework):
# 6. Check that the unspents are now spendable on node1
assert_array_result(self.nodes[1].listunspent(),
- {"address": address_to_import},
- {"spendable": True})
+ {"address": address_to_import},
+ {"spendable": True})
# Mine a block from node0 to an address from node1
- cbAddr = self.nodes[1].getnewaddress()
- blkHash = self.nodes[0].generatetoaddress(1, cbAddr)[0]
- cbTxId = self.nodes[0].getblock(blkHash)['tx'][0]
+ coinbase_addr = self.nodes[1].getnewaddress()
+ block_hash = self.nodes[0].generatetoaddress(1, coinbase_addr)[0]
+ coinbase_txid = self.nodes[0].getblock(block_hash)['tx'][0]
self.sync_all([self.nodes[0:3]])
# Check that the txid and balance is found by node1
- self.nodes[1].gettransaction(cbTxId)
+ self.nodes[1].gettransaction(coinbase_txid)
# check if wallet or blockchain maintenance changes the balance
self.sync_all([self.nodes[0:3]])
@@ -353,14 +363,14 @@ class WalletTest(BitcoinTestFramework):
# - True: unicode escaped as \u....
# - False: unicode directly as UTF-8
for mode in [True, False]:
- self.nodes[0].ensure_ascii = mode
+ self.nodes[0].rpc.ensure_ascii = mode
# unicode check: Basic Multilingual Plane, Supplementary Plane respectively
- for s in [u'рыба', u'𝅘𝅥𝅯']:
- addr = self.nodes[0].getaccountaddress(s)
- label = self.nodes[0].getaccount(addr)
- assert_equal(label, s)
- assert(s in self.nodes[0].listaccounts().keys())
- self.nodes[0].ensure_ascii = True # restore to default
+ for label in [u'рыба', u'𝅘𝅥𝅯']:
+ addr = self.nodes[0].getnewaddress()
+ self.nodes[0].setlabel(addr, label)
+ assert_equal(self.nodes[0].getaddressinfo(addr)['label'], label)
+ assert(label in self.nodes[0].listlabels())
+ self.nodes[0].rpc.ensure_ascii = True # restore to default
# maintenance tests
maintenance = [
@@ -376,9 +386,9 @@ class WalletTest(BitcoinTestFramework):
self.log.info("check " + m)
self.stop_nodes()
# set lower ancestor limit for later
- self.start_node(0, [m, "-limitancestorcount="+str(chainlimit)])
- self.start_node(1, [m, "-limitancestorcount="+str(chainlimit)])
- self.start_node(2, [m, "-limitancestorcount="+str(chainlimit)])
+ self.start_node(0, [m, "-limitancestorcount=" + str(chainlimit)])
+ self.start_node(1, [m, "-limitancestorcount=" + str(chainlimit)])
+ self.start_node(2, [m, "-limitancestorcount=" + str(chainlimit)])
if m == '-reindex':
# reindex will leave rpc warm up "early"; Wait for it to finish
wait_until(lambda: [block_count] * 3 == [self.nodes[i].getblockcount() for i in range(3)])
@@ -399,7 +409,7 @@ class WalletTest(BitcoinTestFramework):
self.nodes[0].generate(1)
node0_balance = self.nodes[0].getbalance()
# Split into two chains
- rawtx = self.nodes[0].createrawtransaction([{"txid":singletxid, "vout":0}], {chain_addrs[0]:node0_balance/2-Decimal('0.01'), chain_addrs[1]:node0_balance/2-Decimal('0.01')})
+ rawtx = self.nodes[0].createrawtransaction([{"txid": singletxid, "vout": 0}], {chain_addrs[0]: node0_balance / 2 - Decimal('0.01'), chain_addrs[1]: node0_balance / 2 - Decimal('0.01')})
signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx)
singletxid = self.nodes[0].sendrawtransaction(signedtx["hex"])
self.nodes[0].generate(1)
@@ -410,10 +420,10 @@ class WalletTest(BitcoinTestFramework):
# So we should be able to generate exactly chainlimit txs for each original output
sending_addr = self.nodes[1].getnewaddress()
txid_list = []
- for i in range(chainlimit*2):
+ for i in range(chainlimit * 2):
txid_list.append(self.nodes[0].sendtoaddress(sending_addr, Decimal('0.0001')))
- assert_equal(self.nodes[0].getmempoolinfo()['size'], chainlimit*2)
- assert_equal(len(txid_list), chainlimit*2)
+ assert_equal(self.nodes[0].getmempoolinfo()['size'], chainlimit * 2)
+ assert_equal(len(txid_list), chainlimit * 2)
# Without walletrejectlongchains, we will still generate a txid
# The tx will be stored in the wallet but not accepted to the mempool
@@ -421,26 +431,26 @@ class WalletTest(BitcoinTestFramework):
assert(extra_txid not in self.nodes[0].getrawmempool())
assert(extra_txid in [tx["txid"] for tx in self.nodes[0].listtransactions()])
self.nodes[0].abandontransaction(extra_txid)
- total_txs = len(self.nodes[0].listtransactions("*",99999))
+ total_txs = len(self.nodes[0].listtransactions("*", 99999))
# Try with walletrejectlongchains
# Double chain limit but require combining inputs, so we pass SelectCoinsMinConf
self.stop_node(0)
- self.start_node(0, extra_args=["-walletrejectlongchains", "-limitancestorcount="+str(2*chainlimit)])
+ self.start_node(0, extra_args=["-walletrejectlongchains", "-limitancestorcount=" + str(2 * chainlimit)])
# wait for loadmempool
timeout = 10
- while (timeout > 0 and len(self.nodes[0].getrawmempool()) < chainlimit*2):
+ while (timeout > 0 and len(self.nodes[0].getrawmempool()) < chainlimit * 2):
time.sleep(0.5)
timeout -= 0.5
- assert_equal(len(self.nodes[0].getrawmempool()), chainlimit*2)
+ assert_equal(len(self.nodes[0].getrawmempool()), chainlimit * 2)
node0_balance = self.nodes[0].getbalance()
# With walletrejectlongchains we will not create the tx and store it in our wallet.
assert_raises_rpc_error(-4, "Transaction has too long of a mempool chain", self.nodes[0].sendtoaddress, sending_addr, node0_balance - Decimal('0.01'))
# Verify nothing new in wallet
- assert_equal(total_txs, len(self.nodes[0].listtransactions("*",99999)))
+ assert_equal(total_txs, len(self.nodes[0].listtransactions("*", 99999)))
# Test getaddressinfo. Note that these addresses are taken from disablewallet.py
assert_raises_rpc_error(-5, "Invalid address", self.nodes[0].getaddressinfo, "3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy")
diff --git a/test/functional/wallet_dump.py b/test/functional/wallet_dump.py
index 997f67ec7e..01d27dabe7 100755
--- a/test/functional/wallet_dump.py
+++ b/test/functional/wallet_dump.py
@@ -136,7 +136,7 @@ class WalletDumpTest(BitcoinTestFramework):
assert_equal(found_addr, test_addr_count)
assert_equal(found_script_addr, 2)
assert_equal(found_addr_chg, 90*2 + 50) # old reserve keys are marked as change now
- assert_equal(found_addr_rsv, 90*2)
+ assert_equal(found_addr_rsv, 90*2)
assert_equal(witness_addr_ret, witness_addr)
# Overwriting should fail
diff --git a/test/functional/wallet_hd.py b/test/functional/wallet_hd.py
index 8c754807e6..97a945721f 100755
--- a/test/functional/wallet_hd.py
+++ b/test/functional/wallet_hd.py
@@ -11,6 +11,7 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
connect_nodes_bi,
+ assert_raises_rpc_error
)
@@ -120,5 +121,39 @@ class WalletHDTest(BitcoinTestFramework):
assert_equal(keypath[0:7], "m/0'/1'")
+ # Generate a new HD seed on node 1 and make sure it is set
+ orig_masterkeyid = self.nodes[1].getwalletinfo()['hdmasterkeyid']
+ self.nodes[1].sethdseed()
+ new_masterkeyid = self.nodes[1].getwalletinfo()['hdmasterkeyid']
+ assert orig_masterkeyid != new_masterkeyid
+ addr = self.nodes[1].getnewaddress()
+ assert_equal(self.nodes[1].getaddressinfo(addr)['hdkeypath'], 'm/0\'/0\'/0\'') # Make sure the new address is the first from the keypool
+ self.nodes[1].keypoolrefill(1) # Fill keypool with 1 key
+
+ # Set a new HD seed on node 1 without flushing the keypool
+ new_seed = self.nodes[0].dumpprivkey(self.nodes[0].getnewaddress())
+ orig_masterkeyid = new_masterkeyid
+ self.nodes[1].sethdseed(False, new_seed)
+ new_masterkeyid = self.nodes[1].getwalletinfo()['hdmasterkeyid']
+ assert orig_masterkeyid != new_masterkeyid
+ addr = self.nodes[1].getnewaddress()
+ assert_equal(orig_masterkeyid, self.nodes[1].getaddressinfo(addr)['hdmasterkeyid'])
+ assert_equal(self.nodes[1].getaddressinfo(addr)['hdkeypath'], 'm/0\'/0\'/1\'') # Make sure the new address continues previous keypool
+
+ # Check that the next address is from the new seed
+ self.nodes[1].keypoolrefill(1)
+ next_addr = self.nodes[1].getnewaddress()
+ assert_equal(new_masterkeyid, self.nodes[1].getaddressinfo(next_addr)['hdmasterkeyid'])
+ assert_equal(self.nodes[1].getaddressinfo(next_addr)['hdkeypath'], 'm/0\'/0\'/0\'') # Make sure the new address is not from previous keypool
+ assert next_addr != addr
+
+ # Sethdseed parameter validity
+ assert_raises_rpc_error(-1, 'sethdseed', self.nodes[0].sethdseed, False, new_seed, 0)
+ assert_raises_rpc_error(-5, "Invalid private key", self.nodes[1].sethdseed, False, "not_wif")
+ assert_raises_rpc_error(-1, "JSON value is not a boolean as expected", self.nodes[1].sethdseed, "Not_bool")
+ assert_raises_rpc_error(-1, "JSON value is not a string as expected", self.nodes[1].sethdseed, False, True)
+ assert_raises_rpc_error(-5, "Already have this key", self.nodes[1].sethdseed, False, new_seed)
+ assert_raises_rpc_error(-5, "Already have this key", self.nodes[1].sethdseed, False, self.nodes[1].dumpprivkey(self.nodes[1].getnewaddress()))
+
if __name__ == '__main__':
WalletHDTest().main ()
diff --git a/test/functional/wallet_import_rescan.py b/test/functional/wallet_import_rescan.py
index b66e9b5d91..4c0ffb938b 100755
--- a/test/functional/wallet_import_rescan.py
+++ b/test/functional/wallet_import_rescan.py
@@ -42,16 +42,15 @@ class Variant(collections.namedtuple("Variant", "call data rescan prune")):
def do_import(self, timestamp):
"""Call one key import RPC."""
+ rescan = self.rescan == Rescan.yes
if self.call == Call.single:
if self.data == Data.address:
- response = self.try_rpc(self.node.importaddress, self.address["address"], self.label,
- self.rescan == Rescan.yes)
+ response = self.try_rpc(self.node.importaddress, address=self.address["address"], rescan=rescan)
elif self.data == Data.pub:
- response = self.try_rpc(self.node.importpubkey, self.address["pubkey"], self.label,
- self.rescan == Rescan.yes)
+ response = self.try_rpc(self.node.importpubkey, pubkey=self.address["pubkey"], rescan=rescan)
elif self.data == Data.priv:
- response = self.try_rpc(self.node.importprivkey, self.key, self.label, self.rescan == Rescan.yes)
+ response = self.try_rpc(self.node.importprivkey, privkey=self.key, rescan=rescan)
assert_equal(response, None)
elif self.call == Call.multi:
@@ -62,30 +61,22 @@ class Variant(collections.namedtuple("Variant", "call data rescan prune")):
"timestamp": timestamp + TIMESTAMP_WINDOW + (1 if self.rescan == Rescan.late_timestamp else 0),
"pubkeys": [self.address["pubkey"]] if self.data == Data.pub else [],
"keys": [self.key] if self.data == Data.priv else [],
- "label": self.label,
"watchonly": self.data != Data.priv
}], {"rescan": self.rescan in (Rescan.yes, Rescan.late_timestamp)})
assert_equal(response, [{"success": True}])
def check(self, txid=None, amount=None, confirmations=None):
- """Verify that getbalance/listtransactions return expected values."""
+ """Verify that listreceivedbyaddress returns expected values."""
- balance = self.node.getbalance(self.label, 0, True)
- assert_equal(balance, self.expected_balance)
-
- txs = self.node.listtransactions(self.label, 10000, 0, True)
- assert_equal(len(txs), self.expected_txs)
+ addresses = self.node.listreceivedbyaddress(minconf=0, include_watchonly=True, address_filter=self.address['address'])
+ if self.expected_txs:
+ assert_equal(len(addresses[0]["txids"]), self.expected_txs)
if txid is not None:
- tx, = [tx for tx in txs if tx["txid"] == txid]
- assert_equal(tx["label"], self.label)
- assert_equal(tx["address"], self.address["address"])
- assert_equal(tx["amount"], amount)
- assert_equal(tx["category"], "receive")
- assert_equal(tx["label"], self.label)
- assert_equal(tx["txid"], txid)
- assert_equal(tx["confirmations"], confirmations)
- assert_equal("trusted" not in tx, True)
+ address, = [ad for ad in addresses if txid in ad["txids"]]
+ assert_equal(address["address"], self.address["address"])
+ assert_equal(address["amount"], self.expected_balance)
+ assert_equal(address["confirmations"], confirmations)
# Verify the transaction is correctly marked watchonly depending on
# whether the transaction pays to an imported public key or
# imported private key. The test setup ensures that transaction
@@ -93,9 +84,9 @@ class Variant(collections.namedtuple("Variant", "call data rescan prune")):
# involvesWatchonly will be true if either the transaction output
# or inputs are watchonly).
if self.data != Data.priv:
- assert_equal(tx["involvesWatchonly"], True)
+ assert_equal(address["involvesWatchonly"], True)
else:
- assert_equal("involvesWatchonly" not in tx, True)
+ assert_equal("involvesWatchonly" not in address, True)
# List of Variants for each way a key or address could be imported.
@@ -130,11 +121,10 @@ class ImportRescanTest(BitcoinTestFramework):
connect_nodes(self.nodes[i], 0)
def run_test(self):
- # Create one transaction on node 0 with a unique amount and label for
+ # Create one transaction on node 0 with a unique amount for
# each possible type of wallet import RPC.
for i, variant in enumerate(IMPORT_VARIANTS):
- variant.label = "label {} {}".format(i, variant)
- variant.address = self.nodes[1].getaddressinfo(self.nodes[1].getnewaddress(variant.label))
+ variant.address = self.nodes[1].getaddressinfo(self.nodes[1].getnewaddress())
variant.key = self.nodes[1].dumpprivkey(variant.address["address"])
variant.initial_amount = 10 - (i + 1) / 4.0
variant.initial_txid = self.nodes[0].sendtoaddress(variant.address["address"], variant.initial_amount)
diff --git a/test/functional/wallet_importprunedfunds.py b/test/functional/wallet_importprunedfunds.py
index 5f5bfcf683..9cee9aa49a 100755
--- a/test/functional/wallet_importprunedfunds.py
+++ b/test/functional/wallet_importprunedfunds.py
@@ -3,13 +3,19 @@
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test the importprunedfunds and removeprunedfunds RPCs."""
+from decimal import Decimal
+
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import *
+from test_framework.util import (
+ assert_equal,
+ assert_raises_rpc_error,
+)
class ImportPrunedFundsTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 2
+ self.extra_args = [['-deprecatedrpc=accounts']] * 2
def run_test(self):
self.log.info("Mining blocks...")
@@ -23,18 +29,18 @@ class ImportPrunedFundsTest(BitcoinTestFramework):
address2 = self.nodes[0].getnewaddress()
# privkey
address3 = self.nodes[0].getnewaddress()
- address3_privkey = self.nodes[0].dumpprivkey(address3) # Using privkey
+ address3_privkey = self.nodes[0].dumpprivkey(address3) # Using privkey
- #Check only one address
+ # Check only one address
address_info = self.nodes[0].getaddressinfo(address1)
assert_equal(address_info['ismine'], True)
self.sync_all()
- #Node 1 sync test
- assert_equal(self.nodes[1].getblockcount(),101)
+ # Node 1 sync test
+ assert_equal(self.nodes[1].getblockcount(), 101)
- #Address Test - before import
+ # Address Test - before import
address_info = self.nodes[1].getaddressinfo(address1)
assert_equal(address_info['iswatchonly'], False)
assert_equal(address_info['ismine'], False)
@@ -47,7 +53,7 @@ class ImportPrunedFundsTest(BitcoinTestFramework):
assert_equal(address_info['iswatchonly'], False)
assert_equal(address_info['ismine'], False)
- #Send funds to self
+ # Send funds to self
txnid1 = self.nodes[0].sendtoaddress(address1, 0.1)
self.nodes[0].generate(1)
rawtxn1 = self.nodes[0].gettransaction(txnid1)['hex']
@@ -65,19 +71,19 @@ class ImportPrunedFundsTest(BitcoinTestFramework):
self.sync_all()
- #Import with no affiliated address
+ # Import with no affiliated address
assert_raises_rpc_error(-5, "No addresses", self.nodes[1].importprunedfunds, rawtxn1, proof1)
balance1 = self.nodes[1].getbalance("", 0, True)
assert_equal(balance1, Decimal(0))
- #Import with affiliated address with no rescan
+ # Import with affiliated address with no rescan
self.nodes[1].importaddress(address2, "add2", False)
self.nodes[1].importprunedfunds(rawtxn2, proof2)
balance2 = self.nodes[1].getbalance("add2", 0, True)
assert_equal(balance2, Decimal('0.05'))
- #Import with private key with no rescan
+ # Import with private key with no rescan
self.nodes[1].importprivkey(privkey=address3_privkey, label="add3", rescan=False)
self.nodes[1].importprunedfunds(rawtxn3, proof3)
balance3 = self.nodes[1].getbalance("add3", 0, False)
@@ -85,7 +91,7 @@ class ImportPrunedFundsTest(BitcoinTestFramework):
balance3 = self.nodes[1].getbalance("*", 0, True)
assert_equal(balance3, Decimal('0.075'))
- #Addresses Test - after import
+ # Addresses Test - after import
address_info = self.nodes[1].getaddressinfo(address1)
assert_equal(address_info['iswatchonly'], False)
assert_equal(address_info['ismine'], False)
@@ -96,7 +102,7 @@ class ImportPrunedFundsTest(BitcoinTestFramework):
assert_equal(address_info['iswatchonly'], False)
assert_equal(address_info['ismine'], True)
- #Remove transactions
+ # Remove transactions
assert_raises_rpc_error(-8, "Transaction does not exist in wallet.", self.nodes[1].removeprunedfunds, txnid1)
balance1 = self.nodes[1].getbalance("*", 0, True)
diff --git a/test/functional/wallet_keypool_topup.py b/test/functional/wallet_keypool_topup.py
index 30a0c9a760..d657dc30c4 100755
--- a/test/functional/wallet_keypool_topup.py
+++ b/test/functional/wallet_keypool_topup.py
@@ -25,7 +25,7 @@ class KeypoolRestoreTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 2
- self.extra_args = [[], ['-keypool=100', '-keypoolmin=20']]
+ self.extra_args = [[], ['-keypool=100']]
def run_test(self):
wallet_path = os.path.join(self.nodes[1].datadir, "regtest", "wallets", "wallet.dat")
diff --git a/test/functional/wallet_labels.py b/test/functional/wallet_labels.py
index 90eefc0438..705dd8985e 100755
--- a/test/functional/wallet_labels.py
+++ b/test/functional/wallet_labels.py
@@ -6,25 +6,38 @@
RPCs tested are:
- getlabeladdress
- - getaddressesbyaccount
+ - getaddressesbyaccount/getaddressesbylabel
- listaddressgroupings
- setlabel
- sendfrom (with account arguments)
- move (with account arguments)
+
+Run the test twice - once using the accounts API and once using the labels API.
+The accounts API test can be removed in V0.18.
"""
from collections import defaultdict
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal
+from test_framework.util import assert_equal, assert_raises_rpc_error
class WalletLabelsTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
- self.num_nodes = 1
- self.extra_args = [[]]
+ self.num_nodes = 2
+ self.extra_args = [['-deprecatedrpc=accounts'], []]
+
+ def setup_network(self):
+ """Don't connect nodes."""
+ self.setup_nodes()
def run_test(self):
- node = self.nodes[0]
+ """Run the test twice - once using the accounts API and once using the labels API."""
+ self.log.info("Test accounts API")
+ self._run_subtest(True, self.nodes[0])
+ self.log.info("Test labels API")
+ self._run_subtest(False, self.nodes[1])
+
+ def _run_subtest(self, accounts_api, node):
# Check that there's no UTXO on any of the nodes
assert_equal(len(node.listunspent()), 0)
@@ -77,7 +90,7 @@ class WalletLabelsTest(BitcoinTestFramework):
# Create labels and make sure subsequent label API calls
# recognize the label/address associations.
- labels = [Label(name) for name in ("a", "b", "c", "d", "e")]
+ labels = [Label(name, accounts_api) for name in ("a", "b", "c", "d", "e")]
for label in labels:
label.add_receive_address(node.getlabeladdress(label=label.name, force=True))
label.verify(node)
@@ -101,21 +114,23 @@ class WalletLabelsTest(BitcoinTestFramework):
# Check that sendfrom label reduces listaccounts balances.
for i, label in enumerate(labels):
- to_label = labels[(i+1) % len(labels)]
+ to_label = labels[(i + 1) % len(labels)]
node.sendfrom(label.name, to_label.receive_address, amount_to_send)
node.generate(1)
for label in labels:
label.add_receive_address(node.getlabeladdress(label.name))
label.verify(node)
assert_equal(node.getreceivedbylabel(label.name), 2)
- node.move(label.name, "", node.getbalance(label.name))
+ if accounts_api:
+ node.move(label.name, "", node.getbalance(label.name))
label.verify(node)
node.generate(101)
expected_account_balances = {"": 5200}
for label in labels:
expected_account_balances[label.name] = 0
- assert_equal(node.listaccounts(), expected_account_balances)
- assert_equal(node.getbalance(""), 5200)
+ if accounts_api:
+ assert_equal(node.listaccounts(), expected_account_balances)
+ assert_equal(node.getbalance(""), 5200)
# Check that setlabel can assign a label to a new unused address.
for label in labels:
@@ -123,7 +138,10 @@ class WalletLabelsTest(BitcoinTestFramework):
node.setlabel(address, label.name)
label.add_address(address)
label.verify(node)
- assert(address not in node.getaddressesbyaccount(""))
+ if accounts_api:
+ assert(address not in node.getaddressesbyaccount(""))
+ else:
+ assert_raises_rpc_error(-11, "No addresses with label", node.getaddressesbylabel, "")
# Check that addmultisigaddress can assign labels.
for label in labels:
@@ -136,8 +154,9 @@ class WalletLabelsTest(BitcoinTestFramework):
label.verify(node)
node.sendfrom("", multisig_address, 50)
node.generate(101)
- for label in labels:
- assert_equal(node.getbalance(label.name), 50)
+ if accounts_api:
+ for label in labels:
+ assert_equal(node.getbalance(label.name), 50)
# Check that setlabel can change the label of an address from a
# different label.
@@ -156,9 +175,10 @@ class WalletLabelsTest(BitcoinTestFramework):
change_label(node, labels[2].receive_address, labels[2], labels[2])
class Label:
- def __init__(self, name):
+ def __init__(self, name, accounts_api):
# Label name
self.name = name
+ self.accounts_api = accounts_api
# Current receiving address associated with this label.
self.receive_address = None
# List of all addresses assigned with this label
@@ -184,13 +204,16 @@ class Label:
node.getaddressinfo(address)['labels'][0],
{"name": self.name,
"purpose": self.purpose[address]})
- assert_equal(node.getaccount(address), self.name)
+ if self.accounts_api:
+ assert_equal(node.getaccount(address), self.name)
+ else:
+ assert_equal(node.getaddressinfo(address)['label'], self.name)
assert_equal(
node.getaddressesbylabel(self.name),
{address: {"purpose": self.purpose[address]} for address in self.addresses})
- assert_equal(
- set(node.getaddressesbyaccount(self.name)), set(self.addresses))
+ if self.accounts_api:
+ assert_equal(set(node.getaddressesbyaccount(self.name)), set(self.addresses))
def change_label(node, address, old_label, new_label):
diff --git a/test/functional/wallet_listreceivedby.py b/test/functional/wallet_listreceivedby.py
index 7b34febddc..7447211296 100755
--- a/test/functional/wallet_listreceivedby.py
+++ b/test/functional/wallet_listreceivedby.py
@@ -50,37 +50,37 @@ class ReceivedByTest(BitcoinTestFramework):
{"address": empty_addr},
{"address": empty_addr, "label": "", "amount": 0, "confirmations": 0, "txids": []})
- #Test Address filtering
- #Only on addr
- expected = {"address":addr, "label":"", "amount":Decimal("0.1"), "confirmations":10, "txids":[txid,]}
+ # Test Address filtering
+ # Only on addr
+ expected = {"address": addr, "label": "", "amount": Decimal("0.1"), "confirmations": 10, "txids": [txid, ]}
res = self.nodes[1].listreceivedbyaddress(minconf=0, include_empty=True, include_watchonly=True, address_filter=addr)
- assert_array_result(res, {"address":addr}, expected)
+ assert_array_result(res, {"address": addr}, expected)
assert_equal(len(res), 1)
- #Error on invalid address
+ # Error on invalid address
assert_raises_rpc_error(-4, "address_filter parameter was invalid", self.nodes[1].listreceivedbyaddress, minconf=0, include_empty=True, include_watchonly=True, address_filter="bamboozling")
- #Another address receive money
+ # Another address receive money
res = self.nodes[1].listreceivedbyaddress(0, True, True)
- assert_equal(len(res), 2) #Right now 2 entries
+ assert_equal(len(res), 2) # Right now 2 entries
other_addr = self.nodes[1].getnewaddress()
txid2 = self.nodes[0].sendtoaddress(other_addr, 0.1)
self.nodes[0].generate(1)
self.sync_all()
- #Same test as above should still pass
- expected = {"address":addr, "label":"", "amount":Decimal("0.1"), "confirmations":11, "txids":[txid,]}
+ # Same test as above should still pass
+ expected = {"address": addr, "label": "", "amount": Decimal("0.1"), "confirmations": 11, "txids": [txid, ]}
res = self.nodes[1].listreceivedbyaddress(0, True, True, addr)
- assert_array_result(res, {"address":addr}, expected)
+ assert_array_result(res, {"address": addr}, expected)
assert_equal(len(res), 1)
- #Same test as above but with other_addr should still pass
- expected = {"address":other_addr, "label":"", "amount":Decimal("0.1"), "confirmations":1, "txids":[txid2,]}
+ # Same test as above but with other_addr should still pass
+ expected = {"address": other_addr, "label": "", "amount": Decimal("0.1"), "confirmations": 1, "txids": [txid2, ]}
res = self.nodes[1].listreceivedbyaddress(0, True, True, other_addr)
- assert_array_result(res, {"address":other_addr}, expected)
+ assert_array_result(res, {"address": other_addr}, expected)
assert_equal(len(res), 1)
- #Should be two entries though without filter
+ # Should be two entries though without filter
res = self.nodes[1].listreceivedbyaddress(0, True, True)
- assert_equal(len(res), 3) #Became 3 entries
+ assert_equal(len(res), 3) # Became 3 entries
- #Not on random addr
- other_addr = self.nodes[0].getnewaddress() # note on node[0]! just a random addr
+ # Not on random addr
+ other_addr = self.nodes[0].getnewaddress() # note on node[0]! just a random addr
res = self.nodes[1].listreceivedbyaddress(0, True, True, other_addr)
assert_equal(len(res), 0)
@@ -111,8 +111,9 @@ class ReceivedByTest(BitcoinTestFramework):
self.log.info("listreceivedbylabel + getreceivedbylabel Test")
# set pre-state
- addrArr = self.nodes[1].getnewaddress()
- label = self.nodes[1].getaccount(addrArr)
+ label = ''
+ address = self.nodes[1].getnewaddress()
+ assert_equal(self.nodes[1].getaddressinfo(address)['label'], label)
received_by_label_json = [r for r in self.nodes[1].listreceivedbylabel() if r["label"] == label][0]
balance_by_label = self.nodes[1].getreceivedbylabel(label)
@@ -140,7 +141,8 @@ class ReceivedByTest(BitcoinTestFramework):
assert_equal(balance, balance_by_label + Decimal("0.1"))
# Create a new label named "mynewlabel" that has a 0 balance
- self.nodes[1].getlabeladdress(label="mynewlabel", force=True)
+ address = self.nodes[1].getnewaddress()
+ self.nodes[1].setlabel(address, "mynewlabel")
received_by_label_json = [r for r in self.nodes[1].listreceivedbylabel(0, True) if r["label"] == "mynewlabel"][0]
# Test includeempty of listreceivedbylabel
diff --git a/test/functional/wallet_listsinceblock.py b/test/functional/wallet_listsinceblock.py
index 0f2434ff0d..63c179411c 100755
--- a/test/functional/wallet_listsinceblock.py
+++ b/test/functional/wallet_listsinceblock.py
@@ -149,26 +149,26 @@ class ListSinceBlockTest (BitcoinTestFramework):
# send from nodes[1] using utxo to nodes[0]
change = '%.8f' % (float(utxo['amount']) - 1.0003)
- recipientDict = {
+ recipient_dict = {
self.nodes[0].getnewaddress(): 1,
self.nodes[1].getnewaddress(): change,
}
- utxoDicts = [{
+ utxo_dicts = [{
'txid': utxo['txid'],
'vout': utxo['vout'],
}]
txid1 = self.nodes[1].sendrawtransaction(
self.nodes[1].signrawtransactionwithwallet(
- self.nodes[1].createrawtransaction(utxoDicts, recipientDict))['hex'])
+ self.nodes[1].createrawtransaction(utxo_dicts, recipient_dict))['hex'])
# send from nodes[2] using utxo to nodes[3]
- recipientDict2 = {
+ recipient_dict2 = {
self.nodes[3].getnewaddress(): 1,
self.nodes[2].getnewaddress(): change,
}
self.nodes[2].sendrawtransaction(
self.nodes[2].signrawtransactionwithwallet(
- self.nodes[2].createrawtransaction(utxoDicts, recipientDict2))['hex'])
+ self.nodes[2].createrawtransaction(utxo_dicts, recipient_dict2))['hex'])
# generate on both sides
lastblockhash = self.nodes[1].generate(3)[2]
@@ -224,16 +224,16 @@ class ListSinceBlockTest (BitcoinTestFramework):
utxos = self.nodes[2].listunspent()
utxo = utxos[0]
change = '%.8f' % (float(utxo['amount']) - 1.0003)
- recipientDict = {
+ recipient_dict = {
self.nodes[0].getnewaddress(): 1,
self.nodes[2].getnewaddress(): change,
}
- utxoDicts = [{
+ utxo_dicts = [{
'txid': utxo['txid'],
'vout': utxo['vout'],
}]
signedtxres = self.nodes[2].signrawtransactionwithwallet(
- self.nodes[2].createrawtransaction(utxoDicts, recipientDict))
+ self.nodes[2].createrawtransaction(utxo_dicts, recipient_dict))
assert signedtxres['complete']
signedtx = signedtxres['hex']
diff --git a/test/functional/rpc_listtransactions.py b/test/functional/wallet_listtransactions.py
index 0dd7372e6b..7cf2e456cf 100755
--- a/test/functional/rpc_listtransactions.py
+++ b/test/functional/wallet_listtransactions.py
@@ -3,13 +3,20 @@
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test the listtransactions API."""
-
-from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import *
-from test_framework.mininode import CTransaction, COIN
+from decimal import Decimal
from io import BytesIO
-def txFromHex(hexstring):
+from test_framework.mininode import CTransaction, COIN
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_array_result,
+ assert_equal,
+ bytes_to_hex_str,
+ hex_str_to_bytes,
+ sync_mempools,
+)
+
+def tx_from_hex(hexstring):
tx = CTransaction()
f = BytesIO(hex_str_to_bytes(hexstring))
tx.deserialize(f)
@@ -25,61 +32,61 @@ class ListTransactionsTest(BitcoinTestFramework):
txid = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 0.1)
self.sync_all()
assert_array_result(self.nodes[0].listtransactions(),
- {"txid":txid},
- {"category":"send","account":"","amount":Decimal("-0.1"),"confirmations":0})
+ {"txid": txid},
+ {"category": "send", "amount": Decimal("-0.1"), "confirmations": 0})
assert_array_result(self.nodes[1].listtransactions(),
- {"txid":txid},
- {"category":"receive","account":"","amount":Decimal("0.1"),"confirmations":0})
+ {"txid": txid},
+ {"category": "receive", "amount": Decimal("0.1"), "confirmations": 0})
# mine a block, confirmations should change:
self.nodes[0].generate(1)
self.sync_all()
assert_array_result(self.nodes[0].listtransactions(),
- {"txid":txid},
- {"category":"send","account":"","amount":Decimal("-0.1"),"confirmations":1})
+ {"txid": txid},
+ {"category": "send", "amount": Decimal("-0.1"), "confirmations": 1})
assert_array_result(self.nodes[1].listtransactions(),
- {"txid":txid},
- {"category":"receive","account":"","amount":Decimal("0.1"),"confirmations":1})
+ {"txid": txid},
+ {"category": "receive", "amount": Decimal("0.1"), "confirmations": 1})
# send-to-self:
txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 0.2)
assert_array_result(self.nodes[0].listtransactions(),
- {"txid":txid, "category":"send"},
- {"amount":Decimal("-0.2")})
+ {"txid": txid, "category": "send"},
+ {"amount": Decimal("-0.2")})
assert_array_result(self.nodes[0].listtransactions(),
- {"txid":txid, "category":"receive"},
- {"amount":Decimal("0.2")})
+ {"txid": txid, "category": "receive"},
+ {"amount": Decimal("0.2")})
# sendmany from node1: twice to self, twice to node2:
- send_to = { self.nodes[0].getnewaddress() : 0.11,
- self.nodes[1].getnewaddress() : 0.22,
- self.nodes[0].getaccountaddress("from1") : 0.33,
- self.nodes[1].getaccountaddress("toself") : 0.44 }
+ send_to = {self.nodes[0].getnewaddress(): 0.11,
+ self.nodes[1].getnewaddress(): 0.22,
+ self.nodes[0].getnewaddress(): 0.33,
+ self.nodes[1].getnewaddress(): 0.44}
txid = self.nodes[1].sendmany("", send_to)
self.sync_all()
assert_array_result(self.nodes[1].listtransactions(),
- {"category":"send","amount":Decimal("-0.11")},
- {"txid":txid} )
+ {"category": "send", "amount": Decimal("-0.11")},
+ {"txid": txid})
assert_array_result(self.nodes[0].listtransactions(),
- {"category":"receive","amount":Decimal("0.11")},
- {"txid":txid} )
+ {"category": "receive", "amount": Decimal("0.11")},
+ {"txid": txid})
assert_array_result(self.nodes[1].listtransactions(),
- {"category":"send","amount":Decimal("-0.22")},
- {"txid":txid} )
+ {"category": "send", "amount": Decimal("-0.22")},
+ {"txid": txid})
assert_array_result(self.nodes[1].listtransactions(),
- {"category":"receive","amount":Decimal("0.22")},
- {"txid":txid} )
+ {"category": "receive", "amount": Decimal("0.22")},
+ {"txid": txid})
assert_array_result(self.nodes[1].listtransactions(),
- {"category":"send","amount":Decimal("-0.33")},
- {"txid":txid} )
+ {"category": "send", "amount": Decimal("-0.33")},
+ {"txid": txid})
assert_array_result(self.nodes[0].listtransactions(),
- {"category":"receive","amount":Decimal("0.33")},
- {"txid":txid, "account" : "from1"} )
+ {"category": "receive", "amount": Decimal("0.33")},
+ {"txid": txid})
assert_array_result(self.nodes[1].listtransactions(),
- {"category":"send","amount":Decimal("-0.44")},
- {"txid":txid, "account" : ""} )
+ {"category": "send", "amount": Decimal("-0.44")},
+ {"txid": txid})
assert_array_result(self.nodes[1].listtransactions(),
- {"category":"receive","amount":Decimal("0.44")},
- {"txid":txid, "account" : "toself"} )
+ {"category": "receive", "amount": Decimal("0.44")},
+ {"txid": txid})
pubkey = self.nodes[1].getaddressinfo(self.nodes[1].getnewaddress())['pubkey']
multisig = self.nodes[1].createmultisig(1, [pubkey])
@@ -87,10 +94,9 @@ class ListTransactionsTest(BitcoinTestFramework):
txid = self.nodes[1].sendtoaddress(multisig["address"], 0.1)
self.nodes[1].generate(1)
self.sync_all()
- assert(len(self.nodes[0].listtransactions("watchonly", 100, 0, False)) == 0)
- assert_array_result(self.nodes[0].listtransactions("watchonly", 100, 0, True),
- {"category":"receive","amount":Decimal("0.1")},
- {"txid":txid, "account" : "watchonly"} )
+ assert not [tx for tx in self.nodes[0].listtransactions(dummy="*", count=100, skip=0, include_watchonly=False) if "label" in tx and tx["label"] == "watchonly"]
+ txs = [tx for tx in self.nodes[0].listtransactions(dummy="*", count=100, skip=0, include_watchonly=True) if "label" in tx and tx['label'] == 'watchonly']
+ assert_array_result(txs, {"category": "receive", "amount": Decimal("0.1")}, {"txid": txid})
self.run_rbf_opt_in_test()
@@ -116,9 +122,9 @@ class ListTransactionsTest(BitcoinTestFramework):
# 1. Chain a few transactions that don't opt-in.
txid_1 = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 1)
assert(not is_opt_in(self.nodes[0], txid_1))
- assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_1}, {"bip125-replaceable":"no"})
+ assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_1}, {"bip125-replaceable": "no"})
sync_mempools(self.nodes)
- assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_1}, {"bip125-replaceable":"no"})
+ assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_1}, {"bip125-replaceable": "no"})
# Tx2 will build off txid_1, still not opting in to RBF.
utxo_to_use = get_unconfirmed_utxo_entry(self.nodes[0], txid_1)
@@ -128,7 +134,7 @@ class ListTransactionsTest(BitcoinTestFramework):
assert_equal(utxo_to_use["safe"], False)
# Create tx2 using createrawtransaction
- inputs = [{"txid":utxo_to_use["txid"], "vout":utxo_to_use["vout"]}]
+ inputs = [{"txid": utxo_to_use["txid"], "vout": utxo_to_use["vout"]}]
outputs = {self.nodes[0].getnewaddress(): 0.999}
tx2 = self.nodes[1].createrawtransaction(inputs, outputs)
tx2_signed = self.nodes[1].signrawtransactionwithwallet(tx2)["hex"]
@@ -136,51 +142,51 @@ class ListTransactionsTest(BitcoinTestFramework):
# ...and check the result
assert(not is_opt_in(self.nodes[1], txid_2))
- assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_2}, {"bip125-replaceable":"no"})
+ assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_2}, {"bip125-replaceable": "no"})
sync_mempools(self.nodes)
- assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_2}, {"bip125-replaceable":"no"})
+ assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_2}, {"bip125-replaceable": "no"})
# Tx3 will opt-in to RBF
utxo_to_use = get_unconfirmed_utxo_entry(self.nodes[0], txid_2)
- inputs = [{"txid": txid_2, "vout":utxo_to_use["vout"]}]
+ inputs = [{"txid": txid_2, "vout": utxo_to_use["vout"]}]
outputs = {self.nodes[1].getnewaddress(): 0.998}
tx3 = self.nodes[0].createrawtransaction(inputs, outputs)
- tx3_modified = txFromHex(tx3)
+ tx3_modified = tx_from_hex(tx3)
tx3_modified.vin[0].nSequence = 0
tx3 = bytes_to_hex_str(tx3_modified.serialize())
tx3_signed = self.nodes[0].signrawtransactionwithwallet(tx3)['hex']
txid_3 = self.nodes[0].sendrawtransaction(tx3_signed)
assert(is_opt_in(self.nodes[0], txid_3))
- assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_3}, {"bip125-replaceable":"yes"})
+ assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_3}, {"bip125-replaceable": "yes"})
sync_mempools(self.nodes)
- assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_3}, {"bip125-replaceable":"yes"})
+ assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_3}, {"bip125-replaceable": "yes"})
# Tx4 will chain off tx3. Doesn't signal itself, but depends on one
# that does.
utxo_to_use = get_unconfirmed_utxo_entry(self.nodes[1], txid_3)
- inputs = [{"txid": txid_3, "vout":utxo_to_use["vout"]}]
+ inputs = [{"txid": txid_3, "vout": utxo_to_use["vout"]}]
outputs = {self.nodes[0].getnewaddress(): 0.997}
tx4 = self.nodes[1].createrawtransaction(inputs, outputs)
tx4_signed = self.nodes[1].signrawtransactionwithwallet(tx4)["hex"]
txid_4 = self.nodes[1].sendrawtransaction(tx4_signed)
assert(not is_opt_in(self.nodes[1], txid_4))
- assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_4}, {"bip125-replaceable":"yes"})
+ assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_4}, {"bip125-replaceable": "yes"})
sync_mempools(self.nodes)
- assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_4}, {"bip125-replaceable":"yes"})
+ assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_4}, {"bip125-replaceable": "yes"})
# Replace tx3, and check that tx4 becomes unknown
tx3_b = tx3_modified
- tx3_b.vout[0].nValue -= int(Decimal("0.004") * COIN) # bump the fee
+ tx3_b.vout[0].nValue -= int(Decimal("0.004") * COIN) # bump the fee
tx3_b = bytes_to_hex_str(tx3_b.serialize())
tx3_b_signed = self.nodes[0].signrawtransactionwithwallet(tx3_b)['hex']
txid_3b = self.nodes[0].sendrawtransaction(tx3_b_signed, True)
assert(is_opt_in(self.nodes[0], txid_3b))
- assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_4}, {"bip125-replaceable":"unknown"})
+ assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_4}, {"bip125-replaceable": "unknown"})
sync_mempools(self.nodes)
- assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_4}, {"bip125-replaceable":"unknown"})
+ assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_4}, {"bip125-replaceable": "unknown"})
# Check gettransaction as well:
for n in self.nodes[0:2]:
@@ -196,7 +202,5 @@ class ListTransactionsTest(BitcoinTestFramework):
assert_equal(self.nodes[0].gettransaction(txid_3b)["bip125-replaceable"], "no")
assert_equal(self.nodes[0].gettransaction(txid_4)["bip125-replaceable"], "unknown")
-
if __name__ == '__main__':
ListTransactionsTest().main()
-
diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py
index 0a24d34398..e0571ea8f9 100755
--- a/test/functional/wallet_multiwallet.py
+++ b/test/functional/wallet_multiwallet.py
@@ -91,6 +91,19 @@ class MultiWalletTest(BitcoinTestFramework):
open(not_a_dir, 'a').close()
self.nodes[0].assert_start_raises_init_error(['-walletdir=' + not_a_dir], 'Error: Specified -walletdir "' + not_a_dir + '" is not a directory')
+ self.log.info("Do not allow -zapwallettxes with multiwallet")
+ self.nodes[0].assert_start_raises_init_error(['-zapwallettxes', '-wallet=w1', '-wallet=w2'], "Error: -zapwallettxes is only allowed with a single wallet file")
+ self.nodes[0].assert_start_raises_init_error(['-zapwallettxes=1', '-wallet=w1', '-wallet=w2'], "Error: -zapwallettxes is only allowed with a single wallet file")
+ self.nodes[0].assert_start_raises_init_error(['-zapwallettxes=2', '-wallet=w1', '-wallet=w2'], "Error: -zapwallettxes is only allowed with a single wallet file")
+
+ self.log.info("Do not allow -salvagewallet with multiwallet")
+ self.nodes[0].assert_start_raises_init_error(['-salvagewallet', '-wallet=w1', '-wallet=w2'], "Error: -salvagewallet is only allowed with a single wallet file")
+ self.nodes[0].assert_start_raises_init_error(['-salvagewallet=1', '-wallet=w1', '-wallet=w2'], "Error: -salvagewallet is only allowed with a single wallet file")
+
+ self.log.info("Do not allow -upgradewallet with multiwallet")
+ self.nodes[0].assert_start_raises_init_error(['-upgradewallet', '-wallet=w1', '-wallet=w2'], "Error: -upgradewallet is only allowed with a single wallet file")
+ self.nodes[0].assert_start_raises_init_error(['-upgradewallet=1', '-wallet=w1', '-wallet=w2'], "Error: -upgradewallet is only allowed with a single wallet file")
+
# if wallets/ doesn't exist, datadir should be the default wallet dir
wallet_dir2 = data_dir('walletdir')
os.rename(wallet_dir(), wallet_dir2)
@@ -150,5 +163,12 @@ class MultiWalletTest(BitcoinTestFramework):
assert_equal(batch[0]["result"]["chain"], "regtest")
assert_equal(batch[1]["result"]["walletname"], "w1")
+ self.log.info('Check for per-wallet settxfee call')
+ assert_equal(w1.getwalletinfo()['paytxfee'], 0)
+ assert_equal(w2.getwalletinfo()['paytxfee'], 0)
+ w2.settxfee(4.0)
+ assert_equal(w1.getwalletinfo()['paytxfee'], 0)
+ assert_equal(w2.getwalletinfo()['paytxfee'], 4.0)
+
if __name__ == '__main__':
MultiWalletTest().main()
diff --git a/test/functional/wallet_txn_clone.py b/test/functional/wallet_txn_clone.py
index 7577c4a0d2..72ae2d5db7 100755
--- a/test/functional/wallet_txn_clone.py
+++ b/test/functional/wallet_txn_clone.py
@@ -5,7 +5,12 @@
"""Test the wallet accounts properly when there are cloned transactions with malleated scriptsigs."""
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import *
+from test_framework.util import (
+ assert_equal,
+ connect_nodes,
+ disconnect_nodes,
+ sync_blocks,
+)
class TxnMallTest(BitcoinTestFramework):
def set_test_params(self):
@@ -25,55 +30,53 @@ class TxnMallTest(BitcoinTestFramework):
def run_test(self):
if self.options.segwit:
- output_type="p2sh-segwit"
+ output_type = "p2sh-segwit"
else:
- output_type="legacy"
+ output_type = "legacy"
# All nodes should start with 1,250 BTC:
starting_balance = 1250
for i in range(4):
assert_equal(self.nodes[i].getbalance(), starting_balance)
- self.nodes[i].getnewaddress("") # bug workaround, coins generated assigned to first getnewaddress!
+ self.nodes[i].getnewaddress() # bug workaround, coins generated assigned to first getnewaddress!
- # Assign coins to foo and bar accounts:
self.nodes[0].settxfee(.001)
- node0_address_foo = self.nodes[0].getnewaddress("foo", output_type)
- fund_foo_txid = self.nodes[0].sendfrom("", node0_address_foo, 1219)
- fund_foo_tx = self.nodes[0].gettransaction(fund_foo_txid)
+ node0_address1 = self.nodes[0].getnewaddress(address_type=output_type)
+ node0_txid1 = self.nodes[0].sendtoaddress(node0_address1, 1219)
+ node0_tx1 = self.nodes[0].gettransaction(node0_txid1)
- node0_address_bar = self.nodes[0].getnewaddress("bar", output_type)
- fund_bar_txid = self.nodes[0].sendfrom("", node0_address_bar, 29)
- fund_bar_tx = self.nodes[0].gettransaction(fund_bar_txid)
+ node0_address2 = self.nodes[0].getnewaddress(address_type=output_type)
+ node0_txid2 = self.nodes[0].sendtoaddress(node0_address2, 29)
+ node0_tx2 = self.nodes[0].gettransaction(node0_txid2)
- assert_equal(self.nodes[0].getbalance(""),
- starting_balance - 1219 - 29 + fund_foo_tx["fee"] + fund_bar_tx["fee"])
+ assert_equal(self.nodes[0].getbalance(),
+ starting_balance + node0_tx1["fee"] + node0_tx2["fee"])
# Coins are sent to node1_address
- node1_address = self.nodes[1].getnewaddress("from0")
+ node1_address = self.nodes[1].getnewaddress()
- # Send tx1, and another transaction tx2 that won't be cloned
- txid1 = self.nodes[0].sendfrom("foo", node1_address, 40, 0)
- txid2 = self.nodes[0].sendfrom("bar", node1_address, 20, 0)
+ # Send tx1, and another transaction tx2 that won't be cloned
+ txid1 = self.nodes[0].sendtoaddress(node1_address, 40)
+ txid2 = self.nodes[0].sendtoaddress(node1_address, 20)
- # Construct a clone of tx1, to be malleated
- rawtx1 = self.nodes[0].getrawtransaction(txid1,1)
- clone_inputs = [{"txid":rawtx1["vin"][0]["txid"],"vout":rawtx1["vin"][0]["vout"]}]
- clone_outputs = {rawtx1["vout"][0]["scriptPubKey"]["addresses"][0]:rawtx1["vout"][0]["value"],
- rawtx1["vout"][1]["scriptPubKey"]["addresses"][0]:rawtx1["vout"][1]["value"]}
+ # Construct a clone of tx1, to be malleated
+ rawtx1 = self.nodes[0].getrawtransaction(txid1, 1)
+ clone_inputs = [{"txid": rawtx1["vin"][0]["txid"], "vout": rawtx1["vin"][0]["vout"]}]
+ clone_outputs = {rawtx1["vout"][0]["scriptPubKey"]["addresses"][0]: rawtx1["vout"][0]["value"],
+ rawtx1["vout"][1]["scriptPubKey"]["addresses"][0]: rawtx1["vout"][1]["value"]}
clone_locktime = rawtx1["locktime"]
clone_raw = self.nodes[0].createrawtransaction(clone_inputs, clone_outputs, clone_locktime)
# createrawtransaction randomizes the order of its outputs, so swap them if necessary.
# output 0 is at version+#inputs+input+sigstub+sequence+#outputs
# 40 BTC serialized is 00286bee00000000
- pos0 = 2*(4+1+36+1+4+1)
+ pos0 = 2 * (4 + 1 + 36 + 1 + 4 + 1)
hex40 = "00286bee00000000"
- output_len = 16 + 2 + 2 * int("0x" + clone_raw[pos0 + 16 : pos0 + 16 + 2], 0)
- if (rawtx1["vout"][0]["value"] == 40 and clone_raw[pos0 : pos0 + 16] != hex40 or
- rawtx1["vout"][0]["value"] != 40 and clone_raw[pos0 : pos0 + 16] == hex40):
- output0 = clone_raw[pos0 : pos0 + output_len]
- output1 = clone_raw[pos0 + output_len : pos0 + 2 * output_len]
+ output_len = 16 + 2 + 2 * int("0x" + clone_raw[pos0 + 16:pos0 + 16 + 2], 0)
+ if (rawtx1["vout"][0]["value"] == 40 and clone_raw[pos0:pos0 + 16] != hex40 or rawtx1["vout"][0]["value"] != 40 and clone_raw[pos0:pos0 + 16] == hex40):
+ output0 = clone_raw[pos0:pos0 + output_len]
+ output1 = clone_raw[pos0 + output_len:pos0 + 2 * output_len]
clone_raw = clone_raw[:pos0] + output1 + output0 + clone_raw[pos0 + 2 * output_len:]
# Use a different signature hash type to sign. This creates an equivalent but malleated clone.
@@ -91,28 +94,22 @@ class TxnMallTest(BitcoinTestFramework):
# Node0's balance should be starting balance, plus 50BTC for another
# matured block, minus tx1 and tx2 amounts, and minus transaction fees:
- expected = starting_balance + fund_foo_tx["fee"] + fund_bar_tx["fee"]
+ expected = starting_balance + node0_tx1["fee"] + node0_tx2["fee"]
if self.options.mine_block:
expected += 50
expected += tx1["amount"] + tx1["fee"]
expected += tx2["amount"] + tx2["fee"]
assert_equal(self.nodes[0].getbalance(), expected)
- # foo and bar accounts should be debited:
- assert_equal(self.nodes[0].getbalance("foo", 0), 1219 + tx1["amount"] + tx1["fee"])
- assert_equal(self.nodes[0].getbalance("bar", 0), 29 + tx2["amount"] + tx2["fee"])
-
if self.options.mine_block:
assert_equal(tx1["confirmations"], 1)
assert_equal(tx2["confirmations"], 1)
- # Node1's "from0" balance should be both transaction amounts:
- assert_equal(self.nodes[1].getbalance("from0"), -(tx1["amount"] + tx2["amount"]))
else:
assert_equal(tx1["confirmations"], 0)
assert_equal(tx2["confirmations"], 0)
# Send clone and its parent to miner
- self.nodes[2].sendrawtransaction(fund_foo_tx["hex"])
+ self.nodes[2].sendrawtransaction(node0_tx1["hex"])
txid1_clone = self.nodes[2].sendrawtransaction(tx1_clone["hex"])
if self.options.segwit:
assert_equal(txid1, txid1_clone)
@@ -123,7 +120,7 @@ class TxnMallTest(BitcoinTestFramework):
# Reconnect the split network, and sync chain:
connect_nodes(self.nodes[1], 2)
- self.nodes[2].sendrawtransaction(fund_bar_tx["hex"])
+ self.nodes[2].sendrawtransaction(node0_tx2["hex"])
self.nodes[2].sendrawtransaction(tx2["hex"])
self.nodes[2].generate(1) # Mine another block to make sure we sync
sync_blocks(self.nodes)
@@ -141,27 +138,9 @@ class TxnMallTest(BitcoinTestFramework):
# Check node0's total balance; should be same as before the clone, + 100 BTC for 2 matured,
# less possible orphaned matured subsidy
expected += 100
- if (self.options.mine_block):
+ if (self.options.mine_block):
expected -= 50
assert_equal(self.nodes[0].getbalance(), expected)
- assert_equal(self.nodes[0].getbalance("*", 0), expected)
-
- # Check node0's individual account balances.
- # "foo" should have been debited by the equivalent clone of tx1
- assert_equal(self.nodes[0].getbalance("foo"), 1219 + tx1["amount"] + tx1["fee"])
- # "bar" should have been debited by (possibly unconfirmed) tx2
- assert_equal(self.nodes[0].getbalance("bar", 0), 29 + tx2["amount"] + tx2["fee"])
- # "" should have starting balance, less funding txes, plus subsidies
- assert_equal(self.nodes[0].getbalance("", 0), starting_balance
- - 1219
- + fund_foo_tx["fee"]
- - 29
- + fund_bar_tx["fee"]
- + 100)
-
- # Node1's "from0" account balance
- assert_equal(self.nodes[1].getbalance("from0", 0), -(tx1["amount"] + tx2["amount"]))
if __name__ == '__main__':
TxnMallTest().main()
-
diff --git a/test/functional/wallet_txn_doublespend.py b/test/functional/wallet_txn_doublespend.py
index 8419d6b545..7ee60d5611 100755
--- a/test/functional/wallet_txn_doublespend.py
+++ b/test/functional/wallet_txn_doublespend.py
@@ -3,9 +3,16 @@
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test the wallet accounts properly when there is a double-spend conflict."""
+from decimal import Decimal
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import *
+from test_framework.util import (
+ assert_equal,
+ connect_nodes,
+ disconnect_nodes,
+ find_output,
+ sync_blocks,
+)
class TxnMallTest(BitcoinTestFramework):
def set_test_params(self):
@@ -28,20 +35,20 @@ class TxnMallTest(BitcoinTestFramework):
assert_equal(self.nodes[i].getbalance(), starting_balance)
self.nodes[i].getnewaddress("") # bug workaround, coins generated assigned to first getnewaddress!
- # Assign coins to foo and bar accounts:
- node0_address_foo = self.nodes[0].getnewaddress("foo")
- fund_foo_txid = self.nodes[0].sendfrom("", node0_address_foo, 1219)
+ # Assign coins to foo and bar addresses:
+ node0_address_foo = self.nodes[0].getnewaddress()
+ fund_foo_txid = self.nodes[0].sendtoaddress(node0_address_foo, 1219)
fund_foo_tx = self.nodes[0].gettransaction(fund_foo_txid)
- node0_address_bar = self.nodes[0].getnewaddress("bar")
- fund_bar_txid = self.nodes[0].sendfrom("", node0_address_bar, 29)
+ node0_address_bar = self.nodes[0].getnewaddress()
+ fund_bar_txid = self.nodes[0].sendtoaddress(node0_address_bar, 29)
fund_bar_tx = self.nodes[0].gettransaction(fund_bar_txid)
- assert_equal(self.nodes[0].getbalance(""),
- starting_balance - 1219 - 29 + fund_foo_tx["fee"] + fund_bar_tx["fee"])
+ assert_equal(self.nodes[0].getbalance(),
+ starting_balance + fund_foo_tx["fee"] + fund_bar_tx["fee"])
# Coins are sent to node1_address
- node1_address = self.nodes[1].getnewaddress("from0")
+ node1_address = self.nodes[1].getnewaddress()
# First: use raw transaction API to send 1240 BTC to node1_address,
# but don't broadcast:
@@ -62,8 +69,8 @@ class TxnMallTest(BitcoinTestFramework):
assert_equal(doublespend["complete"], True)
# Create two spends using 1 50 BTC coin each
- txid1 = self.nodes[0].sendfrom("foo", node1_address, 40, 0)
- txid2 = self.nodes[0].sendfrom("bar", node1_address, 20, 0)
+ txid1 = self.nodes[0].sendtoaddress(node1_address, 40)
+ txid2 = self.nodes[0].sendtoaddress(node1_address, 20)
# Have node0 mine a block:
if (self.options.mine_block):
@@ -82,15 +89,11 @@ class TxnMallTest(BitcoinTestFramework):
expected += tx2["amount"] + tx2["fee"]
assert_equal(self.nodes[0].getbalance(), expected)
- # foo and bar accounts should be debited:
- assert_equal(self.nodes[0].getbalance("foo", 0), 1219+tx1["amount"]+tx1["fee"])
- assert_equal(self.nodes[0].getbalance("bar", 0), 29+tx2["amount"]+tx2["fee"])
-
if self.options.mine_block:
assert_equal(tx1["confirmations"], 1)
assert_equal(tx2["confirmations"], 1)
- # Node1's "from0" balance should be both transaction amounts:
- assert_equal(self.nodes[1].getbalance("from0"), -(tx1["amount"]+tx2["amount"]))
+ # Node1's balance should be both transaction amounts:
+ assert_equal(self.nodes[1].getbalance(), starting_balance - tx1["amount"] - tx2["amount"])
else:
assert_equal(tx1["confirmations"], 0)
assert_equal(tx2["confirmations"], 0)
@@ -116,29 +119,14 @@ class TxnMallTest(BitcoinTestFramework):
assert_equal(tx1["confirmations"], -2)
assert_equal(tx2["confirmations"], -2)
- # Node0's total balance should be starting balance, plus 100BTC for
+ # Node0's total balance should be starting balance, plus 100BTC for
# two more matured blocks, minus 1240 for the double-spend, plus fees (which are
# negative):
expected = starting_balance + 100 - 1240 + fund_foo_tx["fee"] + fund_bar_tx["fee"] + doublespend_fee
assert_equal(self.nodes[0].getbalance(), expected)
- assert_equal(self.nodes[0].getbalance("*"), expected)
-
- # Final "" balance is starting_balance - amount moved to accounts - doublespend + subsidies +
- # fees (which are negative)
- assert_equal(self.nodes[0].getbalance("foo"), 1219)
- assert_equal(self.nodes[0].getbalance("bar"), 29)
- assert_equal(self.nodes[0].getbalance(""), starting_balance
- -1219
- - 29
- -1240
- + 100
- + fund_foo_tx["fee"]
- + fund_bar_tx["fee"]
- + doublespend_fee)
-
- # Node1's "from0" account balance should be just the doublespend:
- assert_equal(self.nodes[1].getbalance("from0"), 1240)
+
+ # Node1's balance should be its initial balance (1250 for 25 block rewards) plus the doublespend:
+ assert_equal(self.nodes[1].getbalance(), 1250 + 1240)
if __name__ == '__main__':
TxnMallTest().main()
-
diff --git a/test/util/data/blanktxv1.json b/test/util/data/blanktxv1.json
index 9fe2de649b..3d5a1cccae 100644
--- a/test/util/data/blanktxv1.json
+++ b/test/util/data/blanktxv1.json
@@ -4,6 +4,7 @@
"version": 1,
"size": 10,
"vsize": 10,
+ "weight": 40,
"locktime": 0,
"vin": [
],
diff --git a/test/util/data/blanktxv2.json b/test/util/data/blanktxv2.json
index e97626e421..8374a34adc 100644
--- a/test/util/data/blanktxv2.json
+++ b/test/util/data/blanktxv2.json
@@ -4,6 +4,7 @@
"version": 2,
"size": 10,
"vsize": 10,
+ "weight": 40,
"locktime": 0,
"vin": [
],
diff --git a/test/util/data/tt-delin1-out.json b/test/util/data/tt-delin1-out.json
index de647f98b6..9fc2ddc376 100644
--- a/test/util/data/tt-delin1-out.json
+++ b/test/util/data/tt-delin1-out.json
@@ -4,6 +4,7 @@
"version": 1,
"size": 3040,
"vsize": 3040,
+ "weight": 12160,
"locktime": 0,
"vin": [
{
diff --git a/test/util/data/tt-delout1-out.json b/test/util/data/tt-delout1-out.json
index 067ffe74e7..922d048900 100644
--- a/test/util/data/tt-delout1-out.json
+++ b/test/util/data/tt-delout1-out.json
@@ -4,6 +4,7 @@
"version": 1,
"size": 3155,
"vsize": 3155,
+ "weight": 12620,
"locktime": 0,
"vin": [
{
diff --git a/test/util/data/tt-locktime317000-out.json b/test/util/data/tt-locktime317000-out.json
index af7903d1dd..c97206f1ea 100644
--- a/test/util/data/tt-locktime317000-out.json
+++ b/test/util/data/tt-locktime317000-out.json
@@ -4,6 +4,7 @@
"version": 1,
"size": 3189,
"vsize": 3189,
+ "weight": 12756,
"locktime": 317000,
"vin": [
{
diff --git a/test/util/data/txcreate1.json b/test/util/data/txcreate1.json
index 83a86649e0..ca9eacd546 100644
--- a/test/util/data/txcreate1.json
+++ b/test/util/data/txcreate1.json
@@ -4,6 +4,7 @@
"version": 2,
"size": 201,
"vsize": 201,
+ "weight": 804,
"locktime": 0,
"vin": [
{
diff --git a/test/util/data/txcreate2.json b/test/util/data/txcreate2.json
index cca00f752b..ee9b9c3c17 100644
--- a/test/util/data/txcreate2.json
+++ b/test/util/data/txcreate2.json
@@ -4,6 +4,7 @@
"version": 2,
"size": 19,
"vsize": 19,
+ "weight": 76,
"locktime": 0,
"vin": [
],
diff --git a/test/util/data/txcreatedata1.json b/test/util/data/txcreatedata1.json
index 15a4246ae5..39909c2e3f 100644
--- a/test/util/data/txcreatedata1.json
+++ b/test/util/data/txcreatedata1.json
@@ -4,6 +4,7 @@
"version": 1,
"size": 176,
"vsize": 176,
+ "weight": 704,
"locktime": 0,
"vin": [
{
diff --git a/test/util/data/txcreatedata2.json b/test/util/data/txcreatedata2.json
index cb93c27971..2958006e58 100644
--- a/test/util/data/txcreatedata2.json
+++ b/test/util/data/txcreatedata2.json
@@ -4,6 +4,7 @@
"version": 2,
"size": 176,
"vsize": 176,
+ "weight": 704,
"locktime": 0,
"vin": [
{
diff --git a/test/util/data/txcreatedata_seq0.json b/test/util/data/txcreatedata_seq0.json
index 4b5a7cab4a..a6656b5ad5 100644
--- a/test/util/data/txcreatedata_seq0.json
+++ b/test/util/data/txcreatedata_seq0.json
@@ -4,6 +4,7 @@
"version": 2,
"size": 85,
"vsize": 85,
+ "weight": 340,
"locktime": 0,
"vin": [
{
diff --git a/test/util/data/txcreatedata_seq1.json b/test/util/data/txcreatedata_seq1.json
index dea48ba373..e5980427b1 100644
--- a/test/util/data/txcreatedata_seq1.json
+++ b/test/util/data/txcreatedata_seq1.json
@@ -4,6 +4,7 @@
"version": 1,
"size": 126,
"vsize": 126,
+ "weight": 504,
"locktime": 0,
"vin": [
{
diff --git a/test/util/data/txcreatemultisig1.json b/test/util/data/txcreatemultisig1.json
index 72e20c8691..c32e755db1 100644
--- a/test/util/data/txcreatemultisig1.json
+++ b/test/util/data/txcreatemultisig1.json
@@ -4,6 +4,7 @@
"version": 1,
"size": 124,
"vsize": 124,
+ "weight": 496,
"locktime": 0,
"vin": [
],
diff --git a/test/util/data/txcreatemultisig2.json b/test/util/data/txcreatemultisig2.json
index 7d94ce7396..f97d265894 100644
--- a/test/util/data/txcreatemultisig2.json
+++ b/test/util/data/txcreatemultisig2.json
@@ -4,6 +4,7 @@
"version": 1,
"size": 42,
"vsize": 42,
+ "weight": 168,
"locktime": 0,
"vin": [
],
diff --git a/test/util/data/txcreatemultisig3.json b/test/util/data/txcreatemultisig3.json
index 6c5b49d876..b355d7b191 100644
--- a/test/util/data/txcreatemultisig3.json
+++ b/test/util/data/txcreatemultisig3.json
@@ -4,6 +4,7 @@
"version": 1,
"size": 53,
"vsize": 53,
+ "weight": 212,
"locktime": 0,
"vin": [
],
diff --git a/test/util/data/txcreatemultisig4.json b/test/util/data/txcreatemultisig4.json
index 9a5d2f4a06..a00dbe3f5d 100644
--- a/test/util/data/txcreatemultisig4.json
+++ b/test/util/data/txcreatemultisig4.json
@@ -4,6 +4,7 @@
"version": 1,
"size": 42,
"vsize": 42,
+ "weight": 168,
"locktime": 0,
"vin": [
],
diff --git a/test/util/data/txcreatemultisig5.json b/test/util/data/txcreatemultisig5.json
index 20e9bb077b..ea07822ddd 100644
--- a/test/util/data/txcreatemultisig5.json
+++ b/test/util/data/txcreatemultisig5.json
@@ -4,6 +4,7 @@
"version": 2,
"size": 42,
"vsize": 42,
+ "weight": 168,
"locktime": 0,
"vin": [
],
diff --git a/test/util/data/txcreateoutpubkey1.json b/test/util/data/txcreateoutpubkey1.json
index 2704ed7673..32097b3ebe 100644
--- a/test/util/data/txcreateoutpubkey1.json
+++ b/test/util/data/txcreateoutpubkey1.json
@@ -4,6 +4,7 @@
"version": 1,
"size": 54,
"vsize": 54,
+ "weight": 216,
"locktime": 0,
"vin": [
],
diff --git a/test/util/data/txcreateoutpubkey2.json b/test/util/data/txcreateoutpubkey2.json
index 4ba5dcb282..c0ee181ede 100644
--- a/test/util/data/txcreateoutpubkey2.json
+++ b/test/util/data/txcreateoutpubkey2.json
@@ -4,6 +4,7 @@
"version": 1,
"size": 41,
"vsize": 41,
+ "weight": 164,
"locktime": 0,
"vin": [
],
diff --git a/test/util/data/txcreateoutpubkey3.json b/test/util/data/txcreateoutpubkey3.json
index 0a5d489e15..4d904df3c8 100644
--- a/test/util/data/txcreateoutpubkey3.json
+++ b/test/util/data/txcreateoutpubkey3.json
@@ -4,6 +4,7 @@
"version": 1,
"size": 42,
"vsize": 42,
+ "weight": 168,
"locktime": 0,
"vin": [
],
diff --git a/test/util/data/txcreatescript1.json b/test/util/data/txcreatescript1.json
index 5072452fed..af1c4c35e2 100644
--- a/test/util/data/txcreatescript1.json
+++ b/test/util/data/txcreatescript1.json
@@ -4,6 +4,7 @@
"version": 1,
"size": 20,
"vsize": 20,
+ "weight": 80,
"locktime": 0,
"vin": [
],
diff --git a/test/util/data/txcreatescript2.json b/test/util/data/txcreatescript2.json
index 94b669ffb6..32dd644579 100644
--- a/test/util/data/txcreatescript2.json
+++ b/test/util/data/txcreatescript2.json
@@ -4,6 +4,7 @@
"version": 1,
"size": 42,
"vsize": 42,
+ "weight": 168,
"locktime": 0,
"vin": [
],
diff --git a/test/util/data/txcreatescript3.json b/test/util/data/txcreatescript3.json
index 31b6459214..b9192d9a82 100644
--- a/test/util/data/txcreatescript3.json
+++ b/test/util/data/txcreatescript3.json
@@ -4,6 +4,7 @@
"version": 1,
"size": 53,
"vsize": 53,
+ "weight": 212,
"locktime": 0,
"vin": [
],
diff --git a/test/util/data/txcreatescript4.json b/test/util/data/txcreatescript4.json
index eecdf858b7..2271ecfa0a 100644
--- a/test/util/data/txcreatescript4.json
+++ b/test/util/data/txcreatescript4.json
@@ -4,6 +4,7 @@
"version": 1,
"size": 42,
"vsize": 42,
+ "weight": 168,
"locktime": 0,
"vin": [
],
diff --git a/test/util/data/txcreatesignv1.json b/test/util/data/txcreatesignv1.json
index 92a3f76a07..64e5137f4b 100644
--- a/test/util/data/txcreatesignv1.json
+++ b/test/util/data/txcreatesignv1.json
@@ -4,6 +4,7 @@
"version": 1,
"size": 224,
"vsize": 224,
+ "weight": 896,
"locktime": 0,
"vin": [
{
diff --git a/test/util/rpcauth-test.py b/test/util/rpcauth-test.py
new file mode 100755
index 0000000000..2456feb102
--- /dev/null
+++ b/test/util/rpcauth-test.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python3
+# Copyright (c) 2015-2018 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Test share/rpcauth/rpcauth.py
+"""
+import base64
+import configparser
+import hmac
+import importlib
+import os
+import sys
+import unittest
+
+class TestRPCAuth(unittest.TestCase):
+ def setUp(self):
+ config = configparser.ConfigParser()
+ config_path = os.path.abspath(
+ os.path.join(os.sep, os.path.abspath(os.path.dirname(__file__)),
+ "../config.ini"))
+ with open(config_path) as config_file:
+ config.read_file(config_file)
+ sys.path.insert(0, os.path.dirname(config['environment']['RPCAUTH']))
+ self.rpcauth = importlib.import_module('rpcauth')
+
+ def test_generate_salt(self):
+ self.assertLessEqual(len(self.rpcauth.generate_salt()), 32)
+ self.assertGreaterEqual(len(self.rpcauth.generate_salt()), 16)
+
+ def test_generate_password(self):
+ password = self.rpcauth.generate_password()
+ expected_password = base64.urlsafe_b64encode(
+ base64.urlsafe_b64decode(password)).decode('utf-8')
+ self.assertEqual(expected_password, password)
+
+ def test_check_password_hmac(self):
+ salt = self.rpcauth.generate_salt()
+ password = self.rpcauth.generate_password()
+ password_hmac = self.rpcauth.password_to_hmac(salt, password)
+
+ m = hmac.new(bytearray(salt, 'utf-8'),
+ bytearray(password, 'utf-8'), 'SHA256')
+ expected_password_hmac = m.hexdigest()
+
+ self.assertEqual(expected_password_hmac, password_hmac)
+
+if __name__ == '__main__':
+ unittest.main()