aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.appveyor.yml28
-rw-r--r--.cirrus.yml24
-rw-r--r--.github/ISSUE_TEMPLATE.md2
-rw-r--r--.github/ISSUE_TEMPLATE/bug_report.md41
-rw-r--r--.github/ISSUE_TEMPLATE/feature_request.md20
-rw-r--r--.gitignore8
-rw-r--r--.travis.yml119
-rw-r--r--.travis/README.md8
-rwxr-xr-x.travis/test_04_install.sh37
-rw-r--r--.tx/config2
-rw-r--r--CONTRIBUTING.md33
-rw-r--r--Makefile.am1
-rw-r--r--build-aux/m4/bitcoin_find_bdb48.m42
-rw-r--r--build_msvc/.gitignore2
-rw-r--r--build_msvc/README.md65
-rw-r--r--build_msvc/bench_bitcoin/bench_bitcoin.vcxproj.in (renamed from build_msvc/bench_bitcoin/bench_bitcoin.vcxproj)22
-rw-r--r--build_msvc/bitcoin-qt/bitcoin-qt.vcxproj81
-rw-r--r--build_msvc/bitcoin.sln22
-rw-r--r--build_msvc/common.init.vcxproj13
-rw-r--r--build_msvc/common.qt.init.vcxproj16
-rw-r--r--build_msvc/libbitcoin_qt/libbitcoin_qt.vcxproj1148
-rw-r--r--build_msvc/msvc-autogen.py1
-rw-r--r--build_msvc/test_bitcoin-qt/test_bitcoin-qt.vcxproj235
-rw-r--r--ci/README.md31
-rwxr-xr-xci/extended_lint/04_install.sh (renamed from .travis/extended_lint_04_install.sh)0
-rwxr-xr-xci/extended_lint/06_script.sh (renamed from .travis/extended_lint_06_script.sh)0
-rwxr-xr-xci/lint/04_install.sh (renamed from .travis/lint_04_install.sh)6
-rwxr-xr-xci/lint/05_before_script.sh (renamed from .travis/lint_05_before_script.sh)0
-rwxr-xr-xci/lint/06_script.sh (renamed from .travis/lint_06_script.sh)0
-rw-r--r--ci/retry/README.md123
-rwxr-xr-xci/retry/retry163
-rwxr-xr-xci/test/00_setup_env.sh43
-rw-r--r--ci/test/00_setup_env_amd64_asan.sh13
-rw-r--r--ci/test/00_setup_env_amd64_fuzz.sh16
-rw-r--r--ci/test/00_setup_env_amd64_nowallet.sh13
-rw-r--r--ci/test/00_setup_env_amd64_qt5.sh14
-rw-r--r--ci/test/00_setup_env_amd64_trusty.sh15
-rw-r--r--ci/test/00_setup_env_amd64_tsan.sh14
-rw-r--r--ci/test/00_setup_env_arm.sh16
-rw-r--r--ci/test/00_setup_env_i686.sh14
-rw-r--r--ci/test/00_setup_env_mac.sh15
-rw-r--r--ci/test/00_setup_env_win64.sh13
-rwxr-xr-xci/test/03_before_install.sh (renamed from .travis/test_03_before_install.sh)1
-rwxr-xr-xci/test/04_install.sh50
-rwxr-xr-xci/test/05_before_script.sh (renamed from .travis/test_05_before_script.sh)0
-rwxr-xr-xci/test/06_script_a.sh (renamed from .travis/test_06_script_a.sh)12
-rwxr-xr-xci/test/06_script_b.sh (renamed from .travis/test_06_script_b.sh)6
-rwxr-xr-xci/test_run_all.sh14
-rw-r--r--configure.ac26
-rw-r--r--contrib/README.md8
-rw-r--r--contrib/bitcoin-qt.pro1
-rw-r--r--contrib/debian/copyright19
-rw-r--r--contrib/devtools/README.md72
-rwxr-xr-xcontrib/devtools/clang-format-diff.py4
-rwxr-xr-xcontrib/devtools/copyright_header.py30
-rwxr-xr-xcontrib/devtools/github-merge.py413
-rwxr-xr-xcontrib/devtools/symbol-check.py4
-rwxr-xr-xcontrib/devtools/update-translations.py215
-rw-r--r--contrib/gitian-keys/keys.txt1
-rw-r--r--contrib/guix/libexec/build.sh42
-rw-r--r--contrib/init/bitcoind.service12
-rwxr-xr-xcontrib/linearize/linearize-data.py4
-rwxr-xr-xcontrib/linearize/linearize-hashes.py4
-rw-r--r--contrib/macdeploy/README.md2
-rwxr-xr-xcontrib/macdeploy/macdeployqtplus150
-rwxr-xr-xcontrib/seeds/generate-seeds.py3
-rw-r--r--contrib/testgen/README.md2
-rwxr-xr-xcontrib/verify-commits/verify-commits.py2
-rw-r--r--depends/Makefile8
-rw-r--r--depends/README.md9
-rw-r--r--depends/config.site.in4
-rw-r--r--depends/description.md4
-rw-r--r--depends/packages.md23
-rw-r--r--depends/packages/fontconfig.mk2
-rw-r--r--depends/packages/libXau.mk4
-rw-r--r--depends/packages/libxcb.mk14
-rw-r--r--depends/packages/packages.mk6
-rw-r--r--depends/packages/qrencode.mk3
-rw-r--r--depends/packages/qt.mk10
-rw-r--r--depends/packages/rapidcheck.mk6
-rw-r--r--depends/packages/xproto.mk2
-rw-r--r--depends/packages/zeromq.mk4
-rw-r--r--depends/packages/zlib.mk21
-rw-r--r--doc/REST-interface.md2
-rw-r--r--doc/benchmarking.md51
-rw-r--r--doc/bitcoin-conf.md25
-rw-r--r--doc/build-osx.md2
-rw-r--r--doc/build-unix.md32
-rw-r--r--doc/dependencies.md2
-rw-r--r--doc/developer-notes.md173
-rw-r--r--doc/init.md26
-rw-r--r--doc/rapidcheck.md84
-rw-r--r--doc/release-notes-0.18.1-16257.md6
-rw-r--r--doc/release-notes-13756.md41
-rw-r--r--doc/release-notes-14054.md7
-rw-r--r--doc/release-notes-14802.md3
-rw-r--r--doc/release-notes-14954.md3
-rw-r--r--doc/release-notes-15006.md4
-rw-r--r--doc/release-notes-15427.md9
-rw-r--r--doc/release-notes-15566.md3
-rw-r--r--doc/release-notes-15584.md4
-rw-r--r--doc/release-notes-15620.md13
-rw-r--r--doc/release-notes-15637.md3
-rw-r--r--doc/release-notes-15730.md5
-rw-r--r--doc/release-notes-15849.md6
-rw-r--r--doc/release-notes-15993.md3
-rw-r--r--doc/release-notes-16185.md6
-rw-r--r--doc/release-notes-16695.md5
-rw-r--r--doc/release-notes-16787.md3
-rw-r--r--doc/release-notes.md284
-rw-r--r--doc/release-notes/release-notes-0.18.1.md136
-rw-r--r--doc/release-notes/release-notes-16152.md7
-rw-r--r--doc/tor.md5
-rw-r--r--doc/translation_process.md6
-rw-r--r--doc/zmq.md4
-rw-r--r--share/examples/bitcoin.conf14
-rw-r--r--share/qt/Info.plist.in4
-rw-r--r--share/setup.nsi.in2
-rw-r--r--src/Makefile.am6
-rw-r--r--src/Makefile.qt.include16
-rw-r--r--src/bech32.cpp8
-rw-r--r--src/bech32.h2
-rw-r--r--src/bench/bench_bitcoin.cpp16
-rw-r--r--src/bitcoin-cli.cpp34
-rw-r--r--src/bitcoin-tx.cpp36
-rw-r--r--src/bitcoin-wallet.cpp14
-rw-r--r--src/bitcoind.cpp13
-rw-r--r--src/chain.h6
-rw-r--r--src/chainparams.cpp49
-rw-r--r--src/chainparamsbase.cpp8
-rw-r--r--src/consensus/params.h8
-rw-r--r--src/consensus/tx_check.cpp2
-rw-r--r--src/core_write.cpp4
-rw-r--r--src/dummywallet.cpp7
-rw-r--r--src/init.cpp362
-rw-r--r--src/interfaces/chain.cpp16
-rw-r--r--src/interfaces/chain.h18
-rw-r--r--src/interfaces/node.cpp13
-rw-r--r--src/interfaces/node.h11
-rw-r--r--src/interfaces/wallet.cpp2
-rw-r--r--src/net.cpp136
-rw-r--r--src/net.h184
-rw-r--r--src/net_permissions.cpp107
-rw-r--r--src/net_permissions.h62
-rw-r--r--src/net_processing.cpp446
-rw-r--r--src/netbase.cpp4
-rw-r--r--src/netbase.h2
-rw-r--r--src/node/coin.cpp3
-rw-r--r--src/node/coinstats.cpp77
-rw-r--r--src/node/coinstats.h33
-rw-r--r--src/node/transaction.cpp65
-rw-r--r--src/node/transaction.h15
-rw-r--r--src/noui.cpp44
-rw-r--r--src/noui.h6
-rw-r--r--src/obj-test/.gitignore2
-rw-r--r--src/obj/.gitignore2
-rw-r--r--src/qt/addresstablemodel.cpp10
-rw-r--r--src/qt/askpassphrasedialog.cpp52
-rw-r--r--src/qt/askpassphrasedialog.h5
-rw-r--r--src/qt/bantablemodel.cpp4
-rw-r--r--src/qt/bitcoin.cpp49
-rw-r--r--src/qt/bitcoin.h4
-rw-r--r--src/qt/bitcoin.qrc10
-rw-r--r--src/qt/bitcoinamountfield.cpp3
-rw-r--r--src/qt/bitcoingui.cpp93
-rw-r--r--src/qt/bitcoingui.h1
-rw-r--r--src/qt/bitcoinstrings.cpp5
-rw-r--r--src/qt/createwalletdialog.cpp62
-rw-r--r--src/qt/createwalletdialog.h35
-rw-r--r--src/qt/forms/askpassphrasedialog.ui2
-rw-r--r--src/qt/forms/createwalletdialog.ui151
-rw-r--r--src/qt/forms/debugwindow.ui19
-rw-r--r--src/qt/forms/intro.ui10
-rw-r--r--src/qt/guiconstants.h2
-rw-r--r--src/qt/guiutil.cpp26
-rw-r--r--src/qt/guiutil.h8
-rw-r--r--src/qt/intro.cpp18
-rw-r--r--src/qt/intro.h3
-rw-r--r--src/qt/locale/bitcoin_en.ts391
-rw-r--r--src/qt/macdockiconhandler.mm21
-rw-r--r--src/qt/networkstyle.cpp15
-rw-r--r--src/qt/networkstyle.h2
-rw-r--r--src/qt/optionsmodel.cpp22
-rw-r--r--src/qt/optionsmodel.h3
-rw-r--r--src/qt/overviewpage.cpp5
-rw-r--r--src/qt/paymentserver.cpp14
-rw-r--r--src/qt/peertablemodel.cpp4
-rw-r--r--src/qt/platformstyle.cpp5
-rw-r--r--src/qt/platformstyle.h3
-rw-r--r--src/qt/receivecoinsdialog.cpp15
-rw-r--r--src/qt/recentrequeststablemodel.cpp4
-rw-r--r--src/qt/recentrequeststablemodel.h2
-rw-r--r--src/qt/res/icons/about.pngbin3717 -> 0 bytes
-rw-r--r--src/qt/res/icons/about_qt.pngbin2240 -> 0 bytes
-rw-r--r--src/qt/res/icons/configure.pngbin2865 -> 0 bytes
-rw-r--r--src/qt/res/icons/debugwindow.pngbin1327 -> 0 bytes
-rw-r--r--src/qt/res/icons/filesave.pngbin2359 -> 0 bytes
-rw-r--r--src/qt/res/icons/info.pngbin2028 -> 0 bytes
-rw-r--r--src/qt/res/icons/key.pngbin1759 -> 0 bytes
-rw-r--r--src/qt/res/icons/open.pngbin1694 -> 0 bytes
-rw-r--r--src/qt/res/icons/quit.pngbin1091 -> 0 bytes
-rw-r--r--src/qt/res/icons/verify.pngbin2034 -> 0 bytes
-rw-r--r--src/qt/res/src/verify.svg14
-rw-r--r--src/qt/rpcconsole.cpp46
-rw-r--r--src/qt/rpcconsole.h4
-rw-r--r--src/qt/sendcoinsdialog.cpp9
-rw-r--r--src/qt/splashscreen.cpp16
-rw-r--r--src/qt/test/apptests.cpp3
-rw-r--r--src/qt/test/paymentservertests.cpp2
-rw-r--r--src/qt/test/test_main.cpp6
-rw-r--r--src/qt/transactiontablemodel.cpp6
-rw-r--r--src/qt/transactionview.cpp12
-rw-r--r--src/qt/transactionview.h2
-rw-r--r--src/qt/walletcontroller.cpp196
-rw-r--r--src/qt/walletcontroller.h82
-rw-r--r--src/qt/walletview.cpp4
-rw-r--r--src/random.cpp5
-rw-r--r--src/random.h4
-rw-r--r--src/rest.cpp4
-rw-r--r--src/rpc/blockchain.cpp265
-rw-r--r--src/rpc/client.cpp1
-rw-r--r--src/rpc/mining.cpp7
-rw-r--r--src/rpc/misc.cpp12
-rw-r--r--src/rpc/net.cpp24
-rw-r--r--src/rpc/rawtransaction.cpp29
-rw-r--r--src/rpc/rawtransaction_util.cpp90
-rw-r--r--src/rpc/rawtransaction_util.h16
-rw-r--r--src/rpc/server.cpp4
-rw-r--r--src/rpc/util.cpp32
-rw-r--r--src/rpc/util.h4
-rw-r--r--src/script/descriptor.cpp241
-rw-r--r--src/script/descriptor.h16
-rw-r--r--src/script/interpreter.cpp4
-rw-r--r--src/serialize.h13
-rw-r--r--src/test/README.md4
-rw-r--r--src/test/data/script_tests.json7
-rw-r--r--src/test/denialofservice_tests.cpp12
-rw-r--r--src/test/descriptor_tests.cpp138
-rw-r--r--src/test/getarg_tests.cpp26
-rw-r--r--src/test/merkle_tests.cpp100
-rw-r--r--src/test/miner_tests.cpp6
-rw-r--r--src/test/netbase_tests.cpp79
-rw-r--r--src/test/serialize_tests.cpp8
-rw-r--r--src/test/setup_common.cpp13
-rw-r--r--src/test/timedata_tests.cpp65
-rw-r--r--src/test/txvalidationcache_tests.cpp24
-rw-r--r--src/test/util_tests.cpp92
-rw-r--r--src/txdb.cpp2
-rw-r--r--src/txdb.h5
-rw-r--r--src/txmempool.h2
-rw-r--r--src/util/error.cpp13
-rw-r--r--src/util/error.h10
-rw-r--r--src/util/strencodings.cpp13
-rw-r--r--src/util/strencodings.h33
-rw-r--r--src/util/string.cpp5
-rw-r--r--src/util/string.h35
-rw-r--r--src/util/system.cpp148
-rw-r--r--src/util/system.h31
-rw-r--r--src/validation.cpp1116
-rw-r--r--src/validation.h129
-rw-r--r--src/versionbits.cpp1
-rw-r--r--src/versionbits.h28
-rw-r--r--src/versionbitsinfo.cpp8
-rw-r--r--src/wallet/init.cpp50
-rw-r--r--src/wallet/load.h2
-rw-r--r--src/wallet/rpcdump.cpp8
-rw-r--r--src/wallet/rpcwallet.cpp143
-rw-r--r--src/wallet/test/init_tests.cpp7
-rw-r--r--src/wallet/test/wallet_tests.cpp22
-rw-r--r--src/wallet/wallet.cpp244
-rw-r--r--src/wallet/wallet.h127
-rw-r--r--src/wallet/walletdb.cpp18
-rw-r--r--src/walletinitinterface.h4
-rwxr-xr-xtest/functional/combine_logs.py40
-rw-r--r--test/functional/data/blockheader_testnet3.hex548
-rw-r--r--test/functional/data/invalid_txs.py56
-rw-r--r--test/functional/data/wallets/high_minversion/.walletlock0
-rw-r--r--test/functional/data/wallets/high_minversion/GENERATE.md8
-rw-r--r--test/functional/data/wallets/high_minversion/db.log0
-rw-r--r--test/functional/data/wallets/high_minversion/wallet.datbin0 -> 16384 bytes
-rwxr-xr-xtest/functional/feature_assumevalid.py6
-rwxr-xr-xtest/functional/feature_bip68_sequence.py14
-rwxr-xr-xtest/functional/feature_block.py107
-rwxr-xr-xtest/functional/feature_blocksdir.py8
-rwxr-xr-xtest/functional/feature_cltv.py17
-rwxr-xr-xtest/functional/feature_config_args.py21
-rwxr-xr-xtest/functional/feature_csv_activation.py97
-rwxr-xr-xtest/functional/feature_dbcrash.py23
-rwxr-xr-xtest/functional/feature_dersig.py17
-rwxr-xr-xtest/functional/feature_fee_estimation.py32
-rwxr-xr-xtest/functional/feature_logging.py2
-rwxr-xr-xtest/functional/feature_nulldummy.py2
-rwxr-xr-xtest/functional/feature_pruning.py2
-rwxr-xr-xtest/functional/feature_segwit.py29
-rwxr-xr-xtest/functional/feature_uacomment.py4
-rwxr-xr-xtest/functional/interface_bitcoin_cli.py2
-rwxr-xr-xtest/functional/interface_zmq.py109
-rwxr-xr-xtest/functional/mempool_accept.py1
-rwxr-xr-xtest/functional/mempool_package_onemore.py10
-rwxr-xr-xtest/functional/p2p_blocksonly.py10
-rwxr-xr-xtest/functional/p2p_compactblocks.py4
-rwxr-xr-xtest/functional/p2p_dos_header_tree.py90
-rwxr-xr-xtest/functional/p2p_invalid_block.py44
-rwxr-xr-xtest/functional/p2p_invalid_messages.py2
-rwxr-xr-xtest/functional/p2p_permissions.py103
-rwxr-xr-xtest/functional/p2p_segwit.py82
-rwxr-xr-xtest/functional/p2p_tx_download.py175
-rwxr-xr-xtest/functional/rpc_bind.py2
-rwxr-xr-xtest/functional/rpc_blockchain.py28
-rwxr-xr-xtest/functional/rpc_createmultisig.py21
-rwxr-xr-xtest/functional/rpc_deriveaddresses.py4
-rwxr-xr-xtest/functional/rpc_fundrawtransaction.py157
-rwxr-xr-xtest/functional/rpc_invalidateblock.py6
-rwxr-xr-xtest/functional/rpc_net.py39
-rwxr-xr-xtest/functional/rpc_psbt.py24
-rwxr-xr-xtest/functional/rpc_scantxoutset.py7
-rwxr-xr-xtest/functional/rpc_setban.py47
-rw-r--r--test/functional/test_framework/address.py9
-rw-r--r--test/functional/test_framework/blocktools.py28
-rwxr-xr-xtest/functional/test_framework/messages.py8
-rwxr-xr-xtest/functional/test_framework/mininode.py37
-rwxr-xr-xtest/functional/test_framework/test_framework.py21
-rwxr-xr-xtest/functional/test_framework/test_node.py31
-rw-r--r--test/functional/test_framework/util.py50
-rwxr-xr-xtest/functional/test_runner.py32
-rwxr-xr-xtest/functional/tool_wallet.py1
-rwxr-xr-xtest/functional/wallet_address_types.py18
-rwxr-xr-xtest/functional/wallet_backup.py1
-rwxr-xr-xtest/functional/wallet_basic.py30
-rwxr-xr-xtest/functional/wallet_createwallet.py15
-rwxr-xr-xtest/functional/wallet_import_rescan.py77
-rwxr-xr-xtest/functional/wallet_importmulti.py6
-rwxr-xr-xtest/functional/wallet_multiwallet.py31
-rwxr-xr-xtest/functional/wallet_reorgsrestore.py105
-rwxr-xr-xtest/functional/wallet_watchonly.py107
-rwxr-xr-xtest/lint/check-doc.py4
-rwxr-xr-xtest/lint/check-rpc-mappings.py6
-rwxr-xr-xtest/lint/lint-format-strings.py2
-rwxr-xr-xtest/lint/lint-includes.sh3
-rw-r--r--test/lint/lint-python-dead-code-whitelist45
-rwxr-xr-xtest/lint/lint-python-dead-code.sh4
-rwxr-xr-xtest/lint/lint-python-mutable-default-parameters.sh52
-rwxr-xr-xtest/lint/lint-python.sh1
-rw-r--r--test/lint/lint-spelling.ignore-words.txt1
-rwxr-xr-xtest/lint/lint-spelling.sh5
-rw-r--r--test/util/data/txcreateoutpubkey1.json6
346 files changed, 8948 insertions, 5295 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
index ac17e2eeb6..ed2ab49554 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -7,12 +7,16 @@ clone_depth: 5
environment:
APPVEYOR_SAVE_CACHE_ON_ERROR: true
CLCACHE_SERVER: 1
- PACKAGES: berkeleydb boost-filesystem boost-signals2 boost-test libevent openssl rapidcheck zeromq
+ PACKAGES: berkeleydb boost-filesystem boost-signals2 boost-test libevent openssl rapidcheck zeromq double-conversion
PATH: 'C:\Python37-x64;C:\Python37-x64\Scripts;%PATH%'
PYTHONUTF8: 1
+ QT_DOWNLOAD_URL: 'https://github.com/sipsorcery/qt_win_binary/releases/download/v1.0/Qt5.9.7_ssl_x64_static_vs2017.zip'
+ QT_DOWNLOAD_HASH: 'D4D35B8112302B67E5610A03421BB3E43FE13F14D9A5F637C22AE60DCEC0E0F5'
+ QT_LOCAL_PATH: 'C:\Qt5.9.7_ssl_x64_static_vs2017'
cache:
-- C:\tools\vcpkg\installed -> .appveyor.yml
+- C:\tools\vcpkg\installed
- C:\Users\appveyor\clcache -> .appveyor.yml, build_msvc\**, **\Makefile.am, **\*.vcxproj.in
+- C:\Qt5.9.7_ssl_x64_static_vs2017
install:
- cmd: pip install --quiet git+https://github.com/frerich/clcache.git@v4.2.0
# Disable zmq test for now since python zmq library on Windows would cause Access violation sometimes.
@@ -22,6 +26,23 @@ install:
- cmd: vcpkg install --triplet %PLATFORM%-windows-static %PACKAGES% > NUL
before_build:
- ps: clcache -M 536870912
+- ps: |
+ if(!(Test-Path -Path ($env:QT_LOCAL_PATH))) {
+ Write-Host "Downloading Qt binaries.";
+ Invoke-WebRequest -Uri $env:QT_DOWNLOAD_URL -Out qtdownload.zip;
+ Write-Host "Qt binaries successfully downloaded, checking hash against $env:QT_DOWNLOAD_HASH...";
+ if((Get-FileHash qtdownload.zip).Hash -eq $env:QT_DOWNLOAD_HASH) {
+ Expand-Archive qtdownload.zip -DestinationPath $env:QT_LOCAL_PATH;
+ Write-Host "Qt binary download matched the expected hash.";
+ }
+ else {
+ Write-Host "ERROR: Qt binary download did not match the expected hash.";
+ Exit-AppveyorBuild;
+ }
+ }
+ else {
+ Write-Host "Qt binaries already present.";
+ }
- cmd: python build_msvc\msvc-autogen.py
- ps: $files = (Get-ChildItem -Recurse | where {$_.extension -eq ".vcxproj"}).FullName
- ps: for (${i} = 0; ${i} -lt ${files}.length; ${i}++) {
@@ -37,10 +58,13 @@ build_script:
after_build:
- ps: fsutil behavior set disablelastaccess 1 # Disable Access time feature on Windows (better performance)
- ps: clcache -z
+#- 7z a bitcoin-%APPVEYOR_BUILD_VERSION%.zip %APPVEYOR_BUILD_FOLDER%\build_msvc\%platform%\%configuration%\*.exe
test_script:
- cmd: src\test_bitcoin.exe -k stdout -e stdout 2> NUL
- cmd: src\bench_bitcoin.exe -evals=1 -scaling=0 > NUL
- ps: python test\util\bitcoin-util-test.py
- cmd: python test\util\rpcauth-test.py
- cmd: python test\functional\test_runner.py --ci --quiet --combinedlogslen=4000 --failfast
+artifacts:
+#- path: bitcoin-%APPVEYOR_BUILD_VERSION%.zip
deploy: off
diff --git a/.cirrus.yml b/.cirrus.yml
index ce17a223b1..1e6e6937da 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -27,3 +27,27 @@ task:
- gmake check ${MAKEJOBS} VERBOSE=1
functional_test_script:
- ./test/functional/test_runner.py --jobs 9 --ci --extended --exclude feature_dbcrash --combinedlogslen=1000 --quiet --failfast
+task:
+ name: "x86_64 Linux [GOAL: install] [bionic] [Using ./ci/ system]"
+ container:
+ image: ubuntu:18.04
+ cpu: 8
+ memory: 8G
+ timeout_in: 60m
+ env:
+ MAKEJOBS: "-j9"
+ RUN_CI_ON_HOST: "1"
+ CCACHE_SIZE: "200M"
+ CCACHE_DIR: "/tmp/ccache_dir"
+ ccache_cache:
+ folder: "/tmp/ccache_dir"
+ depends_built_cache:
+ folder: "/tmp/cirrus-ci-build/depends/built"
+ depends_sdk_cache:
+ folder: "/tmp/cirrus-ci-build/depends/sdk-sources"
+ install_script:
+ - apt-get update
+ - apt-get -y install git bash ccache
+ - ccache --max-size=${CCACHE_SIZE}
+ ci_script:
+ - ./ci/test_run_all.sh
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
index 8768a8ca6b..35b42424ad 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE.md
@@ -17,7 +17,7 @@ If the node is "stuck" during sync or giving "block checksum mismatch" errors, p
<!-- What type of machine are you observing the error on (OS/CPU and disk type)? -->
-<!-- For the GUI-related issue on Linux provide names and versions of a distro, a desktop environment and a graphical shell (if relevant). -->
+<!-- GUI-related issue? What is your operating system and its version? If Linux, what is your desktop environment and graphical shell? -->
<!-- Any extra information that might be useful in the debugging process. -->
<!--- This is normally the contents of a `debug.log` or `config.log` file. Raw text or a link to a pastebin type site are preferred. -->
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 0000000000..bf094e8325
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,41 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: Bug
+assignees: ''
+
+---
+
+<!-- This issue tracker is only for technical issues related to Bitcoin Core.
+
+General bitcoin questions and/or support requests are best directed to the Bitcoin StackExchange at https://bitcoin.stackexchange.com.
+
+For reporting security issues, please read instructions at https://bitcoincore.org/en/contact/.
+
+If the node is "stuck" during sync or giving "block checksum mismatch" errors, please ensure your hardware is stable by running memtest and observe CPU temperature with a load-test tool such as linpack before creating an issue! -->
+
+<!-- Describe the issue -->
+
+**Expected behavior**
+
+<!--- What behavior did you expect? -->
+
+**Actual behavior**
+
+<!--- What was the actual behavior (provide screenshots if the issue is GUI-related)? -->
+
+**To reproduce**
+
+<!--- How reliably can you reproduce the issue, what are the steps to do so? -->
+
+**System information**
+
+<!-- What version of Bitcoin Core are you using, where did you get it (website, self-compiled, etc)? -->
+
+<!-- What type of machine are you observing the error on (OS/CPU and disk type)? -->
+
+<!-- GUI-related issue? What is your operating system and its version? If Linux, what is your desktop environment and graphical shell? -->
+
+<!-- Any extra information that might be useful in the debugging process. -->
+<!--- This is normally the contents of a `debug.log` or `config.log` file. Raw text or a link to a pastebin type site are preferred. -->
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 0000000000..2d5685185e
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,20 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+title: ''
+labels: Feature
+assignees: ''
+
+---
+
+**Is your feature request related to a problem? Please describe.**
+<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
+
+**Describe the solution you'd like**
+<!-- A clear and concise description of what you want to happen. -->
+
+**Describe alternatives you've considered**
+<!-- A clear and concise description of any alternative solutions or features you've considered. -->
+
+**Additional context**
+<!-- Add any other context or screenshots about the feature request here. -->
diff --git a/.gitignore b/.gitignore
index cec1fb3dec..809e851ff1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,6 +35,7 @@ libtool
src/config/bitcoin-config.h
src/config/bitcoin-config.h.in
src/config/stamp-h1
+src/obj
share/setup.nsi
share/qt/Info.plist
@@ -91,6 +92,9 @@ bitcoin-qt
Bitcoin-Qt.app
background.tiff*
+# Qt Creator
+Makefile.am.user
+
# Unit-tests
Makefile.test
bitcoin-qt_test
@@ -129,3 +133,7 @@ db4/
# clang-check
*.plist
+
+osx_volname
+dist/
+*.background.tiff
diff --git a/.travis.yml b/.travis.yml
index 422e9149f4..ec08ab2efb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -17,7 +17,7 @@
# Bitcoin Core GitHub member via the Travis web interface [0].
#
# Travis CI uploads the cache after the script phase of the build [1].
-# However, the build is terminated without saving the chache if it takes over
+# However, the build is terminated without saving the cache if it takes over
# 50 minutes [2]. Thus, if we spent too much time in early build stages, fail
# with an error and save the cache.
#
@@ -33,43 +33,32 @@ cache:
directories:
- $TRAVIS_BUILD_DIR/depends/built
- $TRAVIS_BUILD_DIR/depends/sdk-sources
- - $HOME/.ccache
+ - $TRAVIS_BUILD_DIR/ci/scratch/.ccache
stages:
- lint
- test
- extended-lint
env:
global:
- - MAKEJOBS=-j3
- - RUN_UNIT_TESTS=true
- - RUN_FUNCTIONAL_TESTS=true
- - RUN_FUZZ_TESTS=false
- - DOCKER_NAME_TAG=ubuntu:18.04
- - BOOST_TEST_RANDOM=1$TRAVIS_BUILD_ID
- - CCACHE_SIZE=100M
- - CCACHE_TEMPDIR=/tmp/.ccache-temp
- - CCACHE_COMPRESS=1
- - CCACHE_DIR=$HOME/.ccache
- - BASE_OUTDIR=$TRAVIS_BUILD_DIR/out
- - SDK_URL=https://bitcoincore.org/depends-sources/sdks
- - WINEDEBUG=fixme-all
- - DOCKER_PACKAGES="build-essential libtool autotools-dev automake pkg-config bsdmainutils curl git ca-certificates ccache"
+ - CI_RETRY_EXE="travis_retry"
- CACHE_ERR_MSG="Error! Initial build successful, but not enough time remains to run later build stages and tests. Please manually re-run this job by using the travis restart button or asking a bitcoin maintainer to restart. The next run should not time out because the build cache has been saved."
before_install:
- - set -o errexit; source .travis/test_03_before_install.sh
+ - set -o errexit; source ./ci/test/00_setup_env.sh
+ - set -o errexit; source ./ci/test/03_before_install.sh
install:
- - set -o errexit; source .travis/test_04_install.sh
+ - set -o errexit; source ./ci/test/04_install.sh
before_script:
- - set -o errexit; source .travis/test_05_before_script.sh
+ - set -o errexit; source ./ci/test/05_before_script.sh
script:
- export CONTINUE=1
- if [ $SECONDS -gt 1200 ]; then export CONTINUE=0; fi # Likely the depends build took very long
- - if [ $CONTINUE = "1" ]; then set -o errexit; source .travis/test_06_script_a.sh; else set +o errexit; echo "$CACHE_ERR_MSG"; false; fi
+ - if [ $TRAVIS_REPO_SLUG = "bitcoin/bitcoin" ]; then export CONTINUE=1; fi # Whitelisted repo (90 minutes build time)
+ - if [ $CONTINUE = "1" ]; then set -o errexit; source ./ci/test/06_script_a.sh; else set +o errexit; echo "$CACHE_ERR_MSG"; false; fi
- if [ $SECONDS -gt 2000 ]; then export CONTINUE=0; fi # Likely the build took very long; The tests take about 1000s, so we should abort if we have less than 50*60-1000=2000s left
- - if [ $CONTINUE = "1" ]; then set -o errexit; source .travis/test_06_script_b.sh; else set +o errexit; echo "$CACHE_ERR_MSG"; false; fi
+ - if [ $TRAVIS_REPO_SLUG = "bitcoin/bitcoin" ]; then export CONTINUE=1; fi # Whitelisted repo (90 minutes build time)
+ - if [ $CONTINUE = "1" ]; then set -o errexit; source ./ci/test/06_script_b.sh; else set +o errexit; echo "$CACHE_ERR_MSG"; false; fi
after_script:
- echo $TRAVIS_COMMIT_RANGE
- - echo $TRAVIS_COMMIT_LOG
jobs:
include:
@@ -80,11 +69,11 @@ jobs:
language: python
python: '3.5' # Oldest supported version according to doc/dependencies.md
install:
- - set -o errexit; source .travis/lint_04_install.sh
+ - set -o errexit; source ./ci/lint/04_install.sh
before_script:
- - set -o errexit; source .travis/lint_05_before_script.sh
+ - set -o errexit; source ./ci/lint/05_before_script.sh
script:
- - set -o errexit; source .travis/lint_06_script.sh
+ - set -o errexit; source ./ci/lint/06_script.sh
- stage: extended-lint
name: 'extended lint [runtime >= 60 seconds]'
@@ -93,110 +82,58 @@ jobs:
language: python
python: '3.5'
install:
- - set -o errexit; source .travis/extended_lint_04_install.sh
+ - set -o errexit; source ./ci/extended_lint/04_install.sh
before_script:
- - set -o errexit; source .travis/lint_05_before_script.sh
+ - set -o errexit; source ./ci/lint/05_before_script.sh
script:
- - set -o errexit; source .travis/extended_lint_06_script.sh
+ - set -o errexit; source ./ci/extended_lint/06_script.sh
- stage: test
name: 'ARM [GOAL: install] [no unit or functional tests]'
env: >-
- HOST=arm-linux-gnueabihf
- PACKAGES="python3 g++-arm-linux-gnueabihf"
- RUN_UNIT_TESTS=false
- RUN_FUNCTIONAL_TESTS=false
- GOAL="install"
- # -Wno-psabi is to disable ABI warnings: "note: parameter passing for argument of type ... changed in GCC 7.1"
- # This could be removed once the ABI change warning does not show up by default
- BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports CXXFLAGS=-Wno-psabi"
+ FILE_ENV="./ci/test/00_setup_env_arm.sh"
- stage: test
name: 'Win64 [GOAL: deploy] [no gui or functional tests]'
env: >-
- HOST=x86_64-w64-mingw32
- PACKAGES="python3 nsis g++-mingw-w64-x86-64 wine-binfmt wine64"
- RUN_FUNCTIONAL_TESTS=false
- GOAL="deploy"
- BITCOIN_CONFIG="--enable-reduce-exports --disable-gui-tests"
+ FILE_ENV="./ci/test/00_setup_env_win64.sh"
- stage: test
- name: '32-bit + dash [GOAL: install] [GUI: no BIP70]'
+ name: '32-bit + dash [GOAL: install] [GUI: BIP70 enabled]'
env: >-
- HOST=i686-pc-linux-gnu
- PACKAGES="g++-multilib python3-zmq"
- GOAL="install"
- BITCOIN_CONFIG="--enable-zmq --with-gui=qt5 --disable-bip70 --enable-glibc-back-compat --enable-reduce-exports LDFLAGS=-static-libstdc++"
- CONFIG_SHELL="/bin/dash"
+ FILE_ENV="./ci/test/00_setup_env_i686.sh"
- stage: test
name: 'x86_64 Linux [GOAL: install] [bionic] [uses qt5 dev package instead of depends Qt to speed up build and avoid timeout] [unsigned char]'
env: >-
- HOST=x86_64-unknown-linux-gnu
- PACKAGES="python3-zmq qtbase5-dev qttools5-dev-tools protobuf-compiler libdbus-1-dev libharfbuzz-dev libprotobuf-dev"
- DEP_OPTS="NO_QT=1 NO_UPNP=1 DEBUG=1 ALLOW_HOST_PACKAGES=1"
- TEST_RUNNER_EXTRA="--coverage --extended --exclude feature_dbcrash" # Run extended tests so that coverage does not fail, but exclude the very slow dbcrash
- GOAL="install"
- BITCOIN_CONFIG="--enable-zmq --with-gui=qt5 --enable-glibc-back-compat --enable-reduce-exports --enable-debug CFLAGS=\"-g0 -O2 -funsigned-char\" CXXFLAGS=\"-g0 -O2 -funsigned-char\""
+ FILE_ENV="./ci/test/00_setup_env_amd64_qt5.sh"
- stage: test
name: 'x86_64 Linux [GOAL: install] [trusty] [no functional tests, no depends, only system libs]'
env: >-
- HOST=x86_64-unknown-linux-gnu
- DOCKER_NAME_TAG=ubuntu:14.04
- PACKAGES="python3-zmq qtbase5-dev qttools5-dev-tools libicu-dev libpng-dev libssl-dev libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-test-dev libboost-thread-dev libdb5.1++-dev libzmq3-dev libprotobuf-dev protobuf-compiler libqrencode-dev"
- NO_DEPENDS=1
- RUN_FUNCTIONAL_TESTS=false
- GOAL="install"
- BITCOIN_CONFIG="--enable-zmq --with-incompatible-bdb --with-gui=no"
+ FILE_ENV="./ci/test/00_setup_env_amd64_trusty.sh"
- stage: test
name: 'x86_64 Linux [GOAL: install] [xenial] [no depends, only system libs, sanitizers: thread (TSan), no wallet]'
env: >-
- HOST=x86_64-unknown-linux-gnu
- DOCKER_NAME_TAG=ubuntu:16.04
- PACKAGES="clang llvm python3-zmq qtbase5-dev qttools5-dev-tools libssl-dev libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libzmq3-dev libprotobuf-dev protobuf-compiler libqrencode-dev"
- NO_DEPENDS=1
- GOAL="install"
- BITCOIN_CONFIG="--enable-zmq --disable-wallet --with-gui=qt5 CPPFLAGS=-DDEBUG_LOCKORDER --with-sanitizers=thread --disable-hardening --disable-asm CC=clang CXX=clang++"
+ FILE_ENV="./ci/test/00_setup_env_amd64_tsan.sh"
- stage: test
name: 'x86_64 Linux [GOAL: install] [bionic] [no depends, only system libs, sanitizers: address/leak (ASan + LSan) + undefined (UBSan) + integer]'
env: >-
- HOST=x86_64-unknown-linux-gnu
- PACKAGES="clang llvm python3-zmq qtbase5-dev qttools5-dev-tools libssl1.0-dev libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libzmq3-dev libprotobuf-dev protobuf-compiler libqrencode-dev"
- NO_DEPENDS=1
- GOAL="install"
- BITCOIN_CONFIG="--enable-zmq --with-incompatible-bdb --with-gui=qt5 CPPFLAGS=-DDEBUG_LOCKORDER --with-sanitizers=address,integer,undefined CC=clang CXX=clang++"
+ FILE_ENV="./ci/test/00_setup_env_amd64_asan.sh"
- stage: test
name: 'x86_64 Linux [GOAL: install] [bionic] [no depends, only system libs, sanitizers: fuzzer,address]'
env: >-
- HOST=x86_64-unknown-linux-gnu
- PACKAGES="clang llvm python3 libssl1.0-dev libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-test-dev libboost-thread-dev"
- NO_DEPENDS=1
- RUN_UNIT_TESTS=false
- RUN_FUNCTIONAL_TESTS=false
- RUN_FUZZ_TESTS=true
- GOAL="install"
- BITCOIN_CONFIG="--enable-fuzz --with-sanitizers=fuzzer,address CC=clang CXX=clang++"
+ FILE_ENV="./ci/test/00_setup_env_amd64_fuzz.sh"
- stage: test
name: 'x86_64 Linux [GOAL: install] [bionic] [no wallet]'
env: >-
- HOST=x86_64-unknown-linux-gnu
- PACKAGES="python3-zmq"
- DEP_OPTS="NO_WALLET=1"
- GOAL="install"
- BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports"
+ FILE_ENV="./ci/test/00_setup_env_amd64_nowallet.sh"
- stage: test
name: 'macOS 10.10 [GOAL: deploy] [no functional tests]'
env: >-
- HOST=x86_64-apple-darwin14
- PACKAGES="cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python3-dev python3-setuptools"
- OSX_SDK=10.11
- RUN_UNIT_TESTS=false
- RUN_FUNCTIONAL_TESTS=false
- GOAL="deploy"
- BITCOIN_CONFIG="--enable-gui --enable-reduce-exports --enable-werror"
+ FILE_ENV="./ci/test/00_setup_env_mac.sh"
diff --git a/.travis/README.md b/.travis/README.md
deleted file mode 100644
index 21d1b9cc03..0000000000
--- a/.travis/README.md
+++ /dev/null
@@ -1,8 +0,0 @@
-## travis build scripts
-
-The `.travis` directory contains scripts for each build step in each build stage.
-Currently the travis build defines two stages `lint` and `test`. Each stage has
-it's own [lifecycle](https://docs.travis-ci.com/user/customizing-the-build/#the-build-lifecycle).
-Every script in here is named and numbered according to which stage and lifecycle
-step it belongs to.
-
diff --git a/.travis/test_04_install.sh b/.travis/test_04_install.sh
deleted file mode 100755
index 319f2c5b21..0000000000
--- a/.travis/test_04_install.sh
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/usr/bin/env bash
-#
-# Copyright (c) 2018 The Bitcoin Core developers
-# Distributed under the MIT software license, see the accompanying
-# file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-export LC_ALL=C.UTF-8
-
-free -m -h
-echo "Number of CPUs (nproc): $(nproc)"
-
-travis_retry docker pull "$DOCKER_NAME_TAG"
-
-export DIR_FUZZ_IN=${TRAVIS_BUILD_DIR}/qa-assets
-git clone https://github.com/bitcoin-core/qa-assets ${DIR_FUZZ_IN}
-export DIR_FUZZ_IN=${DIR_FUZZ_IN}/fuzz_seed_corpus/
-
-mkdir -p "${TRAVIS_BUILD_DIR}/sanitizer-output/"
-export ASAN_OPTIONS=""
-export LSAN_OPTIONS="suppressions=${TRAVIS_BUILD_DIR}/test/sanitizer_suppressions/lsan"
-export TSAN_OPTIONS="suppressions=${TRAVIS_BUILD_DIR}/test/sanitizer_suppressions/tsan:log_path=${TRAVIS_BUILD_DIR}/sanitizer-output/tsan"
-export UBSAN_OPTIONS="suppressions=${TRAVIS_BUILD_DIR}/test/sanitizer_suppressions/ubsan:print_stacktrace=1:halt_on_error=1"
-env | grep -E '^(BITCOIN_CONFIG|CCACHE_|WINEDEBUG|LC_ALL|BOOST_TEST_RANDOM|CONFIG_SHELL|(ASAN|LSAN|TSAN|UBSAN)_OPTIONS)' | tee /tmp/env
-if [[ $HOST = *-mingw32 ]]; then
- DOCKER_ADMIN="--cap-add SYS_ADMIN"
-elif [[ $BITCOIN_CONFIG = *--with-sanitizers=*address* ]]; then # If ran with (ASan + LSan), Docker needs access to ptrace (https://github.com/google/sanitizers/issues/764)
- DOCKER_ADMIN="--cap-add SYS_PTRACE"
-fi
-DOCKER_ID=$(docker run $DOCKER_ADMIN -idt --mount type=bind,src=$TRAVIS_BUILD_DIR,dst=$TRAVIS_BUILD_DIR --mount type=bind,src=$CCACHE_DIR,dst=$CCACHE_DIR -w $TRAVIS_BUILD_DIR --env-file /tmp/env $DOCKER_NAME_TAG)
-
-DOCKER_EXEC () {
- docker exec $DOCKER_ID bash -c "cd $PWD && $*"
-}
-
-travis_retry DOCKER_EXEC apt-get update
-travis_retry DOCKER_EXEC apt-get install --no-install-recommends --no-upgrade -qq $PACKAGES $DOCKER_PACKAGES
-
diff --git a/.tx/config b/.tx/config
index 743510a7f2..0e18a0df98 100644
--- a/.tx/config
+++ b/.tx/config
@@ -1,7 +1,7 @@
[main]
host = https://www.transifex.com
-[bitcoin.qt-translation-018x]
+[bitcoin.qt-translation-019x]
file_filter = src/qt/locale/bitcoin_<lang>.ts
source_file = src/qt/locale/bitcoin_en.ts
source_lang = en
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 268259c82d..1f4a2c081e 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -79,25 +79,26 @@ about Git.
The title of the pull request should be prefixed by the component or area that
the pull request affects. Valid areas as:
- - *Consensus* for changes to consensus critical code
- - *Doc* for changes to the documentation
- - *Qt* for changes to bitcoin-qt
- - *Log* Changes to log messages
- - *Mining* for changes to the mining code
- - *Net* or *P2P* for changes to the peer-to-peer network code
- - *Refactor* for structural changes that do not change behavior
- - *RPC/REST/ZMQ* for changes to the RPC, REST or ZMQ APIs
- - *Scripts and tools* for changes to the scripts and tools
- - *Test* for changes to the bitcoin unit tests or QA tests
- - *Utils and libraries* for changes to the utils and libraries
- - *Wallet* for changes to the wallet code
+ - `consensus` for changes to consensus critical code
+ - `doc` for changes to the documentation
+ - `qt` or `gui` for changes to bitcoin-qt
+ - `log` for changes to log messages
+ - `mining` for changes to the mining code
+ - `net` or `p2p` for changes to the peer-to-peer network code
+ - `refactor` for structural changes that do not change behavior
+ - `rpc`, `rest` or `zmq` for changes to the RPC, REST or ZMQ APIs
+ - `script` for changes to the scripts and tools
+ - `test` for changes to the bitcoin unit tests or QA tests
+ - `util` or `lib` for changes to the utils or libraries
+ - `wallet` for changes to the wallet code
+ - `build` for changes to the GNU Autotools, reproducible builds or CI code
Examples:
- Consensus: Add new opcode for BIP-XXXX OP_CHECKAWESOMESIG
- Net: Automatically create hidden service, listen on Tor
- Qt: Add feed bump button
- Log: Fix typo in log message
+ consensus: Add new opcode for BIP-XXXX OP_CHECKAWESOMESIG
+ net: Automatically create hidden service, listen on Tor
+ qt: Add feed bump button
+ log: Fix typo in log message
Note that translations should not be submitted as pull requests, please see
[Translation Process](https://github.com/bitcoin/bitcoin/blob/master/doc/translation_process.md)
diff --git a/Makefile.am b/Makefile.am
index 712f2a454d..8b1e2a6b5b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -310,4 +310,5 @@ clean-docs:
clean-local: clean-docs
rm -rf coverage_percent.txt test_bitcoin.coverage/ total.coverage/ test/tmp/ cache/ $(OSX_APP)
rm -rf test/functional/__pycache__ test/functional/test_framework/__pycache__ test/cache share/rpcauth/__pycache__
+ rm -rf osx_volname dist/ dpi36.background.tiff dpi72.background.tiff
diff --git a/build-aux/m4/bitcoin_find_bdb48.m4 b/build-aux/m4/bitcoin_find_bdb48.m4
index ea9c795daa..aa0111e5a2 100644
--- a/build-aux/m4/bitcoin_find_bdb48.m4
+++ b/build-aux/m4/bitcoin_find_bdb48.m4
@@ -61,7 +61,7 @@ AC_DEFUN([BITCOIN_FIND_BDB48],[
BDB_CPPFLAGS=${BDB_CFLAGS}
fi
AC_SUBST(BDB_CPPFLAGS)
-
+
if test "x$BDB_LIBS" = "x"; then
# TODO: Ideally this could find the library version and make sure it matches the headers being used
for searchlib in db_cxx-4.8 db_cxx db4_cxx; do
diff --git a/build_msvc/.gitignore b/build_msvc/.gitignore
index 8ba65dda8f..4d4aef7e35 100644
--- a/build_msvc/.gitignore
+++ b/build_msvc/.gitignore
@@ -10,3 +10,5 @@ packages/*
*.vcxproj.user
*.vcxproj
*/Win32
+libbitcoin_qt/QtGeneratedFiles/*
+test_bitcoin-qt/QtGeneratedFiles/*
diff --git a/build_msvc/README.md b/build_msvc/README.md
index 2e93979aca..88b1f514bd 100644
--- a/build_msvc/README.md
+++ b/build_msvc/README.md
@@ -3,13 +3,23 @@ Building Bitcoin Core with Visual Studio
Introduction
---------------------
-Solution and project files to build the Bitcoin Core applications (except Qt dependent ones) with Visual Studio 2017 can be found in the build_msvc directory.
+Solution and project files to build the Bitcoin Core applications `msbuild` or Visual Studio can be found in the build_msvc directory. The build has been tested with Visual Studio 2017 and 2019.
Building with Visual Studio is an alternative to the Linux based [cross-compiler build](https://github.com/bitcoin/bitcoin/blob/master/doc/build-windows.md).
+Quick Start
+---------------------
+The minimal steps required to build Bitcoin Core with the msbuild toolchain are below. More detailed instructions are contained in the following sections.
+
+```
+vcpkg install --triplet x64-windows-static boost-filesystem boost-signals2 boost-test libevent openssl zeromq berkeleydb rapidcheck double-conversion
+py -3 build_msvc\msvc-autogen.py
+msbuild /m build_msvc\bitcoin.sln /p:Platform=x64 /p:Configuration=Release /t:build
+```
+
Dependencies
---------------------
-A number of [open source libraries](https://github.com/bitcoin/bitcoin/blob/master/doc/dependencies.md) are required in order to be able to build Bitcoin.
+A number of [open source libraries](https://github.com/bitcoin/bitcoin/blob/master/doc/dependencies.md) are required in order to be able to build Bitcoin Core.
Options for installing the dependencies in a Visual Studio compatible manner are:
@@ -17,18 +27,30 @@ Options for installing the dependencies in a Visual Studio compatible manner are
- Download the source code, build each dependency, add the required include paths, link libraries and binary tools to the Visual Studio project files.
- Use [nuget](https://www.nuget.org/) packages with the understanding that any binary files have been compiled by an untrusted third party.
-The external dependencies required for the Visual Studio build are (see [dependencies.md](https://github.com/bitcoin/bitcoin/blob/master/doc/dependencies.md) for more info):
+The [external dependencies](https://github.com/bitcoin/bitcoin/blob/master/doc/dependencies.md) required for building are:
- Berkeley DB
-- OpenSSL
- Boost
+- DoubleConversion
- libevent
-- ZeroMQ
+- OpenSSL
+- Qt5
- RapidCheck
+- ZeroMQ
+
+Qt
+---------------------
+All the Bitcoin Core applications are configured to build with static linking. In order to build the Bitcoin Core Qt applications a static build of Qt is required.
+
+The runtime library version (e.g. v141, v142) and platform type (x86 or x64) must also match. OpenSSL must also be linked into the Qt binaries in order to provide full functionality of the Bitcoin Core Qt programs. An example of the configure command to build Qtv5.9.7 locally to link with Bitcoin Core is shown below (adjust paths accordingly), note it can be expected that the configure and subsequent build will fail numerous times until dependency issues are resolved.
+
+````
+..\Qtv5.9.7_src\configure -developer-build -confirm-license -debug-and-release -opensource -platform win32-msvc -opengl desktop -no-shared -static -no-static-runtime -mp -qt-zlib -qt-pcre -qt-libpng -ltcg -make libs -make tools -no-libjpeg -nomake examples -no-compile-examples -no-dbus -no-libudev -no-qml-debug -no-icu -no-gtk -no-opengles3 -no-angle -no-sql-sqlite -no-sql-odbc -no-sqlite -no-libudev -skip qt3d -skip qtactiveqt -skip qtandroidextras -skip qtcanvas3d -skip qtcharts -skip qtconnectivity -skip qtdatavis3d -skip qtdeclarative -skip qtdoc -skip qtgamepad -skip qtgraphicaleffects -skip qtimageformats -skip qtlocation -skip qtmacextras -skip qtmultimedia -skip qtnetworkauth -skip qtpurchasing -skip qtquickcontrols -skip qtquickcontrols2 -skip qtscript -skip qtscxml -skip qtsensors -skip qtserialbus -skip qtserialport -skip qtspeech -skip qtvirtualkeyboard -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebsockets -skip qtwebview -skip qtx11extras -skip qtxmlpatterns -nomake tests -openssl-linked -IC:\Dev\github\vcpkg\installed\x64-windows-static\include -LC:\Dev\github\vcpkg\installed\x64-windows-static\lib OPENSSL_LIBS="-llibeay32 -lssleay32 -lgdi32 -luser32 -lwsock32 -ladvapi32" -prefix C:\Qt5.9.7_ssl_x64_static_vs2017
+````
+
+A prebuilt version for x64 and Visual C++ runtime v141 (Visual Studio 2017) can be downloaded from [here](https://github.com/sipsorcery/qt_win_binary/releases). Please be aware this download is NOT an officially sanctioned Bitcoin Core distribution and is provided for developer convenience. It should NOT be used for builds that will be used in a production environment or with real funds.
-Additional dependencies required from the [bitcoin-core](https://github.com/bitcoin-core) GitHub repository are:
-- libsecp256k1
-- LevelDB
+To build Bitcoin Core without Qt unload or disable the bitcoin-qt, libbitcoin_qt and test_bitcoin-qt projects.
Building
---------------------
@@ -38,7 +60,7 @@ The instructions below use `vcpkg` to install the dependencies.
- Install the required packages (replace x64 with x86 as required):
```
- PS >.\vcpkg install --triplet x64-windows-static boost-filesystem boost-signals2 boost-test libevent openssl zeromq berkeleydb secp256k1 leveldb rapidcheck
+ PS >.\vcpkg install --triplet x64-windows-static boost-filesystem boost-signals2 boost-test libevent openssl zeromq berkeleydb rapidcheck double-conversion
```
- Use Python to generate *.vcxproj from Makefile
@@ -47,4 +69,27 @@ The instructions below use `vcpkg` to install the dependencies.
PS >py -3 msvc-autogen.py
```
-- Build in Visual Studio.
+- An optional step is to adjust the settings in the build_msvc directory and the common.init.vcxproj file. This project file contains settings that are common to all projects such as the runtime library version and target Windows SDK version. The Qt directories can also be set.
+
+- Build with Visual Studio 2017 or msbuild.
+
+```
+msbuild /m bitcoin.sln /p:Platform=x64 /p:Configuration=Release /t:build
+```
+
+- Build with Visual Studio 2019 or msbuild.
+
+```
+msbuild /m bitcoin.sln /p:Platform=x64 /p:Configuration=Release /p:PlatformToolset=v142 /t:build
+```
+
+AppVeyor
+---------------------
+The .appveyor.yml in the root directory is suitable to perform builds on [AppVeyor](https://www.appveyor.com/) Continuous Integration servers. The simplest way to perform an AppVeyor build is to fork Bitcoin Core and then configure a new AppVeyor Project pointing to the forked repository.
+
+For safety reasons the Bitcoin Core .appveyor.yml file has the artifact options disabled. The build will be performed but no executable files will be available. To enable artifacts on a forked repository uncomment the lines shown below:
+
+```
+ #- 7z a bitcoin-%APPVEYOR_BUILD_VERSION%.zip %APPVEYOR_BUILD_FOLDER%\build_msvc\%platform%\%configuration%\*.exe
+ #- path: bitcoin-%APPVEYOR_BUILD_VERSION%.zip
+```
diff --git a/build_msvc/bench_bitcoin/bench_bitcoin.vcxproj b/build_msvc/bench_bitcoin/bench_bitcoin.vcxproj.in
index e64614c09d..56401d7618 100644
--- a/build_msvc/bench_bitcoin/bench_bitcoin.vcxproj
+++ b/build_msvc/bench_bitcoin/bench_bitcoin.vcxproj.in
@@ -9,27 +9,7 @@
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
</PropertyGroup>
<ItemGroup>
- <ClCompile Include="..\..\src\test\util.cpp" />
- <ClCompile Include="..\..\src\test\setup_common.cpp" />
- <ClCompile Include="..\..\src\bench\base58.cpp" />
- <ClCompile Include="..\..\src\bench\bech32.cpp" />
- <ClCompile Include="..\..\src\bench\bench.cpp" />
- <ClCompile Include="..\..\src\bench\bench_bitcoin.cpp" />
- <ClCompile Include="..\..\src\bench\ccoins_caching.cpp" />
- <ClCompile Include="..\..\src\bench\checkblock.cpp" />
- <ClCompile Include="..\..\src\bench\checkqueue.cpp" />
- <ClCompile Include="..\..\src\bench\coin_selection.cpp" />
- <ClCompile Include="..\..\src\bench\crypto_hash.cpp" />
- <ClCompile Include="..\..\src\bench\data.cpp" />
- <ClCompile Include="..\..\src\bench\examples.cpp" />
- <ClCompile Include="..\..\src\bench\lockedpool.cpp" />
- <ClCompile Include="..\..\src\bench\mempool_eviction.cpp" />
- <ClCompile Include="..\..\src\bench\rpc_blockchain.cpp" />
- <ClCompile Include="..\..\src\bench\rpc_mempool.cpp" />
- <ClCompile Include="..\..\src\bench\merkle_root.cpp" />
- <ClCompile Include="..\..\src\bench\rollingbloom.cpp" />
- <ClCompile Include="..\..\src\bench\wallet_balance.cpp" />
- <ClCompile Include="..\..\src\bench\verify_script.cpp" />
+@SOURCE_FILES@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\libbitcoinconsensus\libbitcoinconsensus.vcxproj">
diff --git a/build_msvc/bitcoin-qt/bitcoin-qt.vcxproj b/build_msvc/bitcoin-qt/bitcoin-qt.vcxproj
new file mode 100644
index 0000000000..fdeec55ee8
--- /dev/null
+++ b/build_msvc/bitcoin-qt/bitcoin-qt.vcxproj
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="..\common.init.vcxproj" />
+ <Import Project="..\common.qt.init.vcxproj" />
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{7E99172D-7FF2-4CB6-B736-AC9B76ED412A}</ProjectGuid>
+ <ConfigurationType>Application</ConfigurationType>
+ <OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
+ </PropertyGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\src\qt\main.cpp" />
+ <ResourceCompile Include="..\..\src\qt\res\bitcoin-qt-res.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\libbitcoinconsensus\libbitcoinconsensus.vcxproj">
+ <Project>{2b384fa8-9ee1-4544-93cb-0d733c25e8ce}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\libbitcoin_cli\libbitcoin_cli.vcxproj">
+ <Project>{0667528c-d734-4009-adf9-c0d6c4a5a5a6}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\libbitcoin_common\libbitcoin_common.vcxproj">
+ <Project>{7c87e378-df58-482e-aa2f-1bc129bc19ce}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\libbitcoin_crypto\libbitcoin_crypto.vcxproj">
+ <Project>{6190199c-6cf4-4dad-bfbd-93fa72a760c1}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\libbitcoin_qt\libbitcoin_qt.vcxproj">
+ <Project>{2b4abff8-d1fd-4845-88c9-1f3c0a6512bf}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\libbitcoin_server\libbitcoin_server.vcxproj">
+ <Project>{460fee33-1fe1-483f-b3bf-931ff8e969a5}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\libbitcoin_util\libbitcoin_util.vcxproj">
+ <Project>{b53a5535-ee9d-4c6f-9a26-f79ee3bc3754}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\libbitcoin_wallet\libbitcoin_wallet.vcxproj">
+ <Project>{93b86837-b543-48a5-a89b-7c87abb77df2}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\libbitcoin_zmq\libbitcoin_zmq.vcxproj">
+ <Project>{792d487f-f14c-49fc-a9de-3fc150f31c3f}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\libleveldb\libleveldb.vcxproj">
+ <Project>{18430fef-6b61-4c53-b396-718e02850f1b}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\libsecp256k1\libsecp256k1.vcxproj">
+ <Project>{bb493552-3b8c-4a8c-bf69-a6e7a51d2ea6}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\libunivalue\libunivalue.vcxproj">
+ <Project>{5724ba7d-a09a-4ba8-800b-c4c1561b3d69}</Project>
+ </ProjectReference>
+ </ItemGroup>
+
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <AdditionalIncludeDirectories>$(QtIncludes);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(QtReleaseLibraries);%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>..\..\src;</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>HAVE_CONFIG_H;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ </ItemDefinitionGroup>
+
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <AdditionalIncludeDirectories>$(QtIncludes);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(QtDebugLibraries);%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>..\..\src;</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>HAVE_CONFIG_H;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ </ItemDefinitionGroup>
+
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+</Project>
diff --git a/build_msvc/bitcoin.sln b/build_msvc/bitcoin.sln
index 5a6eaf54ad..d4b83b6529 100644
--- a/build_msvc/bitcoin.sln
+++ b/build_msvc/bitcoin.sln
@@ -40,6 +40,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libsecp256k1", "libsecp256k
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libleveldb", "libleveldb\libleveldb.vcxproj", "{18430FEF-6B61-4C53-B396-718E02850F1B}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libbitcoin_qt", "libbitcoin_qt\libbitcoin_qt.vcxproj", "{2B4ABFF8-D1FD-4845-88C9-1F3C0A6512BF}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bitcoin-qt", "bitcoin-qt\bitcoin-qt.vcxproj", "{7E99172D-7FF2-4CB6-B736-AC9B76ED412A}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -200,11 +204,27 @@ Global
{18430FEF-6B61-4C53-B396-718E02850F1B}.Release|x64.Build.0 = Release|x64
{18430FEF-6B61-4C53-B396-718E02850F1B}.Release|x86.ActiveCfg = Release|Win32
{18430FEF-6B61-4C53-B396-718E02850F1B}.Release|x86.Build.0 = Release|Win32
+ {2B4ABFF8-D1FD-4845-88C9-1F3C0A6512BF}.Debug|x64.ActiveCfg = Debug|x64
+ {2B4ABFF8-D1FD-4845-88C9-1F3C0A6512BF}.Debug|x64.Build.0 = Debug|x64
+ {2B4ABFF8-D1FD-4845-88C9-1F3C0A6512BF}.Debug|x86.ActiveCfg = Debug|Win32
+ {2B4ABFF8-D1FD-4845-88C9-1F3C0A6512BF}.Debug|x86.Build.0 = Debug|Win32
+ {2B4ABFF8-D1FD-4845-88C9-1F3C0A6512BF}.Release|x64.ActiveCfg = Release|x64
+ {2B4ABFF8-D1FD-4845-88C9-1F3C0A6512BF}.Release|x64.Build.0 = Release|x64
+ {2B4ABFF8-D1FD-4845-88C9-1F3C0A6512BF}.Release|x86.ActiveCfg = Release|Win32
+ {2B4ABFF8-D1FD-4845-88C9-1F3C0A6512BF}.Release|x86.Build.0 = Release|Win32
+ {7E99172D-7FF2-4CB6-B736-AC9B76ED412A}.Debug|x64.ActiveCfg = Debug|x64
+ {7E99172D-7FF2-4CB6-B736-AC9B76ED412A}.Debug|x64.Build.0 = Debug|x64
+ {7E99172D-7FF2-4CB6-B736-AC9B76ED412A}.Debug|x86.ActiveCfg = Debug|Win32
+ {7E99172D-7FF2-4CB6-B736-AC9B76ED412A}.Debug|x86.Build.0 = Debug|Win32
+ {7E99172D-7FF2-4CB6-B736-AC9B76ED412A}.Release|x64.ActiveCfg = Release|x64
+ {7E99172D-7FF2-4CB6-B736-AC9B76ED412A}.Release|x64.Build.0 = Release|x64
+ {7E99172D-7FF2-4CB6-B736-AC9B76ED412A}.Release|x86.ActiveCfg = Release|Win32
+ {7E99172D-7FF2-4CB6-B736-AC9B76ED412A}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {DA7D16A6-E5F0-45B3-B194-C3FE64F1BFCD}
+ SolutionGuid = {8AA72EDA-2CD4-4564-B1E4-688B760EEEE9}
EndGlobalSection
EndGlobal
diff --git a/build_msvc/common.init.vcxproj b/build_msvc/common.init.vcxproj
index 0d186b5af2..77f6a5c621 100644
--- a/build_msvc/common.init.vcxproj
+++ b/build_msvc/common.init.vcxproj
@@ -6,7 +6,8 @@
<VCProjectVersion>16.0</VCProjectVersion>
<VcpkgTriplet Condition="'$(Platform)'=='Win32'">x86-windows-static</VcpkgTriplet>
<VcpkgTriplet Condition="'$(Platform)'=='x64'">x64-windows-static</VcpkgTriplet>
-</PropertyGroup>
+ </PropertyGroup>
+
<PropertyGroup Condition="'$(WindowsTargetPlatformVersion)'=='' and !Exists('$(WindowsSdkDir)\DesignTime\CommonConfiguration\Neutral\Windows.props')">
<WindowsTargetPlatformVersion_10 Condition="'$(WindowsTargetPlatformVersion_10)' == ''">$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0@ProductVersion)</WindowsTargetPlatformVersion_10>
<WindowsTargetPlatformVersion_10 Condition="'$(WindowsTargetPlatformVersion_10)' == ''">$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Microsoft SDKs\Windows\v10.0@ProductVersion)</WindowsTargetPlatformVersion_10>
@@ -20,7 +21,7 @@
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
- <ProjectConfiguration Include="Debug|x64">
+ <ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
@@ -66,6 +67,7 @@
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
+
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<Optimization>Disabled</Optimization>
@@ -75,6 +77,7 @@
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
</ItemDefinitionGroup>
+
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<Optimization>MaxSpeed</Optimization>
@@ -88,6 +91,7 @@
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
+
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<Optimization>Disabled</Optimization>
@@ -103,7 +107,7 @@
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<AdditionalOptions>/utf-8 %(AdditionalOptions)</AdditionalOptions>
- <DisableSpecificWarnings>4018;4221;4244;4267;4715;4805;</DisableSpecificWarnings>
+ <DisableSpecificWarnings>4018;4221;4244;4267;4334;4715;4805;</DisableSpecificWarnings>
<TreatWarningAsError>true</TreatWarningAsError>
<PreprocessorDefinitions>ZMQ_STATIC;NOMINMAX;WIN32;HAVE_CONFIG_H;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_CONSOLE;_WIN32_WINNT=0x0601;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;..\..\src\secp256k1\include;..\..\src\leveldb\include;..\..\src\leveldb\helpers\memenv;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
@@ -113,6 +117,9 @@
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>crypt32.lib;Iphlpapi.lib;ws2_32.lib;Shlwapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
+ <Lib>
+ <AdditionalOptions>/ignore:4221</AdditionalOptions>
+ </Lib>
</ItemDefinitionGroup>
<Import Project="common.init.vcxproj.user" Condition="Exists('common.init.vcxproj.user')" />
</Project>
diff --git a/build_msvc/common.qt.init.vcxproj b/build_msvc/common.qt.init.vcxproj
new file mode 100644
index 0000000000..e21288e26b
--- /dev/null
+++ b/build_msvc/common.qt.init.vcxproj
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+
+ <PropertyGroup Label="QtGlobals">
+ <QtBaseDir>C:\Qt5.9.7_ssl_x64_static_vs2017</QtBaseDir>
+ <QtPluginsLibraryDir>$(QtBaseDir)\plugins</QtPluginsLibraryDir>
+ <QtLibraryDir>$(QtBaseDir)\lib</QtLibraryDir>
+ <QtIncludeDir>$(QtBaseDir)\include</QtIncludeDir>
+ <QtIncludes>$(QtIncludeDir);$(QtIncludeDir)\QtNetwork;$(QtIncludeDir)\QtCore;$(QtIncludeDir)\QtWidgets;$(QtIncludeDir)\QtGui;</QtIncludes>
+ <GeneratedFilesOutDir>.\QtGeneratedFiles\qt</GeneratedFilesOutDir>
+ <QtToolsDir>$(QtBaseDir)\bin</QtToolsDir>
+ <QtReleaseLibraries>$(QtPluginsLibraryDir)\platforms\qminimal.lib;$(QtPluginsLibraryDir)\platforms\qwindows.lib;$(QtLibraryDir)\qtfreetype.lib;$(QtLibraryDir)\qtharfbuzz.lib;$(QtLibraryDir)\qtlibpng.lib;$(QtLibraryDir)\qtpcre2.lib;$(QtLibraryDir)\Qt5AccessibilitySupport.lib;$(QtLibraryDir)\Qt5Core.lib;$(QtLibraryDir)\Qt5Concurrent.lib;$(QtLibraryDir)\Qt5EventDispatcherSupport.lib;$(QtLibraryDir)\Qt5FontDatabaseSupport.lib;$(QtLibraryDir)\Qt5Gui.lib;$(QtLibraryDir)\Qt5Network.lib;$(QtLibraryDir)\Qt5PlatformCompositorSupport.lib;$(QtLibraryDir)\Qt5ThemeSupport.lib;$(QtLibraryDir)\Qt5Widgets.lib;$(QtLibraryDir)\Qt5WinExtras.lib;$(QtLibraryDir)\qtmain.lib;userenv.lib;netapi32.lib;imm32.lib;Dwmapi.lib;version.lib;winmm.lib;UxTheme.lib</QtReleaseLibraries>
+ <QtDebugLibraries>$(QtPluginsLibraryDir)\platforms\qwindowsd.lib;$(QtPluginsLibraryDir)\platforms\qminimald.lib;$(QtLibraryDir)\*d.lib;crypt32.lib;userenv.lib;netapi32.lib;imm32.lib;Dwmapi.lib;version.lib;winmm.lib;UxTheme.lib</QtDebugLibraries>
+ </PropertyGroup>
+
+</Project>
diff --git a/build_msvc/libbitcoin_qt/libbitcoin_qt.vcxproj b/build_msvc/libbitcoin_qt/libbitcoin_qt.vcxproj
index 73ba90aa88..992f64ec2e 100644
--- a/build_msvc/libbitcoin_qt/libbitcoin_qt.vcxproj
+++ b/build_msvc/libbitcoin_qt/libbitcoin_qt.vcxproj
@@ -1,976 +1,230 @@
<?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <Import Label="configInitTarget" Project="..\common.init.vcxproj" />
- <ItemGroup Label="ProjectConfigurations">
- <ProjectConfiguration Include="Debug|Win32">
- <Configuration>Debug</Configuration>
- <Platform>Win32</Platform>
- </ProjectConfiguration>
- <ProjectConfiguration Include="Release|Win32">
- <Configuration>Release</Configuration>
- <Platform>Win32</Platform>
- </ProjectConfiguration>
- <ProjectConfiguration Include="Debug|x64">
- <Configuration>Debug</Configuration>
- <Platform>x64</Platform>
- </ProjectConfiguration>
- <ProjectConfiguration Include="Release|x64">
- <Configuration>Release</Configuration>
- <Platform>x64</Platform>
- </ProjectConfiguration>
- </ItemGroup>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="..\common.init.vcxproj" />
+ <Import Project="..\common.qt.init.vcxproj" />
<PropertyGroup Label="Globals">
- <VCProjectVersion>15.0</VCProjectVersion>
- <VcpkgTriplet Condition="'$(Platform)'=='Win32'">x86-windows-static</VcpkgTriplet>
- <VcpkgTriplet Condition="'$(Platform)'=='x64'">x64-windows-static</VcpkgTriplet>
<ProjectGuid>{2B4ABFF8-D1FD-4845-88C9-1F3C0A6512BF}</ProjectGuid>
+ <ConfigurationType>StaticLibrary</ConfigurationType>
</PropertyGroup>
<ItemGroup>
- <CustomBuild Include="..\..\src\qt\bitcoin.qrc">
- <Command>"$(QTDIR)\bincc.exe" -name bitcoin "%(Fullpath)" -o .\GeneratedFiles\qrc_bitcoin.cpp</Command>
- <Message>Qt rcc generation for %(Identity)</Message>
- <Outputs>.\GeneratedFiles\qrc_bitcoin.cpp</Outputs>
- <AdditionalInputs>(QTDIR)\bincc.exe</AdditionalInputs>
- </CustomBuild>
- <CustomBuild Include="..\..\src\qt\bitcoin_locale.qrc">
- <Command>"$(QTDIR)\bincc.exe" -name bitcoin_locale "%(Fullpath)" -o .\GeneratedFiles\qrc_bitcoin_locale.cpp</Command>
- <Message>Qt rcc generation for %(Identity)</Message>
- <Outputs>.\GeneratedFiles\qrc_bitcoin_locale.cpp</Outputs>
- <AdditionalInputs>(QTDIR)\bincc.exe</AdditionalInputs>
- </CustomBuild>
- <None Include="..\..\src\qt\forms\addressbookpage.ui" />
- <None Include="..\..\src\qt\forms\askpassphrasedialog.ui" />
- <None Include="..\..\src\qt\forms\coincontroldialog.ui" />
- <None Include="..\..\src\qt\forms\debugwindow.ui" />
- <None Include="..\..\src\qt\forms\editaddressdialog.ui" />
- <None Include="..\..\src\qt\forms\helpmessagedialog.ui" />
- <None Include="..\..\src\qt\forms\intro.ui" />
- <None Include="..\..\src\qt\forms\modaloverlay.ui" />
- <None Include="..\..\src\qt\forms\openuridialog.ui" />
- <None Include="..\..\src\qt\forms\optionsdialog.ui" />
- <None Include="..\..\src\qt\forms\overviewpage.ui" />
- <None Include="..\..\src\qt\formseceivecoinsdialog.ui" />
- <None Include="..\..\src\qt\formseceiverequestdialog.ui" />
- <None Include="..\..\src\qt\forms\sendcoinsdialog.ui" />
- <None Include="..\..\src\qt\forms\sendcoinsentry.ui" />
- <None Include="..\..\src\qt\forms\signverifymessagedialog.ui" />
- <None Include="..\..\src\qt\forms\transactiondescdialog.ui" />
- <None Include="..\..\src\qt\locale\bitcoin_af.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_af_ZA.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_am.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_ar.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_be_BY.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_bg.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_bg_BG.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_ca.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_ca%40valencia.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_ca_ES.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_cs.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_cy.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_da.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_de.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_el.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_el_GR.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_en.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_en_GB.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_eo.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_es.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_es_AR.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_es_CL.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_es_CO.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_es_DO.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_es_ES.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_es_MX.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_es_UY.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_es_VE.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_et.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_et_EE.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_eu_ES.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_fa.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_fa_IR.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_fi.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_fr.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_fr_CA.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_fr_FR.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_gl.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_he.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_hi_IN.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_hr.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_hu.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_id.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_id_ID.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_is.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_it.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_it_IT.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_ja.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_ka.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_kk_KZ.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_ko.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_ko_KR.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_ku_IQ.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_ky.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_la.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_lt.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_lv_LV.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_mk_MK.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_ml.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_mn.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_ms_MY.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_nb.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_ne.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_nl.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_pam.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_pl.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_pt_BR.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_pt_PT.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_ro.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_ro_RO.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_ru.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_ru_RU.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_sk.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_sl_SI.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_sn.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_sq.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_sr.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_sr%40latin.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_sv.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_szl.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_ta.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_th_TH.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_tr.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_tr_TR.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_uk.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_ur_PK.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_uz%40Cyrl.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_vi.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_vi_VN.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_zh.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_zh_CN.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_zh_HK.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qt\locale\bitcoin_zh_TW.ts">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <CustomBuild Include="..\..\src\qt\paymentrequest.proto">
- <FileType>Document</FileType>
- <Command>F:\Dependencies\protobuf-cpp-3.4.1\protobuf-3.4.1\cmake\build\vs\Debug\protoc.exe --proto_path=%(RootDir)%(Directory) %(Fullpath) --cpp_out=.\GeneratedFiles</Command>
- <Message>ProtoBuf source generation %(RootDir)%(Directory) %(Filename)</Message>
- <Outputs>.\GeneratedFiles\%(Filename).pb.h;.\GeneratedFiles\(%Filename).pb.cc</Outputs>
- <AdditionalInputs>F:\Dependencies\protobuf-cpp-3.4.1\protobuf-3.4.1\cmake\build\vs\Debug\protoc.exe</AdditionalInputs>
- <LinkObjects>false</LinkObjects>
- <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">F:\deps\protobuf\protobuf-3.4.1\cmake\build\vs\Debug\protoc.exe --proto_path=%(RootDir)%(Directory) %(Fullpath) --cpp_out=.\GeneratedFiles</Command>
- <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">F:\deps\protobuf\protobuf-3.4.1\cmake\build\vs\Debug\protoc.exe</AdditionalInputs>
- </CustomBuild>
- <None Include="..\..\src\qt\macdockiconhandler.mm" />
- <None Include="..\..\src\qt\macnotificationhandler.mm" />
- <None Include="..\..\src\qtes\icons\bitcoin.icns">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qtes\src\bitcoin.svg">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qtes\src\clock_0.svg">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qtes\src\clock_1.svg">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qtes\src\clock_2.svg">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qtes\src\clock_3.svg">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qtes\src\clock_4.svg">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qtes\src\connect-0.svg">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qtes\src\connect-1.svg">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qtes\src\connect-2.svg">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qtes\src\connect-3.svg">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qtes\src\connect-4.svg">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qtes\src\hd_disabled.svg">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qtes\src\hd_enabled.svg">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qtes\src\mine.svg">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qtes\src\network_disabled.svg">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qtes\src\qt.svg">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qtes\src\transaction0.svg">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qtes\src\tx_in.svg">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qtes\src\tx_inout.svg">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="..\..\src\qtes\src\verify.svg">
- <DeploymentContent>true</DeploymentContent>
- </None>
- <None Include="GeneratedFiles\bitcoin.moc" />
- <None Include="GeneratedFiles\bitcoinamountfield.moc" />
- <None Include="GeneratedFiles\intro.moc" />
- <None Include="GeneratedFiles\overviewpage.moc" />
- <None Include="GeneratedFilespcconsole.moc" />
- </ItemGroup>
- <ItemGroup>
- <Image Include="..\..\src\qtes\icons\about.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\about_qt.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\add.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\address-book.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\bitcoin.ico">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\bitcoin.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\bitcoin_testnet.ico">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\chevron.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\clock1.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\clock2.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\clock3.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\clock4.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\clock5.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\configure.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\connect0.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\connect1.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\connect2.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\connect3.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\connect4.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\debugwindow.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\edit.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\editcopy.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\editpaste.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\export.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\eye.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\eye_minus.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\eye_plus.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\filesave.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\fontbigger.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\fontsmaller.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\hd_disabled.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\hd_enabled.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\history.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\info.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\key.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\lock_closed.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\lock_open.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\network_disabled.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\open.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\overview.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\quit.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\iconseceive.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\iconsemove.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\send.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\synced.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\transaction0.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\transaction2.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\transaction_abandoned.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\transaction_conflicted.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\tx_inout.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\tx_input.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\tx_mined.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\tx_output.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\verify.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\icons\warning.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\movies\spinner-000.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\movies\spinner-001.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\movies\spinner-002.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\movies\spinner-003.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\movies\spinner-004.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\movies\spinner-005.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\movies\spinner-006.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\movies\spinner-007.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\movies\spinner-008.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\movies\spinner-009.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\movies\spinner-010.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\movies\spinner-011.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\movies\spinner-012.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\movies\spinner-013.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\movies\spinner-014.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\movies\spinner-015.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\movies\spinner-016.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\movies\spinner-017.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\movies\spinner-018.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\movies\spinner-019.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\movies\spinner-020.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\movies\spinner-021.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\movies\spinner-022.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\movies\spinner-023.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\movies\spinner-024.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\movies\spinner-025.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\movies\spinner-026.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\movies\spinner-027.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\movies\spinner-028.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\movies\spinner-029.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\movies\spinner-030.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\movies\spinner-031.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\movies\spinner-032.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\movies\spinner-033.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\movies\spinner-034.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\movies\spinner-035.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- <Image Include="..\..\src\qtes\src\spinner.png">
- <DeploymentContent>true</DeploymentContent>
- </Image>
- </ItemGroup>
- <ItemGroup>
- <ClCompile Include="GeneratedFiles\qrc_bitcoin.cpp" />
- <ClCompile Include="GeneratedFiles\qrc_bitcoin_locale.cpp" />
- <CustomBuild Include="..\..\src\qt\bitcoin.cpp">
- <Command>"$(QTDIR)\bin\moc.exe" "%(Fullpath)" -o .\GeneratedFiles\%(Filename).moc $(MOC_DEF)</Command>
- <Message>Qt moc generation for %(Identity)</Message>
- <Outputs>.\GeneratedFiles\%(Filename).moc</Outputs>
- <AdditionalInputs>(QTDIR)\bin\moc.exe</AdditionalInputs>
- </CustomBuild>
- <CustomBuild Include="..\..\src\qt\bitcoinamountfield.cpp">
- <Command>"$(QTDIR)\bin\moc.exe" "%(Fullpath)" -o .\GeneratedFiles\%(Filename).moc $(MOC_DEF)</Command>
- <Message>Qt moc generation for %(Identity)</Message>
- <Outputs>.\GeneratedFiles\%(Filename).moc</Outputs>
- <AdditionalInputs>(QTDIR)\bin\moc.exe</AdditionalInputs>
- </CustomBuild>
- <CustomBuild Include="..\..\src\qt\intro.cpp">
- <Command>"$(QTDIR)\bin\moc.exe" "%(Fullpath)" -o .\GeneratedFiles\%(Filename).moc $(MOC_DEF)</Command>
- <Message>Qt moc generation for %(Identity)</Message>
- <Outputs>.\GeneratedFiles\%(Filename).moc</Outputs>
- <AdditionalInputs>(QTDIR)\bin\moc.exe</AdditionalInputs>
- </CustomBuild>
- <CustomBuild Include="..\..\src\qt\overviewpage.cpp">
- <Command>"$(QTDIR)\bin\moc.exe" "%(Fullpath)" -o .\GeneratedFiles\%(Filename).moc $(MOC_DEF)</Command>
- <Message>Qt moc generation for %(Identity)</Message>
- <Outputs>.\GeneratedFiles\%(Filename).moc</Outputs>
- <AdditionalInputs>(QTDIR)\bin\moc.exe</AdditionalInputs>
- </CustomBuild>
- <CustomBuild Include="..\..\src\qtpcconsole.cpp">
- <Command>"$(QTDIR)\bin\moc.exe" "%(Fullpath)" -o .\GeneratedFiles\%(Filename).moc $(MOC_DEF)</Command>
- <Message>Qt moc generation for %(Identity)</Message>
- <Outputs>.\GeneratedFiles\%(Filename).moc</Outputs>
- <AdditionalInputs>(QTDIR)\bin\moc.exe</AdditionalInputs>
- </CustomBuild>
- <ClCompile Include="GeneratedFiles\moc_addressbookpage.cpp" />
- <ClCompile Include="GeneratedFiles\moc_addresstablemodel.cpp" />
- <ClCompile Include="GeneratedFiles\moc_askpassphrasedialog.cpp" />
- <ClCompile Include="GeneratedFiles\moc_bantablemodel.cpp" />
- <ClCompile Include="GeneratedFiles\moc_bitcoinaddressvalidator.cpp" />
- <ClCompile Include="GeneratedFiles\moc_bitcoinamountfield.cpp" />
- <ClCompile Include="GeneratedFiles\moc_bitcoingui.cpp" />
- <ClCompile Include="GeneratedFiles\moc_bitcoinunits.cpp" />
- <ClCompile Include="GeneratedFiles\moc_callback.cpp" />
- <ClCompile Include="GeneratedFiles\moc_clientmodel.cpp" />
- <ClCompile Include="GeneratedFiles\moc_coincontroldialog.cpp" />
- <ClCompile Include="GeneratedFiles\moc_coincontroltreewidget.cpp" />
- <ClCompile Include="GeneratedFiles\moc_csvmodelwriter.cpp" />
- <ClCompile Include="GeneratedFiles\moc_editaddressdialog.cpp" />
- <ClCompile Include="GeneratedFiles\moc_guiconstants.cpp" />
- <ClCompile Include="GeneratedFiles\moc_guiutil.cpp" />
- <ClCompile Include="GeneratedFiles\moc_intro.cpp" />
- <ClCompile Include="GeneratedFiles\moc_macdockiconhandler.cpp" />
- <ClCompile Include="GeneratedFiles\moc_macnotificationhandler.cpp" />
- <ClCompile Include="GeneratedFiles\moc_modaloverlay.cpp" />
- <ClCompile Include="GeneratedFiles\moc_networkstyle.cpp" />
- <ClCompile Include="GeneratedFiles\moc_notificator.cpp" />
- <ClCompile Include="GeneratedFiles\moc_openuridialog.cpp" />
- <ClCompile Include="GeneratedFiles\moc_optionsdialog.cpp" />
- <ClCompile Include="GeneratedFiles\moc_optionsmodel.cpp" />
- <ClCompile Include="GeneratedFiles\moc_overviewpage.cpp" />
- <ClCompile Include="GeneratedFiles\moc_paymentrequestplus.cpp" />
- <ClCompile Include="GeneratedFiles\moc_paymentserver.cpp" />
- <ClCompile Include="GeneratedFiles\moc_peertablemodel.cpp" />
- <ClCompile Include="GeneratedFiles\moc_platformstyle.cpp" />
- <ClCompile Include="GeneratedFiles\moc_qvalidatedlineedit.cpp" />
- <ClCompile Include="GeneratedFiles\moc_qvaluecombobox.cpp" />
- <ClCompile Include="GeneratedFiles\moc_receivecoinsdialog.cpp" />
- <ClCompile Include="GeneratedFiles\moc_receiverequestdialog.cpp" />
- <ClCompile Include="GeneratedFiles\moc_recentrequeststablemodel.cpp" />
- <ClCompile Include="GeneratedFiles\moc_rpcconsole.cpp" />
- <ClCompile Include="GeneratedFiles\moc_sendcoinsdialog.cpp" />
- <ClCompile Include="GeneratedFiles\moc_sendcoinsentry.cpp" />
- <ClCompile Include="GeneratedFiles\moc_signverifymessagedialog.cpp" />
- <ClCompile Include="GeneratedFiles\moc_splashscreen.cpp" />
- <ClCompile Include="GeneratedFiles\moc_trafficgraphwidget.cpp" />
- <ClCompile Include="GeneratedFiles\moc_transactiondesc.cpp" />
- <ClCompile Include="GeneratedFiles\moc_transactiondescdialog.cpp" />
- <ClCompile Include="GeneratedFiles\moc_transactionfilterproxy.cpp" />
- <ClCompile Include="GeneratedFiles\moc_transactionrecord.cpp" />
- <ClCompile Include="GeneratedFiles\moc_transactiontablemodel.cpp" />
- <ClCompile Include="GeneratedFiles\moc_transactionview.cpp" />
- <ClCompile Include="GeneratedFiles\moc_utilitydialog.cpp" />
- <ClCompile Include="GeneratedFiles\moc_walletframe.cpp" />
- <ClCompile Include="GeneratedFiles\moc_walletmodel.cpp" />
- <ClCompile Include="GeneratedFiles\moc_walletmodeltransaction.cpp" />
- <ClCompile Include="GeneratedFiles\moc_walletview.cpp" />
- <ClCompile Include="GeneratedFiles\moc_winshutdownmonitor.cpp" />
- <ClCompile Include="GeneratedFiles\paymentrequest.pb.cc" />
- </ItemGroup>
- <ItemGroup>
- <ClInclude Include="GeneratedFiles\paymentrequest.pb.h" />
- <ClInclude Include="GeneratedFiles\ui_addressbookpage.h" />
- <ClInclude Include="GeneratedFiles\ui_askpassphrasedialog.h" />
- <ClInclude Include="GeneratedFiles\ui_coincontroldialog.h" />
- <ClInclude Include="GeneratedFiles\ui_debugwindow.h" />
- <ClInclude Include="GeneratedFiles\ui_editaddressdialog.h" />
- <ClInclude Include="GeneratedFiles\ui_helpmessagedialog.h" />
- <ClInclude Include="GeneratedFiles\ui_intro.h" />
- <ClInclude Include="GeneratedFiles\ui_modaloverlay.h" />
- <ClInclude Include="GeneratedFiles\ui_openuridialog.h" />
- <ClInclude Include="GeneratedFiles\ui_optionsdialog.h" />
- <ClInclude Include="GeneratedFiles\ui_overviewpage.h" />
- <ClInclude Include="GeneratedFiles\ui_receivecoinsdialog.h" />
- <ClInclude Include="GeneratedFiles\ui_receiverequestdialog.h" />
- <ClInclude Include="GeneratedFiles\ui_sendcoinsdialog.h" />
- <ClInclude Include="GeneratedFiles\ui_sendcoinsentry.h" />
- <ClInclude Include="GeneratedFiles\ui_signverifymessagedialog.h" />
- <ClInclude Include="GeneratedFiles\ui_transactiondescdialog.h" />
+ <ClCompile Include="..\..\src\qt\addressbookpage.cpp" />
+ <ClCompile Include="..\..\src\qt\addresstablemodel.cpp" />
+ <ClCompile Include="..\..\src\qt\askpassphrasedialog.cpp" />
+ <ClCompile Include="..\..\src\qt\bantablemodel.cpp" />
+ <ClCompile Include="..\..\src\qt\bitcoin.cpp" />
+ <ClCompile Include="..\..\src\qt\bitcoinaddressvalidator.cpp" />
+ <ClCompile Include="..\..\src\qt\bitcoinamountfield.cpp" />
+ <ClCompile Include="..\..\src\qt\bitcoingui.cpp" />
+ <ClCompile Include="..\..\src\qt\bitcoinstrings.cpp" />
+ <ClCompile Include="..\..\src\qt\bitcoinunits.cpp" />
+ <ClCompile Include="..\..\src\qt\clientmodel.cpp" />
+ <ClCompile Include="..\..\src\qt\coincontroldialog.cpp" />
+ <ClCompile Include="..\..\src\qt\coincontroltreewidget.cpp" />
+ <ClCompile Include="..\..\src\qt\createwalletdialog.cpp" />
+ <ClCompile Include="..\..\src\qt\csvmodelwriter.cpp" />
+ <ClCompile Include="..\..\src\qt\editaddressdialog.cpp" />
+ <ClCompile Include="..\..\src\qt\guiutil.cpp" />
+ <ClCompile Include="..\..\src\qt\intro.cpp" />
+ <ClCompile Include="..\..\src\qt\modaloverlay.cpp" />
+ <ClCompile Include="..\..\src\qt\networkstyle.cpp" />
+ <ClCompile Include="..\..\src\qt\notificator.cpp" />
+ <ClCompile Include="..\..\src\qt\openuridialog.cpp" />
+ <ClCompile Include="..\..\src\qt\optionsdialog.cpp" />
+ <ClCompile Include="..\..\src\qt\optionsmodel.cpp" />
+ <ClCompile Include="..\..\src\qt\overviewpage.cpp" />
+ <ClCompile Include="..\..\src\qt\paymentserver.cpp" />
+ <ClCompile Include="..\..\src\qt\peertablemodel.cpp" />
+ <ClCompile Include="..\..\src\qt\platformstyle.cpp" />
+ <ClCompile Include="..\..\src\qt\qrimagewidget.cpp" />
+ <ClCompile Include="..\..\src\qt\qvalidatedlineedit.cpp" />
+ <ClCompile Include="..\..\src\qt\qvaluecombobox.cpp" />
+ <ClCompile Include="..\..\src\qt\receivecoinsdialog.cpp" />
+ <ClCompile Include="..\..\src\qt\receiverequestdialog.cpp" />
+ <ClCompile Include="..\..\src\qt\recentrequeststablemodel.cpp" />
+ <ClCompile Include="..\..\src\qt\rpcconsole.cpp" />
+ <ClCompile Include="..\..\src\qt\sendcoinsdialog.cpp" />
+ <ClCompile Include="..\..\src\qt\sendcoinsentry.cpp" />
+ <ClCompile Include="..\..\src\qt\signverifymessagedialog.cpp" />
+ <ClCompile Include="..\..\src\qt\splashscreen.cpp" />
+ <ClCompile Include="..\..\src\qt\trafficgraphwidget.cpp" />
+ <ClCompile Include="..\..\src\qt\transactiondesc.cpp" />
+ <ClCompile Include="..\..\src\qt\transactiondescdialog.cpp" />
+ <ClCompile Include="..\..\src\qt\transactionfilterproxy.cpp" />
+ <ClCompile Include="..\..\src\qt\transactionrecord.cpp" />
+ <ClCompile Include="..\..\src\qt\transactiontablemodel.cpp" />
+ <ClCompile Include="..\..\src\qt\transactionview.cpp" />
+ <ClCompile Include="..\..\src\qt\utilitydialog.cpp" />
+ <ClCompile Include="..\..\src\qt\walletcontroller.cpp" />
+ <ClCompile Include="..\..\src\qt\walletframe.cpp" />
+ <ClCompile Include="..\..\src\qt\walletmodel.cpp" />
+ <ClCompile Include="..\..\src\qt\walletmodeltransaction.cpp" />
+ <ClCompile Include="..\..\src\qt\walletview.cpp" />
+ <ClCompile Include="..\..\src\qt\winshutdownmonitor.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_addressbookpage.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_addresstablemodel.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_askpassphrasedialog.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_bantablemodel.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_bitcoin.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_bitcoinaddressvalidator.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_bitcoinamountfield.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_bitcoingui.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_bitcoinunits.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_clientmodel.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_coincontroldialog.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_coincontroltreewidget.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_createwalletdialog.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_csvmodelwriter.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_editaddressdialog.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_guiutil.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_intro.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_modaloverlay.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_networkstyle.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_notificator.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_openuridialog.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_optionsdialog.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_optionsmodel.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_overviewpage.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_paymentserver.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_peertablemodel.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_platformstyle.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_qrimagewidget.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_qvalidatedlineedit.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_qvaluecombobox.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_receivecoinsdialog.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_receiverequestdialog.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_recentrequeststablemodel.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_rpcconsole.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_sendcoinsdialog.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_sendcoinsentry.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_signverifymessagedialog.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_splashscreen.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_trafficgraphwidget.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_transactiondesc.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_transactiondescdialog.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_transactionfilterproxy.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_transactionrecord.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_transactiontablemodel.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_transactionview.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_utilitydialog.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_walletcontroller.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_walletframe.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_walletmodel.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_walletmodeltransaction.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_walletview.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_winshutdownmonitor.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\rcc\qrc_bitcoin.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\rcc\qrc_bitcoin_locale.cpp" />
</ItemGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
- <ConfigurationType>StaticLibrary</ConfigurationType>
- <UseDebugLibraries>true</UseDebugLibraries>
- <PlatformToolset>v141</PlatformToolset>
- <CharacterSet>Unicode</CharacterSet>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
- <ConfigurationType>StaticLibrary</ConfigurationType>
- <UseDebugLibraries>false</UseDebugLibraries>
- <PlatformToolset>v141</PlatformToolset>
- <WholeProgramOptimization>true</WholeProgramOptimization>
- <CharacterSet>Unicode</CharacterSet>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
- <ConfigurationType>StaticLibrary</ConfigurationType>
- <UseDebugLibraries>true</UseDebugLibraries>
- <PlatformToolset>v141</PlatformToolset>
- <CharacterSet>Unicode</CharacterSet>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
- <ConfigurationType>StaticLibrary</ConfigurationType>
- <UseDebugLibraries>false</UseDebugLibraries>
- <PlatformToolset>v141</PlatformToolset>
- <WholeProgramOptimization>true</WholeProgramOptimization>
- <CharacterSet>Unicode</CharacterSet>
- </PropertyGroup>
+
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
- <ImportGroup Label="ExtensionSettings">
- </ImportGroup>
- <ImportGroup Label="Shared">
- </ImportGroup>
- <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
- <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- </ImportGroup>
- <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
- <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- </ImportGroup>
- <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
- <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- </ImportGroup>
- <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
- <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- </ImportGroup>
- <PropertyGroup Label="UserMacros" />
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
- <LinkIncremental>false</LinkIncremental>
- <Linkage-protobuf>static</Linkage-protobuf>
- <CustomBuildBeforeTargets>ClCompile</CustomBuildBeforeTargets>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
- <LinkIncremental>true</LinkIncremental>
- <Linkage-protobuf>static</Linkage-protobuf>
- <CustomBuildBeforeTargets>ClCompile</CustomBuildBeforeTargets>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
- <LinkIncremental>true</LinkIncremental>
- <Linkage-protobuf>static</Linkage-protobuf>
- <CustomBuildBeforeTargets>ClCompile</CustomBuildBeforeTargets>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
- <LinkIncremental>false</LinkIncremental>
- <Linkage-protobuf>static</Linkage-protobuf>
- <CustomBuildBeforeTargets>ClCompile</CustomBuildBeforeTargets>
- </PropertyGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
- <PrecompiledHeader>NotUsing</PrecompiledHeader>
- <WarningLevel>Level3</WarningLevel>
- <Optimization>Disabled</Optimization>
- <FunctionLevelLinking>true</FunctionLevelLinking>
- <IntrinsicFunctions>false</IntrinsicFunctions>
- <PreprocessorDefinitions>_X86_;WIN32;HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;WIN32;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>.\GeneratedFiles;..\..\src;..\..\src\univalue\include;.\QtGenerated\mocheaders</AdditionalIncludeDirectories>
- <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <PreprocessorDefinitions>_AMD64_;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(QtIncludes);$(GeneratedFilesOutDir)\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
- <Link>
- <SubSystem>Console</SubSystem>
- <EnableCOMDATFolding>true</EnableCOMDATFolding>
- <OptimizeReferences>true</OptimizeReferences>
- <GenerateDebugInformation>true</GenerateDebugInformation>
- </Link>
</ItemDefinitionGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
- <PrecompiledHeader>NotUsing</PrecompiledHeader>
- <WarningLevel>Level3</WarningLevel>
- <Optimization>Disabled</Optimization>
- <PreprocessorDefinitions>_X86_;WIN32;HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>.\GeneratedFiles;..\..\src;..\..\src\univalue\include;.\QtGenerated\mocheaders</AdditionalIncludeDirectories>
- <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <PreprocessorDefinitions>_AMD64_;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(QtIncludes);$(GeneratedFilesOutDir)\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
- <Link>
- <SubSystem>Console</SubSystem>
- <GenerateDebugInformation>true</GenerateDebugInformation>
- </Link>
</ItemDefinitionGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
- <PrecompiledHeader>NotUsing</PrecompiledHeader>
- <WarningLevel>Level3</WarningLevel>
- <Optimization>Disabled</Optimization>
- <PreprocessorDefinitions>WIN32;HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>.\GeneratedFiles;..\..\src;..\..\src\univalue\include;.\QtGenerated\mocheaders</AdditionalIncludeDirectories>
- <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <PreprocessorDefinitions>_X86_;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(QtIncludes);$(GeneratedFilesOutDir)\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
- <Link>
- <SubSystem>Console</SubSystem>
- <GenerateDebugInformation>true</GenerateDebugInformation>
- </Link>
</ItemDefinitionGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
- <PrecompiledHeader>NotUsing</PrecompiledHeader>
- <WarningLevel>Level3</WarningLevel>
- <Optimization>MaxSpeed</Optimization>
- <FunctionLevelLinking>true</FunctionLevelLinking>
- <IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>WIN32;HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>.\GeneratedFiles;..\..\src;..\..\src\univalue\include;.\QtGenerated\mocheaders</AdditionalIncludeDirectories>
- <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <PreprocessorDefinitions>_X86_;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(QtIncludes);$(GeneratedFilesOutDir)\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
- <Link>
- <SubSystem>Console</SubSystem>
- <EnableCOMDATFolding>true</EnableCOMDATFolding>
- <OptimizeReferences>true</OptimizeReferences>
- <GenerateDebugInformation>true</GenerateDebugInformation>
- </Link>
</ItemDefinitionGroup>
- <Import Label="configTarget" Project="..\common.vcxproj" />
- <Target Name="QtHeadersMocCodeGeneration" BeforeTargets="PrepareForBuild">
+
+ <ItemGroup>
+ <QT_MOC Include="..\..\src\qt\bitcoinamountfield.cpp" />
+ <QT_MOC Include="..\..\src\qt\intro.cpp" />
+ <QT_MOC Include="..\..\src\qt\overviewpage.cpp" />
+ <QT_MOC Include="..\..\src\qt\rpcconsole.cpp" />
+ <MocHeaderFiles Include="..\..\src\qt\*.h" />
+ <ResourceTemplates Include="..\..\src\qt\*.qrc" />
+ <UiFormFiles Include="..\..\src\qt\forms\*.ui" />
+ <TranslationFiles Include="..\..\src\qt\locale\*.ts" />
+ </ItemGroup>
+
+ <Target Name="moccode" Inputs="@(QT_MOC)" Outputs="@(QT_MOC->'$(GeneratedFilesOutDir)\%(Filename).moc')">
<PropertyGroup>
- <ErrorText>There was an error executing the Qt headers moc code generation tasks.</ErrorText>
+ <ErrorText>There was an error executing the libbitcoin_qt moc code include generation task.</ErrorText>
</PropertyGroup>
- <ItemGroup>
- <QtMocHeaderFiles Include="..\..\src\qt\*.h" />
- </ItemGroup>
- <Exec Command="$(QTDIR)\bin\moc.exe &quot;%(QtMocHeaderFiles.Identity)&quot; -o .\GeneratedFiles\moc_%(Filename).cpp $(MOC_DEF)" />
+ <MakeDir Directories="$(GeneratedFilesOutDir)" />
+ <Exec Command="echo Performing libbitcoin_qt moc code include generation task, output path $(GeneratedFilesOutDir)." />
+ <Exec Command="echo $(QtToolsDir)\moc.exe $(MOC_DEFINES) &quot;%(QT_MOC.Identity)&quot; -o $(GeneratedFilesOutDir)\%(Filename).moc." />
+ <Exec Command="$(QtToolsDir)\moc.exe $(MOC_DEFINES) &quot;%(QT_MOC.Identity)&quot; -o $(GeneratedFilesOutDir)\%(Filename).moc" />
</Target>
- <Target Name="QtFormsCodeGeneration" BeforeTargets="PrepareForBuild">
+
+ <Target Name="mocheader" Inputs="@(MocHeaderFiles)" Outputs="@(MocHeaderFiles->'$(GeneratedFilesOutDir)\moc\moc_%(Filename).cpp')">
<PropertyGroup>
- <ErrorText>There was an error executing the Qt forms code generation tasks.</ErrorText>
+ <ErrorText>There was an error executing the libbitcoin_qt moc header generation task.</ErrorText>
</PropertyGroup>
- <ItemGroup>
- <QtFormFiles Include="..\..\src\qt\forms\*.ui" />
- </ItemGroup>
- <Exec Command="$(QTDIR)\bin\uic.exe &quot;%(QtFormFiles.Identity)&quot; -o .\GeneratedFiles\ui_%(Filename).h" />
+ <Exec Command="echo Performing libbitcoin_qt moc header generation task, output path $(GeneratedFilesOutDir)\moc." />
+ <Exec Command="echo $(QtToolsDir)\moc.exe $(MOC_DEFINES) &quot;%(MocHeaderFiles.Identity)&quot; -o $(GeneratedFilesOutDir)\moc\moc_%(Filename).cpp." />
+ <MakeDir Directories="$(GeneratedFilesOutDir)\moc\" />
+ <Exec Command="$(QtToolsDir)\moc.exe $(MOC_DEFINES) &quot;%(MocHeaderFiles.Identity)&quot; -o $(GeneratedFilesOutDir)\moc\moc_%(Filename).cpp" />
</Target>
- <Target Name="QtLocaleCodeGeneration" BeforeTargets="PrepareForBuild">
+
+ <Target Name="forms" Inputs="@(UiFormFiles)" Outputs="@(UiFormFiles->'$(GeneratedFilesOutDir)\forms\ui_%(Filename).h')">
<PropertyGroup>
- <ErrorText>There was an error executing the Qt local code generation tasks.</ErrorText>
+ <ErrorText>There was an error executing the libbitcoin_qt forms header generation task.</ErrorText>
</PropertyGroup>
- <ItemGroup>
- <QtLocaleFiles Include="..\..\src\qt\locale\*.ts" />
- </ItemGroup>
- <Exec Command="$(QTDIR)\bin\lrelease.exe &quot;%(QtLocaleFiles.Identity)&quot; -qm ..\..\src\qt\locale\%(Filename).qm" />
+ <Exec Command="echo Performing libbitcoin_qt forms header generation task, output path $(GeneratedFilesOutDir)\forms." />
+ <MakeDir Directories="$(GeneratedFilesOutDir)\forms\" />
+ <Exec Command="$(QtToolsDir)\uic.exe &quot;%(UiFormFiles.Identity)&quot; -o $(GeneratedFilesOutDir)\forms\ui_%(Filename).h" />
</Target>
- <ImportGroup Label="ExtensionTargets">
- <!--<Import Label="berkleyDbTarget" Project="f:\deps\db-4.8.30\db.targets" />
- <Import Label="opensslTarget" Project="f:\deps\openssl\1.0.2\openssl.targets" />
- <Import Label="qtTarget" Project="F:\deps\qt\5.9.2-git-ssl\vc141-x86elease\qt.targets" />
- <Import Label="protobufTarget" Project="f:\deps\protobuf\protobuf.targets" />-->
- </ImportGroup>
- <ProjectExtensions>
- <VisualStudio>
- <UserProperties MocDir=".\GeneratedFiles" UicDir=".\GeneratedFiles" RccDir=".\GeneratedFiles" lupdateOptions="" lupdateOnBuild="0" lreleaseOptions="" Qt5Version_x0020_Win32="5.9.1_vs140_x86" Qt5Version_x0020_x64="5.9.1_vs140_x86" MocOptions="DUNICODE -DWIN32 -DQT_DLL -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB &quot;-I.\GeneratedFiles&quot; &quot;-I.&quot; &quot;-I$(QTDIR)\include&quot; &quot;-I$(QTDIR)\include\QtCore&quot; &quot;-I$(QTDIR)\include\QtGui&quot; &quot;-I$(QTDIR)\include\QtWidgets&quot;" />
- </VisualStudio>
- </ProjectExtensions>
+
+ <Target Name="translation" Inputs="@(TranslationFiles)" Outputs="@(TranslationFiles->'..\..\src\qt\locale\%(Filename).qm')">
+ <PropertyGroup>
+ <ErrorText>There was an error executing the libbitcoin_qt translation file generation task.</ErrorText>
+ </PropertyGroup>
+ <Exec Command="echo Performing libbitcoin_qt translation file generation task." />
+ <Exec Command="$(QtToolsDir)\lrelease.exe &quot;%(TranslationFiles.Identity)&quot; -qm ..\..\src\qt\locale\%(Filename).qm" />
+ </Target>
+
+ <Target Name="resource" Inputs="@(ResourceTemplates)" Outputs="@(ResourceTemplates->'$(GeneratedFilesOutDir)\rcc\qrc_%(Filename).cpp')" DependsOnTargets="translation">
+ <PropertyGroup>
+ <ErrorText>There was an error executing the libbitcoin_qt resource code generation task.</ErrorText>
+ </PropertyGroup>
+ <Exec Command="echo Performing libbitcoin_qt resource code generation task, output path $(GeneratedFilesOutDir)\rcc." />
+ <MakeDir Directories="$(GeneratedFilesOutDir)\rcc\" />
+ <Exec Command="$(QtToolsDir)\rcc.exe --verbose --name %(Filename) &quot;%(ResourceTemplates.Identity)&quot; -o $(GeneratedFilesOutDir)\rcc\qrc_%(Filename).cpp" />
+ </Target>
+
+ <Target Name="qtclean">
+ <Exec Command="echo Clean libbitcoin_qt generated files from $(GeneratedFilesOutDir)." />
+ <RemoveDir Directories="$(GeneratedFilesOutDir)\forms;$(GeneratedFilesOutDir)\moc;$(GeneratedFilesOutDir)\rcc;" />
+ <RemoveDir Directories="$(GeneratedFilesOutDir)" />
+ </Target>
+
+ <PropertyGroup>
+ <BuildDependsOn>
+ moccode;
+ mocheader;
+ forms;
+ translation;
+ resource;
+ $(BuildDependsOn);
+ </BuildDependsOn>
+ </PropertyGroup>
+ <PropertyGroup>
+ <CleanDependsOn>
+ qtclean;
+ $(CleanDependsOn);
+ </CleanDependsOn>
+ </PropertyGroup>
+
</Project>
diff --git a/build_msvc/msvc-autogen.py b/build_msvc/msvc-autogen.py
index b612467bf3..5ddda3c03e 100644
--- a/build_msvc/msvc-autogen.py
+++ b/build_msvc/msvc-autogen.py
@@ -17,6 +17,7 @@ libs = [
'libbitcoin_wallet_tool',
'libbitcoin_wallet',
'libbitcoin_zmq',
+ 'bench_bitcoin',
]
ignore_list = [
diff --git a/build_msvc/test_bitcoin-qt/test_bitcoin-qt.vcxproj b/build_msvc/test_bitcoin-qt/test_bitcoin-qt.vcxproj
index a5d666c114..8e54bc7653 100644
--- a/build_msvc/test_bitcoin-qt/test_bitcoin-qt.vcxproj
+++ b/build_msvc/test_bitcoin-qt/test_bitcoin-qt.vcxproj
@@ -1,146 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <Import Label="configInitTarget" Project="..\common.init.vcxproj" />
- <ItemGroup Label="ProjectConfigurations">
- <ProjectConfiguration Include="Debug|Win32">
- <Configuration>Debug</Configuration>
- <Platform>Win32</Platform>
- </ProjectConfiguration>
- <ProjectConfiguration Include="Release|Win32">
- <Configuration>Release</Configuration>
- <Platform>Win32</Platform>
- </ProjectConfiguration>
- <ProjectConfiguration Include="Debug|x64">
- <Configuration>Debug</Configuration>
- <Platform>x64</Platform>
- </ProjectConfiguration>
- <ProjectConfiguration Include="Release|x64">
- <Configuration>Release</Configuration>
- <Platform>x64</Platform>
- </ProjectConfiguration>
- </ItemGroup>
+ <Import Project="..\common.init.vcxproj" />
+ <Import Project="..\common.qt.init.vcxproj" />
<PropertyGroup Label="Globals">
- <VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{51201D5E-D939-4854-AE9D-008F03FF518E}</ProjectGuid>
- <Keyword>Win32Proj</Keyword>
- <RootNamespace>test_bitcoinqt</RootNamespace>
- </PropertyGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
- <ConfigurationType>Application</ConfigurationType>
- <UseDebugLibraries>true</UseDebugLibraries>
- <PlatformToolset>v141</PlatformToolset>
- <CharacterSet>Unicode</CharacterSet>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
- <ConfigurationType>Application</ConfigurationType>
- <UseDebugLibraries>false</UseDebugLibraries>
- <PlatformToolset>v141</PlatformToolset>
- <WholeProgramOptimization>true</WholeProgramOptimization>
- <CharacterSet>Unicode</CharacterSet>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
- <ConfigurationType>Application</ConfigurationType>
- <UseDebugLibraries>true</UseDebugLibraries>
- <PlatformToolset>v141</PlatformToolset>
- <CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <PropertyGroup Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
- <UseDebugLibraries>false</UseDebugLibraries>
- <PlatformToolset>v141</PlatformToolset>
- <WholeProgramOptimization>true</WholeProgramOptimization>
- <CharacterSet>Unicode</CharacterSet>
+ <OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
</PropertyGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
- <ImportGroup Label="ExtensionSettings">
- </ImportGroup>
- <ImportGroup Label="Shared">
- </ImportGroup>
- <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
- <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- </ImportGroup>
- <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
- <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- </ImportGroup>
- <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
- <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- </ImportGroup>
- <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
- <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- </ImportGroup>
- <PropertyGroup Label="UserMacros" />
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
- <LinkIncremental>false</LinkIncremental>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
- <LinkIncremental>true</LinkIncremental>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
- <LinkIncremental>true</LinkIncremental>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
- <LinkIncremental>false</LinkIncremental>
- </PropertyGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
- <ClCompile>
- <PrecompiledHeader>NotUsing</PrecompiledHeader>
- <WarningLevel>Level3</WarningLevel>
- <Optimization>MaxSpeed</Optimization>
- <FunctionLevelLinking>true</FunctionLevelLinking>
- <IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>WIN32;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <SDLCheck>true</SDLCheck>
- </ClCompile>
- <Link>
- <SubSystem>Console</SubSystem>
- <EnableCOMDATFolding>true</EnableCOMDATFolding>
- <OptimizeReferences>true</OptimizeReferences>
- <GenerateDebugInformation>true</GenerateDebugInformation>
- </Link>
- </ItemDefinitionGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
- <ClCompile>
- <PrecompiledHeader>NotUsing</PrecompiledHeader>
- <WarningLevel>Level3</WarningLevel>
- <Optimization>Disabled</Optimization>
- <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <SDLCheck>true</SDLCheck>
- </ClCompile>
- <Link>
- <SubSystem>Console</SubSystem>
- <GenerateDebugInformation>true</GenerateDebugInformation>
- </Link>
- </ItemDefinitionGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ItemGroup>
+ <ClCompile Include="..\..\src\test\setup_common.cpp" />
+ <ClCompile Include="..\..\src\qt\test\addressbooktests.cpp" />
+ <ClCompile Include="..\..\src\qt\test\apptests.cpp" />
+ <ClCompile Include="..\..\src\qt\test\compattests.cpp" />
+ <ClCompile Include="..\..\src\qt\test\rpcnestedtests.cpp" />
+ <ClCompile Include="..\..\src\qt\test\test_main.cpp" />
+ <ClCompile Include="..\..\src\qt\test\uritests.cpp" />
+ <ClCompile Include="..\..\src\qt\test\util.cpp" />
+ <ClCompile Include="..\..\src\qt\test\wallettests.cpp" />
+ <ClCompile Include="..\..\src\wallet\test\wallet_test_fixture.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_addressbooktests.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_apptests.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_compattests.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_rpcnestedtests.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_uritests.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_wallettests.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\libbitcoinconsensus\libbitcoinconsensus.vcxproj">
+ <Project>{2b384fa8-9ee1-4544-93cb-0d733c25e8ce}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\libbitcoin_cli\libbitcoin_cli.vcxproj">
+ <Project>{0667528c-d734-4009-adf9-c0d6c4a5a5a6}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\libbitcoin_common\libbitcoin_common.vcxproj">
+ <Project>{7c87e378-df58-482e-aa2f-1bc129bc19ce}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\libbitcoin_crypto\libbitcoin_crypto.vcxproj">
+ <Project>{6190199c-6cf4-4dad-bfbd-93fa72a760c1}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\libbitcoin_qt\libbitcoin_qt.vcxproj">
+ <Project>{2b4abff8-d1fd-4845-88c9-1f3c0a6512bf}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\libbitcoin_server\libbitcoin_server.vcxproj">
+ <Project>{460fee33-1fe1-483f-b3bf-931ff8e969a5}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\libbitcoin_util\libbitcoin_util.vcxproj">
+ <Project>{b53a5535-ee9d-4c6f-9a26-f79ee3bc3754}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\libbitcoin_wallet\libbitcoin_wallet.vcxproj">
+ <Project>{93b86837-b543-48a5-a89b-7c87abb77df2}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\libbitcoin_zmq\libbitcoin_zmq.vcxproj">
+ <Project>{792d487f-f14c-49fc-a9de-3fc150f31c3f}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\libleveldb\libleveldb.vcxproj">
+ <Project>{18430fef-6b61-4c53-b396-718e02850f1b}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\libsecp256k1\libsecp256k1.vcxproj">
+ <Project>{bb493552-3b8c-4a8c-bf69-a6e7a51d2ea6}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\libunivalue\libunivalue.vcxproj">
+ <Project>{5724ba7d-a09a-4ba8-800b-c4c1561b3d69}</Project>
+ </ProjectReference>
+ </ItemGroup>
+
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
- <PrecompiledHeader>NotUsing</PrecompiledHeader>
- <WarningLevel>Level3</WarningLevel>
- <Optimization>Disabled</Optimization>
- <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <SDLCheck>true</SDLCheck>
+ <AdditionalIncludeDirectories>..\libbitcoin_qt\$(GeneratedFilesOutDir)\..\;$(QtIncludeDir)\QtTest;$(QtIncludes);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
- <SubSystem>Console</SubSystem>
- <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>$(QtReleaseLibaries);%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
- <PrecompiledHeader>NotUsing</PrecompiledHeader>
- <WarningLevel>Level3</WarningLevel>
- <Optimization>MaxSpeed</Optimization>
- <FunctionLevelLinking>true</FunctionLevelLinking>
- <IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <SDLCheck>true</SDLCheck>
+ <AdditionalIncludeDirectories>..\libbitcoin_qt\$(GeneratedFilesOutDir)\..\;$(QtIncludeDir)\QtTest;$(QtIncludes);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
- <SubSystem>Console</SubSystem>
- <EnableCOMDATFolding>true</EnableCOMDATFolding>
- <OptimizeReferences>true</OptimizeReferences>
- <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>$(QtDebugLibraries);%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
- <Import Label="configTarget" Project="..\common.vcxproj" />
-</Project>
+
+ <ItemGroup>
+ <MocTestFiles Include="..\..\src\qt\test\addressbooktests.h" />
+ <MocTestFiles Include="..\..\src\qt\test\apptests.h" />
+ <MocTestFiles Include="..\..\src\qt\test\compattests.h" />
+ <MocTestFiles Include="..\..\src\qt\test\paymentservertests.h" />
+ <MocTestFiles Include="..\..\src\qt\test\rpcnestedtests.h" />
+ <MocTestFiles Include="..\..\src\qt\test\uritests.h" />
+ <MocTestFiles Include="..\..\src\qt\test\wallettests.h" />
+ </ItemGroup>
+ <Target Name="moccode" Inputs="@(MocTestFiles)" Outputs="@(MocTestFiles->'$(GeneratedFilesOutDir)\moc\moc_%(Filename).cpp')">
+ <PropertyGroup>
+ <ErrorText>There was an error executing the test_bitcoin-qt moc code generation task.</ErrorText>
+ </PropertyGroup>
+ <Exec Command="echo Performing test_bitcoin-qt moc generation task, output path $(GeneratedFilesOutDir)\moc." />
+ <MakeDir Directories="$(GeneratedFilesOutDir)\moc\" />
+ <Exec Command="$(QtToolsDir)\moc.exe $(MOC_DEFINES) &quot;%(MocTestFiles.Identity)&quot; -o $(GeneratedFilesOutDir)\moc\moc_%(Filename).cpp" />
+ </Target>
+ <Target Name="QtTestCleanGeneratedFiles">
+ <Exec Command="echo Clean test_bitcoin-qt generated files from $(GeneratedFilesOutDir)." />
+ <RemoveDir Directories="$(GeneratedFilesOutDir)\moc\*" />
+ <RemoveDir Directories="$(GeneratedFilesOutDir)\moc" />
+ </Target>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <PropertyGroup>
+ <BuildDependsOn>
+ moccode;
+ $(BuildDependsOn);
+ </BuildDependsOn>
+ </PropertyGroup>
+ <PropertyGroup>
+ <CleanDependsOn>
+ QtTestCleanGeneratedFiles;
+ $(CleanDependsOn);
+ </CleanDependsOn>
+ </PropertyGroup>
+ </Project>
diff --git a/ci/README.md b/ci/README.md
new file mode 100644
index 0000000000..16c481158f
--- /dev/null
+++ b/ci/README.md
@@ -0,0 +1,31 @@
+## ci scripts
+
+This directory contains scripts for each build step in each build stage.
+
+Currently three stages `lint`, `extended_lint` and `test` are defined. Each stage has its own lifecycle, similar to the
+[Travis CI lifecycle](https://docs.travis-ci.com/user/job-lifecycle#the-job-lifecycle). Every script in here is named
+and numbered according to which stage and lifecycle step it belongs to.
+
+### Running a stage locally
+
+To allow for a wide range of tested environments, but also ensure reproducibility to some extent, the test stage
+requires `docker` to be installed. To install all requirements on Ubuntu, run
+
+```
+sudo apt install docker.io ccache bash git
+```
+
+To run the default test stage,
+
+```
+./ci/test_run_all.sh
+```
+
+To run the test stage with a specific configuration,
+
+```
+FILE_ENV="./ci/test/00_setup_env_arm.sh" ./ci/test_run_all.sh
+```
+
+Be aware that the tests will be build and run in-place, so please run at your own risk.
+If the repository is not a fresh git clone, you might have to clean files from previous builds or test runs first.
diff --git a/.travis/extended_lint_04_install.sh b/ci/extended_lint/04_install.sh
index 123d874a84..123d874a84 100755
--- a/.travis/extended_lint_04_install.sh
+++ b/ci/extended_lint/04_install.sh
diff --git a/.travis/extended_lint_06_script.sh b/ci/extended_lint/06_script.sh
index e8228c9c4d..e8228c9c4d 100755
--- a/.travis/extended_lint_06_script.sh
+++ b/ci/extended_lint/06_script.sh
diff --git a/.travis/lint_04_install.sh b/ci/lint/04_install.sh
index 62174620f2..01322f61e6 100755
--- a/.travis/lint_04_install.sh
+++ b/ci/lint/04_install.sh
@@ -6,9 +6,9 @@
export LC_ALL=C
-travis_retry pip install codespell==1.15.0
-travis_retry pip install flake8==3.5.0
-travis_retry pip install vulture==0.29
+travis_retry pip3 install codespell==1.15.0
+travis_retry pip3 install flake8==3.7.8
+travis_retry pip3 install vulture==0.29
SHELLCHECK_VERSION=v0.6.0
curl -s "https://storage.googleapis.com/shellcheck/shellcheck-${SHELLCHECK_VERSION}.linux.x86_64.tar.xz" | tar --xz -xf - --directory /tmp/
diff --git a/.travis/lint_05_before_script.sh b/ci/lint/05_before_script.sh
index 28bcbb47f7..28bcbb47f7 100755
--- a/.travis/lint_05_before_script.sh
+++ b/ci/lint/05_before_script.sh
diff --git a/.travis/lint_06_script.sh b/ci/lint/06_script.sh
index c7dea599dc..c7dea599dc 100755
--- a/.travis/lint_06_script.sh
+++ b/ci/lint/06_script.sh
diff --git a/ci/retry/README.md b/ci/retry/README.md
new file mode 100644
index 0000000000..983a498070
--- /dev/null
+++ b/ci/retry/README.md
@@ -0,0 +1,123 @@
+retry - The command line retry tool
+------------------------------------------
+
+Retry any shell command with exponential backoff or constant delay.
+
+### Instructions
+
+Install:
+
+retry is a shell script, so drop it somewhere and make sure it's added to your $PATH. Or you can use the following one-liner:
+
+```sh
+sudo sh -c "curl https://raw.githubusercontent.com/kadwanev/retry/master/retry -o /usr/local/bin/retry && chmod +x /usr/local/bin/retry"
+```
+
+If you're on OS X, retry is also on Homebrew:
+
+```
+brew pull 27283
+brew install retry
+```
+Not popular enough for homebrew-core. Please star this project to help.
+
+### Usage
+
+Help:
+
+`retry -?`
+
+ Usage: retry [options] -- execute command
+ -h, -?, --help
+ -v, --verbose Verbose output
+ -t, --tries=# Set max retries: Default 10
+ -s, --sleep=secs Constant sleep amount (seconds)
+ -m, --min=secs Exponenetial Backoff: minimum sleep amount (seconds): Default 0.3
+ -x, --max=secs Exponenetial Backoff: maximum sleep amount (seconds): Default 60
+ -f, --fail="script +cmds" Fail Script: run in case of final failure
+
+### Examples
+
+No problem:
+
+`retry echo u work good`
+
+ u work good
+
+Test functionality:
+
+`retry 'echo "y u no work"; false'`
+
+ y u no work
+ Before retry #1: sleeping 0.3 seconds
+ y u no work
+ Before retry #2: sleeping 0.6 seconds
+ y u no work
+ Before retry #3: sleeping 1.2 seconds
+ y u no work
+ Before retry #4: sleeping 2.4 seconds
+ y u no work
+ Before retry #5: sleeping 4.8 seconds
+ y u no work
+ Before retry #6: sleeping 9.6 seconds
+ y u no work
+ Before retry #7: sleeping 19.2 seconds
+ y u no work
+ Before retry #8: sleeping 38.4 seconds
+ y u no work
+ Before retry #9: sleeping 60.0 seconds
+ y u no work
+ Before retry #10: sleeping 60.0 seconds
+ y u no work
+ etc..
+
+Limit retries:
+
+`retry -t 4 'echo "y u no work"; false'`
+
+ y u no work
+ Before retry #1: sleeping 0.3 seconds
+ y u no work
+ Before retry #2: sleeping 0.6 seconds
+ y u no work
+ Before retry #3: sleeping 1.2 seconds
+ y u no work
+ Before retry #4: sleeping 2.4 seconds
+ y u no work
+ Retries exhausted
+
+Bad command:
+
+`retry poop`
+
+ bash: poop: command not found
+
+Fail command:
+
+`retry -t 3 -f 'echo "oh poopsickles"' 'echo "y u no work"; false'`
+
+ y u no work
+ Before retry #1: sleeping 0.3 seconds
+ y u no work
+ Before retry #2: sleeping 0.6 seconds
+ y u no work
+ Before retry #3: sleeping 1.2 seconds
+ y u no work
+ Retries exhausted, running fail script
+ oh poopsickles
+
+Last attempt passed:
+
+`retry -t 3 -- 'if [ $RETRY_ATTEMPT -eq 3 ]; then echo Passed at attempt $RETRY_ATTEMPT; true; else echo Failed at attempt $RETRY_ATTEMPT; false; fi;'`
+
+ Failed at attempt 0
+ Before retry #1: sleeping 0.3 seconds
+ Failed at attempt 1
+ Before retry #2: sleeping 0.6 seconds
+ Failed at attempt 2
+ Before retry #3: sleeping 1.2 seconds
+ Passed at attempt 3
+
+### License
+
+Apache 2.0 - go nuts
diff --git a/ci/retry/retry b/ci/retry/retry
new file mode 100755
index 0000000000..0e5f6e9701
--- /dev/null
+++ b/ci/retry/retry
@@ -0,0 +1,163 @@
+#!/usr/bin/env bash
+
+GETOPT_BIN=$IN_GETOPT_BIN
+GETOPT_BIN=${GETOPT_BIN:-getopt}
+
+__sleep_amount() {
+ if [ -n "$constant_sleep" ]; then
+ sleep_time=$constant_sleep
+ else
+ #TODO: check for awk
+ #TODO: check if user would rather use one of the other possible dependencies: python, ruby, bc, dc
+ sleep_time=`awk "BEGIN {t = $min_sleep * $(( (1<<($attempts -1)) )); print (t > $max_sleep ? $max_sleep : t)}"`
+ fi
+}
+
+__log_out() {
+ echo "$1" 1>&2
+}
+
+# Paramters: max_tries min_sleep max_sleep constant_sleep fail_script EXECUTION_COMMAND
+retry()
+{
+ local max_tries="$1"; shift
+ local min_sleep="$1"; shift
+ local max_sleep="$1"; shift
+ local constant_sleep="$1"; shift
+ local fail_script="$1"; shift
+ if [ -n "$VERBOSE" ]; then
+ __log_out "Retry Parameters: max_tries=$max_tries min_sleep=$min_sleep max_sleep=$max_sleep constant_sleep=$constant_sleep"
+ if [ -n "$fail_script" ]; then __log_out "Fail script: $fail_script"; fi
+ __log_out ""
+ __log_out "Execution Command: $*"
+ __log_out ""
+ fi
+
+ local attempts=0
+ local return_code=1
+
+
+ while [[ $return_code -ne 0 && $attempts -le $max_tries ]]; do
+ if [ $attempts -gt 0 ]; then
+ __sleep_amount
+ __log_out "Before retry #$attempts: sleeping $sleep_time seconds"
+ sleep $sleep_time
+ fi
+
+ P="$1"
+ for param in "${@:2}"; do P="$P '$param'"; done
+ #TODO: replace single quotes in each arg with '"'"' ?
+ export RETRY_ATTEMPT=$attempts
+ bash -c "$P"
+ return_code=$?
+ #__log_out "Process returned $return_code on attempt $attempts"
+ if [ $return_code -eq 127 ]; then
+ # command not found
+ exit $return_code
+ elif [ $return_code -ne 0 ]; then
+ attempts=$[$attempts +1]
+ fi
+ done
+
+ if [ $attempts -gt $max_tries ]; then
+ if [ -n "$fail_script" ]; then
+ __log_out "Retries exhausted, running fail script"
+ eval $fail_script
+ else
+ __log_out "Retries exhausted"
+ fi
+ fi
+
+ exit $return_code
+}
+
+# If we're being sourced, don't worry about such things
+if [ "$BASH_SOURCE" == "$0" ]; then
+ # Prints the help text
+ help()
+ {
+ local retry=$(basename $0)
+ cat <<EOF
+Usage: $retry [options] -- execute command
+ -h, -?, --help
+ -v, --verbose Verbose output
+ -t, --tries=# Set max retries: Default 10
+ -s, --sleep=secs Constant sleep amount (seconds)
+ -m, --min=secs Exponenetial Backoff: minimum sleep amount (seconds): Default 0.3
+ -x, --max=secs Exponenetial Backoff: maximum sleep amount (seconds): Default 60
+ -f, --fail="script +cmds" Fail Script: run in case of final failure
+EOF
+ }
+
+ # show help for no arguments if stdin is a terminal
+ if { [ -z "$1" ] && [ -t 0 ] ; } || [ "$1" == '-h' ] || [ "$1" == '-?' ] || [ "$1" == '--help' ]
+ then
+ help
+ exit 0
+ fi
+
+ $GETOPT_BIN --test > /dev/null
+ if [[ $? -ne 4 ]]; then
+ echo "I’m sorry, 'getopt --test' failed in this environment. Please load GNU getopt."
+ exit 1
+ fi
+
+ OPTIONS=vt:s:m:x:f:
+ LONGOPTIONS=verbose,tries:,sleep:,min:,max:,fail:
+
+ PARSED=$($GETOPT_BIN --options="$OPTIONS" --longoptions="$LONGOPTIONS" --name "$0" -- "$@")
+ if [[ $? -ne 0 ]]; then
+ # e.g. $? == 1
+ # then getopt has complained about wrong arguments to stdout
+ exit 2
+ fi
+ # read getopt’s output this way to handle the quoting right:
+ eval set -- "$PARSED"
+
+ max_tries=10
+ min_sleep=0.3
+ max_sleep=60.0
+ constant_sleep=
+ fail_script=
+
+ # now enjoy the options in order and nicely split until we see --
+ while true; do
+ case "$1" in
+ -v|--verbose)
+ VERBOSE=true
+ shift
+ ;;
+ -t|--tries)
+ max_tries="$2"
+ shift 2
+ ;;
+ -s|--sleep)
+ constant_sleep="$2"
+ shift 2
+ ;;
+ -m|--min)
+ min_sleep="$2"
+ shift 2
+ ;;
+ -x|--max)
+ max_sleep="$2"
+ shift 2
+ ;;
+ -f|--fail)
+ fail_script="$2"
+ shift 2
+ ;;
+ --)
+ shift
+ break
+ ;;
+ *)
+ echo "Programming error"
+ exit 3
+ ;;
+ esac
+ done
+
+ retry "$max_tries" "$min_sleep" "$max_sleep" "$constant_sleep" "$fail_script" "$@"
+
+fi
diff --git a/ci/test/00_setup_env.sh b/ci/test/00_setup_env.sh
new file mode 100755
index 0000000000..51b5cfdd3f
--- /dev/null
+++ b/ci/test/00_setup_env.sh
@@ -0,0 +1,43 @@
+#!/usr/bin/env bash
+#
+# Copyright (c) 2019 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+export LC_ALL=C.UTF-8
+
+echo "Setting default values in env"
+
+BASE_ROOT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )"/../../ >/dev/null 2>&1 && pwd )
+export BASE_ROOT_DIR
+
+# The number of parallel jobs to pass down to make and test_runner.py
+export MAKEJOBS=${MAKEJOBS:--j4}
+# A folder for the ci system to put temporary files (ccache, datadirs for tests, ...)
+export BASE_SCRATCH_DIR=${BASE_SCRATCH_DIR:-$BASE_ROOT_DIR/ci/scratch/}
+export HOST=${HOST:-x86_64-unknown-linux-gnu}
+export RUN_UNIT_TESTS=${RUN_UNIT_TESTS:-true}
+export RUN_FUNCTIONAL_TESTS=${RUN_FUNCTIONAL_TESTS:-true}
+export RUN_FUZZ_TESTS=${RUN_FUZZ_TESTS:-false}
+export DOCKER_NAME_TAG=${DOCKER_NAME_TAG:-ubuntu:18.04}
+export BOOST_TEST_RANDOM=${BOOST_TEST_RANDOM:-1$TRAVIS_BUILD_ID}
+export CCACHE_SIZE=${CCACHE_SIZE:-100M}
+export CCACHE_TEMPDIR=${CCACHE_TEMPDIR:-/tmp/.ccache-temp}
+export CCACHE_COMPRESS=${CCACHE_COMPRESS:-1}
+export CCACHE_DIR=${CCACHE_DIR:-$BASE_SCRATCH_DIR/.ccache}
+export BASE_BUILD_DIR=${BASE_BUILD_DIR:-${TRAVIS_BUILD_DIR:-$BASE_ROOT_DIR}}
+export BASE_OUTDIR=${BASE_OUTDIR:-$BASE_BUILD_DIR/out/$HOST}
+export SDK_URL=${SDK_URL:-https://bitcoincore.org/depends-sources/sdks}
+export WINEDEBUG=${WINEDEBUG:-fixme-all}
+export DOCKER_PACKAGES=${DOCKER_PACKAGES:-build-essential libtool autotools-dev automake pkg-config bsdmainutils curl ca-certificates ccache python3}
+export GOAL=${GOAL:-install}
+export DIR_QA_ASSETS=${DIR_QA_ASSETS:-${BASE_BUILD_DIR}/qa-assets}
+export PATH=${BASE_ROOT_DIR}/ci/retry:$PATH
+export CI_RETRY_EXE=${CI_RETRY_EXE:retry}
+
+echo "Setting specific values in env"
+if [ -n "${FILE_ENV}" ]; then
+ set -o errexit;
+ # shellcheck disable=SC1090
+ source "${FILE_ENV}"
+fi
diff --git a/ci/test/00_setup_env_amd64_asan.sh b/ci/test/00_setup_env_amd64_asan.sh
new file mode 100644
index 0000000000..46b870e145
--- /dev/null
+++ b/ci/test/00_setup_env_amd64_asan.sh
@@ -0,0 +1,13 @@
+#!/usr/bin/env bash
+#
+# Copyright (c) 2019 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+export LC_ALL=C.UTF-8
+
+export HOST=x86_64-unknown-linux-gnu
+export PACKAGES="clang llvm python3-zmq qtbase5-dev qttools5-dev-tools libssl1.0-dev libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libzmq3-dev libqrencode-dev"
+export NO_DEPENDS=1
+export GOAL="install"
+export BITCOIN_CONFIG="--enable-zmq --with-incompatible-bdb --with-gui=qt5 CPPFLAGS=-DDEBUG_LOCKORDER --with-sanitizers=address,integer,undefined CC=clang CXX=clang++"
diff --git a/ci/test/00_setup_env_amd64_fuzz.sh b/ci/test/00_setup_env_amd64_fuzz.sh
new file mode 100644
index 0000000000..edcb65af28
--- /dev/null
+++ b/ci/test/00_setup_env_amd64_fuzz.sh
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+#
+# Copyright (c) 2019 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+export LC_ALL=C.UTF-8
+
+export HOST=x86_64-unknown-linux-gnu
+export PACKAGES="clang llvm python3 libssl1.0-dev libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-test-dev libboost-thread-dev"
+export NO_DEPENDS=1
+export RUN_UNIT_TESTS=false
+export RUN_FUNCTIONAL_TESTS=false
+export RUN_FUZZ_TESTS=true
+export GOAL="install"
+export BITCOIN_CONFIG="--enable-fuzz --with-sanitizers=fuzzer,address CC=clang CXX=clang++"
diff --git a/ci/test/00_setup_env_amd64_nowallet.sh b/ci/test/00_setup_env_amd64_nowallet.sh
new file mode 100644
index 0000000000..d5a2ba3111
--- /dev/null
+++ b/ci/test/00_setup_env_amd64_nowallet.sh
@@ -0,0 +1,13 @@
+#!/usr/bin/env bash
+#
+# Copyright (c) 2019 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+export LC_ALL=C.UTF-8
+
+export HOST=x86_64-unknown-linux-gnu
+export PACKAGES="python3-zmq"
+export DEP_OPTS="NO_WALLET=1"
+export GOAL="install"
+export BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports"
diff --git a/ci/test/00_setup_env_amd64_qt5.sh b/ci/test/00_setup_env_amd64_qt5.sh
new file mode 100644
index 0000000000..55820ea835
--- /dev/null
+++ b/ci/test/00_setup_env_amd64_qt5.sh
@@ -0,0 +1,14 @@
+#!/usr/bin/env bash
+#
+# Copyright (c) 2019 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+export LC_ALL=C.UTF-8
+
+export HOST=x86_64-unknown-linux-gnu
+export PACKAGES="python3-zmq qtbase5-dev qttools5-dev-tools libdbus-1-dev libharfbuzz-dev"
+export DEP_OPTS="NO_QT=1 NO_UPNP=1 DEBUG=1 ALLOW_HOST_PACKAGES=1"
+export TEST_RUNNER_EXTRA="--coverage --extended --exclude feature_dbcrash" # Run extended tests so that coverage does not fail, but exclude the very slow dbcrash
+export GOAL="install"
+export BITCOIN_CONFIG="--enable-zmq --with-gui=qt5 --enable-glibc-back-compat --enable-reduce-exports --enable-debug CFLAGS=\"-g0 -O2 -funsigned-char\" CXXFLAGS=\"-g0 -O2 -funsigned-char\""
diff --git a/ci/test/00_setup_env_amd64_trusty.sh b/ci/test/00_setup_env_amd64_trusty.sh
new file mode 100644
index 0000000000..51e98788c7
--- /dev/null
+++ b/ci/test/00_setup_env_amd64_trusty.sh
@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+#
+# Copyright (c) 2019 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+export LC_ALL=C.UTF-8
+
+export HOST=x86_64-unknown-linux-gnu
+export DOCKER_NAME_TAG=ubuntu:14.04
+export PACKAGES="python3-zmq qtbase5-dev qttools5-dev-tools libicu-dev libpng-dev libssl-dev libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-test-dev libboost-thread-dev libdb5.1++-dev libzmq3-dev libqrencode-dev"
+export NO_DEPENDS=1
+export RUN_FUNCTIONAL_TESTS=false
+export GOAL="install"
+export BITCOIN_CONFIG="--enable-zmq --with-incompatible-bdb --with-gui=no"
diff --git a/ci/test/00_setup_env_amd64_tsan.sh b/ci/test/00_setup_env_amd64_tsan.sh
new file mode 100644
index 0000000000..82ac988c41
--- /dev/null
+++ b/ci/test/00_setup_env_amd64_tsan.sh
@@ -0,0 +1,14 @@
+#!/usr/bin/env bash
+#
+# Copyright (c) 2019 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+export LC_ALL=C.UTF-8
+
+export HOST=x86_64-unknown-linux-gnu
+export DOCKER_NAME_TAG=ubuntu:16.04
+export PACKAGES="clang llvm python3-zmq qtbase5-dev qttools5-dev-tools libssl-dev libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libzmq3-dev libqrencode-dev"
+export NO_DEPENDS=1
+export GOAL="install"
+export BITCOIN_CONFIG="--enable-zmq --disable-wallet --with-gui=qt5 CPPFLAGS=-DDEBUG_LOCKORDER --with-sanitizers=thread --disable-hardening --disable-asm CC=clang CXX=clang++"
diff --git a/ci/test/00_setup_env_arm.sh b/ci/test/00_setup_env_arm.sh
new file mode 100644
index 0000000000..ac7ace8c3b
--- /dev/null
+++ b/ci/test/00_setup_env_arm.sh
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+#
+# Copyright (c) 2019 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+export LC_ALL=C.UTF-8
+
+export HOST=arm-linux-gnueabihf
+export PACKAGES="python3 g++-arm-linux-gnueabihf"
+export RUN_UNIT_TESTS=false
+export RUN_FUNCTIONAL_TESTS=false
+export GOAL="install"
+# -Wno-psabi is to disable ABI warnings: "note: parameter passing for argument of type ... changed in GCC 7.1"
+# This could be removed once the ABI change warning does not show up by default
+export BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports CXXFLAGS=-Wno-psabi"
diff --git a/ci/test/00_setup_env_i686.sh b/ci/test/00_setup_env_i686.sh
new file mode 100644
index 0000000000..63068dc95d
--- /dev/null
+++ b/ci/test/00_setup_env_i686.sh
@@ -0,0 +1,14 @@
+#!/usr/bin/env bash
+#
+# Copyright (c) 2019 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+export LC_ALL=C.UTF-8
+
+export HOST=i686-pc-linux-gnu
+export DEP_OPTS="PROTOBUF=1"
+export PACKAGES="g++-multilib python3-zmq"
+export GOAL="install"
+export BITCOIN_CONFIG="--enable-zmq --with-gui=qt5 --enable-bip70 --enable-glibc-back-compat --enable-reduce-exports LDFLAGS=-static-libstdc++"
+export CONFIG_SHELL="/bin/dash"
diff --git a/ci/test/00_setup_env_mac.sh b/ci/test/00_setup_env_mac.sh
new file mode 100644
index 0000000000..f384ba9263
--- /dev/null
+++ b/ci/test/00_setup_env_mac.sh
@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+#
+# Copyright (c) 2019 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+export LC_ALL=C.UTF-8
+
+export HOST=x86_64-apple-darwin14
+export PACKAGES="cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python3-dev python3-setuptools"
+export OSX_SDK=10.11
+export RUN_UNIT_TESTS=false
+export RUN_FUNCTIONAL_TESTS=false
+export GOAL="deploy"
+export BITCOIN_CONFIG="--enable-gui --enable-reduce-exports --enable-werror"
diff --git a/ci/test/00_setup_env_win64.sh b/ci/test/00_setup_env_win64.sh
new file mode 100644
index 0000000000..1e04c4287a
--- /dev/null
+++ b/ci/test/00_setup_env_win64.sh
@@ -0,0 +1,13 @@
+#!/usr/bin/env bash
+#
+# Copyright (c) 2019 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+export LC_ALL=C.UTF-8
+
+export HOST=x86_64-w64-mingw32
+export PACKAGES="python3 nsis g++-mingw-w64-x86-64 wine-binfmt wine64"
+export RUN_FUNCTIONAL_TESTS=false
+export GOAL="deploy"
+export BITCOIN_CONFIG="--enable-reduce-exports --disable-gui-tests"
diff --git a/.travis/test_03_before_install.sh b/ci/test/03_before_install.sh
index 3c9fcf3f98..5086114ba1 100755
--- a/.travis/test_03_before_install.sh
+++ b/ci/test/03_before_install.sh
@@ -6,7 +6,6 @@
export LC_ALL=C.UTF-8
-PATH=$(echo $PATH | tr ':' "\n" | sed '/\/opt\/python/d' | tr "\n" ":" | sed "s|::|:|g")
# Add llvm-symbolizer directory to PATH. Needed to get symbolized stack traces from the sanitizers.
PATH=$PATH:/usr/lib/llvm-6.0/bin/
export PATH
diff --git a/ci/test/04_install.sh b/ci/test/04_install.sh
new file mode 100755
index 0000000000..54d7a9b814
--- /dev/null
+++ b/ci/test/04_install.sh
@@ -0,0 +1,50 @@
+#!/usr/bin/env bash
+#
+# Copyright (c) 2018 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+export LC_ALL=C.UTF-8
+
+mkdir -p "${BASE_SCRATCH_DIR}"
+ccache echo "Creating ccache dir if it didn't already exist"
+
+if [ ! -d ${DIR_QA_ASSETS} ]; then
+ git clone https://github.com/bitcoin-core/qa-assets ${DIR_QA_ASSETS}
+fi
+export DIR_FUZZ_IN=${DIR_QA_ASSETS}/fuzz_seed_corpus/
+
+mkdir -p "${BASE_BUILD_DIR}/sanitizer-output/"
+export ASAN_OPTIONS=""
+export LSAN_OPTIONS="suppressions=${BASE_BUILD_DIR}/test/sanitizer_suppressions/lsan"
+export TSAN_OPTIONS="suppressions=${BASE_BUILD_DIR}/test/sanitizer_suppressions/tsan:log_path=${BASE_BUILD_DIR}/sanitizer-output/tsan"
+export UBSAN_OPTIONS="suppressions=${BASE_BUILD_DIR}/test/sanitizer_suppressions/ubsan:print_stacktrace=1:halt_on_error=1"
+env | grep -E '^(BITCOIN_CONFIG|CCACHE_|WINEDEBUG|LC_ALL|BOOST_TEST_RANDOM|CONFIG_SHELL|(ASAN|LSAN|TSAN|UBSAN)_OPTIONS)' | tee /tmp/env
+if [[ $HOST = *-mingw32 ]]; then
+ DOCKER_ADMIN="--cap-add SYS_ADMIN"
+elif [[ $BITCOIN_CONFIG = *--with-sanitizers=*address* ]]; then # If ran with (ASan + LSan), Docker needs access to ptrace (https://github.com/google/sanitizers/issues/764)
+ DOCKER_ADMIN="--cap-add SYS_PTRACE"
+fi
+
+if [ -z "$RUN_CI_ON_HOST" ]; then
+ echo "Creating $DOCKER_NAME_TAG container to run in"
+ ${CI_RETRY_EXE} docker pull "$DOCKER_NAME_TAG"
+
+ DOCKER_ID=$(docker run $DOCKER_ADMIN -idt --mount type=bind,src=$BASE_BUILD_DIR,dst=$BASE_BUILD_DIR --mount type=bind,src=$CCACHE_DIR,dst=$CCACHE_DIR -w $BASE_BUILD_DIR --env-file /tmp/env $DOCKER_NAME_TAG)
+
+ DOCKER_EXEC () {
+ docker exec $DOCKER_ID bash -c "cd $PWD && $*"
+ }
+else
+ echo "Running on host system without docker wrapper"
+ DOCKER_EXEC () {
+ bash -c "cd $PWD && $*"
+ }
+fi
+
+DOCKER_EXEC free -m -h
+DOCKER_EXEC echo "Number of CPUs \(nproc\): $(nproc)"
+
+${CI_RETRY_EXE} DOCKER_EXEC apt-get update
+${CI_RETRY_EXE} DOCKER_EXEC apt-get install --no-install-recommends --no-upgrade -qq $PACKAGES $DOCKER_PACKAGES
+
diff --git a/.travis/test_05_before_script.sh b/ci/test/05_before_script.sh
index 516d3fc042..516d3fc042 100755
--- a/.travis/test_05_before_script.sh
+++ b/ci/test/05_before_script.sh
diff --git a/.travis/test_06_script_a.sh b/ci/test/06_script_a.sh
index 8cc593f936..eb6ade7919 100755
--- a/.travis/test_06_script_a.sh
+++ b/ci/test/06_script_a.sh
@@ -6,11 +6,7 @@
export LC_ALL=C.UTF-8
-TRAVIS_COMMIT_LOG=$(git log --format=fuller -1)
-export TRAVIS_COMMIT_LOG
-
-OUTDIR=$BASE_OUTDIR/$TRAVIS_PULL_REQUEST/$TRAVIS_JOB_NUMBER-$HOST
-BITCOIN_CONFIG_ALL="--disable-dependency-tracking --prefix=$TRAVIS_BUILD_DIR/depends/$HOST --bindir=$OUTDIR/bin --libdir=$OUTDIR/lib"
+BITCOIN_CONFIG_ALL="--disable-dependency-tracking --prefix=$BASE_BUILD_DIR/depends/$HOST --bindir=$BASE_OUTDIR/bin --libdir=$BASE_OUTDIR/lib"
if [ -z "$NO_DEPENDS" ]; then
DOCKER_EXEC ccache --max-size=$CCACHE_SIZE
fi
@@ -23,7 +19,7 @@ else
fi
END_FOLD
-mkdir build
+mkdir -p build
cd build || (echo "could not enter build directory"; exit 1)
BEGIN_FOLD configure
@@ -41,10 +37,10 @@ DOCKER_EXEC ./configure --cache-file=../config.cache $BITCOIN_CONFIG_ALL $BITCOI
END_FOLD
set -o errtrace
-trap 'DOCKER_EXEC "cat ${TRAVIS_BUILD_DIR}/sanitizer-output/* 2> /dev/null"' ERR
+trap 'DOCKER_EXEC "cat ${BASE_BUILD_DIR}/sanitizer-output/* 2> /dev/null"' ERR
BEGIN_FOLD build
DOCKER_EXEC make $MAKEJOBS $GOAL || ( echo "Build failure. Verbose build follows." && DOCKER_EXEC make $GOAL V=1 ; false )
END_FOLD
-cd ${TRAVIS_BUILD_DIR} || (echo "could not enter travis build dir $TRAVIS_BUILD_DIR"; exit 1)
+cd ${BASE_BUILD_DIR} || (echo "could not enter travis build dir $BASE_BUILD_DIR"; exit 1)
diff --git a/.travis/test_06_script_b.sh b/ci/test/06_script_b.sh
index e40055a6ee..ea7beae85f 100755
--- a/.travis/test_06_script_b.sh
+++ b/ci/test/06_script_b.sh
@@ -10,13 +10,13 @@ cd "build/bitcoin-$HOST" || (echo "could not enter distdir build/bitcoin-$HOST";
if [ "$RUN_UNIT_TESTS" = "true" ]; then
BEGIN_FOLD unit-tests
- DOCKER_EXEC LD_LIBRARY_PATH=$TRAVIS_BUILD_DIR/depends/$HOST/lib make $MAKEJOBS check VERBOSE=1
+ DOCKER_EXEC LD_LIBRARY_PATH=$BASE_BUILD_DIR/depends/$HOST/lib make $MAKEJOBS check VERBOSE=1
END_FOLD
fi
if [ "$RUN_FUNCTIONAL_TESTS" = "true" ]; then
BEGIN_FOLD functional-tests
- DOCKER_EXEC test/functional/test_runner.py --ci --combinedlogslen=4000 ${TEST_RUNNER_EXTRA} --quiet --failfast
+ DOCKER_EXEC test/functional/test_runner.py --ci $MAKEJOBS --tmpdirprefix "${BASE_SCRATCH_DIR}/test_runner/" --ansi --combinedlogslen=4000 ${TEST_RUNNER_EXTRA} --quiet --failfast
END_FOLD
fi
@@ -26,4 +26,4 @@ if [ "$RUN_FUZZ_TESTS" = "true" ]; then
END_FOLD
fi
-cd ${TRAVIS_BUILD_DIR} || (echo "could not enter travis build dir $TRAVIS_BUILD_DIR"; exit 1)
+cd ${BASE_BUILD_DIR} || (echo "could not enter travis build dir $BASE_BUILD_DIR"; exit 1)
diff --git a/ci/test_run_all.sh b/ci/test_run_all.sh
new file mode 100755
index 0000000000..a1d4bd1952
--- /dev/null
+++ b/ci/test_run_all.sh
@@ -0,0 +1,14 @@
+#!/usr/bin/env bash
+#
+# Copyright (c) 2019 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+export LC_ALL=C.UTF-8
+
+set -o errexit; source ./ci/test/00_setup_env.sh
+set -o errexit; source ./ci/test/03_before_install.sh
+set -o errexit; source ./ci/test/04_install.sh
+set -o errexit; source ./ci/test/05_before_script.sh
+set -o errexit; source ./ci/test/06_script_a.sh
+set -o errexit; source ./ci/test/06_script_b.sh
diff --git a/configure.ac b/configure.ac
index 118d52d423..e1b7281c30 100644
--- a/configure.ac
+++ b/configure.ac
@@ -133,7 +133,7 @@ AC_ARG_ENABLE(gui-tests,
AC_ARG_WITH([rapidcheck],
[AS_HELP_STRING([--with-rapidcheck],
- [enable RapidCheck property based tests (default is yes if librapidcheck is found)])],
+ [enable RapidCheck property-based tests (default is yes if librapidcheck is found)])],
[use_rapidcheck=$withval],
[use_rapidcheck=auto])
@@ -223,10 +223,10 @@ AC_ARG_ENABLE([zmq],
[use_zmq=$enableval],
[use_zmq=yes])
AC_ARG_ENABLE([bip70],
- [AS_HELP_STRING([--disable-bip70],
- [disable BIP70 (payment protocol) support in GUI (enabled by default)])],
+ [AS_HELP_STRING([--enable-bip70],
+ [enable BIP70 (payment protocol) support in the GUI (default is to disable)])],
[enable_bip70=$enableval],
- [enable_bip70=auto])
+ [enable_bip70=no])
AC_ARG_WITH([protoc-bindir],[AS_HELP_STRING([--with-protoc-bindir=BIN_DIR],[specify protoc bin path])], [protoc_bin_path=$withval], [])
@@ -239,7 +239,7 @@ AM_CONDITIONAL(ENABLE_MAN, test "$enable_man" != no)
# Enable debug
AC_ARG_ENABLE([debug],
[AS_HELP_STRING([--enable-debug],
- [use debug compiler flags and macros (default is no)])],
+ [use compiler flags and macros suited for debugging (default is no)])],
[enable_debug=$enableval],
[enable_debug=no])
@@ -271,12 +271,9 @@ if test "x$enable_debug" = xyes; then
if test "x$CXXFLAGS_overridden" = xno; then
CXXFLAGS=""
fi
- # Prefer -Og, fall back to -O0 if that is unavailable.
- AX_CHECK_COMPILE_FLAG(
- [-Og],
- [[DEBUG_CXXFLAGS="$DEBUG_CXXFLAGS -Og"]],
- [AX_CHECK_COMPILE_FLAG([-O0],[[DEBUG_CXXFLAGS="$DEBUG_CXXFLAGS -O0"]],,[[$CXXFLAG_WERROR]])],
- [[$CXXFLAG_WERROR]])
+
+ # Disable all optimizations
+ AX_CHECK_COMPILE_FLAG([-O0], [[DEBUG_CXXFLAGS="$DEBUG_CXXFLAGS -O0"]],,[[$CXXFLAG_WERROR]])
# Prefer -g3, fall back to -g if that is unavailable.
AX_CHECK_COMPILE_FLAG(
@@ -588,7 +585,7 @@ case $host in
fi
AX_CHECK_LINK_FLAG([[-Wl,-headerpad_max_install_names]], [LDFLAGS="$LDFLAGS -Wl,-headerpad_max_install_names"])
- CPPFLAGS="$CPPFLAGS -DMAC_OSX"
+ CPPFLAGS="$CPPFLAGS -DMAC_OSX -DOBJC_OLD_DISPATCH_PROTOTYPES=0"
OBJCXXFLAGS="$CXXFLAGS"
;;
*android*)
@@ -1278,7 +1275,7 @@ AC_CHECK_DECLS([EVP_MD_CTX_new],,,[AC_INCLUDES_DEFAULT
])
CXXFLAGS="${save_CXXFLAGS}"
-dnl RapidCheck Property Based Testing
+dnl RapidCheck property-based testing
enable_property_tests=no
if test "x$use_rapidcheck" = xauto; then
@@ -1625,7 +1622,7 @@ if test x$need_bundled_univalue = xyes; then
AC_CONFIG_SUBDIRS([src/univalue])
fi
-ac_configure_args="${ac_configure_args} --disable-shared --with-pic --with-bignum=no --enable-module-recovery --disable-jni"
+ac_configure_args="${ac_configure_args} --disable-shared --with-pic --enable-benchmark=no --with-bignum=no --enable-module-recovery --disable-jni"
AC_CONFIG_SUBDIRS([src/secp256k1])
AC_OUTPUT
@@ -1660,6 +1657,7 @@ fi
echo " with zmq = $use_zmq"
echo " with test = $use_tests"
if test x$use_tests != xno; then
+ echo " with prop = $enable_property_tests"
echo " with fuzz = $enable_fuzz"
fi
echo " with bench = $use_bench"
diff --git a/contrib/README.md b/contrib/README.md
index 8915919766..361975baa4 100644
--- a/contrib/README.md
+++ b/contrib/README.md
@@ -3,10 +3,10 @@ Repository Tools
### [Developer tools](/contrib/devtools) ###
Specific tools for developers working on this repository.
-Contains the script `github-merge.py` for merging GitHub pull requests securely and signing them using GPG.
+Additional tools, including the `github-merge.py` script, are available in the [maintainer-tools](https://github.com/bitcoin-core/bitcoin-maintainer-tools) repository.
### [Verify-Commits](/contrib/verify-commits) ###
-Tool to verify that every merge commit was signed by a developer using the above `github-merge.py` script.
+Tool to verify that every merge commit was signed by a developer using the `github-merge.py` script.
### [Linearize](/contrib/linearize) ###
Construct a linear, no-fork, best version of the blockchain.
@@ -33,12 +33,12 @@ Files used during the gitian build process. For more information about gitian, s
PGP keys used for signing Bitcoin Core [Gitian release](/doc/release-process.md) results.
### [MacDeploy](/contrib/macdeploy) ###
-Scripts and notes for Mac builds.
+Scripts and notes for Mac builds.
### [Gitian-build](/contrib/gitian-build.py) ###
Script for running full Gitian builds.
-Test and Verify Tools
+Test and Verify Tools
---------------------
### [TestGen](/contrib/testgen) ###
diff --git a/contrib/bitcoin-qt.pro b/contrib/bitcoin-qt.pro
index b8133bf789..0e4eeee0a7 100644
--- a/contrib/bitcoin-qt.pro
+++ b/contrib/bitcoin-qt.pro
@@ -16,6 +16,7 @@ FORMS += \
../src/qt/forms/sendcoinsentry.ui \
../src/qt/forms/signverifymessagedialog.ui \
../src/qt/forms/transactiondescdialog.ui \
+ ../src/qt/forms/createwalletdialog.ui
RESOURCES += \
../src/qt/bitcoin.qrc
diff --git a/contrib/debian/copyright b/contrib/debian/copyright
index 2d5b0188d2..0eccbacb96 100644
--- a/contrib/debian/copyright
+++ b/contrib/debian/copyright
@@ -26,21 +26,14 @@ License: GNU-All-permissive-License
Files: src/qt/res/icons/add.png
src/qt/res/icons/address-book.png
src/qt/res/icons/chevron.png
- src/qt/res/icons/configure.png
- src/qt/res/icons/debugwindow.png
src/qt/res/icons/edit.png
src/qt/res/icons/editcopy.png
src/qt/res/icons/editpaste.png
src/qt/res/icons/export.png
src/qt/res/icons/eye.png
- src/qt/res/icons/filesave.png
src/qt/res/icons/history.png
- src/qt/res/icons/info.png
- src/qt/res/icons/key.png
src/qt/res/icons/lock_*.png
- src/qt/res/icons/open.png
src/qt/res/icons/overview.png
- src/qt/res/icons/quit.png
src/qt/res/icons/receive.png
src/qt/res/icons/remove.png
src/qt/res/icons/send.png
@@ -60,7 +53,7 @@ Files: src/qt/res/icons/connect*.png
Copyright: Marco Falke
Luke Dashjr
License: Expat
-Comment: Inspired by Stephan Hutchings Typicons
+Comment: Inspired by Stephen Hutchings' Typicons
Files: src/qt/res/icons/tx_mined.png
src/qt/res/src/mine.svg
@@ -72,21 +65,17 @@ Files: src/qt/res/icons/tx_mined.png
src/qt/res/src/hd_enabled.svg
Copyright: Jonas Schnelli
License: Expat
-Comment:
Files: src/qt/res/icons/clock*.png
src/qt/res/icons/eye_*.png
src/qt/res/icons/tx_in*.png
- src/qt/res/icons/verify.png
src/qt/res/src/clock_*.svg
src/qt/res/src/tx_*.svg
- src/qt/res/src/verify.svg
-Copyright: Stephan Hutching, Jonas Schnelli
+Copyright: Stephen Hutchings, Jonas Schnelli
License: Expat
-Comment: Modifications of Stephan Hutchings Typicons
+Comment: Modifications of Stephen Hutchings' Typicons
-Files: src/qt/res/icons/about.png
- src/qt/res/icons/bitcoin.*
+Files: src/qt/res/icons/bitcoin.*
share/pixmaps/bitcoin*
src/qt/res/src/bitcoin.svg
Copyright: Bitboy, Jonas Schnelli
diff --git a/contrib/devtools/README.md b/contrib/devtools/README.md
index 4994d7f0a5..04fa02484f 100644
--- a/contrib/devtools/README.md
+++ b/contrib/devtools/README.md
@@ -89,66 +89,6 @@ example:
BUILDDIR=$PWD/build contrib/devtools/gen-manpages.sh
```
-github-merge.py
-===============
-
-A small script to automate merging pull-requests securely and sign them with GPG.
-
-For example:
-
- ./github-merge.py 3077
-
-(in any git repository) will help you merge pull request #3077 for the
-bitcoin/bitcoin repository.
-
-What it does:
-* Fetch master and the pull request.
-* Locally construct a merge commit.
-* Show the diff that merge results in.
-* Ask you to verify the resulting source tree (so you can do a make
-check or whatever).
-* Ask you whether to GPG sign the merge commit.
-* Ask you whether to push the result upstream.
-
-This means that there are no potential race conditions (where a
-pullreq gets updated while you're reviewing it, but before you click
-merge), and when using GPG signatures, that even a compromised GitHub
-couldn't mess with the sources.
-
-Setup
----------
-Configuring the github-merge tool for the bitcoin repository is done in the following way:
-
- git config githubmerge.repository bitcoin/bitcoin
- git config githubmerge.testcmd "make -j4 check" (adapt to whatever you want to use for testing)
- git config --global user.signingkey mykeyid
-
-Authentication (optional)
---------------------------
-
-The API request limit for unauthenticated requests is quite low, but the
-limit for authenticated requests is much higher. If you start running
-into rate limiting errors it can be useful to set an authentication token
-so that the script can authenticate requests.
-
-- First, go to [Personal access tokens](https://github.com/settings/tokens).
-- Click 'Generate new token'.
-- Fill in an arbitrary token description. No further privileges are needed.
-- Click the `Generate token` button at the bottom of the form.
-- Copy the generated token (should be a hexadecimal string)
-
-Then do:
-
- git config --global user.ghtoken "pasted token"
-
-Create and verify timestamps of merge commits
----------------------------------------------
-To create or verify timestamps on the merge commits, install the OpenTimestamps
-client via `pip3 install opentimestamps-client`. Then, download the gpg wrapper
-`ots-git-gpg-wrapper.sh` and set it as git's `gpg.program`. See
-[the ots git integration documentation](https://github.com/opentimestamps/opentimestamps-client/blob/master/doc/git-integration.md#usage)
-for further details.
-
optimize-pngs.py
================
@@ -180,18 +120,6 @@ If there are 'unsupported' symbols, the return value will be 1 a list like this
.../64/test_bitcoin: symbol std::out_of_range::~out_of_range() from unsupported version GLIBCXX_3.4.15
.../64/test_bitcoin: symbol _ZNSt8__detail15_List_nod from unsupported version GLIBCXX_3.4.15
-update-translations.py
-======================
-
-Run this script from the root of the repository to update all translations from transifex.
-It will do the following automatically:
-
-- fetch all translations
-- post-process them into valid and committable format
-- add missing translations to the build system (TODO)
-
-See doc/translation-process.md for more information.
-
circular-dependencies.py
========================
diff --git a/contrib/devtools/clang-format-diff.py b/contrib/devtools/clang-format-diff.py
index f322b3a880..98eee67f43 100755
--- a/contrib/devtools/clang-format-diff.py
+++ b/contrib/devtools/clang-format-diff.py
@@ -106,7 +106,7 @@ def main():
filename = None
lines_by_file = {}
for line in sys.stdin:
- match = re.search('^\+\+\+\ (.*?/){%s}(\S*)' % args.p, line)
+ match = re.search(r'^\+\+\+\ (.*?/){%s}(\S*)' % args.p, line)
if match:
filename = match.group(2)
if filename is None:
@@ -119,7 +119,7 @@ def main():
if not re.match('^%s$' % args.iregex, filename, re.IGNORECASE):
continue
- match = re.search('^@@.*\+(\d+)(,(\d+))?', line)
+ match = re.search(r'^@@.*\+(\d+)(,(\d+))?', line)
if match:
start_line = int(match.group(1))
line_count = 1
diff --git a/contrib/devtools/copyright_header.py b/contrib/devtools/copyright_header.py
index fc01e570aa..67e77bc63d 100755
--- a/contrib/devtools/copyright_header.py
+++ b/contrib/devtools/copyright_header.py
@@ -71,7 +71,7 @@ def get_filenames_to_examine(base_directory):
################################################################################
-COPYRIGHT_WITH_C = 'Copyright \(c\)'
+COPYRIGHT_WITH_C = r'Copyright \(c\)'
COPYRIGHT_WITHOUT_C = 'Copyright'
ANY_COPYRIGHT_STYLE = '(%s|%s)' % (COPYRIGHT_WITH_C, COPYRIGHT_WITHOUT_C)
@@ -85,21 +85,21 @@ ANY_COPYRIGHT_STYLE_OR_YEAR_STYLE = ("%s %s" % (ANY_COPYRIGHT_STYLE,
ANY_COPYRIGHT_COMPILED = re.compile(ANY_COPYRIGHT_STYLE_OR_YEAR_STYLE)
def compile_copyright_regex(copyright_style, year_style, name):
- return re.compile('%s %s,? %s' % (copyright_style, year_style, name))
+ return re.compile(r'%s %s,? %s( +\*)?\n' % (copyright_style, year_style, name))
EXPECTED_HOLDER_NAMES = [
- "Satoshi Nakamoto\n",
- "The Bitcoin Core developers\n",
- "BitPay Inc\.\n",
- "University of Illinois at Urbana-Champaign\.\n",
- "Pieter Wuille\n",
- "Wladimir J. van der Laan\n",
- "Jeff Garzik\n",
- "Jan-Klaas Kollhof\n",
- "ArtForz -- public domain half-a-node\n",
- "Intel Corporation",
- "The Zcash developers",
- "Jeremy Rubin",
+ r"Satoshi Nakamoto",
+ r"The Bitcoin Core developers",
+ r"BitPay Inc\.",
+ r"University of Illinois at Urbana-Champaign\.",
+ r"Pieter Wuille",
+ r"Wladimir J\. van der Laan",
+ r"Jeff Garzik",
+ r"Jan-Klaas Kollhof",
+ r"ArtForz -- public domain half-a-node",
+ r"Intel Corporation ?",
+ r"The Zcash developers",
+ r"Jeremy Rubin",
]
DOMINANT_STYLE_COMPILED = {}
@@ -329,7 +329,7 @@ def write_file_lines(filename, file_lines):
# update header years execution
################################################################################
-COPYRIGHT = 'Copyright \(c\)'
+COPYRIGHT = r'Copyright \(c\)'
YEAR = "20[0-9][0-9]"
YEAR_RANGE = '(%s)(-%s)?' % (YEAR, YEAR)
HOLDER = 'The Bitcoin Core developers'
diff --git a/contrib/devtools/github-merge.py b/contrib/devtools/github-merge.py
deleted file mode 100755
index 78ac671bfe..0000000000
--- a/contrib/devtools/github-merge.py
+++ /dev/null
@@ -1,413 +0,0 @@
-#!/usr/bin/env python3
-# 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.
-
-# This script will locally construct a merge commit for a pull request on a
-# github repository, inspect it, sign it and optionally push it.
-
-# The following temporary branches are created/overwritten and deleted:
-# * pull/$PULL/base (the current master we're merging onto)
-# * pull/$PULL/head (the current state of the remote pull request)
-# * pull/$PULL/merge (github's merge)
-# * pull/$PULL/local-merge (our merge)
-
-# In case of a clean merge that is accepted by the user, the local branch with
-# name $BRANCH is overwritten with the merged result, and optionally pushed.
-import os
-from sys import stdin,stdout,stderr
-import argparse
-import hashlib
-import subprocess
-import sys
-import json
-import codecs
-from urllib.request import Request, urlopen
-from urllib.error import HTTPError
-
-# External tools (can be overridden using environment)
-GIT = os.getenv('GIT','git')
-BASH = os.getenv('BASH','bash')
-
-# OS specific configuration for terminal attributes
-ATTR_RESET = ''
-ATTR_PR = ''
-ATTR_NAME = ''
-ATTR_WARN = ''
-COMMIT_FORMAT = '%H %s (%an)%d'
-if os.name == 'posix': # if posix, assume we can use basic terminal escapes
- ATTR_RESET = '\033[0m'
- ATTR_PR = '\033[1;36m'
- ATTR_NAME = '\033[0;36m'
- ATTR_WARN = '\033[1;31m'
- COMMIT_FORMAT = '%C(bold blue)%H%Creset %s %C(cyan)(%an)%Creset%C(green)%d%Creset'
-
-def git_config_get(option, default=None):
- '''
- Get named configuration option from git repository.
- '''
- try:
- return subprocess.check_output([GIT,'config','--get',option]).rstrip().decode('utf-8')
- except subprocess.CalledProcessError:
- return default
-
-def get_response(req_url, ghtoken):
- req = Request(req_url)
- if ghtoken is not None:
- req.add_header('Authorization', 'token ' + ghtoken)
- return urlopen(req)
-
-def retrieve_json(req_url, ghtoken, use_pagination=False):
- '''
- Retrieve json from github.
- Return None if an error happens.
- '''
- try:
- reader = codecs.getreader('utf-8')
- if not use_pagination:
- return json.load(reader(get_response(req_url, ghtoken)))
-
- obj = []
- page_num = 1
- while True:
- req_url_page = '{}?page={}'.format(req_url, page_num)
- result = get_response(req_url_page, ghtoken)
- obj.extend(json.load(reader(result)))
-
- link = result.headers.get('link', None)
- if link is not None:
- link_next = [l for l in link.split(',') if 'rel="next"' in l]
- if len(link_next) > 0:
- page_num = int(link_next[0][link_next[0].find("page=")+5:link_next[0].find(">")])
- continue
- break
- return obj
- except HTTPError as e:
- error_message = e.read()
- print('Warning: unable to retrieve pull information from github: %s' % e)
- print('Detailed error: %s' % error_message)
- return None
- except Exception as e:
- print('Warning: unable to retrieve pull information from github: %s' % e)
- return None
-
-def retrieve_pr_info(repo,pull,ghtoken):
- req_url = "https://api.github.com/repos/"+repo+"/pulls/"+pull
- return retrieve_json(req_url,ghtoken)
-
-def retrieve_pr_comments(repo,pull,ghtoken):
- req_url = "https://api.github.com/repos/"+repo+"/issues/"+pull+"/comments"
- return retrieve_json(req_url,ghtoken,use_pagination=True)
-
-def retrieve_pr_reviews(repo,pull,ghtoken):
- req_url = "https://api.github.com/repos/"+repo+"/pulls/"+pull+"/reviews"
- return retrieve_json(req_url,ghtoken,use_pagination=True)
-
-def ask_prompt(text):
- print(text,end=" ",file=stderr)
- stderr.flush()
- reply = stdin.readline().rstrip()
- print("",file=stderr)
- return reply
-
-def get_symlink_files():
- files = sorted(subprocess.check_output([GIT, 'ls-tree', '--full-tree', '-r', 'HEAD']).splitlines())
- ret = []
- for f in files:
- if (int(f.decode('utf-8').split(" ")[0], 8) & 0o170000) == 0o120000:
- ret.append(f.decode('utf-8').split("\t")[1])
- return ret
-
-def tree_sha512sum(commit='HEAD'):
- # request metadata for entire tree, recursively
- files = []
- blob_by_name = {}
- for line in subprocess.check_output([GIT, 'ls-tree', '--full-tree', '-r', commit]).splitlines():
- name_sep = line.index(b'\t')
- metadata = line[:name_sep].split() # perms, 'blob', blobid
- assert(metadata[1] == b'blob')
- name = line[name_sep+1:]
- files.append(name)
- blob_by_name[name] = metadata[2]
-
- files.sort()
- # open connection to git-cat-file in batch mode to request data for all blobs
- # this is much faster than launching it per file
- p = subprocess.Popen([GIT, 'cat-file', '--batch'], stdout=subprocess.PIPE, stdin=subprocess.PIPE)
- overall = hashlib.sha512()
- for f in files:
- blob = blob_by_name[f]
- # request blob
- p.stdin.write(blob + b'\n')
- p.stdin.flush()
- # read header: blob, "blob", size
- reply = p.stdout.readline().split()
- assert(reply[0] == blob and reply[1] == b'blob')
- size = int(reply[2])
- # hash the blob data
- intern = hashlib.sha512()
- ptr = 0
- while ptr < size:
- bs = min(65536, size - ptr)
- piece = p.stdout.read(bs)
- if len(piece) == bs:
- intern.update(piece)
- else:
- raise IOError('Premature EOF reading git cat-file output')
- ptr += bs
- dig = intern.hexdigest()
- assert(p.stdout.read(1) == b'\n') # ignore LF that follows blob data
- # update overall hash with file hash
- overall.update(dig.encode("utf-8"))
- overall.update(" ".encode("utf-8"))
- overall.update(f)
- overall.update("\n".encode("utf-8"))
- p.stdin.close()
- if p.wait():
- raise IOError('Non-zero return value executing git cat-file')
- return overall.hexdigest()
-
-def get_acks_from_comments(head_commit, comments):
- # Look for abbreviated commit id, because not everyone wants to type/paste
- # the whole thing and the chance of collisions within a PR is small enough
- head_abbrev = head_commit[0:6]
- acks = []
- for c in comments:
- review = [l for l in c['body'].split('\r\n') if 'ACK' in l and head_abbrev in l]
- if review:
- acks.append((c['user']['login'], review[0]))
- return acks
-
-def make_acks_message(head_commit, acks):
- if acks:
- ack_str ='\n\nACKs for top commit:\n'.format(head_commit)
- for name, msg in acks:
- ack_str += ' {}:\n'.format(name)
- ack_str += ' {}\n'.format(msg)
- else:
- ack_str ='\n\nTop commit has no ACKs.\n'
- return ack_str
-
-def print_merge_details(pull, title, branch, base_branch, head_branch, acks):
- print('%s#%s%s %s %sinto %s%s' % (ATTR_RESET+ATTR_PR,pull,ATTR_RESET,title,ATTR_RESET+ATTR_PR,branch,ATTR_RESET))
- subprocess.check_call([GIT,'log','--graph','--topo-order','--pretty=format:'+COMMIT_FORMAT,base_branch+'..'+head_branch])
- if acks is not None:
- if acks:
- print('{}ACKs:{}'.format(ATTR_PR, ATTR_RESET))
- for (name, message) in acks:
- print('* {} {}({}){}'.format(message, ATTR_NAME, name, ATTR_RESET))
- else:
- print('{}Top commit has no ACKs!{}'.format(ATTR_WARN, ATTR_RESET))
-
-def parse_arguments():
- epilog = '''
- In addition, you can set the following git configuration variables:
- githubmerge.repository (mandatory),
- user.signingkey (mandatory),
- user.ghtoken (default: none).
- githubmerge.host (default: git@github.com),
- githubmerge.branch (no default),
- githubmerge.testcmd (default: none).
- '''
- parser = argparse.ArgumentParser(description='Utility to merge, sign and push github pull requests',
- epilog=epilog)
- parser.add_argument('pull', metavar='PULL', type=int, nargs=1,
- help='Pull request ID to merge')
- parser.add_argument('branch', metavar='BRANCH', type=str, nargs='?',
- default=None, help='Branch to merge against (default: githubmerge.branch setting, or base branch for pull, or \'master\')')
- return parser.parse_args()
-
-def main():
- # Extract settings from git repo
- repo = git_config_get('githubmerge.repository')
- host = git_config_get('githubmerge.host','git@github.com')
- opt_branch = git_config_get('githubmerge.branch',None)
- testcmd = git_config_get('githubmerge.testcmd')
- ghtoken = git_config_get('user.ghtoken')
- signingkey = git_config_get('user.signingkey')
- if repo is None:
- print("ERROR: No repository configured. Use this command to set:", file=stderr)
- print("git config githubmerge.repository <owner>/<repo>", file=stderr)
- sys.exit(1)
- if signingkey is None:
- print("ERROR: No GPG signing key set. Set one using:",file=stderr)
- print("git config --global user.signingkey <key>",file=stderr)
- sys.exit(1)
-
- if host.startswith(('https:','http:')):
- host_repo = host+"/"+repo+".git"
- else:
- host_repo = host+":"+repo
-
- # Extract settings from command line
- args = parse_arguments()
- pull = str(args.pull[0])
-
- # Receive pull information from github
- info = retrieve_pr_info(repo,pull,ghtoken)
- if info is None:
- sys.exit(1)
- title = info['title'].strip()
- body = info['body'].strip()
- # precedence order for destination branch argument:
- # - command line argument
- # - githubmerge.branch setting
- # - base branch for pull (as retrieved from github)
- # - 'master'
- branch = args.branch or opt_branch or info['base']['ref'] or 'master'
-
- # Initialize source branches
- head_branch = 'pull/'+pull+'/head'
- base_branch = 'pull/'+pull+'/base'
- merge_branch = 'pull/'+pull+'/merge'
- local_merge_branch = 'pull/'+pull+'/local-merge'
-
- devnull = open(os.devnull, 'w', encoding="utf8")
- try:
- subprocess.check_call([GIT,'checkout','-q',branch])
- except subprocess.CalledProcessError:
- print("ERROR: Cannot check out branch %s." % (branch), file=stderr)
- sys.exit(3)
- try:
- subprocess.check_call([GIT,'fetch','-q',host_repo,'+refs/pull/'+pull+'/*:refs/heads/pull/'+pull+'/*',
- '+refs/heads/'+branch+':refs/heads/'+base_branch])
- except subprocess.CalledProcessError:
- print("ERROR: Cannot find pull request #%s or branch %s on %s." % (pull,branch,host_repo), file=stderr)
- sys.exit(3)
- try:
- subprocess.check_call([GIT,'log','-q','-1','refs/heads/'+head_branch], stdout=devnull, stderr=stdout)
- head_commit = subprocess.check_output([GIT,'log','-1','--pretty=format:%H',head_branch]).decode('utf-8')
- assert len(head_commit) == 40
- except subprocess.CalledProcessError:
- print("ERROR: Cannot find head of pull request #%s on %s." % (pull,host_repo), file=stderr)
- sys.exit(3)
- try:
- subprocess.check_call([GIT,'log','-q','-1','refs/heads/'+merge_branch], stdout=devnull, stderr=stdout)
- except subprocess.CalledProcessError:
- print("ERROR: Cannot find merge of pull request #%s on %s." % (pull,host_repo), file=stderr)
- sys.exit(3)
- subprocess.check_call([GIT,'checkout','-q',base_branch])
- subprocess.call([GIT,'branch','-q','-D',local_merge_branch], stderr=devnull)
- subprocess.check_call([GIT,'checkout','-q','-b',local_merge_branch])
-
- try:
- # Go up to the repository's root.
- toplevel = subprocess.check_output([GIT,'rev-parse','--show-toplevel']).strip()
- os.chdir(toplevel)
- # Create unsigned merge commit.
- if title:
- firstline = 'Merge #%s: %s' % (pull,title)
- else:
- firstline = 'Merge #%s' % (pull,)
- message = firstline + '\n\n'
- message += subprocess.check_output([GIT,'log','--no-merges','--topo-order','--pretty=format:%H %s (%an)',base_branch+'..'+head_branch]).decode('utf-8')
- message += '\n\nPull request description:\n\n ' + body.replace('\n', '\n ') + '\n'
- try:
- subprocess.check_call([GIT,'merge','-q','--commit','--no-edit','--no-ff','--no-gpg-sign','-m',message.encode('utf-8'),head_branch])
- except subprocess.CalledProcessError:
- print("ERROR: Cannot be merged cleanly.",file=stderr)
- subprocess.check_call([GIT,'merge','--abort'])
- sys.exit(4)
- logmsg = subprocess.check_output([GIT,'log','--pretty=format:%s','-n','1']).decode('utf-8')
- if logmsg.rstrip() != firstline.rstrip():
- print("ERROR: Creating merge failed (already merged?).",file=stderr)
- sys.exit(4)
-
- symlink_files = get_symlink_files()
- for f in symlink_files:
- print("ERROR: File %s was a symlink" % f)
- if len(symlink_files) > 0:
- sys.exit(4)
-
- # Compute SHA512 of git tree (to be able to detect changes before sign-off)
- try:
- first_sha512 = tree_sha512sum()
- except subprocess.CalledProcessError:
- print("ERROR: Unable to compute tree hash")
- sys.exit(4)
-
- print_merge_details(pull, title, branch, base_branch, head_branch, None)
- print()
-
- # Run test command if configured.
- if testcmd:
- if subprocess.call(testcmd,shell=True):
- print("ERROR: Running %s failed." % testcmd,file=stderr)
- sys.exit(5)
-
- # Show the created merge.
- diff = subprocess.check_output([GIT,'diff',merge_branch+'..'+local_merge_branch])
- subprocess.check_call([GIT,'diff',base_branch+'..'+local_merge_branch])
- if diff:
- print("WARNING: merge differs from github!",file=stderr)
- reply = ask_prompt("Type 'ignore' to continue.")
- if reply.lower() == 'ignore':
- print("Difference with github ignored.",file=stderr)
- else:
- sys.exit(6)
- else:
- # Verify the result manually.
- print("Dropping you on a shell so you can try building/testing the merged source.",file=stderr)
- print("Run 'git diff HEAD~' to show the changes being merged.",file=stderr)
- print("Type 'exit' when done.",file=stderr)
- if os.path.isfile('/etc/debian_version'): # Show pull number on Debian default prompt
- os.putenv('debian_chroot',pull)
- subprocess.call([BASH,'-i'])
-
- second_sha512 = tree_sha512sum()
- if first_sha512 != second_sha512:
- print("ERROR: Tree hash changed unexpectedly",file=stderr)
- sys.exit(8)
-
- # Retrieve PR comments and ACKs and add to commit message, store ACKs to print them with commit
- # description
- comments = retrieve_pr_comments(repo,pull,ghtoken) + retrieve_pr_reviews(repo,pull,ghtoken)
- if comments is None:
- print("ERROR: Could not fetch PR comments and reviews",file=stderr)
- sys.exit(1)
- acks = get_acks_from_comments(head_commit=head_commit, comments=comments)
- message += make_acks_message(head_commit=head_commit, acks=acks)
- # end message with SHA512 tree hash, then update message
- message += '\n\nTree-SHA512: ' + first_sha512
- try:
- subprocess.check_call([GIT,'commit','--amend','--no-gpg-sign','-m',message.encode('utf-8')])
- except subprocess.CalledProcessError:
- print("ERROR: Cannot update message.", file=stderr)
- sys.exit(4)
-
- # Sign the merge commit.
- print_merge_details(pull, title, branch, base_branch, head_branch, acks)
- while True:
- reply = ask_prompt("Type 's' to sign off on the above merge, or 'x' to reject and exit.").lower()
- if reply == 's':
- try:
- subprocess.check_call([GIT,'commit','-q','--gpg-sign','--amend','--no-edit'])
- break
- except subprocess.CalledProcessError:
- print("Error while signing, asking again.",file=stderr)
- elif reply == 'x':
- print("Not signing off on merge, exiting.",file=stderr)
- sys.exit(1)
-
- # Put the result in branch.
- subprocess.check_call([GIT,'checkout','-q',branch])
- subprocess.check_call([GIT,'reset','-q','--hard',local_merge_branch])
- finally:
- # Clean up temporary branches.
- subprocess.call([GIT,'checkout','-q',branch])
- subprocess.call([GIT,'branch','-q','-D',head_branch],stderr=devnull)
- subprocess.call([GIT,'branch','-q','-D',base_branch],stderr=devnull)
- subprocess.call([GIT,'branch','-q','-D',merge_branch],stderr=devnull)
- subprocess.call([GIT,'branch','-q','-D',local_merge_branch],stderr=devnull)
-
- # Push the result.
- while True:
- reply = ask_prompt("Type 'push' to push the result to %s, branch %s, or 'x' to exit without pushing." % (host_repo,branch)).lower()
- if reply == 'push':
- subprocess.check_call([GIT,'push',host_repo,'refs/heads/'+branch])
- break
- elif reply == 'x':
- sys.exit(1)
-
-if __name__ == '__main__':
- main()
diff --git a/contrib/devtools/symbol-check.py b/contrib/devtools/symbol-check.py
index dd35d862c9..d8b684026c 100755
--- a/contrib/devtools/symbol-check.py
+++ b/contrib/devtools/symbol-check.py
@@ -141,7 +141,7 @@ def read_libraries(filename):
for line in stdout.splitlines():
tokens = line.split()
if len(tokens)>2 and tokens[1] == '(NEEDED)':
- match = re.match('^Shared library: \[(.*)\]$', ' '.join(tokens[2:]))
+ match = re.match(r'^Shared library: \[(.*)\]$', ' '.join(tokens[2:]))
if match:
libraries.append(match.group(1))
else:
@@ -171,5 +171,3 @@ if __name__ == '__main__':
retval = 1
sys.exit(retval)
-
-
diff --git a/contrib/devtools/update-translations.py b/contrib/devtools/update-translations.py
deleted file mode 100755
index 1b9d3a4c27..0000000000
--- a/contrib/devtools/update-translations.py
+++ /dev/null
@@ -1,215 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (c) 2014 Wladimir J. van der Laan
-# Distributed under the MIT software license, see the accompanying
-# file COPYING or http://www.opensource.org/licenses/mit-license.php.
-'''
-Run this script from the root of the repository to update all translations from
-transifex.
-It will do the following automatically:
-
-- fetch all translations using the tx tool
-- post-process them into valid and committable format
- - remove invalid control characters
- - remove location tags (makes diffs less noisy)
-
-TODO:
-- auto-add new translations to the build system according to the translation process
-'''
-import subprocess
-import re
-import sys
-import os
-import io
-import xml.etree.ElementTree as ET
-
-# Name of transifex tool
-TX = 'tx'
-# Name of source language file
-SOURCE_LANG = 'bitcoin_en.ts'
-# Directory with locale files
-LOCALE_DIR = 'src/qt/locale'
-# Minimum number of messages for translation to be considered at all
-MIN_NUM_MESSAGES = 10
-# Regexp to check for Bitcoin addresses
-ADDRESS_REGEXP = re.compile('([13]|bc1)[a-zA-Z0-9]{30,}')
-
-def check_at_repository_root():
- if not os.path.exists('.git'):
- print('No .git directory found')
- print('Execute this script at the root of the repository', file=sys.stderr)
- sys.exit(1)
-
-def fetch_all_translations():
- if subprocess.call([TX, 'pull', '-f', '-a']):
- print('Error while fetching translations', file=sys.stderr)
- sys.exit(1)
-
-def find_format_specifiers(s):
- '''Find all format specifiers in a string.'''
- pos = 0
- specifiers = []
- while True:
- percent = s.find('%', pos)
- if percent < 0:
- break
- specifiers.append(s[percent+1])
- pos = percent+2
- return specifiers
-
-def split_format_specifiers(specifiers):
- '''Split format specifiers between numeric (Qt) and others (strprintf)'''
- numeric = []
- other = []
- for s in specifiers:
- if s in {'1','2','3','4','5','6','7','8','9'}:
- numeric.append(s)
- else:
- other.append(s)
-
- # If both numeric format specifiers and "others" are used, assume we're dealing
- # with a Qt-formatted message. In the case of Qt formatting (see https://doc.qt.io/qt-5/qstring.html#arg)
- # only numeric formats are replaced at all. This means "(percentage: %1%)" is valid, without needing
- # any kind of escaping that would be necessary for strprintf. Without this, this function
- # would wrongly detect '%)' as a printf format specifier.
- if numeric:
- other = []
-
- # numeric (Qt) can be present in any order, others (strprintf) must be in specified order
- return set(numeric),other
-
-def sanitize_string(s):
- '''Sanitize string for printing'''
- return s.replace('\n',' ')
-
-def check_format_specifiers(source, translation, errors, numerus):
- source_f = split_format_specifiers(find_format_specifiers(source))
- # assert that no source messages contain both Qt and strprintf format specifiers
- # if this fails, go change the source as this is hacky and confusing!
- assert(not(source_f[0] and source_f[1]))
- try:
- translation_f = split_format_specifiers(find_format_specifiers(translation))
- except IndexError:
- errors.append("Parse error in translation for '%s': '%s'" % (sanitize_string(source), sanitize_string(translation)))
- return False
- else:
- if source_f != translation_f:
- if numerus and source_f == (set(), ['n']) and translation_f == (set(), []) and translation.find('%') == -1:
- # Allow numerus translations to omit %n specifier (usually when it only has one possible value)
- return True
- errors.append("Mismatch between '%s' and '%s'" % (sanitize_string(source), sanitize_string(translation)))
- return False
- return True
-
-def all_ts_files(suffix=''):
- for filename in os.listdir(LOCALE_DIR):
- # process only language files, and do not process source language
- if not filename.endswith('.ts'+suffix) or filename == SOURCE_LANG+suffix:
- continue
- if suffix: # remove provided suffix
- filename = filename[0:-len(suffix)]
- filepath = os.path.join(LOCALE_DIR, filename)
- yield(filename, filepath)
-
-FIX_RE = re.compile(b'[\x00-\x09\x0b\x0c\x0e-\x1f]')
-def remove_invalid_characters(s):
- '''Remove invalid characters from translation string'''
- return FIX_RE.sub(b'', s)
-
-# Override cdata escape function to make our output match Qt's (optional, just for cleaner diffs for
-# comparison, disable by default)
-_orig_escape_cdata = None
-def escape_cdata(text):
- text = _orig_escape_cdata(text)
- text = text.replace("'", '&apos;')
- text = text.replace('"', '&quot;')
- return text
-
-def contains_bitcoin_addr(text, errors):
- if text is not None and ADDRESS_REGEXP.search(text) is not None:
- errors.append('Translation "%s" contains a bitcoin address. This will be removed.' % (text))
- return True
- return False
-
-def postprocess_translations(reduce_diff_hacks=False):
- print('Checking and postprocessing...')
-
- if reduce_diff_hacks:
- global _orig_escape_cdata
- _orig_escape_cdata = ET._escape_cdata
- ET._escape_cdata = escape_cdata
-
- for (filename,filepath) in all_ts_files():
- os.rename(filepath, filepath+'.orig')
-
- have_errors = False
- for (filename,filepath) in all_ts_files('.orig'):
- # pre-fixups to cope with transifex output
- parser = ET.XMLParser(encoding='utf-8') # need to override encoding because 'utf8' is not understood only 'utf-8'
- with open(filepath + '.orig', 'rb') as f:
- data = f.read()
- # remove control characters; this must be done over the entire file otherwise the XML parser will fail
- data = remove_invalid_characters(data)
- tree = ET.parse(io.BytesIO(data), parser=parser)
-
- # iterate over all messages in file
- root = tree.getroot()
- for context in root.findall('context'):
- for message in context.findall('message'):
- numerus = message.get('numerus') == 'yes'
- source = message.find('source').text
- translation_node = message.find('translation')
- # pick all numerusforms
- if numerus:
- translations = [i.text for i in translation_node.findall('numerusform')]
- else:
- translations = [translation_node.text]
-
- for translation in translations:
- if translation is None:
- continue
- errors = []
- valid = check_format_specifiers(source, translation, errors, numerus) and not contains_bitcoin_addr(translation, errors)
-
- for error in errors:
- print('%s: %s' % (filename, error))
-
- if not valid: # set type to unfinished and clear string if invalid
- translation_node.clear()
- translation_node.set('type', 'unfinished')
- have_errors = True
-
- # Remove location tags
- for location in message.findall('location'):
- message.remove(location)
-
- # Remove entire message if it is an unfinished translation
- if translation_node.get('type') == 'unfinished':
- context.remove(message)
-
- # check if document is (virtually) empty, and remove it if so
- num_messages = 0
- for context in root.findall('context'):
- for message in context.findall('message'):
- num_messages += 1
- if num_messages < MIN_NUM_MESSAGES:
- print('Removing %s, as it contains only %i messages' % (filepath, num_messages))
- continue
-
- # write fixed-up tree
- # if diff reduction requested, replace some XML to 'sanitize' to qt formatting
- if reduce_diff_hacks:
- out = io.BytesIO()
- tree.write(out, encoding='utf-8')
- out = out.getvalue()
- out = out.replace(b' />', b'/>')
- with open(filepath, 'wb') as f:
- f.write(out)
- else:
- tree.write(filepath, encoding='utf-8')
- return have_errors
-
-if __name__ == '__main__':
- check_at_repository_root()
- fetch_all_translations()
- postprocess_translations()
-
diff --git a/contrib/gitian-keys/keys.txt b/contrib/gitian-keys/keys.txt
index 33f0f7e5b0..9222a40b17 100644
--- a/contrib/gitian-keys/keys.txt
+++ b/contrib/gitian-keys/keys.txt
@@ -1,3 +1,4 @@
+9D3CC86A72F8494342EA5FD10A41BDC3F4FAFF1C Aaron Clauson (sipsorcery)
617C90010B3BD370B0AC7D424BB42E31C79111B8 Akira Takizawa
E944AE667CF960B1004BC32FCA662BE18B877A60 Andreas Schildbach
152812300785C96444D3334D17565732E08E5E41 Andrew Chow
diff --git a/contrib/guix/libexec/build.sh b/contrib/guix/libexec/build.sh
index 56b972a5cb..ee207a957c 100644
--- a/contrib/guix/libexec/build.sh
+++ b/contrib/guix/libexec/build.sh
@@ -30,23 +30,38 @@ fi
# Given a package name and an output name, return the path of that output in our
# current guix environment
store_path() {
- grep --extended-regexp "/[^-]{32}-${1}-cross-${HOST}-[^-]+${2:+-${2}}" "${GUIX_ENVIRONMENT}/manifest" \
+ grep --extended-regexp "/[^-]{32}-${1}-[^-]+${2:+-${2}}" "${GUIX_ENVIRONMENT}/manifest" \
| head --lines=1 \
| sed --expression='s|^[[:space:]]*"||' \
--expression='s|"[[:space:]]*$||'
}
# Determine output paths to use in CROSS_* environment variables
-CROSS_GLIBC="$(store_path glibc)"
-CROSS_GLIBC_STATIC="$(store_path glibc static)"
-CROSS_KERNEL="$(store_path linux-libre-headers)"
-CROSS_GCC="$(store_path gcc)"
+CROSS_GLIBC="$(store_path glibc-cross-${HOST})"
+CROSS_GLIBC_STATIC="$(store_path glibc-cross-${HOST} static)"
+CROSS_KERNEL="$(store_path linux-libre-headers-cross-${HOST})"
+CROSS_GCC="$(store_path gcc-cross-${HOST})"
+CROSS_GCC_LIBS=( "${CROSS_GCC}/lib/gcc/${HOST}"/* ) # This expands to an array of directories...
+CROSS_GCC_LIB="${CROSS_GCC_LIBS[0]}" # ...we just want the first one (there should only be one)
# Set environment variables to point Guix's cross-toolchain to the right
# includes/libs for $HOST
-export CROSS_C_INCLUDE_PATH="${CROSS_GCC}/include:${CROSS_GLIBC}/include:${CROSS_KERNEL}/include"
-export CROSS_CPLUS_INCLUDE_PATH="${CROSS_GCC}/include/c++:${CROSS_GLIBC}/include:${CROSS_KERNEL}/include"
-export CROSS_LIBRARY_PATH="${CROSS_GLIBC}/lib:${CROSS_GLIBC_STATIC}/lib:${CROSS_GCC}/lib:${CROSS_GCC}/${HOST}/lib:${CROSS_KERNEL}/lib"
+#
+# NOTE: CROSS_C_INCLUDE_PATH is missing ${CROSS_GCC_LIB}/include-fixed, because
+# the limits.h in it is missing a '#include_next <limits.h>'
+#
+export CROSS_C_INCLUDE_PATH="${CROSS_GCC_LIB}/include:${CROSS_GLIBC}/include:${CROSS_KERNEL}/include"
+export CROSS_CPLUS_INCLUDE_PATH="${CROSS_GCC}/include/c++:${CROSS_GCC}/include/c++/${HOST}:${CROSS_GCC}/include/c++/backward:${CROSS_C_INCLUDE_PATH}"
+export CROSS_LIBRARY_PATH="${CROSS_GCC}/lib:${CROSS_GCC}/${HOST}/lib:${CROSS_GCC_LIB}:${CROSS_GLIBC}/lib:${CROSS_GLIBC_STATIC}/lib"
+
+# Sanity check CROSS_*_PATH directories
+IFS=':' read -ra PATHS <<< "${CROSS_C_INCLUDE_PATH}:${CROSS_CPLUS_INCLUDE_PATH}:${CROSS_LIBRARY_PATH}"
+for p in "${PATHS[@]}"; do
+ if [ ! -d "$p" ]; then
+ echo "'$p' doesn't exist or isn't a directory... Aborting..."
+ exit 1
+ fi
+done
# Disable Guix ld auto-rpath behavior
export GUIX_LD_WRAPPER_DISABLE_RPATH=yes
@@ -121,17 +136,10 @@ DISTNAME="$(basename "$SOURCEDIST" '.tar.gz')"
# Binary Tarball Building #
###########################
-# Create a spec file to normalize ssp linking behaviour
-spec_file="$(mktemp)"
-cat << EOF > "$spec_file"
-*link_ssp:
-%{fstack-protector|fstack-protector-all|fstack-protector-strong|fstack-protector-explicit:}
-EOF
-
# Similar flags to Gitian
CONFIGFLAGS="--enable-glibc-back-compat --enable-reduce-exports --disable-bench --disable-gui-tests"
-HOST_CFLAGS="-O2 -g -specs=${spec_file} -ffile-prefix-map=${PWD}=."
-HOST_CXXFLAGS="-O2 -g -specs=${spec_file} -ffile-prefix-map=${PWD}=."
+HOST_CFLAGS="-O2 -g -ffile-prefix-map=${PWD}=."
+HOST_CXXFLAGS="-O2 -g -ffile-prefix-map=${PWD}=."
HOST_LDFLAGS="-Wl,--as-needed -Wl,--dynamic-linker=$glibc_dynamic_linker -static-libstdc++"
# Make $HOST-specific native binaries from depends available in $PATH
diff --git a/contrib/init/bitcoind.service b/contrib/init/bitcoind.service
index cfc5f77580..34c3e7b3ab 100644
--- a/contrib/init/bitcoind.service
+++ b/contrib/init/bitcoind.service
@@ -5,8 +5,9 @@
# See "man systemd.service" for details.
# Note that almost all daemon options could be specified in
-# /etc/bitcoin/bitcoin.conf, except for those explicitly specified as arguments
-# in ExecStart=
+# /etc/bitcoin/bitcoin.conf, but keep in mind those explicitly
+# specified as arguments in ExecStart= will override those in the
+# config file.
[Unit]
Description=Bitcoin daemon
@@ -18,6 +19,10 @@ ExecStart=/usr/bin/bitcoind -daemon \
-conf=/etc/bitcoin/bitcoin.conf \
-datadir=/var/lib/bitcoind
+# Make sure the config directory is readable by the service user
+PermissionsStartOnly=true
+ExecStartPre=/bin/chgrp bitcoin /etc/bitcoin
+
# Process management
####################
@@ -53,6 +58,9 @@ PrivateTmp=true
# Mount /usr, /boot/ and /etc read-only for the process.
ProtectSystem=full
+# Deny access to /home, /root and /run/user
+ProtectHome=true
+
# Disallow the process and all of its children to gain
# new privileges through execve().
NoNewPrivileges=true
diff --git a/contrib/linearize/linearize-data.py b/contrib/linearize/linearize-data.py
index 468aec04b5..95754ab937 100755
--- a/contrib/linearize/linearize-data.py
+++ b/contrib/linearize/linearize-data.py
@@ -263,12 +263,12 @@ if __name__ == '__main__':
f = open(sys.argv[1], encoding="utf8")
for line in f:
# skip comment lines
- m = re.search('^\s*#', line)
+ m = re.search(r'^\s*#', line)
if m:
continue
# parse key=value lines
- m = re.search('^(\w+)\s*=\s*(\S.*)$', line)
+ m = re.search(r'^(\w+)\s*=\s*(\S.*)$', line)
if m is None:
continue
settings[m.group(1)] = m.group(2)
diff --git a/contrib/linearize/linearize-hashes.py b/contrib/linearize/linearize-hashes.py
index 8529470e09..02c96d2a75 100755
--- a/contrib/linearize/linearize-hashes.py
+++ b/contrib/linearize/linearize-hashes.py
@@ -106,12 +106,12 @@ if __name__ == '__main__':
f = open(sys.argv[1], encoding="utf8")
for line in f:
# skip comment lines
- m = re.search('^\s*#', line)
+ m = re.search(r'^\s*#', line)
if m:
continue
# parse key=value lines
- m = re.search('^(\w+)\s*=\s*(\S.*)$', line)
+ m = re.search(r'^(\w+)\s*=\s*(\S.*)$', line)
if m is None:
continue
settings[m.group(1)] = m.group(2)
diff --git a/contrib/macdeploy/README.md b/contrib/macdeploy/README.md
index 6163734e62..e90120ea79 100644
--- a/contrib/macdeploy/README.md
+++ b/contrib/macdeploy/README.md
@@ -1,7 +1,7 @@
### MacDeploy ###
For Snow Leopard (which uses [Python 2.6](http://www.python.org/download/releases/2.6/)), you will need the param_parser package:
-
+
sudo easy_install argparse
This script should not be run manually, instead, after building as usual:
diff --git a/contrib/macdeploy/macdeployqtplus b/contrib/macdeploy/macdeployqtplus
index 9da03e5b02..d8088aa123 100755
--- a/contrib/macdeploy/macdeployqtplus
+++ b/contrib/macdeploy/macdeployqtplus
@@ -19,6 +19,7 @@
import subprocess, sys, re, os, shutil, stat, os.path, time
from string import Template
from argparse import ArgumentParser
+from typing import List, Optional
# This is ported from the original macdeployqt with modifications
@@ -48,18 +49,18 @@ class FrameworkInfo(object):
return False
def __str__(self):
- return """ Framework name: %s
- Framework directory: %s
- Framework path: %s
- Binary name: %s
- Binary directory: %s
- Binary path: %s
- Version: %s
- Install name: %s
- Deployed install name: %s
- Source file Path: %s
- Deployed Directory (relative to bundle): %s
-""" % (self.frameworkName,
+ return """ Framework name: {}
+ Framework directory: {}
+ Framework path: {}
+ Binary name: {}
+ Binary directory: {}
+ Binary path: {}
+ Version: {}
+ Install name: {}
+ Deployed install name: {}
+ Source file Path: {}
+ Deployed Directory (relative to bundle): {}
+""".format(self.frameworkName,
self.frameworkDirectory,
self.frameworkPath,
self.binaryName,
@@ -85,7 +86,7 @@ class FrameworkInfo(object):
bundleBinaryDirectory = "Contents/MacOS"
@classmethod
- def fromOtoolLibraryLine(cls, line):
+ def fromOtoolLibraryLine(cls, line: str) -> Optional['FrameworkInfo']:
# Note: line must be trimmed
if line == "":
return None
@@ -146,13 +147,12 @@ class FrameworkInfo(object):
info.sourceContentsDirectory = os.path.join(info.frameworkPath, "Contents")
info.sourceVersionContentsDirectory = os.path.join(info.frameworkPath, "Versions", info.version, "Contents")
info.destinationResourcesDirectory = os.path.join(cls.bundleFrameworkDirectory, info.frameworkName, "Resources")
- info.destinationContentsDirectory = os.path.join(cls.bundleFrameworkDirectory, info.frameworkName, "Contents")
info.destinationVersionContentsDirectory = os.path.join(cls.bundleFrameworkDirectory, info.frameworkName, "Versions", info.version, "Contents")
return info
class ApplicationBundleInfo(object):
- def __init__(self, path):
+ def __init__(self, path: str):
self.path = path
appName = "Bitcoin-Qt"
self.binaryPath = os.path.join(path, "Contents", "MacOS", appName)
@@ -167,7 +167,7 @@ class DeploymentInfo(object):
self.pluginPath = None
self.deployedFrameworks = []
- def detectQtPath(self, frameworkDirectory):
+ def detectQtPath(self, frameworkDirectory: str):
parentDir = os.path.dirname(frameworkDirectory)
if os.path.exists(os.path.join(parentDir, "translations")):
# Classic layout, e.g. "/usr/local/Trolltech/Qt-4.x.x"
@@ -180,9 +180,9 @@ class DeploymentInfo(object):
if os.path.exists(pluginPath):
self.pluginPath = pluginPath
- def usesFramework(self, name):
- nameDot = "%s." % name
- libNameDot = "lib%s." % name
+ def usesFramework(self, name: str) -> bool:
+ nameDot = "{}.".format(name)
+ libNameDot = "lib{}.".format(name)
for framework in self.deployedFrameworks:
if framework.endswith(".framework"):
if framework.startswith(nameDot):
@@ -192,7 +192,7 @@ class DeploymentInfo(object):
return True
return False
-def getFrameworks(binaryPath, verbose):
+def getFrameworks(binaryPath: str, verbose: int) -> List[FrameworkInfo]:
if verbose >= 3:
print("Inspecting with otool: " + binaryPath)
otoolbin=os.getenv("OTOOL", "otool")
@@ -202,7 +202,7 @@ def getFrameworks(binaryPath, verbose):
if verbose >= 1:
sys.stderr.write(o_stderr)
sys.stderr.flush()
- raise RuntimeError("otool failed with return code %d" % otool.returncode)
+ raise RuntimeError("otool failed with return code {}".format(otool.returncode))
otoolLines = o_stdout.split("\n")
otoolLines.pop(0) # First line is the inspected binary
@@ -221,11 +221,11 @@ def getFrameworks(binaryPath, verbose):
return libraries
-def runInstallNameTool(action, *args):
+def runInstallNameTool(action: str, *args):
installnametoolbin=os.getenv("INSTALLNAMETOOL", "install_name_tool")
subprocess.check_call([installnametoolbin, "-"+action] + list(args))
-def changeInstallName(oldName, newName, binaryPath, verbose):
+def changeInstallName(oldName: str, newName: str, binaryPath: str, verbose: int):
if verbose >= 3:
print("Using install_name_tool:")
print(" in", binaryPath)
@@ -233,21 +233,21 @@ def changeInstallName(oldName, newName, binaryPath, verbose):
print(" to", newName)
runInstallNameTool("change", oldName, newName, binaryPath)
-def changeIdentification(id, binaryPath, verbose):
+def changeIdentification(id: str, binaryPath: str, verbose: int):
if verbose >= 3:
print("Using install_name_tool:")
print(" change identification in", binaryPath)
print(" to", id)
runInstallNameTool("id", id, binaryPath)
-def runStrip(binaryPath, verbose):
+def runStrip(binaryPath: str, verbose: int):
stripbin=os.getenv("STRIP", "strip")
if verbose >= 3:
print("Using strip:")
print(" stripped", binaryPath)
subprocess.check_call([stripbin, "-x", binaryPath])
-def copyFramework(framework, path, verbose):
+def copyFramework(framework: FrameworkInfo, path: str, verbose: int) -> Optional[str]:
if framework.sourceFilePath.startswith("Qt"):
#standard place for Nokia Qt installer's frameworks
fromPath = "/Library/Frameworks/" + framework.sourceFilePath
@@ -309,7 +309,7 @@ def copyFramework(framework, path, verbose):
return toPath
-def deployFrameworks(frameworks, bundlePath, binaryPath, strip, verbose, deploymentInfo=None):
+def deployFrameworks(frameworks: List[FrameworkInfo], bundlePath: str, binaryPath: str, strip: bool, verbose: int, deploymentInfo: Optional[DeploymentInfo] = None) -> DeploymentInfo:
if deploymentInfo is None:
deploymentInfo = DeploymentInfo()
@@ -355,15 +355,15 @@ def deployFrameworks(frameworks, bundlePath, binaryPath, strip, verbose, deploym
return deploymentInfo
-def deployFrameworksForAppBundle(applicationBundle, strip, verbose):
+def deployFrameworksForAppBundle(applicationBundle: ApplicationBundleInfo, strip: bool, verbose: int) -> DeploymentInfo:
frameworks = getFrameworks(applicationBundle.binaryPath, verbose)
if len(frameworks) == 0 and verbose >= 1:
- print("Warning: Could not find any external frameworks to deploy in %s." % (applicationBundle.path))
+ print("Warning: Could not find any external frameworks to deploy in {}.".format(applicationBundle.path))
return DeploymentInfo()
else:
return deployFrameworks(frameworks, applicationBundle.path, applicationBundle.binaryPath, strip, verbose)
-def deployPlugins(appBundleInfo, deploymentInfo, strip, verbose):
+def deployPlugins(appBundleInfo: ApplicationBundleInfo, deploymentInfo: DeploymentInfo, strip: bool, verbose: int):
# Lookup available plugins, exclude unneeded
plugins = []
if deploymentInfo.pluginPath is None:
@@ -373,10 +373,12 @@ def deployPlugins(appBundleInfo, deploymentInfo, strip, verbose):
if pluginDirectory == "designer":
# Skip designer plugins
continue
- elif pluginDirectory == "phonon" or pluginDirectory == "phonon_backend":
- # Deploy the phonon plugins only if phonon is in use
- if not deploymentInfo.usesFramework("phonon"):
- continue
+ elif pluginDirectory == "printsupport":
+ # Skip printsupport plugins
+ continue
+ elif pluginDirectory == "imageformats":
+ # Skip imageformats plugins
+ continue
elif pluginDirectory == "sqldrivers":
# Deploy the sql plugins only if QtSql is in use
if not deploymentInfo.usesFramework("QtSql"):
@@ -409,6 +411,42 @@ def deployPlugins(appBundleInfo, deploymentInfo, strip, verbose):
# Deploy the mediaservice plugins only if QtMultimediaWidgets is in use
if not deploymentInfo.usesFramework("QtMultimediaWidgets"):
continue
+ elif pluginDirectory == "canbus":
+ # Deploy the canbus plugins only if QtSerialBus is in use
+ if not deploymentInfo.usesFramework("QtSerialBus"):
+ continue
+ elif pluginDirectory == "webview":
+ # Deploy the webview plugins only if QtWebView is in use
+ if not deploymentInfo.usesFramework("QtWebView"):
+ continue
+ elif pluginDirectory == "gamepads":
+ # Deploy the webview plugins only if QtGamepad is in use
+ if not deploymentInfo.usesFramework("QtGamepad"):
+ continue
+ elif pluginDirectory == "geoservices":
+ # Deploy the webview plugins only if QtLocation is in use
+ if not deploymentInfo.usesFramework("QtLocation"):
+ continue
+ elif pluginDirectory == "texttospeech":
+ # Deploy the texttospeech plugins only if QtTextToSpeech is in use
+ if not deploymentInfo.usesFramework("QtTextToSpeech"):
+ continue
+ elif pluginDirectory == "virtualkeyboard":
+ # Deploy the virtualkeyboard plugins only if QtVirtualKeyboard is in use
+ if not deploymentInfo.usesFramework("QtVirtualKeyboard"):
+ continue
+ elif pluginDirectory == "sceneparsers":
+ # Deploy the virtualkeyboard plugins only if Qt3DCore is in use
+ if not deploymentInfo.usesFramework("Qt3DCore"):
+ continue
+ elif pluginDirectory == "renderplugins":
+ # Deploy the renderplugins plugins only if Qt3DCore is in use
+ if not deploymentInfo.usesFramework("Qt3DCore"):
+ continue
+ elif pluginDirectory == "geometryloaders":
+ # Deploy the geometryloaders plugins only if Qt3DCore is in use
+ if not deploymentInfo.usesFramework("Qt3DCore"):
+ continue
for pluginName in filenames:
pluginPath = os.path.join(pluginDirectory, pluginName)
@@ -431,6 +469,10 @@ def deployPlugins(appBundleInfo, deploymentInfo, strip, verbose):
# Deploy the accessible qtquick plugin only if QtQuick is in use
if not deploymentInfo.usesFramework("QtQuick"):
continue
+ elif pluginPath == "platforminputcontexts/libqtvirtualkeyboardplugin.dylib":
+ # Deploy the virtualkeyboardplugin plugin only if QtVirtualKeyboard is in use
+ if not deploymentInfo.usesFramework("QtVirtualKeyboard"):
+ continue
plugins.append((pluginDirectory, pluginName))
@@ -499,7 +541,7 @@ app_bundle = config.app_bundle[0]
if not os.path.exists(app_bundle):
if verbose >= 1:
- sys.stderr.write("Error: Could not find app bundle \"%s\"\n" % (app_bundle))
+ sys.stderr.write("Error: Could not find app bundle \"{}\"\n".format(app_bundle))
sys.exit(1)
app_bundle_name = os.path.splitext(os.path.basename(app_bundle))[0]
@@ -511,7 +553,7 @@ if config.translations_dir and config.translations_dir[0]:
translations_dir = config.translations_dir[0]
else:
if verbose >= 1:
- sys.stderr.write("Error: Could not find translation dir \"%s\"\n" % (translations_dir))
+ sys.stderr.write("Error: Could not find translation dir \"{}\"\n".format(translations_dir))
sys.exit(1)
# ------------------------------------------------
@@ -520,7 +562,7 @@ for p in config.add_resources:
print("Checking for \"%s\"..." % p)
if not os.path.exists(p):
if verbose >= 1:
- sys.stderr.write("Error: Could not find additional resource file \"%s\"\n" % (p))
+ sys.stderr.write("Error: Could not find additional resource file \"{}\"\n".format(p))
sys.exit(1)
# ------------------------------------------------
@@ -537,17 +579,17 @@ if len(config.fancy) == 1:
p = config.fancy[0]
if verbose >= 3:
- print("Fancy: Loading \"%s\"..." % p)
+ print("Fancy: Loading \"{}\"...".format(p))
if not os.path.exists(p):
if verbose >= 1:
- sys.stderr.write("Error: Could not find fancy disk image plist at \"%s\"\n" % (p))
+ sys.stderr.write("Error: Could not find fancy disk image plist at \"{}\"\n".format(p))
sys.exit(1)
try:
fancy = plistlib.readPlist(p)
except:
if verbose >= 1:
- sys.stderr.write("Error: Could not parse fancy disk image plist at \"%s\"\n" % (p))
+ sys.stderr.write("Error: Could not parse fancy disk image plist at \"{}\"\n".format(p))
sys.exit(1)
try:
@@ -561,18 +603,18 @@ if len(config.fancy) == 1:
assert isinstance(value, list) and len(value) == 2 and isinstance(value[0], int) and isinstance(value[1], int)
except:
if verbose >= 1:
- sys.stderr.write("Error: Bad format of fancy disk image plist at \"%s\"\n" % (p))
+ sys.stderr.write("Error: Bad format of fancy disk image plist at \"{}\"\n".format(p))
sys.exit(1)
if "background_picture" in fancy:
bp = fancy["background_picture"]
if verbose >= 3:
- print("Fancy: Resolving background picture \"%s\"..." % bp)
+ print("Fancy: Resolving background picture \"{}\"...".format(bp))
if not os.path.exists(bp):
bp = os.path.join(os.path.dirname(p), bp)
if not os.path.exists(bp):
if verbose >= 1:
- sys.stderr.write("Error: Could not find background picture at \"%s\" or \"%s\"\n" % (fancy["background_picture"], bp))
+ sys.stderr.write("Error: Could not find background picture at \"{}\" or \"{}\"\n".format(fancy["background_picture"], bp))
sys.exit(1)
else:
fancy["background_picture"] = bp
@@ -623,7 +665,7 @@ try:
config.plugins = False
except RuntimeError as e:
if verbose >= 1:
- sys.stderr.write("Error: %s\n" % str(e))
+ sys.stderr.write("Error: {}\n".format(str(e)))
sys.exit(1)
# ------------------------------------------------
@@ -636,7 +678,7 @@ if config.plugins:
deployPlugins(applicationBundle, deploymentInfo, config.strip, verbose)
except RuntimeError as e:
if verbose >= 1:
- sys.stderr.write("Error: %s\n" % str(e))
+ sys.stderr.write("Error: {}\n".format(str(e)))
sys.exit(1)
# ------------------------------------------------
@@ -652,14 +694,14 @@ else:
else:
sys.stderr.write("Error: Could not find Qt translation path\n")
sys.exit(1)
- add_qt_tr = ["qt_%s.qm" % lng for lng in config.add_qt_tr[0].split(",")]
+ add_qt_tr = ["qt_{}.qm".format(lng) for lng in config.add_qt_tr[0].split(",")]
for lng_file in add_qt_tr:
p = os.path.join(qt_tr_dir, lng_file)
if verbose >= 3:
- print("Checking for \"%s\"..." % p)
+ print("Checking for \"{}\"...".format(p))
if not os.path.exists(p):
if verbose >= 1:
- sys.stderr.write("Error: Could not find Qt translation file \"%s\"\n" % (lng_file))
+ sys.stderr.write("Error: Could not find Qt translation file \"{}\"\n".format(lng_file))
sys.exit(1)
# ------------------------------------------------
@@ -700,14 +742,14 @@ if config.sign and 'CODESIGNARGS' not in os.environ:
print("You must set the CODESIGNARGS environment variable. Skipping signing.")
elif config.sign:
if verbose >= 1:
- print("Code-signing app bundle %s"%(target,))
- subprocess.check_call("codesign --force %s %s"%(os.environ['CODESIGNARGS'], target), shell=True)
+ print("Code-signing app bundle {}".format(target))
+ subprocess.check_call("codesign --force {} {}".format(os.environ['CODESIGNARGS'], target), shell=True)
# ------------------------------------------------
if config.dmg is not None:
- def runHDIUtil(verb, image_basename, **kwargs):
+ def runHDIUtil(verb: str, image_basename: str, **kwargs) -> int:
hdiutil_args = ["hdiutil", verb, image_basename + ".dmg"]
if "capture_stdout" in kwargs:
del kwargs["capture_stdout"]
@@ -721,7 +763,7 @@ if config.dmg is not None:
for key, value in kwargs.items():
hdiutil_args.append("-" + key)
- if not value is True:
+ if value is not True:
hdiutil_args.append(str(value))
return run(hdiutil_args, universal_newlines=True)
@@ -765,8 +807,8 @@ if config.dmg is not None:
output = runHDIUtil("attach", dmg_name + ".temp", readwrite=True, noverify=True, noautoopen=True, capture_stdout=True)
except subprocess.CalledProcessError as e:
sys.exit(e.returncode)
-
- m = re.search("/Volumes/(.+$)", output)
+
+ m = re.search(r"/Volumes/(.+$)", output)
disk_root = m.group(0)
disk_name = m.group(1)
diff --git a/contrib/seeds/generate-seeds.py b/contrib/seeds/generate-seeds.py
index fe7cd1d597..7630a7a4fa 100755
--- a/contrib/seeds/generate-seeds.py
+++ b/contrib/seeds/generate-seeds.py
@@ -74,7 +74,7 @@ def name_to_ipv6(addr):
raise ValueError('Could not parse address %s' % addr)
def parse_spec(s, defaultport):
- match = re.match('\[([0-9a-fA-F:]+)\](?::([0-9]+))?$', s)
+ match = re.match(r'\[([0-9a-fA-F:]+)\](?::([0-9]+))?$', s)
if match: # ipv6
host = match.group(1)
port = match.group(2)
@@ -136,4 +136,3 @@ def main():
if __name__ == '__main__':
main()
-
diff --git a/contrib/testgen/README.md b/contrib/testgen/README.md
index 580ed541cf..573a71a675 100644
--- a/contrib/testgen/README.md
+++ b/contrib/testgen/README.md
@@ -2,7 +2,7 @@
Utilities to generate test vectors for the data-driven Bitcoin tests.
-Usage:
+Usage:
PYTHONPATH=../../test/functional/test_framework ./gen_key_io_test_vectors.py valid 50 > ../../src/test/data/key_io_keys_valid.json
PYTHONPATH=../../test/functional/test_framework ./gen_key_io_test_vectors.py invalid 50 > ../../src/test/data/key_io_keys_invalid.json
diff --git a/contrib/verify-commits/verify-commits.py b/contrib/verify-commits/verify-commits.py
index 255ce75092..9ec8663fba 100755
--- a/contrib/verify-commits/verify-commits.py
+++ b/contrib/verify-commits/verify-commits.py
@@ -16,7 +16,7 @@ GIT = os.getenv('GIT', 'git')
def tree_sha512sum(commit='HEAD'):
"""Calculate the Tree-sha512 for the commit.
- This is copied from github-merge.py."""
+ This is copied from github-merge.py. See https://github.com/bitcoin-core/bitcoin-maintainer-tools."""
# request metadata for entire tree, recursively
files = []
diff --git a/depends/Makefile b/depends/Makefile
index 70af875189..b7e9a9213e 100644
--- a/depends/Makefile
+++ b/depends/Makefile
@@ -5,6 +5,7 @@ WORK_PATH = $(BASEDIR)/work
BASE_CACHE ?= $(BASEDIR)/built
SDK_PATH ?= $(BASEDIR)/SDKs
NO_QT ?=
+PROTOBUF ?=
RAPIDCHECK ?=
NO_WALLET ?=
NO_ZMQ ?=
@@ -96,13 +97,15 @@ wallet_packages_$(NO_WALLET) = $(wallet_packages)
upnp_packages_$(NO_UPNP) = $(upnp_packages)
zmq_packages_$(NO_ZMQ) = $(zmq_packages)
+protobuf_packages_$(PROTOBUF) = $(protobuf_packages)
rapidcheck_packages_$(RAPIDCHECK) = $(rapidcheck_packages)
packages += $($(host_arch)_$(host_os)_packages) $($(host_os)_packages) $(qt_packages_) $(wallet_packages_) $(upnp_packages_)
native_packages += $($(host_arch)_$(host_os)_native_packages) $($(host_os)_native_packages)
-ifneq ($(qt_packages_),)
-native_packages += $(qt_native_packages)
+ifeq ($(protobuf_packages_),)
+native_packages += $(protobuf_native_packages)
+packages += $(protobuf_packages)
endif
ifneq ($(zmq_packages_),)
@@ -150,6 +153,7 @@ $(host_prefix)/share/config.site : config.site.in $(host_prefix)/.stamp_$(final_
-e 's|@allow_host_packages@|$(ALLOW_HOST_PACKAGES)|' \
-e 's|@no_qt@|$(NO_QT)|' \
-e 's|@no_zmq@|$(NO_ZMQ)|' \
+ -e 's|@enable_bip70@|$(PROTOBUF)|' \
-e 's|@no_wallet@|$(NO_WALLET)|' \
-e 's|@no_upnp@|$(NO_UPNP)|' \
-e 's|@debug@|$(DEBUG)|' \
diff --git a/depends/README.md b/depends/README.md
index 6dbe365545..cfb9bbfeb0 100644
--- a/depends/README.md
+++ b/depends/README.md
@@ -12,14 +12,18 @@ For example:
make HOST=x86_64-w64-mingw32 -j4
-A prefix will be generated that's suitable for plugging into Bitcoin's
-configure. In the above example, a dir named x86_64-w64-mingw32 will be
+**Bitcoin's configure script by default will ignore the depends output.** In
+order for it to pick up libraries, tools, and settings from the depends build,
+you must point it at the appropriate `--prefix` directory generated by the
+build. In the above example, a prefix dir named x86_64-w64-mingw32 will be
created. To use it for Bitcoin:
./configure --prefix=`pwd`/depends/x86_64-w64-mingw32
Common `host-platform-triplets` for cross compilation are:
+- `i686-pc-linux-gnu` for Linux 32 bit
+- `x86_64-pc-linux-gnu` for x86 Linux
- `x86_64-w64-mingw32` for Win64
- `x86_64-apple-darwin14` for macOS
- `arm-linux-gnueabihf` for Linux ARM 32 bit
@@ -73,6 +77,7 @@ The following can be set when running make: make FOO=bar
NO_UPNP: Don't download/build/cache packages needed for enabling upnp
DEBUG: disable some optimizations and enable more runtime checking
RAPIDCHECK: build rapidcheck (experimental, requires cmake)
+ PROTOBUF: build protobuf (used for deprecated BIP70 support)
HOST_ID_SALT: Optional salt to use when generating host package ids
BUILD_ID_SALT: Optional salt to use when generating build package ids
diff --git a/depends/config.site.in b/depends/config.site.in
index e633752066..d0d36641c4 100644
--- a/depends/config.site.in
+++ b/depends/config.site.in
@@ -37,6 +37,10 @@ if test -z $enable_zmq && test -n "@no_zmq@"; then
enable_zmq=no
fi
+if test -n $enable_bip70 && test -n "@enable_bip70@"; then
+ enable_bip70=yes
+fi
+
if test x@host_os@ = xdarwin; then
BREW=no
PORT=no
diff --git a/depends/description.md b/depends/description.md
index 9fc7093be4..0a6f2e6442 100644
--- a/depends/description.md
+++ b/depends/description.md
@@ -1,4 +1,4 @@
-This is a system of building and caching dependencies necessary for building Bitcoin.
+This is a system of building and caching dependencies necessary for building Bitcoin.
There are several features that make it different from most similar systems:
### It is designed to be builder and host agnostic
@@ -26,7 +26,7 @@ Before building, a unique build-id is generated for each package. This id
consists of a hash of all files used to build the package (Makefiles, packages,
etc), and as well as a hash of the same data for each recursive dependency. If
any portion of a package's build recipe changes, it will be rebuilt as well as
-any other package that depends on it. If any of the main makefiles (Makefile,
+any other package that depends on it. If any of the main makefiles (Makefile,
funcs.mk, etc) are changed, all packages will be rebuilt. After building, the
results are cached into a tarball that can be re-used and distributed.
diff --git a/depends/packages.md b/depends/packages.md
index 2c592885b6..7ed20ea129 100644
--- a/depends/packages.md
+++ b/depends/packages.md
@@ -32,15 +32,15 @@ These variables are optional:
$(package)_build_subdir:
cd to this dir before running configure/build/stage commands.
-
+
$(package)_download_file:
The file-name of the upstream source if it differs from how it should be
stored locally. This can be used to avoid storing file-names with strange
characters.
-
+
$(package)_dependencies:
Names of any other packages that this one depends on.
-
+
$(package)_patches:
Filenames of any patches needed to build the package
@@ -134,7 +134,7 @@ the user. Other variables may be defined as needed.
Stage the build results. If undefined, does nothing.
The following variables are available for each recipe:
-
+
$(1)_staging_dir: package's destination sysroot path
$(1)_staging_prefix_dir: prefix path inside of the package's staging dir
$(1)_extract_dir: path to the package's extracted sources
@@ -181,3 +181,18 @@ For us, it's much easier to just link a static `libsecondary` into a shared
static or dynamic `libseconday`, that's not our concern. With a static
`libseconday`, when we need to link `libprimary` into our executable, there's no
dependency chain to worry about as `libprimary` has all the symbols.
+
+## Build targets:
+
+To build an individual package (useful for debugging), following build targets are available.
+
+ make ${package}
+ make ${package}_fetched
+ make ${package}_extracted
+ make ${package}_preprocessed
+ make ${package}_configured
+ make ${package}_built
+ make ${package}_staged
+ make ${package}_postprocessed
+ make ${package}_cached
+ make ${package}_cached_checksum
diff --git a/depends/packages/fontconfig.mk b/depends/packages/fontconfig.mk
index 293631739d..ed21fbba33 100644
--- a/depends/packages/fontconfig.mk
+++ b/depends/packages/fontconfig.mk
@@ -6,7 +6,7 @@ $(package)_sha256_hash=b449a3e10c47e1d1c7a6ec6e2016cca73d3bd68fbbd4f0ae5cc6b573f
$(package)_dependencies=freetype expat
define $(package)_set_vars
- $(package)_config_opts=--disable-docs --disable-static
+ $(package)_config_opts=--disable-docs --disable-static --disable-libxml2 --disable-iconv
endef
define $(package)_config_cmds
diff --git a/depends/packages/libXau.mk b/depends/packages/libXau.mk
index 0e4a60ad25..058416f793 100644
--- a/depends/packages/libXau.mk
+++ b/depends/packages/libXau.mk
@@ -5,8 +5,10 @@ $(package)_file_name=$(package)-$($(package)_version).tar.bz2
$(package)_sha256_hash=fdd477320aeb5cdd67272838722d6b7d544887dfe7de46e1e7cc0c27c2bea4f2
$(package)_dependencies=xproto
+# When updating this package, check the default value of
+# --disable-xthreads. It is currently enabled.
define $(package)_set_vars
- $(package)_config_opts=--disable-shared
+ $(package)_config_opts=--disable-shared --disable-lint-library --without-lint
$(package)_config_opts_linux=--with-pic
endef
diff --git a/depends/packages/libxcb.mk b/depends/packages/libxcb.mk
index c497c5ac20..49d3e3d15b 100644
--- a/depends/packages/libxcb.mk
+++ b/depends/packages/libxcb.mk
@@ -6,7 +6,19 @@ $(package)_sha256_hash=98d9ab05b636dd088603b64229dd1ab2d2cc02ab807892e107d674f9c
$(package)_dependencies=xcb_proto libXau
define $(package)_set_vars
-$(package)_config_opts=--disable-static
+$(package)_config_opts=--disable-static --disable-build-docs --without-doxygen --without-launchd
+# Because we pass -qt-xcb to Qt, it will compile in a set of xcb helper libraries and extensions,
+# so we skip building all of the extensions here.
+# More info is available from: https://doc.qt.io/qt-5.9/linux-requirements.html
+$(package)_config_opts += --disable-composite --disable-damage --disable-dpms
+$(package)_config_opts += --disable-dri2 --disable-dri3 --disable-glx
+$(package)_config_opts += --disable-present --disable-randr --disable-record
+$(package)_config_opts += --disable-render --disable-resource --disable-screensaver
+$(package)_config_opts += --disable-shape --disable-shm --disable-sync
+$(package)_config_opts += --disable-xevie --disable-xfixes --disable-xfree86-dri
+$(package)_config_opts += --disable-xinerama --disable-xinput --disable-xkb
+$(package)_config_opts += --disable-xprint --disable-selinux --disable-xtest
+$(package)_config_opts += --disable-xv --disable-xvmc
endef
define $(package)_preprocess_cmds
diff --git a/depends/packages/packages.mk b/depends/packages/packages.mk
index 9edcd1eb38..667fde5271 100644
--- a/depends/packages/packages.mk
+++ b/depends/packages/packages.mk
@@ -1,7 +1,9 @@
packages:=boost openssl libevent
-qt_native_packages = native_protobuf
-qt_packages = qrencode protobuf zlib
+protobuf_native_packages = native_protobuf
+protobuf_packages = protobuf
+
+qt_packages = qrencode zlib
qt_linux_packages:=qt expat libxcb xcb_proto libXau xproto freetype fontconfig
diff --git a/depends/packages/qrencode.mk b/depends/packages/qrencode.mk
index 56681b52a1..3bc2cb768c 100644
--- a/depends/packages/qrencode.mk
+++ b/depends/packages/qrencode.mk
@@ -5,7 +5,8 @@ $(package)_file_name=$(package)-$($(package)_version).tar.bz2
$(package)_sha256_hash=efe5188b1ddbcbf98763b819b146be6a90481aac30cfc8d858ab78a19cde1fa5
define $(package)_set_vars
-$(package)_config_opts=--disable-shared -without-tools --disable-sdltest
+$(package)_config_opts=--disable-shared --without-tools --without-tests --disable-sdltest
+$(package)_config_opts += --disable-gprof --disable-gcov --disable-mudflap
$(package)_config_opts_linux=--with-pic
endef
diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk
index 2610c1e748..56045ade50 100644
--- a/depends/packages/qt.mk
+++ b/depends/packages/qt.mk
@@ -1,9 +1,9 @@
PACKAGE=qt
-$(package)_version=5.9.7
+$(package)_version=5.9.8
$(package)_download_path=https://download.qt.io/official_releases/qt/5.9/$($(package)_version)/submodules
$(package)_suffix=opensource-src-$($(package)_version).tar.xz
$(package)_file_name=qtbase-$($(package)_suffix)
-$(package)_sha256_hash=36dd9574f006eaa1e5af780e4b33d11fe39d09fd7c12f3b9d83294174bd28f00
+$(package)_sha256_hash=9b9dec1f67df1f94bce2955c5604de992d529dde72050239154c56352da0907d
$(package)_dependencies=openssl zlib
$(package)_linux_dependencies=freetype fontconfig libxcb
$(package)_build_subdir=qtbase
@@ -11,10 +11,10 @@ $(package)_qt_libs=corelib network widgets gui plugins testlib
$(package)_patches=fix_qt_pkgconfig.patch mac-qmake.conf fix_configure_mac.patch fix_no_printer.patch fix_rcc_determinism.patch fix_riscv64_arch.patch xkb-default.patch no-xlib.patch
$(package)_qttranslations_file_name=qttranslations-$($(package)_suffix)
-$(package)_qttranslations_sha256_hash=b36da7d93c3ab6fca56b32053bb73bc619c8b192bb89b74e3bcde2705f1c2a14
+$(package)_qttranslations_sha256_hash=fb5a47799754af73d3bf501fe513342cfe2fc37f64e80df5533f6110e804220c
$(package)_qttools_file_name=qttools-$($(package)_suffix)
-$(package)_qttools_sha256_hash=d62e0f70d99645d6704dbb8976fb2222443061743689943d40970c52c49367a1
+$(package)_qttools_sha256_hash=a97556eb7b2f30252cdd8a598c396cfce2b2f79d2bae883af6d3b26a2cdcc63c
$(package)_extra_sources = $($(package)_qttranslations_file_name)
$(package)_extra_sources += $($(package)_qttools_file_name)
@@ -156,9 +156,7 @@ define $(package)_preprocess_cmds
sed -i.old "s|updateqm.commands = \$$$$\$$$$LRELEASE|updateqm.commands = $($(package)_extract_dir)/qttools/bin/lrelease|" qttranslations/translations/translations.pro && \
sed -i.old "/updateqm.depends =/d" qttranslations/translations/translations.pro && \
sed -i.old "s/src_plugins.depends = src_sql src_network/src_plugins.depends = src_network/" qtbase/src/src.pro && \
- sed -i.old "s|X11/extensions/XIproto.h|X11/X.h|" qtbase/src/plugins/platforms/xcb/qxcbxsettings.cpp && \
sed -i.old -e 's/if \[ "$$$$XPLATFORM_MAC" = "yes" \]; then xspecvals=$$$$(macSDKify/if \[ "$$$$BUILD_ON_MAC" = "yes" \]; then xspecvals=$$$$(macSDKify/' -e 's|/bin/pwd|pwd|' qtbase/configure && \
- sed -i.old 's/CGEventCreateMouseEvent(0, kCGEventMouseMoved, pos, 0)/CGEventCreateMouseEvent(0, kCGEventMouseMoved, pos, kCGMouseButtonLeft)/' qtbase/src/plugins/platforms/cocoa/qcocoacursor.mm && \
mkdir -p qtbase/mkspecs/macx-clang-linux &&\
cp -f qtbase/mkspecs/macx-clang/Info.plist.lib qtbase/mkspecs/macx-clang-linux/ &&\
cp -f qtbase/mkspecs/macx-clang/Info.plist.app qtbase/mkspecs/macx-clang-linux/ &&\
diff --git a/depends/packages/rapidcheck.mk b/depends/packages/rapidcheck.mk
index fa4fb3c782..a16fee270e 100644
--- a/depends/packages/rapidcheck.mk
+++ b/depends/packages/rapidcheck.mk
@@ -1,11 +1,11 @@
package=rapidcheck
-$(package)_version=3eb9b4ff69f4ff2d9932e8f852c2b2a61d7c20d3
+$(package)_version=d9482c683429fe79122e3dcab14c9655874aeb8e
$(package)_download_path=https://github.com/emil-e/rapidcheck/archive
$(package)_file_name=$($(package)_version).tar.gz
-$(package)_sha256_hash=5fbf82755c9a647127e62563be079448ff8b1db9ca80a52a673dd9a88fdb714b
+$(package)_sha256_hash=b9ee8955b175fd3c0757ebd887bb075541761af08b0c28391b7c6c0685351f6b
define $(package)_config_cmds
- cmake -DCMAKE_INSTALL_PREFIX=$($(package)_staging_dir)$(host_prefix) -DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=true -DRC_INSTALL_ALL_EXTRAS=ON
+ cmake -DCMAKE_INSTALL_PREFIX=$($(package)_staging_dir)$(host_prefix) -DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=true -DRC_ENABLE_BOOST_TEST=ON -B .
endef
define $(package)_preprocess_cmds
diff --git a/depends/packages/xproto.mk b/depends/packages/xproto.mk
index 23ad5ffa10..2462f3c647 100644
--- a/depends/packages/xproto.mk
+++ b/depends/packages/xproto.mk
@@ -5,7 +5,7 @@ $(package)_file_name=$(package)-$($(package)_version).tar.bz2
$(package)_sha256_hash=636162c1759805a5a0114a369dffdeccb8af8c859ef6e1445f26a4e6e046514f
define $(package)_set_vars
-$(package)_config_opts=--disable-shared
+$(package)_config_opts=--without-fop --without-xmlto --without-xsltproc --disable-specs
endef
define $(package)_preprocess_cmds
diff --git a/depends/packages/zeromq.mk b/depends/packages/zeromq.mk
index e69a37e093..9ac037ebb5 100644
--- a/depends/packages/zeromq.mk
+++ b/depends/packages/zeromq.mk
@@ -6,7 +6,9 @@ $(package)_sha256_hash=bcbabe1e2c7d0eec4ed612e10b94b112dd5f06fcefa994a0c79a45d83
$(package)_patches=0001-fix-build-with-older-mingw64.patch 0002-disable-pthread_set_name_np.patch
define $(package)_set_vars
- $(package)_config_opts=--without-docs --disable-shared --without-libsodium --disable-curve --disable-curve-keygen --disable-perf --disable-Werror
+ $(package)_config_opts=--without-docs --disable-shared --disable-curve --disable-curve-keygen --disable-perf --disable-Werror --disable-drafts
+ $(package)_config_opts += --without-libsodium --without-libgssapi_krb5 --without-pgm --without-norm --without-vmci
+ $(package)_config_opts += --disable-libunwind --disable-radix-tree --without-gcov
$(package)_config_opts_linux=--with-pic
$(package)_cxxflags=-std=c++11
endef
diff --git a/depends/packages/zlib.mk b/depends/packages/zlib.mk
index 1600b11a01..168f85e65e 100644
--- a/depends/packages/zlib.mk
+++ b/depends/packages/zlib.mk
@@ -5,23 +5,26 @@ $(package)_file_name=$(package)-$($(package)_version).tar.gz
$(package)_sha256_hash=c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1
define $(package)_set_vars
-$(package)_build_opts= CC="$($(package)_cc)"
-$(package)_build_opts+=CFLAGS="$($(package)_cflags) $($(package)_cppflags) -fPIC"
-$(package)_build_opts+=RANLIB="$($(package)_ranlib)"
-$(package)_build_opts+=AR="$($(package)_ar)"
-$(package)_build_opts_darwin+=AR="$($(package)_libtool)"
-$(package)_build_opts_darwin+=ARFLAGS="-o"
+$(package)_config_opts= CC="$($(package)_cc)"
+$(package)_config_opts+=CFLAGS="$($(package)_cflags) $($(package)_cppflags) -fPIC"
+$(package)_config_opts+=RANLIB="$($(package)_ranlib)"
+$(package)_config_opts+=AR="$($(package)_ar)"
+$(package)_config_opts_darwin+=AR="$($(package)_libtool)"
+$(package)_config_opts_darwin+=ARFLAGS="-o"
endef
+# zlib has its own custom configure script that takes in options like CC,
+# CFLAGS, RANLIB, AR, and ARFLAGS from the environment rather than from
+# command-line arguments.
define $(package)_config_cmds
- ./configure --static --prefix=$(host_prefix)
+ env $($(package)_config_opts) ./configure --static --prefix=$(host_prefix)
endef
define $(package)_build_cmds
- $(MAKE) $($(package)_build_opts) libz.a
+ $(MAKE) libz.a
endef
define $(package)_stage_cmds
- $(MAKE) DESTDIR=$($(package)_staging_dir) install $($(package)_build_opts)
+ $(MAKE) DESTDIR=$($(package)_staging_dir) install
endef
diff --git a/doc/REST-interface.md b/doc/REST-interface.md
index c96871ab5f..3b8b0db162 100644
--- a/doc/REST-interface.md
+++ b/doc/REST-interface.md
@@ -81,7 +81,7 @@ $ curl localhost:18332/rest/getutxos/checkmempool/b2cdfd7b89def827ff8af7cd9bff76
{
"txvers" : 1
"height" : 2147483647,
- "value" : 8.8687,
+ "value" : 8.8687,
"scriptPubKey" : {
"asm" : "OP_DUP OP_HASH160 1c7cebb529b86a04c683dfa87be49de35bcf589e OP_EQUALVERIFY OP_CHECKSIG",
"hex" : "76a9141c7cebb529b86a04c683dfa87be49de35bcf589e88ac",
diff --git a/doc/benchmarking.md b/doc/benchmarking.md
index 48f81cf6c1..8e3d88ab7a 100644
--- a/doc/benchmarking.md
+++ b/doc/benchmarking.md
@@ -2,10 +2,17 @@ Benchmarking
============
Bitcoin Core has an internal benchmarking framework, with benchmarks
-for cryptographic algorithms (e.g. SHA1, SHA256, SHA512, RIPEMD160), as well as the rolling bloom filter.
+for cryptographic algorithms (e.g. SHA1, SHA256, SHA512, RIPEMD160, Poly1305, ChaCha20), rolling bloom filter, coins selection,
+thread queue, wallet balance.
Running
---------------------
+
+For benchmarks purposes you only need to compile `bitcoin_bench`. Beware of configuring without `--enable-debug` as this would impact
+benchmarking by unlatching log printers and lock analysis.
+
+ make -C src bench_bitcoin
+
After compiling bitcoin-core, the benchmarks can be run with:
src/bench/bench_bitcoin
@@ -13,43 +20,29 @@ After compiling bitcoin-core, the benchmarks can be run with:
The output will look similar to:
```
# Benchmark, evals, iterations, total, min, max, median
-Base58CheckEncode, 5, 320000, 120.772, 7.49351e-05, 7.59374e-05, 7.54759e-05
-Base58Decode, 5, 800000, 122.833, 3.0467e-05, 3.11732e-05, 3.06304e-05
-Base58Encode, 5, 470000, 137.094, 5.81061e-05, 5.85109e-05, 5.84462e-05
-BenchLockedPool, 5, 530, 34.2023, 0.0128247, 0.0129613, 0.0129026
-CCheckQueueSpeedPrevectorJob, 5, 1400, 26.1762, 0.00365048, 0.00388629, 0.00367108
-CCoinsCaching, 5, 170000, 48.1074, 5.60229e-05, 5.72316e-05, 5.66214e-05
-CoinSelection, 5, 650, 34.6426, 0.0105801, 0.0107699, 0.010664
-DeserializeAndCheckBlockTest, 5, 160, 39.2084, 0.0483662, 0.0494199, 0.0490138
-DeserializeBlockTest, 5, 130, 23.8129, 0.0357731, 0.0373763, 0.0365858
-FastRandom_1bit, 5, 440000000, 38.1609, 1.72974e-08, 1.73882e-08, 1.73478e-08
-FastRandom_32bit, 5, 110000000, 72.8237, 1.29992e-07, 1.37014e-07, 1.30115e-07
-MempoolEviction, 5, 41000, 89.8883, 0.000432748, 0.000446857, 0.000438483
-PrevectorClear, 5, 5600, 47.9229, 0.00169952, 0.0017455, 0.00170315
-PrevectorDestructor, 5, 5700, 44.5498, 0.0015561, 0.00156977, 0.00156469
-RIPEMD160, 5, 440, 135.988, 0.0615496, 0.062268, 0.0617779
-RollingBloom, 5, 1500000, 36.5109, 4.80961e-06, 4.97463e-06, 4.85811e-06
-SHA1, 5, 570, 51.808, 0.018065, 0.0182623, 0.0181865
-SHA256, 5, 340, 8.31841, 0.00483231, 0.00499803, 0.00485486
-SHA256_32b, 5, 4700000, 10.469, 4.43441e-07, 4.47611e-07, 4.45223e-07
-SHA512, 5, 330, 33.3408, 0.02017, 0.0202554, 0.0201921
-SipHash_32b, 5, 40000000, 38.7088, 1.91103e-07, 1.96998e-07, 1.93792e-07
-Sleep100ms, 5, 10, 5.01062, 0.100131, 0.100368, 0.100147
-Trig, 5, 12000000, 5.95494, 9.78115e-08, 1.04354e-07, 9.80682e-08
-VerifyScriptBench, 5, 6300, 9.02493, 0.000285566, 0.000288433, 0.000286175
+AssembleBlock, 5, 700, 1.79954, 0.000510913, 0.000517018, 0.000514497
+...
```
Help
---------------------
-`-?` will print a list of options and exit:
- src/bench/bench_bitcoin -?
+ src/bench/bench_bitcoin --help
+
+To print options like scaling factor or per-benchmark filter.
Notes
---------------------
More benchmarks are needed for, in no particular order:
- Script Validation
-- CCoinDBView caching
- Coins database
- Memory pool
-- Wallet coin selection
+- Cuckoo Cache
+- P2P throughput
+
+Going Further
+--------------------
+
+To monitor Bitcoin Core performance more in depth (like reindex or IBD): https://github.com/chaincodelabs/bitcoinperf
+
+To generate Flame Graphs for Bitcoin Core: https://github.com/eklitzke/bitcoin/blob/flamegraphs/doc/flamegraphs.md
diff --git a/doc/bitcoin-conf.md b/doc/bitcoin-conf.md
index 88ecb8fe65..f4a8edec75 100644
--- a/doc/bitcoin-conf.md
+++ b/doc/bitcoin-conf.md
@@ -30,8 +30,33 @@ Network specific options can be:
- placed into sections with headers `[main]` (not `[mainnet]`), `[test]` (not `[testnet]`) or `[regtest]`;
- prefixed with a chain name; e.g., `regtest.maxmempool=100`.
+Network specific options take precedence over non-network specific options.
+If multiple values for the same option are found with the same precedence, the
+first one is generally chosen.
+
+This means that given the following configuration, `regtest.rpcport` is set to `3000`:
+
+```
+regtest=1
+rpcport=2000
+regtest.rpcport=3000
+
+[regtest]
+rpcport=4000
+```
+
## Configuration File Path
The configuration file is not automatically created; you can create it using your favorite text editor. By default, the configuration file name is `bitcoin.conf` and it is located in the Bitcoin data directory, but both the Bitcoin data directory and the configuration file path may be changed using the `-datadir` and `-conf` command-line options.
The `includeconf=<file>` option in the `bitcoin.conf` file can be used to include additional configuration files.
+
+### Default configuration file locations
+
+Operating System | Data Directory | Example Path
+-- | -- | --
+Windows | `%APPDATA%\Bitcoin\` | `C:\Users\username\AppData\Roaming\Bitcoin\bitcoin.conf`
+Linux | `$HOME/.bitcoin/` | `/home/username/.bitcoin/bitcoin.conf`
+macOS | `$HOME/Library/Application Support/Bitcoin/` | `/Users/username/Library/Application Support/Bitcoin/bitcoin.conf`
+
+You can find an example bitcoin.conf file in [share/examples/bitcoin.conf](../share/examples/bitcoin.conf).
diff --git a/doc/build-osx.md b/doc/build-osx.md
index 65cfce6b15..9942449bf6 100644
--- a/doc/build-osx.md
+++ b/doc/build-osx.md
@@ -19,7 +19,7 @@ Then install [Homebrew](https://brew.sh).
## Dependencies
```shell
-brew install automake berkeley-db4 libtool boost miniupnpc openssl pkg-config protobuf python qt libevent qrencode
+brew install automake berkeley-db4 libtool boost miniupnpc openssl pkg-config python qt libevent qrencode
```
See [dependencies.md](dependencies.md) for a complete overview.
diff --git a/doc/build-unix.md b/doc/build-unix.md
index da65bc347a..069c983e6e 100644
--- a/doc/build-unix.md
+++ b/doc/build-unix.md
@@ -44,7 +44,7 @@ Optional dependencies:
miniupnpc | UPnP Support | Firewall-jumping support
libdb4.8 | Berkeley DB | Wallet storage (only needed when wallet enabled)
qt | GUI | GUI toolkit (only needed when GUI enabled)
- protobuf | Payments in GUI | Data interchange format used for payment protocol (only needed when GUI enabled)
+ protobuf | Payments in GUI | Data interchange format used for payment protocol (only needed when BIP70 enabled)
libqrencode | QR codes in GUI | Optional for generating QR codes (only needed when GUI enabled)
univalue | Utility | JSON parsing and encoding (bundled version will be used unless --with-system-univalue passed to configure)
libzmq3 | ZMQ notification | Optional, allows generating ZMQ notifications (requires ZMQ version >= 4.0.0)
@@ -61,6 +61,14 @@ tuned to conserve memory with additional CXXFLAGS:
./configure CXXFLAGS="--param ggc-min-expand=1 --param ggc-min-heapsize=32768"
+Alternatively, or in addition, debugging information can be skipped for compilation. The default compile flags are
+`-g -O2`, and can be changed with:
+
+ ./configure CXXFLAGS="-O2"
+
+Finally, clang (often less resource hungry) can be used instead of gcc, which is used by default:
+
+ ./configure CXX=clang++ CC=clang
## Linux Distribution Specific Instructions
@@ -78,7 +86,7 @@ Now, you can either build from self-compiled [depends](/depends/README.md) or in
BerkeleyDB is required for the wallet.
-Ubuntu and Debian have their own libdb-dev and libdb++-dev packages, but these will install
+Ubuntu and Debian have their own `libdb-dev` and `libdb++-dev` packages, but these will install
BerkeleyDB 5.1 or later. This will break binary wallet compatibility with the distributed executables, which
are based on BerkeleyDB 4.8. If you do not care about wallet compatibility,
pass `--with-incompatible-bdb` to configure.
@@ -88,7 +96,7 @@ Otherwise, you can build from self-compiled `depends` (see above).
To build Bitcoin Core without wallet, see [*Disable-wallet mode*](/doc/build-unix.md#disable-wallet-mode)
-Optional (see --with-miniupnpc and --enable-upnp-default):
+Optional (see `--with-miniupnpc` and `--enable-upnp-default`):
sudo apt-get install libminiupnpc-dev
@@ -104,12 +112,16 @@ To build without GUI pass `--without-gui`.
To build with Qt 5 you need the following:
- sudo apt-get install libqt5gui5 libqt5core5a libqt5dbus5 qttools5-dev qttools5-dev-tools libprotobuf-dev protobuf-compiler
+ sudo apt-get install libqt5gui5 libqt5core5a libqt5dbus5 qttools5-dev qttools5-dev-tools
libqrencode (optional) can be installed with:
sudo apt-get install libqrencode-dev
+protobuf (optional) can be installed with:
+
+ sudo apt-get install libprotobuf-dev protobuf-compiler
+
Once these are installed, they will be found by configure and a bitcoin-qt executable will be
built by default.
@@ -122,18 +134,26 @@ Build requirements:
sudo dnf install gcc-c++ libtool make autoconf automake openssl-devel libevent-devel boost-devel libdb4-devel libdb4-cxx-devel python3
-Optional:
+Optional (see `--with-miniupnpc` and `--enable-upnp-default`):
sudo dnf install miniupnpc-devel
+ZMQ dependencies (provides ZMQ API):
+
+ sudo dnf install zeromq-devel
+
To build with Qt 5 you need the following:
- sudo dnf install qt5-qttools-devel qt5-qtbase-devel protobuf-devel
+ sudo dnf install qt5-qttools-devel qt5-qtbase-devel
libqrencode (optional) can be installed with:
sudo dnf install qrencode-devel
+protobuf (optional) can be installed with:
+
+ sudo dnf install protobuf-devel
+
Notes
-----
The release is built with GCC and then "strip bitcoind" to strip the debug
diff --git a/doc/dependencies.md b/doc/dependencies.md
index 0b23ca0a2d..e5b4084d99 100644
--- a/doc/dependencies.md
+++ b/doc/dependencies.md
@@ -35,7 +35,7 @@ Some dependencies are not needed in all configurations. The following are some f
#### Options passed to `./configure`
* MiniUPnPc is not needed with `--with-miniupnpc=no`.
* Berkeley DB is not needed with `--disable-wallet`.
-* protobuf is not needed with `--disable-bip70`.
+* protobuf is only needed with `--enable-bip70`.
* Qt is not needed with `--without-gui`.
* If the qrencode dependency is absent, QR support won't be added. To force an error when that happens, pass `--with-qrencode`.
* ZeroMQ is needed only with the `--with-zmq` option.
diff --git a/doc/developer-notes.md b/doc/developer-notes.md
index 39463dc6f8..561f623cd5 100644
--- a/doc/developer-notes.md
+++ b/doc/developer-notes.md
@@ -27,7 +27,7 @@ Developer Notes
- [General C++](#general-c)
- [C++ data structures](#c-data-structures)
- [Strings and formatting](#strings-and-formatting)
- - [Variable names](#variable-names)
+ - [Shadowing](#shadowing)
- [Threads and synchronization](#threads-and-synchronization)
- [Scripts](#scripts)
- [Shebang](#shebang)
@@ -62,7 +62,7 @@ tool to clean up patches automatically before submission.
- 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`.
- - No extra spaces inside parenthesis; don't do ( this )
+ - No extra spaces inside parenthesis; don't do ( this ).
- No space after function names; one space after `if`, `for` and `while`.
- If an `if` only has a single-statement `then`-clause, it can appear
on the same line as the `if`, without braces. In every other case,
@@ -72,12 +72,12 @@ tool to clean up patches automatically before submission.
- **Symbol naming conventions**. These are preferred in new code, but are not
required when doing so would need changes to significant pieces of existing
code.
- - Variable (including function arguments) and namespace names are all lowercase, and may use `_` to
+ - Variable (including function arguments) and namespace names are all lowercase and may use `_` to
separate words (snake_case).
- Class member variables have a `m_` prefix.
- Global variables have a `g_` prefix.
- Constant names are all uppercase, and use `_` to separate words.
- - Class names, function names and method names are UpperCamelCase
+ - Class names, function names, and method names are UpperCamelCase
(PascalCase). Do not prefix class names with `C`.
- Test suite naming convention: The Boost test suite in file
`src/test/foo_tests.cpp` should be named `foo_tests`. Test suite names
@@ -134,6 +134,7 @@ Bitcoin Core uses [Doxygen](http://www.doxygen.nl/) to generate its official doc
Use Doxygen-compatible comment blocks for functions, methods, and fields.
For example, to describe a function use:
+
```c++
/**
* ... text ...
@@ -143,11 +144,12 @@ For example, to describe a function use:
*/
bool function(int arg1, const char *arg2)
```
+
A complete list of `@xxx` commands can be found at http://www.doxygen.nl/manual/commands.html.
As Doxygen recognizes the comments by the delimiters (`/**` and `*/` in this case), you don't
*need* to provide any commands for a comment to be valid; just a description text is fine.
-To describe a class use the same construct above the class definition:
+To describe a class, use the same construct above the class definition:
```c++
/**
* Alerts are for notifying old versions if they become too obsolete and
@@ -189,7 +191,7 @@ but the above styles are favored.
Documentation can be generated with `make docs` and cleaned up with `make clean-docs`. The resulting files are located in `doc/doxygen/html`; open `index.html` to view the homepage.
-Before running `make docs`, you will need to install dependencies `doxygen` and `dot`. For example, on MacOS via Homebrew:
+Before running `make docs`, you will need to install dependencies `doxygen` and `dot`. For example, on macOS via Homebrew:
```
brew install doxygen --with-graphviz
```
@@ -231,7 +233,7 @@ that run in `-regtest` mode.
Bitcoin Core is a multi-threaded application, and deadlocks or other
multi-threading bugs can be very difficult to track down. The `--enable-debug`
configure option adds `-DDEBUG_LOCKORDER` to the compiler flags. This inserts
-run-time checks to keep track of which locks are held, and adds warnings to the
+run-time checks to keep track of which locks are held and adds warnings to the
debug.log file if inconsistencies are detected.
### Valgrind suppressions file
@@ -299,7 +301,7 @@ $ perf record \
-p `pgrep bitcoind` -- sleep 60
```
-You could then analyze the results by running
+You could then analyze the results by running:
```sh
perf report --stdio | c++filt | less
@@ -364,7 +366,7 @@ Additional resources:
Locking/mutex usage notes
-------------------------
-The code is multi-threaded, and uses mutexes and the
+The code is multi-threaded and uses mutexes and the
`LOCK` and `TRY_LOCK` macros to protect data structures.
Deadlocks due to inconsistent lock ordering (thread 1 locks `cs_main` and then
@@ -389,7 +391,7 @@ Threads
- ThreadDNSAddressSeed : Loads addresses of peers from the DNS.
-- ThreadMapPort : Universal plug-and-play startup/shutdown
+- ThreadMapPort : Universal plug-and-play startup/shutdown.
- ThreadSocketHandler : Sends/Receives data from peers on port 8333.
@@ -408,7 +410,7 @@ Threads
Ignoring IDE/editor files
--------------------------
-In closed-source environments in which everyone uses the same IDE it is common
+In closed-source environments in which everyone uses the same IDE, it is common
to add temporary files it produces to the project-wide `.gitignore` file.
However, in open source software such as Bitcoin Core, where everyone uses
@@ -446,19 +448,19 @@ pay attention to for reviewers of Bitcoin Core code.
General Bitcoin Core
----------------------
-- New features should be exposed on RPC first, then can be made available in the GUI
+- New features should be exposed on RPC first, then can be made available in the GUI.
- *Rationale*: RPC allows for better automatic testing. The test suite for
- the GUI is very limited
+ the GUI is very limited.
-- Make sure pull requests pass Travis CI before merging
+- Make sure pull requests pass Travis CI before merging.
- *Rationale*: Makes sure that they pass thorough testing, and that the tester will keep passing
- on the master branch. Otherwise all new pull requests will start failing the tests, resulting in
- confusion and mayhem
+ on the master branch. Otherwise, all new pull requests will start failing the tests, resulting in
+ confusion and mayhem.
- *Explanation*: If the test suite is to be updated for a change, this has to
- be done first
+ be done first.
Wallet
-------
@@ -466,13 +468,13 @@ Wallet
- Make sure that no crashes happen with run-time option `-disablewallet`.
- *Rationale*: In RPC code that conditionally uses the wallet (such as
- `validateaddress`) it is easy to forget that global pointer `pwalletMain`
+ `validateaddress`), it is easy to forget that global pointer `pwalletMain`
can be nullptr. See `test/functional/disablewallet.py` for functional tests
- exercising the API with `-disablewallet`
+ exercising the API with `-disablewallet`.
-- Include `db_cxx.h` (BerkeleyDB header) only when `ENABLE_WALLET` is set
+- Include `db_cxx.h` (BerkeleyDB header) only when `ENABLE_WALLET` is set.
- - *Rationale*: Otherwise compilation of the disable-wallet build will fail in environments without BerkeleyDB
+ - *Rationale*: Otherwise compilation of the disable-wallet build will fail in environments without BerkeleyDB.
General C++
-------------
@@ -483,26 +485,26 @@ Guidelines](https://isocpp.github.io/CppCoreGuidelines/).
Common misconceptions are clarified in those sections:
- Passing (non-)fundamental types in the [C++ Core
- Guideline](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rf-conventional)
+ Guideline](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rf-conventional).
-- Assertions should not have side-effects
+- Assertions should not have side-effects.
- *Rationale*: Even though the source code is set to refuse to compile
with assertions disabled, having side-effects in assertions is unexpected and
- makes the code harder to understand
+ makes the code harder to understand.
-- If you use the `.h`, you must link the `.cpp`
+- If you use the `.h`, you must link the `.cpp`.
- *Rationale*: Include files define the interface for the code in implementation files. Including one but
not linking the other is confusing. Please avoid that. Moving functions from
- the `.h` to the `.cpp` should not result in build errors
+ the `.h` to the `.cpp` should not result in build errors.
-- Use the RAII (Resource Acquisition Is Initialization) paradigm where possible. For example by using
+- Use the RAII (Resource Acquisition Is Initialization) paradigm where possible. For example, by using
`unique_ptr` for allocations in a function.
- - *Rationale*: This avoids memory and resource leaks, and ensures exception safety
+ - *Rationale*: This avoids memory and resource leaks, and ensures exception safety.
-- Use `MakeUnique()` to construct objects owned by `unique_ptr`s
+- Use `MakeUnique()` to construct objects owned by `unique_ptr`s.
- *Rationale*: `MakeUnique` is concise and ensures exception safety in complex expressions.
`MakeUnique` is a temporary project local implementation of `std::make_unique` (C++14).
@@ -510,27 +512,27 @@ Common misconceptions are clarified in those sections:
C++ data structures
--------------------
-- Never use the `std::map []` syntax when reading from a map, but instead use `.find()`
+- Never use the `std::map []` syntax when reading from a map, but instead use `.find()`.
- *Rationale*: `[]` does an insert (of the default element) if the item doesn't
exist in the map yet. This has resulted in memory leaks in the past, as well as
- race conditions (expecting read-read behavior). Using `[]` is fine for *writing* to a map
+ race conditions (expecting read-read behavior). Using `[]` is fine for *writing* to a map.
- Do not compare an iterator from one data structure with an iterator of
- another data structure (even if of the same type)
+ another data structure (even if of the same type).
- *Rationale*: Behavior is undefined. In C++ parlor this means "may reformat
- the universe", in practice this has resulted in at least one hard-to-debug crash bug
+ the universe", in practice this has resulted in at least one hard-to-debug crash bug.
- Watch out for out-of-bounds vector access. `&vch[vch.size()]` is illegal,
including `&vch[0]` for an empty vector. Use `vch.data()` and `vch.data() +
vch.size()` instead.
-- Vector bounds checking is only enabled in debug mode. Do not rely on it
+- Vector bounds checking is only enabled in debug mode. Do not rely on it.
- Initialize all non-static class members where they are defined.
If this is skipped for a good reason (i.e., optimization on the critical
- path), add an explicit comment about this
+ path), add an explicit comment about this.
- *Rationale*: Ensure determinism by avoiding accidental use of uninitialized
values. Also, static analyzers balk about this.
@@ -554,12 +556,12 @@ class A
`int8_t`. Do not use bare `char` unless it is to pass to a third-party API.
This type can be signed or unsigned depending on the architecture, which can
lead to interoperability problems or dangerous conditions such as
- out-of-bounds array accesses
+ out-of-bounds array accesses.
-- Prefer explicit constructions over implicit ones that rely on 'magical' C++ behavior
+- Prefer explicit constructions over implicit ones that rely on 'magical' C++ behavior.
- *Rationale*: Easier to understand what is happening, thus easier to spot mistakes, even for those
- that are not language lawyers
+ that are not language lawyers.
Strings and formatting
------------------------
@@ -567,17 +569,17 @@ Strings and formatting
- Be careful of `LogPrint` versus `LogPrintf`. `LogPrint` takes a `category` argument, `LogPrintf` does not.
- *Rationale*: Confusion of these can result in runtime exceptions due to
- formatting mismatch, and it is easy to get wrong because of subtly similar naming
+ formatting mismatch, and it is easy to get wrong because of subtly similar naming.
-- Use `std::string`, avoid C string manipulation functions
+- Use `std::string`, avoid C string manipulation functions.
- *Rationale*: C++ string handling is marginally safer, less scope for
- buffer overflows and surprises with `\0` characters. Also some C string manipulations
- tend to act differently depending on platform, or even the user locale
+ buffer overflows, and surprises with `\0` characters. Also, some C string manipulations
+ tend to act differently depending on platform, or even the user locale.
-- Use `ParseInt32`, `ParseInt64`, `ParseUInt32`, `ParseUInt64`, `ParseDouble` from `utilstrencodings.h` for number parsing
+- Use `ParseInt32`, `ParseInt64`, `ParseUInt32`, `ParseUInt64`, `ParseDouble` from `utilstrencodings.h` for number parsing.
- - *Rationale*: These functions do overflow checking, and avoid pesky locale issues.
+ - *Rationale*: These functions do overflow checking and avoid pesky locale issues.
- Avoid using locale dependent functions if possible. You can use the provided
[`lint-locale-dependence.sh`](/test/lint/lint-locale-dependence.sh)
@@ -607,48 +609,32 @@ Strings and formatting
`wcstoll`, `wcstombs`, `wcstoul`, `wcstoull`, `wcstoumax`, `wcswidth`,
`wcsxfrm`, `wctob`, `wctomb`, `wctrans`, `wctype`, `wcwidth`, `wprintf`
-- For `strprintf`, `LogPrint`, `LogPrintf` formatting characters don't need size specifiers
+- For `strprintf`, `LogPrint`, `LogPrintf` formatting characters don't need size specifiers.
- - *Rationale*: Bitcoin Core uses tinyformat, which is type safe. Leave them out to avoid confusion
+ - *Rationale*: Bitcoin Core uses tinyformat, which is type safe. Leave them out to avoid confusion.
-Variable names
+Shadowing
--------------
-Although the shadowing warning (`-Wshadow`) is not enabled by default (it prevents issues rising
+Although the shadowing warning (`-Wshadow`) is not enabled by default (it prevents issues arising
from using a different variable with the same name),
please name variables so that their names do not shadow variables defined in the source code.
-E.g. in member initializers, prepend `_` to the argument name shadowing the
-member name:
-
-```c++
-class AddressBookPage
-{
- Mode m_mode;
-}
-
-AddressBookPage::AddressBookPage(Mode _mode)
- : m_mode(_mode)
-...
-```
-
When using nested cycles, do not name the inner cycle variable the same as in
-upper cycle etc.
-
+the upper cycle, etc.
Threads and synchronization
----------------------------
- Build and run tests with `-DDEBUG_LOCKORDER` to verify that no potential
deadlocks are introduced. As of 0.12, this is defined by default when
- configuring with `--enable-debug`
+ configuring with `--enable-debug`.
- When using `LOCK`/`TRY_LOCK` be aware that the lock exists in the context of
the current scope, so surround the statement and the code that needs the lock
- with braces
+ with braces.
OK:
-
```c++
{
TRY_LOCK(cs_vNodes, lockNodes);
@@ -657,7 +643,6 @@ Threads and synchronization
```
Wrong:
-
```c++
TRY_LOCK(cs_vNodes, lockNodes);
{
@@ -679,13 +664,11 @@ Scripts
`#!/usr/bin/env bash` searches the user's PATH to find the bash binary.
OK:
-
```bash
#!/usr/bin/env bash
```
Wrong:
-
```bash
#!/bin/bash
```
@@ -694,9 +677,9 @@ Source code organization
--------------------------
- Implementation code should go into the `.cpp` file and not the `.h`, unless necessary due to template usage or
- when performance due to inlining is critical
+ when performance due to inlining is critical.
- - *Rationale*: Shorter and simpler header files are easier to read, and reduce compile time
+ - *Rationale*: Shorter and simpler header files are easier to read and reduce compile time.
- Use only the lowercase alphanumerics (`a-z0-9`), underscore (`_`) and hyphen (`-`) in source code filenames.
@@ -714,7 +697,7 @@ Source code organization
- Don't import anything into the global namespace (`using namespace ...`). Use
fully specified types such as `std::string`.
- - *Rationale*: Avoids symbol conflicts
+ - *Rationale*: Avoids symbol conflicts.
- Terminate namespaces with a comment (`// namespace mynamespace`). The comment
should be placed on the same line as the brace closing the namespace, e.g.
@@ -729,7 +712,7 @@ namespace {
} // namespace
```
- - *Rationale*: Avoids confusion about the namespace context
+ - *Rationale*: Avoids confusion about the namespace context.
- Use `#include <primitives/transaction.h>` bracket syntax instead of
`#include "primitives/transactions.h"` quote syntax.
@@ -752,13 +735,13 @@ namespace {
GUI
-----
-- Do not display or manipulate dialogs in model code (classes `*Model`)
+- Do not display or manipulate dialogs in model code (classes `*Model`).
- *Rationale*: Model classes pass through events and data from the core, they
should not interact with the user. That's where View classes come in. The converse also
holds: try to not directly access core data structures from Views.
-- Avoid adding slow or blocking code in the GUI thread. In particular do not
+- Avoid adding slow or blocking code in the GUI thread. In particular, do not
add new `interfaces::Node` and `interfaces::Wallet` method calls, even if they
may be fast now, in case they are changed to lock or communicate across
processes in the future.
@@ -777,12 +760,12 @@ Subtrees
Several parts of the repository are subtrees of software maintained elsewhere.
Some of these are maintained by active developers of Bitcoin Core, in which case changes should probably go
-directly upstream without being PRed directly against the project. They will be merged back in the next
+directly upstream without being PRed directly against the project. They will be merged back in the next
subtree merge.
-Others are external projects without a tight relationship with our project. Changes to these should also
-be sent upstream but bugfixes may also be prudent to PR against Bitcoin Core so that they can be integrated
-quickly. Cosmetic changes should be purely taken upstream.
+Others are external projects without a tight relationship with our project. Changes to these should also
+be sent upstream, but bugfixes may also be prudent to PR against Bitcoin Core so that they can be integrated
+quickly. Cosmetic changes should be purely taken upstream.
There is a tool in `test/lint/git-subtree-check.sh` to check a subtree directory for consistency with
its upstream repository.
@@ -812,11 +795,11 @@ you must be aware of.
### File Descriptor Counts
-In most configurations we use the default LevelDB value for `max_open_files`,
+In most configurations, we use the default LevelDB value for `max_open_files`,
which is 1000 at the time of this writing. If LevelDB actually uses this many
-file descriptors it will cause problems with Bitcoin's `select()` loop, because
+file descriptors, it will cause problems with Bitcoin's `select()` loop, because
it may cause new sockets to be created where the fd value is >= 1024. For this
-reason, on 64-bit Unix systems we rely on an internal LevelDB optimization that
+reason, on 64-bit Unix systems, we rely on an internal LevelDB optimization that
uses `mmap()` + `close()` to open table files without actually retaining
references to the table file descriptors. If you are upgrading LevelDB, you must
sanity check the changes to make sure that this assumption remains valid.
@@ -841,14 +824,14 @@ details.
It is possible for LevelDB changes to inadvertently change consensus
compatibility between nodes. This happened in Bitcoin 0.8 (when LevelDB was
-first introduced). When upgrading LevelDB you should review the upstream changes
+first introduced). When upgrading LevelDB, you should review the upstream changes
to check for issues affecting consensus compatibility.
For example, if LevelDB had a bug that accidentally prevented a key from being
returned in an edge case, and that bug was fixed upstream, the bug "fix" would
-be an incompatible consensus change. In this situation the correct behavior
+be an incompatible consensus change. In this situation, the correct behavior
would be to revert the upstream fix before applying the updates to Bitcoin's
-copy of LevelDB. In general you should be wary of any upstream changes affecting
+copy of LevelDB. In general, you should be wary of any upstream changes affecting
what data is returned from LevelDB queries.
Scripted diffs
@@ -867,7 +850,7 @@ To create a scripted-diff:
- `-BEGIN VERIFY SCRIPT-`
- `-END VERIFY SCRIPT-`
-The scripted-diff is verified by the tool `test/lint/commit-script-check.sh`. The tool's default behavior when supplied
+The scripted-diff is verified by the tool `test/lint/commit-script-check.sh`. The tool's default behavior, when supplied
with a commit is to verify all scripted-diffs from the beginning of time up to said commit. Internally, the tool passes
the first supplied argument to `git rev-list --reverse` to determine which commits to verify script-diffs for, ignoring
commits that don't conform to the commit message format described above.
@@ -900,23 +883,23 @@ RPC interface guidelines
A few guidelines for introducing and reviewing new RPC interfaces:
-- Method naming: use consecutive lower-case names such as `getrawtransaction` and `submitblock`
+- Method naming: use consecutive lower-case names such as `getrawtransaction` and `submitblock`.
- - *Rationale*: Consistency with existing interface.
+ - *Rationale*: Consistency with the existing interface.
- Argument naming: use snake case `fee_delta` (and not, e.g. camel case `feeDelta`)
- - *Rationale*: Consistency with existing interface.
+ - *Rationale*: Consistency with the existing interface.
- Use the JSON parser for parsing, don't manually parse integers or strings from
arguments unless absolutely necessary.
- *Rationale*: Introduces hand-rolled string manipulation code at both the caller and callee sites,
- which is error prone, and it is easy to get things such as escaping wrong.
+ which is error-prone, and it is easy to get things such as escaping wrong.
JSON already supports nested data structures, no need to re-invent the wheel.
- *Exception*: AmountFromValue can parse amounts as string. This was introduced because many JSON
- parsers and formatters hard-code handling decimal numbers as floating point
+ parsers and formatters hard-code handling decimal numbers as floating-point
values, resulting in potential loss of precision. This is unacceptable for
monetary values. **Always** use `AmountFromValue` and `ValueFromAmount` when
inputting or outputting monetary values. The only exceptions to this are
@@ -925,7 +908,7 @@ A few guidelines for introducing and reviewing new RPC interfaces:
- Missing arguments and 'null' should be treated the same: as default values. If there is no
default value, both cases should fail in the same way. The easiest way to follow this
- guideline is detect unspecified arguments with `params[x].isNull()` instead of
+ guideline is to detect unspecified arguments with `params[x].isNull()` instead of
`params.size() <= x`. The former returns true if the argument is either null or missing,
while the latter returns true if is missing, and false if it is null.
@@ -959,7 +942,7 @@ A few guidelines for introducing and reviewing new RPC interfaces:
from there.
- A RPC method must either be a wallet method or a non-wallet method. Do not
- introduce new methods that differ in behavior based on presence of a wallet.
+ introduce new methods that differ in behavior based on the presence of a wallet.
- *Rationale*: as well as complicating the implementation and interfering
with the introduction of multi-wallet, wallet and non-wallet code should be
@@ -967,7 +950,7 @@ A few guidelines for introducing and reviewing new RPC interfaces:
- Try to make the RPC response a JSON object.
- - *Rationale*: If a RPC response is not a JSON object then it is harder to avoid API breakage if
+ - *Rationale*: If a RPC response is not a JSON object, then it is harder to avoid API breakage if
new data in the response is needed.
- Wallet RPCs call BlockUntilSyncedToCurrentChain to maintain consistency with
diff --git a/doc/init.md b/doc/init.md
index a6c9bb94d8..0b327aab58 100644
--- a/doc/init.md
+++ b/doc/init.md
@@ -53,17 +53,17 @@ Paths
All three configurations assume several paths that might need to be adjusted.
-Binary: `/usr/bin/bitcoind`
-Configuration file: `/etc/bitcoin/bitcoin.conf`
-Data directory: `/var/lib/bitcoind`
+Binary: `/usr/bin/bitcoind`
+Configuration file: `/etc/bitcoin/bitcoin.conf`
+Data directory: `/var/lib/bitcoind`
PID file: `/var/run/bitcoind/bitcoind.pid` (OpenRC and Upstart) or `/run/bitcoind/bitcoind.pid` (systemd)
-Lock file: `/var/lock/subsys/bitcoind` (CentOS)
+Lock file: `/var/lock/subsys/bitcoind` (CentOS)
-The configuration file, PID directory (if applicable) and data directory
-should all be owned by the bitcoin user and group. It is advised for security
-reasons to make the configuration file and data directory only readable by the
-bitcoin user and group. Access to bitcoin-cli and other bitcoind rpc clients
-can then be controlled by group membership.
+The PID directory (if applicable) and data directory should both be owned by the
+bitcoin user and group. It is advised for security reasons to make the
+configuration file and data directory only readable by the bitcoin user and
+group. Access to bitcoin-cli and other bitcoind rpc clients can then be
+controlled by group membership.
NOTE: When using the systemd .service file, the creation of the aforementioned
directories and the setting of their permissions is automatically handled by
@@ -83,10 +83,10 @@ OpenRC).
### macOS
-Binary: `/usr/local/bin/bitcoind`
-Configuration file: `~/Library/Application Support/Bitcoin/bitcoin.conf`
-Data directory: `~/Library/Application Support/Bitcoin`
-Lock file: `~/Library/Application Support/Bitcoin/.lock`
+Binary: `/usr/local/bin/bitcoind`
+Configuration file: `~/Library/Application Support/Bitcoin/bitcoin.conf`
+Data directory: `~/Library/Application Support/Bitcoin`
+Lock file: `~/Library/Application Support/Bitcoin/.lock`
Installing Service Configuration
-----------------------------------
diff --git a/doc/rapidcheck.md b/doc/rapidcheck.md
new file mode 100644
index 0000000000..397a907f17
--- /dev/null
+++ b/doc/rapidcheck.md
@@ -0,0 +1,84 @@
+# RapidCheck property-based testing for Bitcoin Core
+
+## Concept
+
+Property-based testing is experimentally being added to Bitcoin Core with
+[RapidCheck](https://github.com/emil-e/rapidcheck), a C++ framework for
+property-based testing inspired by the Haskell library
+[QuickCheck](https://hackage.haskell.org/package/QuickCheck).
+
+RapidCheck performs random testing of program properties. A specification of the
+program is given in the form of properties which functions should satisfy, and
+RapidCheck tests that the properties hold in a large number of randomly
+generated cases.
+
+If an exception is found, RapidCheck tries to find the smallest case, for some
+definition of smallest, for which the property is still false and displays it as
+a counter-example. For example, if the input is an integer, RapidCheck tries to
+find the smallest integer for which the property is false.
+
+## Running
+
+If RapidCheck is installed, Bitcoin Core will automatically run the
+property-based tests with the unit tests during `make check`, unless the
+`--without-rapidcheck` flag is passed when configuring.
+
+For more information, run `./configure --help` and see `--with-rapidcheck` under
+Optional Packages.
+
+## Setup
+
+The following instructions have been tested with Linux Debian and macOS.
+
+1. Clone the RapidCheck source code and cd into the repository.
+
+ ```shell
+ git clone https://github.com/emil-e/rapidcheck.git
+ cd rapidcheck
+ ```
+
+2. Build RapidCheck (requires CMake to be installed).
+
+ ```shell
+ cmake -DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=true -DRC_ENABLE_BOOST_TEST=ON $(pwd)
+ make && make install
+ ```
+
+3. Configure Bitcoin Core with RapidCheck.
+
+ `cd` to the directory of your local bitcoin repository and run
+ `./configure`. In the output you should see:
+
+ ```shell
+ checking rapidcheck.h usability... yes
+ checking rapidcheck.h presence... yes
+ checking for rapidcheck.h... yes
+ [...]
+ Options used to compile and link:
+ [...]
+ with test = yes
+ with prop = yes
+ ```
+
+4. Build Bitcoin Core with RapidCheck.
+
+ Now you can run `make` and should see the property-based tests compiled with
+ the unit tests:
+
+ ```shell
+ Making all in src
+ [...]
+ CXX test/gen/test_bitcoin-crypto_gen.o
+ CXX test/test_bitcoin-key_properties.o
+ ```
+
+5. Run the unit tests with `make check`. The property-based tests will be run
+ with the unit tests.
+
+ ```shell
+ Running tests: crypto_tests from test/crypto_tests.cpp
+ [...]
+ Running tests: key_properties from test/key_properties.cpp
+ ```
+
+That's it! You are now running property-based tests in Bitcoin Core.
diff --git a/doc/release-notes-0.18.1-16257.md b/doc/release-notes-0.18.1-16257.md
deleted file mode 100644
index 21867b7fb2..0000000000
--- a/doc/release-notes-0.18.1-16257.md
+++ /dev/null
@@ -1,6 +0,0 @@
-Wallet changes
---------------
-When creating a transaction with a fee above `-maxtxfee` (default 0.1 BTC),
-the RPC commands `walletcreatefundedpsbt` and `fundrawtransaction` will now fail
-instead of rounding down the fee. Beware that the `feeRate` argument is specified
-in BTC per kilobyte, not satoshi per byte.
diff --git a/doc/release-notes-13756.md b/doc/release-notes-13756.md
deleted file mode 100644
index a500aceb0f..0000000000
--- a/doc/release-notes-13756.md
+++ /dev/null
@@ -1,41 +0,0 @@
-Coin selection
---------------
-
-### Reuse Avoidance
-
-A new wallet flag `avoid_reuse` has been added (default off). When enabled,
-a wallet will distinguish between used and unused addresses, and default to not
-use the former in coin selection.
-
-Rescanning the blockchain is required, to correctly mark previously
-used destinations.
-
-Together with "avoid partial spends" (present as of Bitcoin v0.17), this
-addresses a serious privacy issue where a malicious user can track spends by
-peppering a previously paid to address with near-dust outputs, which would then
-be inadvertently included in future payments.
-
-New RPCs
---------
-
-- A new `setwalletflag` RPC sets/unsets flags for an existing wallet.
-
-
-Updated RPCs
-------------
-
-Several RPCs have been updated to include an "avoid_reuse" flag, used to control
-whether already used addresses should be left out or included in the operation.
-These include:
-
-- createwallet
-- getbalance
-- getbalances
-- sendtoaddress
-
-In addition, `sendtoaddress` has been changed to avoid partial spends when `avoid_reuse`
-is enabled (if not already enabled via the `-avoidpartialspends` command line flag),
-as it would otherwise risk using up the "wrong" UTXO for an address reuse case.
-
-The listunspent RPC has also been updated to now include a "reused" bool, for nodes
-with "avoid_reuse" enabled.
diff --git a/doc/release-notes-14054.md b/doc/release-notes-14054.md
deleted file mode 100644
index d8cad369c5..0000000000
--- a/doc/release-notes-14054.md
+++ /dev/null
@@ -1,7 +0,0 @@
-P2P changes
------------
-
-BIP 61 reject messages were deprecated in v0.18. They are now disabled by
-default, but can be enabled by setting the `-enablebip61` command line option.
-BIP 61 reject messages will be removed entirely in a future version of
-Bitcoin Core.
diff --git a/doc/release-notes-14802.md b/doc/release-notes-14802.md
deleted file mode 100644
index 1fcc38866a..0000000000
--- a/doc/release-notes-14802.md
+++ /dev/null
@@ -1,3 +0,0 @@
-RPC changes
------------
-The `getblockstats` RPC is faster for fee calculation by using BlockUndo data. Also, `-txindex` is no longer required and `getblockstats` works for all non-pruned blocks.
diff --git a/doc/release-notes-14954.md b/doc/release-notes-14954.md
deleted file mode 100644
index 4bb0fcca74..0000000000
--- a/doc/release-notes-14954.md
+++ /dev/null
@@ -1,3 +0,0 @@
-Build system changes
---------------------
-Python >=3.5 is now required by all aspects of the project. This includes the build systems, test framework and linters. The previously supported minimum (3.4), was EOL in March 2019. See #14954 for more details. \ No newline at end of file
diff --git a/doc/release-notes-15006.md b/doc/release-notes-15006.md
deleted file mode 100644
index 76ed3247a6..0000000000
--- a/doc/release-notes-15006.md
+++ /dev/null
@@ -1,4 +0,0 @@
-Miscellaneous RPC changes
-------------
-
-- `createwallet` can now create encrypted wallets if a non-empty passphrase is specified.
diff --git a/doc/release-notes-15427.md b/doc/release-notes-15427.md
deleted file mode 100644
index 25edfd4402..0000000000
--- a/doc/release-notes-15427.md
+++ /dev/null
@@ -1,9 +0,0 @@
-Updated RPCs
-------------
-
-The `utxoupdatepsbt` RPC method has been updated to take a `descriptors`
-argument. When provided, input and output scripts and keys will be filled in
-when known, and P2SH-witness inputs will be filled in from the UTXO set when a
-descriptor is provided that shows they're spending segwit outputs.
-
-See the RPC help text for full details.
diff --git a/doc/release-notes-15566.md b/doc/release-notes-15566.md
deleted file mode 100644
index 49964d7550..0000000000
--- a/doc/release-notes-15566.md
+++ /dev/null
@@ -1,3 +0,0 @@
-Miscellaneous CLI Changes
--------------------------
-- The `testnet` field in `bitcoin-cli -getinfo` has been renamed to `chain` and now returns the current network name as defined in BIP70 (main, test, regtest). \ No newline at end of file
diff --git a/doc/release-notes-15584.md b/doc/release-notes-15584.md
new file mode 100644
index 0000000000..3d9b1cc903
--- /dev/null
+++ b/doc/release-notes-15584.md
@@ -0,0 +1,4 @@
+GUI Changes
+-----------
+- In 0.18.0 a `./configure` flag was introduced to allow disabling BIP70 support in the GUI (support was enabled by default). In 0.19.0 this flag is now __disabled__ by default.
+- If you want compile Bitcoin Core with BIP70 support in the GUI, you can pass `--enable-bip70` to `./configure`. \ No newline at end of file
diff --git a/doc/release-notes-15620.md b/doc/release-notes-15620.md
deleted file mode 100644
index bf89a70a4e..0000000000
--- a/doc/release-notes-15620.md
+++ /dev/null
@@ -1,13 +0,0 @@
-Updated RPCs
-------------
-
-* The -maxtxfee setting no longer has any effect on non-wallet RPCs.
-
- The `sendrawtransaction` and `testmempoolaccept` RPC methods previously
- accepted an `allowhighfees` parameter to fail the mempool acceptance in case
- the transaction's fee would exceed the value of the command line argument
- `-maxtxfee`. To uncouple the RPCs from the global option, they now have a
- hardcoded default for the maximum transaction fee, that can be changed for
- both RPCs on a per-call basis with the `maxfeerate` parameter. The
- `allowhighfees` boolean option has been removed and replaced by the
- `maxfeerate` numeric option.
diff --git a/doc/release-notes-15637.md b/doc/release-notes-15637.md
deleted file mode 100644
index 048d5e7218..0000000000
--- a/doc/release-notes-15637.md
+++ /dev/null
@@ -1,3 +0,0 @@
-RPC changes
------------
-In getmempoolancestors, getmempooldescendants, getmempoolentry and getrawmempool RPCs, to be consistent with the returned value and other RPCs such as getrawtransaction, vsize has been added and size is now deprecated. size will only be returned if bitcoind is started with `-deprecatedrpc=size`. \ No newline at end of file
diff --git a/doc/release-notes-15730.md b/doc/release-notes-15730.md
deleted file mode 100644
index 7a4a60b1ee..0000000000
--- a/doc/release-notes-15730.md
+++ /dev/null
@@ -1,5 +0,0 @@
-RPC changes
------------
-The RPC `getwalletinfo` response now includes the `scanning` key with an object
-if there is a scanning in progress or `false` otherwise. Currently the object
-has the scanning duration and progress.
diff --git a/doc/release-notes-15849.md b/doc/release-notes-15849.md
deleted file mode 100644
index a1df31f250..0000000000
--- a/doc/release-notes-15849.md
+++ /dev/null
@@ -1,6 +0,0 @@
-Thread names in logs
---------------------
-
-On platforms supporting `thread_local`, log lines can be prefixed with the name
-of the thread that caused the log. To enable this behavior, use
-`-logthreadnames=1`.
diff --git a/doc/release-notes-15993.md b/doc/release-notes-15993.md
deleted file mode 100644
index 493c7126ee..0000000000
--- a/doc/release-notes-15993.md
+++ /dev/null
@@ -1,3 +0,0 @@
-Build system changes
---------------------
-The minimum supported miniUPnPc API version is set to 10. This keeps compatibility with Ubuntu 16.04 LTS and Debian 8 `libminiupnpc-dev` packages. Please note, on Debian this package is still vulnerable to [CVE-2017-8798](https://security-tracker.debian.org/tracker/CVE-2017-8798) (in jessie only) and [CVE-2017-1000494](https://security-tracker.debian.org/tracker/CVE-2017-1000494) (both in jessie and in stretch).
diff --git a/doc/release-notes-16185.md b/doc/release-notes-16185.md
new file mode 100644
index 0000000000..2567ebea40
--- /dev/null
+++ b/doc/release-notes-16185.md
@@ -0,0 +1,6 @@
+RPC changes
+-----------
+The `gettransaction` RPC now accepts a third (boolean) argument `verbose`. If
+set to `true`, a new `decoded` field will be added to the response containing
+the decoded transaction. This field is equivalent to RPC `decoderawtransaction`,
+or RPC `getrawtransaction` when `verbose` is passed.
diff --git a/doc/release-notes-16695.md b/doc/release-notes-16695.md
new file mode 100644
index 0000000000..7acf1dcf97
--- /dev/null
+++ b/doc/release-notes-16695.md
@@ -0,0 +1,5 @@
+Updated RPCs
+------------
+
+- The `getchaintxstats` RPC now returns the additional key of
+ `window_final_block_height`.
diff --git a/doc/release-notes-16787.md b/doc/release-notes-16787.md
new file mode 100644
index 0000000000..c42b7a5803
--- /dev/null
+++ b/doc/release-notes-16787.md
@@ -0,0 +1,3 @@
+RPC changes
+-----------
+The `getnetworkinfo` and `getpeerinfo` commands now contain a new field with decoded network service flags.
diff --git a/doc/release-notes.md b/doc/release-notes.md
index e0673ae7ee..04aab56a72 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -64,14 +64,51 @@ platform.
Notable changes
===============
+New user documentation
+----------------------
+
+- [Reduce memory](https://github.com/bitcoin/bitcoin/blob/master/doc/reduce-memory.md)
+ suggests configuration tweaks for running Bitcoin Core on systems with
+ limited memory. (#16339)
+
New RPCs
--------
- `getbalances` returns an object with all balances (`mine`,
`untrusted_pending` and `immature`). Please refer to the RPC help of
`getbalances` for details. The new RPC is intended to replace
- `getunconfirmedbalance` and the balance fields in `getwalletinfo`, as well as
- `getbalance`. The old calls may be removed in a future version.
+ `getbalance`, `getunconfirmedbalance`, and the balance fields in
+ `getwalletinfo`. These old calls and fields may be removed in a
+ future version. (#15930, #16239)
+
+- `setwalletflag` sets and unsets wallet flags that enable or disable
+ features specific to that existing wallet, such as the new
+ `avoid_reuse` feature documented elsewhere in these release notes.
+ (#13756)
+
+- `getblockfilter` gets the BIP158 filter for the specified block. This
+ RPC is only enabled if block filters have been created using the
+ `-blockfilterindex` configuration option. (#14121)
+
+New settings
+------------
+
+- `-blockfilterindex` enables the creation of BIP158 block filters for
+ the entire blockchain. Filters will be created in the background and
+ currently use about 4 GiB of space. Note: this version of Bitcoin
+ Core does not serve block filters over the P2P network, although the
+ local user may obtain block filters using the `getblockfilter` RPC.
+ (#14121)
+
+Updated settings
+----------------
+
+- `whitebind` and `whitelist` now accept a list of permissions to
+ provide peers connecting using the indicated interfaces or IP
+ addresses. If no permissions are specified with an address or CIDR
+ network, the implicit default permissions are the same as previous
+ releases. See the `bitcoind -help` output for these two options for
+ details about the available permissions. (#16248)
Updated RPCs
------------
@@ -79,21 +116,128 @@ Updated RPCs
Note: some low-level RPC changes mainly useful for testing are described in the
Low-level Changes section below.
-- The `sendmany` RPC had an argument `minconf` that was not well specified and
- would lead to RPC errors even when the wallet's coin selection would succeed.
- The `sendtoaddress` RPC never had this check, so to normalize the behavior,
- `minconf` is now ignored in `sendmany`. If the coin selection does not
- succeed due to missing coins, it will still throw an RPC error. Be reminded
- that coin selection is influenced by the `-spendzeroconfchange`,
- `-limitancestorcount`, `-limitdescendantcount` and `-walletrejectlongchains`
- command line arguments.
+- `sendmany` no longer has a `minconf` argument. This argument was not
+ well specified and would lead to RPC errors even when the wallet's
+ coin selection succeeded. Users who want to influence coin selection
+ can use the existing `-spendzeroconfchange`, `-limitancestorcount`,
+ `-limitdescendantcount` and `-walletrejectlongchains` configuration
+ arguments. (#15596)
+
+- `getbalance` and `sendtoaddress`, plus the new RPCs `getbalances` and
+ `createwallet`, now accept an "avoid_reuse" parameter that controls
+ whether already used addresses should be included in the operation.
+ Additionally, `sendtoaddress` will avoid partial spends when
+ `avoid_reuse` is enabled even if this feature is not already enabled
+ via the `-avoidpartialspends` command line flag because not doing so
+ would risk using up the "wrong" UTXO for an address reuse case.
+ (#13756)
+
+- `listunspent` now returns a "reused" bool for each output if the
+ wallet flag "avoid_reuse" is enabled. (#13756)
+
+- `getblockstats` now uses BlockUndo data instead of the transaction
+ index, making it much faster, no longer dependent on the `-txindex`
+ configuration option, and functional for all non-pruned blocks.
+ (#14802)
+
+- `utxoupdatepsbt` now accepts a `descriptors` parameter that will fill
+ out input and output scripts and keys when known. P2SH-witness inputs
+ will be filled in from the UTXO set when a descriptor is provided that
+ shows they're spending segwit outputs. See the RPC help text for full
+ details. (#15427)
+
+- `sendrawtransaction` and `testmempoolaccept` no longer accept a
+ `allowhighfees` parameter to fail mempool acceptance if the
+ transaction fee exceedes the value of the configuration option
+ `-maxtxfee`. Now there is a hardcoded default maximum feerate that
+ can be changed when calling either RPC using a `maxfeerate` parameter.
+ (#15620)
+
+- `getmempoolancestors`, `getmempooldescendants`, `getmempoolentry`, and
+ `getrawmempool` no longer return a `size` field unless the
+ configuration option `-deprecatedrpc=size` is used. Instead a new
+ `vsize` field is returned with the transaction's virtual size
+ (consistent with other RPCs such as `getrawtransaction`). (#15637)
+
+- `getwalletinfo` now includes a `scanning` field that is either `false`
+ (no scanning) or an object with information about the duration and
+ progress of the wallet's scanning historical blocks for transactions
+ affecting its balances. (#15730)
+
+- `createwallet` accepts a new `passphrase` parameter. If set, this
+ will create the new wallet encrypted with the given passphrase. If
+ unset (the default) or set to an empty string, no encryption will be
+ used. (#16394)
+
+- `getmempoolentry` now provides a `weight` field containing the
+ transaction weight as defined in BIP141. (#16647)
+
+- `getdescriptorinfo` now returns an additional `checksum` field
+ containing the checksum for the unmodified descriptor provided by the
+ user (that is, before the descriptor is normalized for the
+ `descriptor` field). (#15986)
+
+- `walletcreatefundedpsbt` now signals BIP125 Replace-by-Fee if the
+ `-walletrbf` configuration option is set to true. (#15911)
+
+GUI changes
+-----------
+
+- Provides bech32 addresses by default. The user may change the address
+ during invoice generation using a GUI toggle, or the default address
+ type may be changed by the `-addresstype` configuration option.
+ (#15711, #16497)
+
+Deprecated or removed configuration options
+-------------------------------------------
+
+- `-mempoolreplacement` is removed, although default node behavior
+ remains the same. This option previously allowed the user to prevent
+ the node from accepting or relaying BIP125 transaction replacements.
+ This is different from the remaining configuration option
+ `-walletrbf`. (#16171)
Deprecated or removed RPCs
--------------------------
-- The `totalFee` option of the `bumpfee` RPC has been deprecated and will be
- removed in 0.20. To continue using this option start with
- `-deprecatedrpc=totalFee`. See the `bumpfee` RPC help text for more details.
+- `bumpfee` no longer accepts a `totalFee` option unless the
+ configuration parameter `deprecatedrpc=totalFee` is specified. This
+ parameter will be fully removed in a subsequent release. (#15996)
+
+- `generate` is now removed after being deprecated in Bitcoin Core 0.18.
+ Use the `generatetoaddress` RPC instead. (#15492)
+
+P2P changes
+-----------
+
+- BIP 61 reject messages were deprecated in v0.18. They are now disabled
+ by default, but can be enabled by setting the `-enablebip61` command
+ line option. BIP 61 reject messages will be removed entirely in a
+ future version of Bitcoin Core. (#14054)
+
+- To eliminate well-known denial-of-service vectors in Bitcoin Core,
+ especially for nodes with spinning disks, the default value for the
+ `-peerbloomfilters` configuration option has been changed to false.
+ This prevents Bitcoin Core from sending the BIP111 NODE_BLOOM service
+ flag, accepting BIP37 bloom filters, or serving merkle blocks or
+ transactions matching a bloom filter. Users who still want to provide
+ bloom filter support may either set the configuration option to true
+ to re-enable both BIP111 and BIP37 support or enable just BIP37
+ support for specific peers using the updated `-whitelist` and
+ `-whitebind` configuration options described elsewhere in these
+ release notes. For the near future, lightweight clients using public
+ BIP111/BIP37 nodes should still be able to connect to older versions
+ of Bitcoin Core and nodes that have manually enabled BIP37 support,
+ but developers of such software should consider migrating to either
+ using specific BIP37 nodes or an alternative transaction filtering
+ system. (#16152)
+
+Miscellaneous CLI Changes
+-------------------------
+
+- The `testnet` field in `bitcoin-cli -getinfo` has been renamed to
+ `chain` and now returns the current network name as defined in BIP70
+ (main, test, regtest). (#15566)
Low-level changes
=================
@@ -101,24 +245,42 @@ Low-level changes
RPC
---
+- `getblockchaininfo` no longer returns a `bip9_softforks` object.
+ Instead, information has been moved into the `softforks` object and
+ an additional `type` field describes how Bitcoin Core determines
+ whether that soft fork is active (e.g. BIP9 or BIP90). See the RPC
+ help for details. (#16060)
+
+- `getblocktemplate` no longer returns a `rules` array containing `CSV`
+ and `segwit` (the BIP9 deployments that are currently in active
+ state). (#16060)
+
+- `getrpcinfo` now returns a `logpath` field with the path to
+ `debug.log`. (#15483)
Tests
-----
-- The regression test chain, that can be enabled by the `-regtest` command line
- flag, now requires transactions to not violate standard policy by default.
- Making the default the same as for mainnet, makes it easier to test mainnet
- behavior on regtest. Be reminded that the testnet still allows non-standard
- txs by default and that the policy can be locally adjusted with the
- `-acceptnonstdtxn` command line flag for both test chains.
+- The regression test chain enabled by the `-regtest` command line flag
+ now requires transactions to not violate standard policy by default.
+ This is the same default used for mainnet and makes it easier to test
+ mainnet behavior on regtest. Note that the testnet still allows
+ non-standard txs by default and that the policy can be locally
+ adjusted with the `-acceptnonstdtxn` command line flag for both test
+ chains. (#15891)
Configuration
------------
-- An error is issued where previously a warning was issued when a setting in
- the config file was specified in the default section, but not overridden for
- the selected network. This change takes only effect if the selected network
- is not mainnet.
+- A setting specified in the default section but not also specified in a
+ network-specific section (e.g. testnet) will now produce a error
+ preventing startup instead of just a warning unless the network is
+ mainnet. This prevents settings intended for mainnet from being
+ applied to testnet or regtest. (#15629)
+
+- On platforms supporting `thread_local`, log lines can be prefixed with
+ the name of the thread that caused the log. To enable this behavior,
+ use `-logthreadnames=1`. (#15849)
Network
-------
@@ -129,17 +291,81 @@ Network
peers' announcements were received. In this release, the download logic has
changed to randomize the fetch order across peers and to prefer sending
download requests to outbound peers over inbound peers. This fixes an issue
- where inbound peers can prevent a node from getting a transaction.
+ where inbound peers could prevent a node from getting a transaction.
+ (#14897, #15834)
+
+- If a Tor hidden service is being used, Bitcoin Core will be bound to
+ the standard port 8333 even if a different port is configured for
+ clearnet connections. This prevents leaking node identity through use
+ of identical non-default port numbers. (#15651)
+
+Mempool and transaction relay
+-----------------------------
+
+- Allows one extra single-ancestor transaction per package. Previously,
+ if a transaction in the mempool had 25 descendants, or it and all of
+ its descendants were over 101,000 vbytes, any newly-received
+ transaction that was also a descendant would be ignored. Now, one
+ extra descendant will be allowed provided it is an immediate
+ descendant (child) and the child's size is 10,000 vbytes or less.
+ This makes it possible for two-party contract protocols such as
+ Lightning Network to give each participant an output they can spend
+ immediately for Child-Pays-For-Parent (CPFP) fee bumping without
+ allowing one malicious participant to fill the entire package and thus
+ prevent the other participant from spending their output. (#15681)
+
+- Transactions with outputs paying v1 to v16 witness versions (future
+ segwit versions) are now accepted into the mempool, relayed, and
+ mined. Attempting to spend those outputs remains forbidden by policy
+ ("non-standard"). When this change has been widely deployed, wallets
+ and services can accept any valid bech32 Bitcoin address without
+ concern that transactions paying future segwit versions will become
+ stuck in an unconfirmed state. (#15846)
+
+- Legacy transactions (transactions with no segwit inputs) must now be
+ sent using the legacy encoding format, enforcing the rule specified in
+ BIP144. (#14039)
Wallet
------
- When in pruned mode, a rescan that was triggered by an `importwallet`,
- `importpubkey`, `importaddress`, or `importprivkey` RPC will only fail when
- blocks have been pruned. Previously it would fail when `-prune` has been set.
- This change allows to set `-prune` to a high value (e.g. the disk size) and
- the calls to any of the import RPCs would fail when the first block is
- pruned.
+ `importpubkey`, `importaddress`, or `importprivkey` RPC will only fail
+ when blocks have been pruned. Previously it would fail when `-prune`
+ has been set. This change allows setting `-prune` to a high value
+ (e.g. the disk size) without the calls to any of the import RPCs
+ failing until the first block is pruned. (#15870)
+
+- When creating a transaction with a fee above `-maxtxfee` (default 0.1
+ BTC), the RPC commands `walletcreatefundedpsbt` and
+ `fundrawtransaction` will now fail instead of rounding down the fee.
+ Be aware that the `feeRate` argument is specified in BTC per 1,000
+ vbytes, not satoshi per vbyte. (#16257)
+
+- A new wallet flag `avoid_reuse` has been added (default off). When
+ enabled, a wallet will distinguish between used and unused addresses,
+ and default to not use the former in coin selection. When setting
+ this flag on an existing wallet, rescanning the blockchain is required
+ to correctly mark previously used destinations. Together with "avoid
+ partial spends" (added in Bitcoin Core v0.17.0), this can eliminate a
+ serious privacy issue where a malicious user can track spends by
+ sending small payments to a previously-paid address that would then
+ be included with unrelated inputs in future payments. (#13756)
+
+Build system changes
+--------------------
+
+- Python >=3.5 is now required by all aspects of the project. This
+ includes the build systems, test framework and linters. The previously
+ supported minimum (3.4), was EOL in March 2019. (#14954)
+
+- The minimum supported miniUPnPc API version is set to 10. This keeps
+ compatibility with Ubuntu 16.04 LTS and Debian 8 `libminiupnpc-dev`
+ packages. Please note, on Debian this package is still vulnerable to
+ [CVE-2017-8798](https://security-tracker.debian.org/tracker/CVE-2017-8798)
+ (in jessie only) and
+ [CVE-2017-1000494](https://security-tracker.debian.org/tracker/CVE-2017-1000494)
+ (both in jessie and in stretch). (#15993)
Credits
=======
diff --git a/doc/release-notes/release-notes-0.18.1.md b/doc/release-notes/release-notes-0.18.1.md
new file mode 100644
index 0000000000..483cc5075e
--- /dev/null
+++ b/doc/release-notes/release-notes-0.18.1.md
@@ -0,0 +1,136 @@
+Bitcoin Core version 0.18.1 is now available from:
+
+ <https://bitcoincore.org/bin/bitcoin-core-0.18.1/>
+
+This is a new minor version release, including new features, various bug
+fixes and performance improvements, as well as updated translations.
+
+Please report bugs using the issue tracker at GitHub:
+
+ <https://github.com/bitcoin/bitcoin/issues>
+
+To receive security and update notifications, please subscribe to:
+
+ <https://bitcoincore.org/en/list/announcements/join/>
+
+How to Upgrade
+==============
+
+If you are running an older version, shut it down. Wait until it has
+completely shut down (which might take a few minutes for older
+versions), then run the installer (on Windows) or just copy over
+`/Applications/Bitcoin-Qt` (on Mac) or `bitcoind`/`bitcoin-qt` (on
+Linux).
+
+The first time you run version 0.15.0 or newer, your chainstate database
+will be converted to a new format, which will take anywhere from a few
+minutes to half an hour, depending on the speed of your machine.
+
+Note that the block database format also changed in version 0.8.0 and
+there is no automatic upgrade code from before version 0.8 to version
+0.15.0 or later. Upgrading directly from 0.7.x and earlier without
+redownloading the blockchain is not supported. However, as usual, old
+wallet versions are still supported.
+
+Compatibility
+==============
+
+Bitcoin Core is supported and extensively tested on operating systems
+using the Linux kernel, macOS 10.10+, and Windows 7 and newer. It is not
+recommended to use Bitcoin Core on unsupported systems.
+
+Bitcoin Core should also work on most other Unix-like systems but is not
+as frequently tested on them.
+
+From 0.17.0 onwards, macOS <10.10 is no longer supported. 0.17.0 is
+built using Qt 5.9.x, which doesn't support versions of macOS older than
+10.10. Additionally, Bitcoin Core does not yet change appearance when
+macOS "dark mode" is activated.
+
+Known issues
+============
+
+Wallet GUI
+----------
+
+For advanced users who have both (1) enabled coin control features, and
+(2) are using multiple wallets loaded at the same time: The coin control
+input selection dialog can erroneously retain wrong-wallet state when
+switching wallets using the dropdown menu. For now, it is recommended
+not to use coin control features with multiple wallets loaded.
+
+0.18.1 change log
+=================
+
+### P2P protocol and network code
+- #15990 Add tests and documentation for blocksonly (MarcoFalke)
+- #16021 Avoid logging transaction decode errors to stderr (MarcoFalke)
+- #16405 fix: tor: Call `event_base_loopbreak` from the event's callback (promag)
+- #16412 Make poll in InterruptibleRecv only filter for POLLIN events (tecnovert)
+
+### Wallet
+- #15913 Add -ignorepartialspends to list of ignored wallet options (luke-jr)
+
+### RPC and other APIs
+- #15991 Bugfix: fix pruneblockchain returned prune height (jonasschnelli)
+- #15899 Document iswitness flag and fix bug in converttopsbt (MarcoFalke)
+- #16026 Ensure that uncompressed public keys in a multisig always returns a legacy address (achow101)
+- #14039 Disallow extended encoding for non-witness transactions (sipa)
+- #16210 add 2nd arg to signrawtransactionwithkey examples (dooglus)
+- #16250 signrawtransactionwithkey: report error when missing redeemScript/witnessScript (ajtowns)
+
+### GUI
+- #16044 fix the bug of OPEN CONFIGURATION FILE on Mac (shannon1916)
+- #15957 Show "No wallets available" in open menu instead of nothing (meshcollider)
+- #16118 Enable open wallet menu on setWalletController (promag)
+- #16135 Set progressDialog to nullptr (promag)
+- #16231 Fix open wallet menu initialization order (promag)
+- #16254 Set `AA_EnableHighDpiScaling` attribute early (hebasto)
+- #16122 Enable console line edit on setClientModel (promag)
+- #16348 Assert QMetaObject::invokeMethod result (promag)
+
+### Build system
+- #15985 Add test for GCC bug 90348 (sipa)
+- #15947 Install bitcoin-wallet manpage (domob1812)
+- #15983 build with -fstack-reuse=none (MarcoFalke)
+
+### Tests and QA
+- #15826 Pure python EC (sipa)
+- #15893 Add test for superfluous witness record in deserialization (instagibbs)
+- #14818 Bugfix: test/functional/rpc_psbt: Remove check for specific error message that depends on uncertain assumptions (luke-jr)
+- #15831 Add test that addmultisigaddress fails for watchonly addresses (MarcoFalke)
+
+### Documentation
+- #15890 Remove text about txes always relayed from -whitelist (harding)
+
+### Miscellaneous
+- #16095 Catch by reference not value in wallettool (kristapsk)
+- #16205 Replace fprintf with tfm::format (MarcoFalke)
+
+Credits
+=======
+
+Thanks to everyone who directly contributed to this release:
+
+- Andrew Chow
+- Anthony Towns
+- Chris Moore
+- Daniel Kraft
+- David A. Harding
+- fanquake
+- Gregory Sanders
+- Hennadii Stepanov
+- John Newbery
+- Jonas Schnelli
+- João Barbosa
+- Kristaps Kaupe
+- Luke Dashjr
+- MarcoFalke
+- Michele Federici
+- Pieter Wuille
+- Samuel Dobson
+- shannon1916
+- tecnovert
+- Wladimir J. van der Laan
+
+As well as everyone that helped translating on [Transifex](https://www.transifex.com/projects/p/bitcoin/).
diff --git a/doc/release-notes/release-notes-16152.md b/doc/release-notes/release-notes-16152.md
deleted file mode 100644
index 9c77cb9ae5..0000000000
--- a/doc/release-notes/release-notes-16152.md
+++ /dev/null
@@ -1,7 +0,0 @@
-P2P Changes
------------
-- The default value for the -peerbloomfilters configuration option (and, thus, NODE_BLOOM support) has been changed to false.
- This resolves well-known DoS vectors in Bitcoin Core, especially for nodes with spinning disks. It is not anticipated that
- this will result in a significant lack of availability of NODE_BLOOM-enabled nodes in the coming years, however, clients
- which rely on the availability of NODE_BLOOM-supporting nodes on the P2P network should consider the process of migrating
- to a more modern (and less trustful and privacy-violating) alternative over the coming years.
diff --git a/doc/tor.md b/doc/tor.md
index cfb7f16666..2c54e32f84 100644
--- a/doc/tor.md
+++ b/doc/tor.md
@@ -114,7 +114,10 @@ preconfigured and the creation of a hidden service is automatic. If permission p
are seen with `-debug=tor` they can be resolved by adding both the user running Tor and
the user running bitcoind to the same group and setting permissions appropriately. On
Debian-based systems the user running bitcoind can be added to the debian-tor group,
-which has the appropriate permissions.
+which has the appropriate permissions. Before starting bitcoind you will need to re-login
+to allow debian-tor group to be applied. Otherwise you will see the following notice: "tor:
+Authentication cookie /run/tor/control.authcookie could not be opened (check permissions)"
+on debug.log.
An alternative authentication method is the use
of the `-torpassword=password` option. The `password` is the clear text form that
diff --git a/doc/translation_process.md b/doc/translation_process.md
index b9a10b6527..0e9245250f 100644
--- a/doc/translation_process.md
+++ b/doc/translation_process.md
@@ -65,13 +65,13 @@ username = USERNAME
The Transifex Bitcoin project config file is included as part of the repo. It can be found at `.tx/config`, however you shouldn’t need to change anything.
### Synchronising translations
-To assist in updating translations, we have created a script to help.
+To assist in updating translations, a helper script is available in the [maintainer-tools repo](https://github.com/bitcoin-core/bitcoin-maintainer-tools).
-1. `python contrib/devtools/update-translations.py`
+1. `python3 ../bitcoin-maintainer-tools/update-translations.py`
2. `git add` new translations from `src/qt/locale/`
3. Update `src/qt/bitcoin_locale.qrc` manually or via
```bash
-git ls-files src/qt/locale/*ts|xargs -n1 basename|sed 's/\(bitcoin_\(.*\)\).ts/<file alias="\2">locale\/\1.qm<\/file>/'
+git ls-files src/qt/locale/*ts|xargs -n1 basename|sed 's/\(bitcoin_\(.*\)\).ts/ <file alias="\2">locale\/\1.qm<\/file>/'
```
4. Update `src/Makefile.qt.include` manually or via
```bash
diff --git a/doc/zmq.md b/doc/zmq.md
index 7ffc5623b6..a309abd0cc 100644
--- a/doc/zmq.md
+++ b/doc/zmq.md
@@ -111,7 +111,9 @@ using other means such as firewalling.
Note that when the block chain tip changes, a reorganisation may occur
and just the tip will be notified. It is up to the subscriber to
-retrieve the chain from the last known block to the new tip.
+retrieve the chain from the last known block to the new tip. Also note
+that no notification occurs if the tip was in the active chain - this
+is the case after calling invalidateblock RPC.
There are several possibilities that ZMQ notification can get lost
during transmission depending on the communication type you are
diff --git a/share/examples/bitcoin.conf b/share/examples/bitcoin.conf
index b5475dc1c6..96fb6658a0 100644
--- a/share/examples/bitcoin.conf
+++ b/share/examples/bitcoin.conf
@@ -1,7 +1,7 @@
##
## bitcoin.conf configuration file. Lines beginning with # are comments.
##
-
+
# Network-related settings:
# Note that if you use testnet or regtest, particularly with the options
@@ -70,8 +70,8 @@
# server=1 tells Bitcoin-Qt and bitcoind to accept JSON-RPC commands
#server=0
-# Bind to given address to listen for JSON-RPC connections. Use [host]:port notation for IPv6.
-# This option can be specified multiple times (default: bind to all interfaces)
+# Bind to given address to listen for JSON-RPC connections.
+# Refer to the manpage or bitcoind -help for further details.
#rpcbind=<addr>
# If no rpcpassword is set, rpc cookie auth is sought. The default `-rpccookiefile` name
@@ -97,7 +97,7 @@
# rpcauth=bob:b2dd077cb54591a2f3139e69a897ac$4e71f08d48b4347cf8eff3815c0e25ae2e9a4340474079f55705f40574f4ec99
# How many seconds bitcoin will wait for a complete RPC HTTP request.
-# after the HTTP connection is established.
+# after the HTTP connection is established.
#rpcclienttimeout=30
# By default, only RPC connections from localhost are allowed.
@@ -108,7 +108,7 @@
# because the rpcpassword is transmitted over the network unencrypted.
# server=1 tells Bitcoin-Qt to accept JSON-RPC commands.
-# it is also read by bitcoind to determine if RPC should be enabled
+# it is also read by bitcoind to determine if RPC should be enabled
#rpcallowip=10.1.1.34/255.255.255.0
#rpcallowip=1.2.3.4/24
#rpcallowip=2001:db8:85a3:0:0:8a2e:370:7334/96
@@ -139,11 +139,11 @@
# both prior transactions and several dozen future transactions.
#keypool=100
-# Enable pruning to reduce storage requirements by deleting old blocks.
+# Enable pruning to reduce storage requirements by deleting old blocks.
# This mode is incompatible with -txindex and -rescan.
# 0 = default (no pruning).
# 1 = allows manual pruning via RPC.
-# >=550 = target to stay under in MiB.
+# >=550 = target to stay under in MiB.
#prune=550
# User interface options
diff --git a/share/qt/Info.plist.in b/share/qt/Info.plist.in
index 3cd130ce48..477985bb33 100644
--- a/share/qt/Info.plist.in
+++ b/share/qt/Info.plist.in
@@ -30,7 +30,7 @@
<key>CFBundleExecutable</key>
<string>Bitcoin-Qt</string>
-
+
<key>CFBundleName</key>
<string>Bitcoin-Qt</string>
@@ -99,7 +99,7 @@
<key>NSRequiresAquaSystemAppearance</key>
<string>True</string>
-
+
<key>LSApplicationCategoryType</key>
<string>public.app-category.finance</string>
</dict>
diff --git a/share/setup.nsi.in b/share/setup.nsi.in
index acf9e86667..e9aa1f2b73 100644
--- a/share/setup.nsi.in
+++ b/share/setup.nsi.in
@@ -101,7 +101,7 @@ Section -post SEC0001
WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayVersion "@PACKAGE_VERSION@"
WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" Publisher "${COMPANY}"
WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" URLInfoAbout "${URL}"
- WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayIcon $INSTDIR\uninstall.exe
+ WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayIcon $INSTDIR\bitcoin-qt.exe
WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" UninstallString $INSTDIR\uninstall.exe
WriteRegDWORD HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" NoModify 1
WriteRegDWORD HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" NoRepair 1
diff --git a/src/Makefile.am b/src/Makefile.am
index ef5b1900d9..8fc7f61d4b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -150,11 +150,13 @@ BITCOIN_CORE_H = \
merkleblock.h \
miner.h \
net.h \
+ net_permissions.h \
net_processing.h \
netaddress.h \
netbase.h \
netmessagemaker.h \
node/coin.h \
+ node/coinstats.h \
node/psbt.h \
node/transaction.h \
noui.h \
@@ -210,6 +212,7 @@ BITCOIN_CORE_H = \
util/memory.h \
util/moneystr.h \
util/rbf.h \
+ util/string.h \
util/threadnames.h \
util/time.h \
util/translation.h \
@@ -276,6 +279,7 @@ libbitcoin_server_a_SOURCES = \
net.cpp \
net_processing.cpp \
node/coin.cpp \
+ node/coinstats.cpp \
node/psbt.cpp \
node/transaction.cpp \
noui.cpp \
@@ -454,6 +458,7 @@ libbitcoin_common_a_SOURCES = \
merkleblock.cpp \
netaddress.cpp \
netbase.cpp \
+ net_permissions.cpp \
outputtype.cpp \
policy/feerate.cpp \
policy/policy.cpp \
@@ -499,6 +504,7 @@ libbitcoin_util_a_SOURCES = \
util/rbf.cpp \
util/threadnames.cpp \
util/strencodings.cpp \
+ util/string.cpp \
util/time.cpp \
util/url.cpp \
util/validation.cpp \
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index 3ae8498a87..7540122418 100644
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -98,6 +98,7 @@ QT_FORMS_UI = \
qt/forms/addressbookpage.ui \
qt/forms/askpassphrasedialog.ui \
qt/forms/coincontroldialog.ui \
+ qt/forms/createwalletdialog.ui \
qt/forms/editaddressdialog.ui \
qt/forms/helpmessagedialog.ui \
qt/forms/intro.ui \
@@ -117,6 +118,7 @@ QT_MOC_CPP = \
qt/moc_addressbookpage.cpp \
qt/moc_addresstablemodel.cpp \
qt/moc_askpassphrasedialog.cpp \
+ qt/moc_createwalletdialog.cpp \
qt/moc_bantablemodel.cpp \
qt/moc_bitcoinaddressvalidator.cpp \
qt/moc_bitcoinamountfield.cpp \
@@ -202,6 +204,7 @@ BITCOIN_QT_H = \
qt/clientmodel.h \
qt/coincontroldialog.h \
qt/coincontroltreewidget.h \
+ qt/createwalletdialog.h \
qt/csvmodelwriter.h \
qt/editaddressdialog.h \
qt/guiconstants.h \
@@ -250,8 +253,6 @@ BITCOIN_QT_H = \
RES_ICONS = \
qt/res/icons/add.png \
qt/res/icons/address-book.png \
- qt/res/icons/about.png \
- qt/res/icons/about_qt.png \
qt/res/icons/bitcoin.ico \
qt/res/icons/bitcoin_testnet.ico \
qt/res/icons/bitcoin.png \
@@ -261,13 +262,11 @@ RES_ICONS = \
qt/res/icons/clock3.png \
qt/res/icons/clock4.png \
qt/res/icons/clock5.png \
- qt/res/icons/configure.png \
qt/res/icons/connect0.png \
qt/res/icons/connect1.png \
qt/res/icons/connect2.png \
qt/res/icons/connect3.png \
qt/res/icons/connect4.png \
- qt/res/icons/debugwindow.png \
qt/res/icons/edit.png \
qt/res/icons/editcopy.png \
qt/res/icons/editpaste.png \
@@ -275,21 +274,16 @@ RES_ICONS = \
qt/res/icons/eye.png \
qt/res/icons/eye_minus.png \
qt/res/icons/eye_plus.png \
- qt/res/icons/filesave.png \
qt/res/icons/fontbigger.png \
qt/res/icons/fontsmaller.png \
qt/res/icons/hd_disabled.png \
qt/res/icons/hd_enabled.png \
qt/res/icons/history.png \
- qt/res/icons/info.png \
- qt/res/icons/key.png \
qt/res/icons/lock_closed.png \
qt/res/icons/lock_open.png \
qt/res/icons/network_disabled.png \
- qt/res/icons/open.png \
qt/res/icons/overview.png \
qt/res/icons/proxy.png \
- qt/res/icons/quit.png \
qt/res/icons/receive.png \
qt/res/icons/remove.png \
qt/res/icons/send.png \
@@ -302,8 +296,7 @@ RES_ICONS = \
qt/res/icons/tx_input.png \
qt/res/icons/tx_output.png \
qt/res/icons/tx_mined.png \
- qt/res/icons/warning.png \
- qt/res/icons/verify.png
+ qt/res/icons/warning.png
BITCOIN_QT_BASE_CPP = \
qt/bantablemodel.cpp \
@@ -338,6 +331,7 @@ BITCOIN_QT_WALLET_CPP = \
qt/askpassphrasedialog.cpp \
qt/coincontroldialog.cpp \
qt/coincontroltreewidget.cpp \
+ qt/createwalletdialog.cpp \
qt/editaddressdialog.cpp \
qt/openuridialog.cpp \
qt/overviewpage.cpp \
diff --git a/src/bech32.cpp b/src/bech32.cpp
index d6b29391a9..4c966350b4 100644
--- a/src/bech32.cpp
+++ b/src/bech32.cpp
@@ -4,6 +4,8 @@
#include <bech32.h>
+#include <assert.h>
+
namespace
{
@@ -58,7 +60,7 @@ uint32_t PolyMod(const data& v)
// During the course of the loop below, `c` contains the bitpacked coefficients of the
// polynomial constructed from just the values of v that were processed so far, mod g(x). In
- // the above example, `c` initially corresponds to 1 mod (x), and after processing 2 inputs of
+ // the above example, `c` initially corresponds to 1 mod g(x), and after processing 2 inputs of
// v, it corresponds to x^2 + v0*x + v1 mod g(x). As 1 mod g(x) = 1, that is the starting value
// for `c`.
uint32_t c = 1;
@@ -145,6 +147,10 @@ namespace bech32
/** Encode a Bech32 string. */
std::string Encode(const std::string& hrp, const data& values) {
+ // First ensure that the HRP is all lowercase. BIP-173 requires an encoder
+ // to return a lowercase Bech32 string, but if given an uppercase HRP, the
+ // result will always be invalid.
+ for (const char& c : hrp) assert(c < 'A' || c > 'Z');
data checksum = CreateChecksum(hrp, values);
data combined = Cat(values, checksum);
std::string ret = hrp + '1';
diff --git a/src/bech32.h b/src/bech32.h
index 2e2823e974..fb39cd352b 100644
--- a/src/bech32.h
+++ b/src/bech32.h
@@ -19,7 +19,7 @@
namespace bech32
{
-/** Encode a Bech32 string. Returns the empty string in case of failure. */
+/** Encode a Bech32 string. If hrp contains uppercase characters, this will cause an assertion error. */
std::string Encode(const std::string& hrp, const std::vector<uint8_t>& values);
/** Decode a Bech32 string. Returns (hrp, data). Empty hrp means failure. */
diff --git a/src/bench/bench_bitcoin.cpp b/src/bench/bench_bitcoin.cpp
index 8eea96d930..d0d7c03ee1 100644
--- a/src/bench/bench_bitcoin.cpp
+++ b/src/bench/bench_bitcoin.cpp
@@ -21,14 +21,14 @@ static void SetupBenchArgs()
{
SetupHelpOptions(gArgs);
- 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);
+ gArgs.AddArg("-list", "List benchmarks without executing them. Can be combined with -scaling and -filter", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-evals=<n>", strprintf("Number of measurement evaluations to perform. (default: %u)", DEFAULT_BENCH_EVALUATIONS), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-filter=<regex>", strprintf("Regular expression filter to select benchmark by name (default: %s)", DEFAULT_BENCH_FILTER), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-scaling=<n>", strprintf("Scaling factor for benchmark's runtime (default: %u)", DEFAULT_BENCH_SCALING), ArgsManager::ALLOW_ANY, 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), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-plot-plotlyurl=<uri>", strprintf("URL to use for plotly.js (default: %s)", DEFAULT_PLOT_PLOTLYURL), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-plot-width=<x>", strprintf("Plot width in pixel (default: %u)", DEFAULT_PLOT_WIDTH), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-plot-height=<x>", strprintf("Plot height in pixel (default: %u)", DEFAULT_PLOT_HEIGHT), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
}
int main(int argc, char** argv)
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index 8ca985458d..a6756fcce7 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -43,22 +43,22 @@ static void SetupCliArgs()
const auto testnetBaseParams = CreateBaseChainParams(CBaseChainParams::TESTNET);
const auto regtestBaseParams = CreateBaseChainParams(CBaseChainParams::REGTEST);
- gArgs.AddArg("-version", "Print version and exit", 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);
+ gArgs.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-conf=<file>", strprintf("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY, 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)", ArgsManager::ALLOW_ANY, 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("-rpccookiefile=<loc>", "Location of the auth cookie. Relative paths will be prefixed by a net-specific datadir location. (default: data dir)", 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, testnet: %u, regtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), regtestBaseParams->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). This changes the RPC endpoint used, e.g. http://127.0.0.1:8332/wallet/<walletname>", 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", "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);
+ gArgs.AddArg("-named", strprintf("Pass named instead of positional arguments (default: %s)", DEFAULT_NAMED), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-rpcclienttimeout=<n>", strprintf("Timeout in seconds during HTTP requests, or 0 for no timeout. (default: %d)", DEFAULT_HTTP_CLIENT_TIMEOUT), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-rpcconnect=<ip>", strprintf("Send commands to node running on <ip> (default: %s)", DEFAULT_RPCCONNECT), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-rpccookiefile=<loc>", "Location of the auth cookie. Relative paths will be prefixed by a net-specific datadir location. (default: data dir)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-rpcpassword=<pw>", "Password for JSON-RPC connections", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-rpcport=<port>", strprintf("Connect to JSON-RPC on <port> (default: %u, testnet: %u, regtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), regtestBaseParams->RPCPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-rpcuser=<user>", "Username for JSON-RPC connections", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-rpcwait", "Wait for RPC server to start", ArgsManager::ALLOW_ANY, 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). This changes the RPC endpoint used, e.g. http://127.0.0.1:8332/wallet/<walletname>", ArgsManager::ALLOW_ANY, 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.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-stdinrpcpass", "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.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
}
/** libevent event log callback */
@@ -125,7 +125,7 @@ static int AppInitRPC(int argc, char* argv[])
}
return EXIT_SUCCESS;
}
- if (!fs::is_directory(GetDataDir(false))) {
+ if (!CheckDataDirOption()) {
tfm::format(std::cerr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "").c_str());
return EXIT_FAILURE;
}
@@ -133,7 +133,7 @@ static int AppInitRPC(int argc, char* argv[])
tfm::format(std::cerr, "Error reading configuration file: %s\n", error.c_str());
return EXIT_FAILURE;
}
- // Check for -testnet or -regtest parameter (BaseParams() calls are only valid after this clause)
+ // Check for -chain, -testnet or -regtest parameter (BaseParams() calls are only valid after this clause)
try {
SelectBaseParams(gArgs.GetChainName());
} catch (const std::exception& e) {
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index 89e2ab305b..88219f0d0f 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -40,36 +40,36 @@ static void SetupBitcoinTxArgs()
{
SetupHelpOptions(gArgs);
- 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);
+ gArgs.AddArg("-create", "Create new, empty TX.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-json", "Select JSON output", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-txid", "Output only the hex-encoded transaction id of the resultant transaction.", ArgsManager::ALLOW_ANY, 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("delin=N", "Delete input N from TX", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
+ gArgs.AddArg("delout=N", "Delete output N from TX", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
+ gArgs.AddArg("in=TXID:VOUT(:SEQUENCE_NUMBER)", "Add input to TX", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
+ gArgs.AddArg("locktime=N", "Set TX lock time to N", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
+ gArgs.AddArg("nversion=N", "Set TX version to N", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
+ gArgs.AddArg("outaddr=VALUE:ADDRESS", "Add address-based output to TX", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
+ gArgs.AddArg("outdata=[VALUE:]DATA", "Add data-based output to TX", ArgsManager::ALLOW_ANY, 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);
+ "Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash.", ArgsManager::ALLOW_ANY, 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);
+ "Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash.", ArgsManager::ALLOW_ANY, 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);
+ "Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash.", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
+ gArgs.AddArg("replaceable(=N)", "Set RBF opt-in sequence number for input N (if not provided, opt-in all available inputs)", ArgsManager::ALLOW_ANY, 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 signrawtransactionwithkey docs for format of sighash flags, JSON objects.", false, OptionsCategory::COMMANDS);
+ "See signrawtransactionwithkey docs for format of sighash flags, JSON objects.", ArgsManager::ALLOW_ANY, 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);
+ gArgs.AddArg("load=NAME:FILENAME", "Load JSON file FILENAME into register NAME", ArgsManager::ALLOW_ANY, OptionsCategory::REGISTER_COMMANDS);
+ gArgs.AddArg("set=NAME:JSON-STRING", "Set register NAME to given JSON-STRING", ArgsManager::ALLOW_ANY, OptionsCategory::REGISTER_COMMANDS);
}
//
@@ -88,7 +88,7 @@ static int AppInitRawTx(int argc, char* argv[])
return EXIT_FAILURE;
}
- // Check for -testnet or -regtest parameter (Params() calls are only valid after this clause)
+ // Check for -chain, -testnet or -regtest parameter (Params() calls are only valid after this clause)
try {
SelectParams(gArgs.GetChainName());
} catch (const std::exception& e) {
diff --git a/src/bitcoin-wallet.cpp b/src/bitcoin-wallet.cpp
index a690e2facb..eb7f0098ec 100644
--- a/src/bitcoin-wallet.cpp
+++ b/src/bitcoin-wallet.cpp
@@ -24,13 +24,13 @@ static void SetupWalletToolArgs()
SetupHelpOptions(gArgs);
SetupChainParamsBaseOptions();
- gArgs.AddArg("-datadir=<dir>", "Specify data directory", false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-wallet=<wallet-name>", "Specify wallet name", false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-debug=<category>", "Output debugging information (default: 0).", false, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("-printtoconsole", "Send trace/debug info to console (default: 1 when no -debug is true, 0 otherwise.", false, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-wallet=<wallet-name>", "Specify wallet name", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-debug=<category>", "Output debugging information (default: 0).", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-printtoconsole", "Send trace/debug info to console (default: 1 when no -debug is true, 0 otherwise).", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("info", "Get wallet info", false, OptionsCategory::COMMANDS);
- gArgs.AddArg("create", "Create new wallet file", false, OptionsCategory::COMMANDS);
+ gArgs.AddArg("info", "Get wallet info", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
+ gArgs.AddArg("create", "Create new wallet file", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
}
static bool WalletAppInit(int argc, char* argv[])
@@ -57,7 +57,7 @@ static bool WalletAppInit(int argc, char* argv[])
// check for printtoconsole, allow -debug
LogInstance().m_print_to_console = gArgs.GetBoolArg("-printtoconsole", gArgs.GetBoolArg("-debug", false));
- if (!fs::is_directory(GetDataDir(false))) {
+ if (!CheckDataDirOption()) {
tfm::format(std::cerr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "").c_str());
return false;
}
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index 8e31f6e32b..83de684a2b 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2018 The Bitcoin Core developers
+// Copyright (c) 2009-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -77,7 +77,7 @@ static bool AppInit(int argc, char* argv[])
// Process help and version before taking care about datadir
if (HelpRequested(gArgs) || gArgs.IsArgSet("-version")) {
- std::string strUsage = PACKAGE_NAME " Daemon version " + FormatFullVersion() + "\n";
+ std::string strUsage = PACKAGE_NAME " version " + FormatFullVersion() + "\n";
if (gArgs.IsArgSet("-version"))
{
@@ -85,7 +85,7 @@ static bool AppInit(int argc, char* argv[])
}
else
{
- strUsage += "\nUsage: bitcoind [options] Start " PACKAGE_NAME " Daemon\n";
+ strUsage += "\nUsage: bitcoind [options] Start " PACKAGE_NAME "\n";
strUsage += "\n" + gArgs.GetHelpMessage();
}
@@ -95,14 +95,13 @@ static bool AppInit(int argc, char* argv[])
try
{
- if (!fs::is_directory(GetDataDir(false)))
- {
+ if (!CheckDataDirOption()) {
return InitError(strprintf("Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "")));
}
if (!gArgs.ReadConfigFiles(error, true)) {
return InitError(strprintf("Error reading configuration file: %s\n", error));
}
- // Check for -testnet or -regtest parameter (Params() calls are only valid after this clause)
+ // Check for -chain, -testnet or -regtest parameter (Params() calls are only valid after this clause)
try {
SelectParams(gArgs.GetChainName());
} catch (const std::exception& e) {
@@ -143,7 +142,7 @@ static bool AppInit(int argc, char* argv[])
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
- tfm::format(std::cout, PACKAGE_NAME " daemon starting\n");
+ tfm::format(std::cout, PACKAGE_NAME " starting\n");
// Daemonize
if (daemon(1, 0)) { // don't chdir (1), do close FDs (0)
diff --git a/src/chain.h b/src/chain.h
index dd9cc2a598..1b67ebbe41 100644
--- a/src/chain.h
+++ b/src/chain.h
@@ -95,8 +95,8 @@ enum BlockStatus: uint32_t {
//! Unused.
BLOCK_VALID_UNKNOWN = 0,
- //! Parsed, version ok, hash satisfies claimed PoW, 1 <= vtx count <= max, timestamp not in future
- BLOCK_VALID_HEADER = 1,
+ //! Reserved (was BLOCK_VALID_HEADER).
+ BLOCK_VALID_RESERVED = 1,
//! All parent headers found, difficulty matches, timestamp >= median previous, checkpoint. Implies all parents
//! are also at least TREE.
@@ -117,7 +117,7 @@ enum BlockStatus: uint32_t {
BLOCK_VALID_SCRIPTS = 5,
//! All validity bits.
- BLOCK_VALID_MASK = BLOCK_VALID_HEADER | BLOCK_VALID_TREE | BLOCK_VALID_TRANSACTIONS |
+ BLOCK_VALID_MASK = BLOCK_VALID_RESERVED | BLOCK_VALID_TREE | BLOCK_VALID_TRANSACTIONS |
BLOCK_VALID_CHAIN | BLOCK_VALID_SCRIPTS,
BLOCK_HAVE_DATA = 8, //!< full block available in blk*.dat
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index c24234aeb7..ad766471dc 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -69,6 +69,8 @@ public:
consensus.BIP34Hash = uint256S("0x000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8");
consensus.BIP65Height = 388381; // 000000000000000004c2b624ed5d7756c508d90fd0da2c7c679febfa6c4735f0
consensus.BIP66Height = 363725; // 00000000000000000379eaa19dce8c9b722d46ae6a57c2f1a988119488b50931
+ consensus.CSVHeight = 419328; // 000000000000000004a1b34462cb8aeebd5799177f7a29cf28f2d1961716b5b5
+ consensus.SegwitHeight = 481824; // 0000000000000000001c8018d9cb3b742ef25114f27563e3fc4a1902167f9893
consensus.powLimit = uint256S("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks
consensus.nPowTargetSpacing = 10 * 60;
@@ -80,16 +82,6 @@ public:
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 1199145601; // January 1, 2008
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 1230767999; // December 31, 2008
- // Deployment of BIP68, BIP112, and BIP113.
- consensus.vDeployments[Consensus::DEPLOYMENT_CSV].bit = 0;
- consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nStartTime = 1462060800; // May 1st, 2016
- consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nTimeout = 1493596800; // May 1st, 2017
-
- // Deployment of SegWit (BIP141, BIP143, and BIP147)
- consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].bit = 1;
- consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nStartTime = 1479168000; // November 15th, 2016.
- consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout = 1510704000; // November 15th, 2017.
-
// The best chain should have at least this much work.
consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000051dc8b82f450202ecb3d471");
@@ -183,6 +175,8 @@ public:
consensus.BIP34Hash = uint256S("0x0000000023b3a96d3484e5abb3755c413e7d41500f8e2a5c3f0dd01299cd8ef8");
consensus.BIP65Height = 581885; // 00000000007f6655f22f98e72ed80d8b06dc761d5da09df0fa1dc4be4f861eb6
consensus.BIP66Height = 330776; // 000000002104c8c45e99a8853285a3b592602a3ccde2b832481da85e9e4ba182
+ consensus.CSVHeight = 770112; // 00000000025e930139bac5c6c31a403776da130831ab85be56578f3fa75369bb
+ consensus.SegwitHeight = 834624; // 00000000002b980fcd729daaa248fd9316a5200e9b367f4ff2c42453e84201ca
consensus.powLimit = uint256S("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks
consensus.nPowTargetSpacing = 10 * 60;
@@ -194,16 +188,6 @@ public:
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 1199145601; // January 1, 2008
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 1230767999; // December 31, 2008
- // Deployment of BIP68, BIP112, and BIP113.
- consensus.vDeployments[Consensus::DEPLOYMENT_CSV].bit = 0;
- consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nStartTime = 1456790400; // March 1st, 2016
- consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nTimeout = 1493596800; // May 1st, 2017
-
- // Deployment of SegWit (BIP141, BIP143, and BIP147)
- consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].bit = 1;
- consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nStartTime = 1462060800; // May 1st 2016
- consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout = 1493596800; // May 1st 2017
-
// The best chain should have at least this much work.
consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000000000007dbe94253893cbd463");
@@ -275,6 +259,8 @@ public:
consensus.BIP34Hash = uint256();
consensus.BIP65Height = 1351; // BIP65 activated on regtest (Used in functional tests)
consensus.BIP66Height = 1251; // BIP66 activated on regtest (Used in functional tests)
+ consensus.CSVHeight = 432; // CSV activated on regtest (Used in rpc activation tests)
+ consensus.SegwitHeight = 0; // SEGWIT is always activated on regtest unless overridden
consensus.powLimit = uint256S("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks
consensus.nPowTargetSpacing = 10 * 60;
@@ -285,12 +271,6 @@ public:
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28;
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 0;
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
- consensus.vDeployments[Consensus::DEPLOYMENT_CSV].bit = 0;
- consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nStartTime = 0;
- consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
- consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].bit = 1;
- consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE;
- consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
// The best chain should have at least this much work.
consensus.nMinimumChainWork = uint256S("0x00");
@@ -307,7 +287,7 @@ public:
m_assumed_blockchain_size = 0;
m_assumed_chain_state_size = 0;
- UpdateVersionBitsParametersFromArgs(args);
+ UpdateActivationParametersFromArgs(args);
genesis = CreateGenesisBlock(1296688602, 2, 0x207fffff, 1, 50 * COIN);
consensus.hashGenesisBlock = genesis.GetHash();
@@ -350,11 +330,22 @@ public:
consensus.vDeployments[d].nStartTime = nStartTime;
consensus.vDeployments[d].nTimeout = nTimeout;
}
- void UpdateVersionBitsParametersFromArgs(const ArgsManager& args);
+ void UpdateActivationParametersFromArgs(const ArgsManager& args);
};
-void CRegTestParams::UpdateVersionBitsParametersFromArgs(const ArgsManager& args)
+void CRegTestParams::UpdateActivationParametersFromArgs(const ArgsManager& args)
{
+ if (gArgs.IsArgSet("-segwitheight")) {
+ int64_t height = gArgs.GetArg("-segwitheight", consensus.SegwitHeight);
+ if (height < -1 || height >= std::numeric_limits<int>::max()) {
+ throw std::runtime_error(strprintf("Activation height %ld for segwit is out of valid range. Use -1 to disable segwit.", height));
+ } else if (height == -1) {
+ LogPrintf("Segwit disabled for testing\n");
+ height = std::numeric_limits<int>::max();
+ }
+ consensus.SegwitHeight = static_cast<int>(height);
+ }
+
if (!args.IsArgSet("-vbparams")) return;
for (const std::string& strDeployment : args.GetArgs("-vbparams")) {
diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp
index f0559a319a..4bb66c8d8b 100644
--- a/src/chainparamsbase.cpp
+++ b/src/chainparamsbase.cpp
@@ -17,10 +17,12 @@ const std::string CBaseChainParams::REGTEST = "regtest";
void SetupChainParamsBaseOptions()
{
+ gArgs.AddArg("-chain=<chain>", "Use the chain <chain> (default: main). Allowed values: main, test, regtest", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
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);
- gArgs.AddArg("-vbparams=deployment:start:end", "Use given start/end times for specified version bits deployment (regtest-only)", true, OptionsCategory::CHAINPARAMS);
+ "This is intended for regression testing tools and app development. Equivalent to -chain=regtest.", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
+ gArgs.AddArg("-segwitheight=<n>", "Set the activation height of segwit. -1 to disable. (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-testnet", "Use the test chain. Equivalent to -chain=test.", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
+ gArgs.AddArg("-vbparams=deployment:start:end", "Use given start/end times for specified version bits deployment (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
}
static std::unique_ptr<CBaseChainParams> globalChainBaseParams;
diff --git a/src/consensus/params.h b/src/consensus/params.h
index 6c3a201f4f..8263b0fef4 100644
--- a/src/consensus/params.h
+++ b/src/consensus/params.h
@@ -16,8 +16,6 @@ namespace Consensus {
enum DeploymentPos
{
DEPLOYMENT_TESTDUMMY,
- DEPLOYMENT_CSV, // Deployment of BIP68, BIP112, and BIP113.
- DEPLOYMENT_SEGWIT, // Deployment of BIP141, BIP143, and BIP147.
// NOTE: Also add new deployments to VersionBitsDeploymentInfo in versionbits.cpp
MAX_VERSION_BITS_DEPLOYMENTS
};
@@ -58,6 +56,12 @@ struct Params {
int BIP65Height;
/** Block height at which BIP66 becomes active */
int BIP66Height;
+ /** Block height at which CSV (BIP68, BIP112 and BIP113) becomes active */
+ int CSVHeight;
+ /** Block height at which Segwit (BIP141, BIP143 and BIP147) becomes active.
+ * Note that segwit v0 script rules are enforced on all blocks except the
+ * BIP 16 exception blocks. */
+ int SegwitHeight;
/**
* Minimum blocks including miner confirmation of the total of 2016 blocks in a retargeting period,
* (nPowTargetTimespan / nPowTargetSpacing) which is also used for BIP9 deployments.
diff --git a/src/consensus/tx_check.cpp b/src/consensus/tx_check.cpp
index 23ed3ecb53..00ebbbd1ab 100644
--- a/src/consensus/tx_check.cpp
+++ b/src/consensus/tx_check.cpp
@@ -18,7 +18,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fChe
if (::GetSerializeSize(tx, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT)
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-oversize");
- // Check for negative or overflow output values
+ // Check for negative or overflow output values (see CVE-2010-5139)
CAmount nValueOut = 0;
for (const auto& txout : tx.vout)
{
diff --git a/src/core_write.cpp b/src/core_write.cpp
index 4d64446d7b..7ce2a49836 100644
--- a/src/core_write.cpp
+++ b/src/core_write.cpp
@@ -144,7 +144,7 @@ void ScriptToUniv(const CScript& script, UniValue& out, bool include_address)
out.pushKV("type", GetTxnOutputType(type));
CTxDestination address;
- if (include_address && ExtractDestination(script, address)) {
+ if (include_address && ExtractDestination(script, address) && type != TX_PUBKEY) {
out.pushKV("address", EncodeDestination(address));
}
}
@@ -160,7 +160,7 @@ void ScriptPubKeyToUniv(const CScript& scriptPubKey,
if (fIncludeHex)
out.pushKV("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end()));
- if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired)) {
+ if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired) || type == TX_PUBKEY) {
out.pushKV("type", GetTxnOutputType(type));
return;
}
diff --git a/src/dummywallet.cpp b/src/dummywallet.cpp
index eeec6dec25..126e3479f3 100644
--- a/src/dummywallet.cpp
+++ b/src/dummywallet.cpp
@@ -5,8 +5,10 @@
#include <stdio.h>
#include <util/system.h>
#include <walletinitinterface.h>
+#include <support/allocators/secure.h>
class CWallet;
+enum class WalletCreationStatus;
namespace interfaces {
class Chain;
@@ -74,6 +76,11 @@ std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string&
throw std::logic_error("Wallet function called in non-wallet build.");
}
+WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, std::string& error, std::string& warning, std::shared_ptr<CWallet>& result)
+{
+ throw std::logic_error("Wallet function called in non-wallet build.");
+}
+
namespace interfaces {
class Wallet;
diff --git a/src/init.cpp b/src/init.cpp
index b84c7dc93d..bb82130542 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -15,7 +15,6 @@
#include <blockfilter.h>
#include <chain.h>
#include <chainparams.h>
-#include <coins.h>
#include <compat/sanity.h>
#include <consensus/validation.h>
#include <fs.h>
@@ -27,6 +26,7 @@
#include <key.h>
#include <miner.h>
#include <net.h>
+#include <net_permissions.h>
#include <net_processing.h>
#include <netbase.h>
#include <policy/feerate.h>
@@ -149,7 +149,6 @@ NODISCARD static bool CreatePidFile()
// shutdown thing.
//
-static std::unique_ptr<CCoinsViewErrorCatcher> pcoinscatcher;
static std::unique_ptr<ECCVerifyHandle> globalVerifyHandle;
static boost::thread_group threadGroup;
@@ -234,8 +233,14 @@ void Shutdown(InitInterfaces& interfaces)
}
// FlushStateToDisk generates a ChainStateFlushed callback, which we should avoid missing
- if (pcoinsTip != nullptr) {
- ::ChainstateActive().ForceFlushStateToDisk();
+ //
+ // g_chainstate is referenced here directly (instead of ::ChainstateActive()) because it
+ // may not have been initialized yet.
+ {
+ LOCK(cs_main);
+ if (g_chainstate && g_chainstate->CanFlushToDisk()) {
+ g_chainstate->ForceFlushStateToDisk();
+ }
}
// After there are no more peers/RPC left to give us new data which may generate
@@ -250,12 +255,10 @@ void Shutdown(InitInterfaces& interfaces)
{
LOCK(cs_main);
- if (pcoinsTip != nullptr) {
- ::ChainstateActive().ForceFlushStateToDisk();
+ if (g_chainstate && g_chainstate->CanFlushToDisk()) {
+ g_chainstate->ForceFlushStateToDisk();
+ g_chainstate->ResetCoinsViews();
}
- pcoinsTip.reset();
- pcoinscatcher.reset();
- pcoinsdbview.reset();
pblocktree.reset();
}
for (const auto& client : interfaces.chain_clients) {
@@ -338,7 +341,7 @@ static void OnRPCStopped()
void SetupServerArgs()
{
SetupHelpOptions(gArgs);
- gArgs.AddArg("-help-debug", "Print help message with debugging options and exit", false, OptionsCategory::DEBUG_TEST); // server-only for now
+ gArgs.AddArg("-help-debug", "Print help message with debugging options and exit", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); // server-only for now
const auto defaultBaseParams = CreateBaseChainParams(CBaseChainParams::MAIN);
const auto testnetBaseParams = CreateBaseChainParams(CBaseChainParams::TESTNET);
@@ -353,103 +356,111 @@ void SetupServerArgs()
// GUI args. These will be overwritten by SetupUIArgs for the GUI
"-allowselfsignedrootcertificates", "-choosedatadir", "-lang=<lang>", "-min", "-resetguisettings", "-rootcertificates=<file>", "-splash", "-uiplatform"};
- gArgs.AddArg("-version", "Print version and exit", false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#if HAVE_SYSTEM
- 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("-alertnotify=<cmd>", "Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#endif
- 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 directory to hold blocks subdirectory for *.dat files (default: <datadir>)", 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()), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-blocksdir=<dir>", "Specify directory to hold blocks subdirectory for *.dat files (default: <datadir>)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#if HAVE_SYSTEM
- gArgs.AddArg("-blocknotify=<cmd>", "Execute command when the best block changes (%s in cmd is replaced by block hash)", false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-blocknotify=<cmd>", "Execute command when the best block changes (%s in cmd is replaced by block hash)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#endif
- 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 reject transactions from network peers. Transactions from the wallet or RPC are not affected. (default: %u)", DEFAULT_BLOCKSONLY), 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("-dbbatchsize", strprintf("Maximum database write batch size in bytes (default: %u)", nDefaultDbBatchSize), true, OptionsCategory::OPTIONS);
- gArgs.AddArg("-dbcache=<n>", strprintf("Maximum database cache size <n> MiB (%d to %d, default: %d). In addition, unused mempool memory is shared for this cache (see -maxmempool).", 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. (-nodebuglogfile to disable; 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("-blockreconstructionextratxn=<n>", strprintf("Extra transactions to keep in memory for compact block reconstructions (default: %u)", DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-blocksonly", strprintf("Whether to reject transactions from network peers. Transactions from the wallet, RPC and relay whitelisted inbound peers are not affected. (default: %u)", DEFAULT_BLOCKSONLY), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-conf=<file>", strprintf("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-dbbatchsize", strprintf("Maximum database write batch size in bytes (default: %u)", nDefaultDbBatchSize), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-dbcache=<n>", strprintf("Maximum database cache size <n> MiB (%d to %d, default: %d). In addition, unused mempool memory is shared for this cache (see -maxmempool).", nMinDbCache, nMaxDbCache, nDefaultDbCache), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-debuglogfile=<file>", strprintf("Specify location of debug log file. Relative paths will be prefixed by a net-specific datadir location. (-nodebuglogfile to disable; default: %s)", DEFAULT_DEBUGLOGFILE), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-feefilter", strprintf("Tell other nodes to filter invs to us by our mempool min fee (default: %u)", DEFAULT_FEEFILTER), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-includeconf=<file>", "Specify additional configuration file, relative to the -datadir path (only useable from configuration file, not command line)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-loadblock=<file>", "Imports blocks from external blk000??.dat file on startup", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-maxmempool=<n>", strprintf("Keep the transaction memory pool below <n> megabytes (default: %u)", DEFAULT_MAX_MEMPOOL_SIZE), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-maxorphantx=<n>", strprintf("Keep at most <n> unconnectable transactions in memory (default: %u)", DEFAULT_MAX_ORPHAN_TRANSACTIONS), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-mempoolexpiry=<n>", strprintf("Do not keep transactions in the mempool longer than <n> hours (default: %u)", DEFAULT_MEMPOOL_EXPIRY), ArgsManager::ALLOW_ANY, 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()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, 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);
- 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);
+ -GetNumCores(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-persistmempool", strprintf("Whether to save the mempool on shutdown and load on restart (default: %u)", DEFAULT_PERSIST_MEMPOOL), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-pid=<file>", strprintf("Specify pid file. Relative paths will be prefixed by a net-specific datadir location. (default: %s)", BITCOIN_PID_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
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), 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. When in pruning mode or if blocks on disk might be corrupted, use full -reindex instead.", false, OptionsCategory::OPTIONS);
+ "(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), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-reindex", "Rebuild chain state and block index from the blk*.dat files on disk", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-reindex-chainstate", "Rebuild chain state from the currently indexed blocks. When in pruning mode or if blocks on disk might be corrupted, use full -reindex instead.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#ifndef WIN32
- gArgs.AddArg("-sysperms", "Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality)", false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-sysperms", "Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#else
hidden_args.emplace_back("-sysperms");
#endif
- gArgs.AddArg("-txindex", strprintf("Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u)", DEFAULT_TXINDEX), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-txindex", strprintf("Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u)", DEFAULT_TXINDEX), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
gArgs.AddArg("-blockfilterindex=<type>",
strprintf("Maintain an index of compact filters by block (default: %s, values: %s).", DEFAULT_BLOCKFILTERINDEX, ListBlockFilterTypes()) +
" If <type> is not supplied or if <type> = 1, indexes for all known types are enabled.",
- 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). This option can be specified multiple times to add multiple nodes.", 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; -noconnect disables automatic connections (the rules for this peer are the same as for -addnode). This option can be specified multiple times to connect to multiple nodes.", 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", strprintf("Allow DNS lookups for -addnode, -seednode and -connect (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("-enablebip61", strprintf("Send reject messages per BIP61 (default: %u)", DEFAULT_ENABLE_BIP61), 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>", "Use separate SOCKS5 proxy to reach peers via Tor hidden services, set -noonion to disable (default: -proxy)", false, OptionsCategory::CONNECTION);
- gArgs.AddArg("-onlynet=<net>", "Make outgoing connections only through network <net> (ipv4, ipv6 or onion). Incoming connections are not affected by this option. This option can be specified multiple times to allow multiple networks.", 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, testnet: %u, regtest: %u)", defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort(), regtestChainParams->GetDefaultPort()), false, OptionsCategory::CONNECTION);
- gArgs.AddArg("-proxy=<ip:port>", "Connect through SOCKS5 proxy, set -noproxy to disable (default: disabled)", 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. This option can be specified multiple times to connect to multiple nodes.", 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("-peertimeout=<n>", strprintf("Specify p2p connection timeout in seconds. This option determines the amount of time a peer may be inactive before the connection to it is dropped. (minimum: 1, default: %d)", DEFAULT_PEER_CONNECT_TIMEOUT), true, 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);
+ ArgsManager::ALLOW_ANY, 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). This option can be specified multiple times to add multiple nodes.", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-banscore=<n>", strprintf("Threshold for disconnecting misbehaving peers (default: %u)", DEFAULT_BANSCORE_THRESHOLD), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-bantime=<n>", strprintf("Number of seconds to keep misbehaving peers from reconnecting (default: %u)", DEFAULT_MISBEHAVING_BANTIME), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-bind=<addr>", "Bind to given address and always listen on it. Use [host]:port notation for IPv6", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-connect=<ip>", "Connect only to the specified node; -noconnect disables automatic connections (the rules for this peer are the same as for -addnode). This option can be specified multiple times to connect to multiple nodes.", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-discover", "Discover own IP addresses (default: 1 when listening and no -externalip or -proxy)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-dns", strprintf("Allow DNS lookups for -addnode, -seednode and -connect (default: %u)", DEFAULT_NAME_LOOKUP), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-dnsseed", "Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect used)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-enablebip61", strprintf("Send reject messages per BIP61 (default: %u)", DEFAULT_ENABLE_BIP61), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-externalip=<ip>", "Specify your own public address", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-forcednsseed", strprintf("Always query for peer addresses via DNS lookup (default: %u)", DEFAULT_FORCEDNSSEED), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-listen", "Accept connections from outside (default: 1 if no -proxy or -connect)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-listenonion", strprintf("Automatically create Tor hidden service (default: %d)", DEFAULT_LISTEN_ONION), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-maxconnections=<n>", strprintf("Maintain at most <n> connections to peers (default: %u)", DEFAULT_MAX_PEER_CONNECTIONS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-maxreceivebuffer=<n>", strprintf("Maximum per-connection receive buffer, <n>*1000 bytes (default: %u)", DEFAULT_MAXRECEIVEBUFFER), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-maxsendbuffer=<n>", strprintf("Maximum per-connection send buffer, <n>*1000 bytes (default: %u)", DEFAULT_MAXSENDBUFFER), ArgsManager::ALLOW_ANY, 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), ArgsManager::ALLOW_ANY, 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), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-onion=<ip:port>", "Use separate SOCKS5 proxy to reach peers via Tor hidden services, set -noonion to disable (default: -proxy)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-onlynet=<net>", "Make outgoing connections only through network <net> (ipv4, ipv6 or onion). Incoming connections are not affected by this option. This option can be specified multiple times to allow multiple networks.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-peerbloomfilters", strprintf("Support filtering of blocks and transaction with bloom filters (default: %u)", DEFAULT_PEERBLOOMFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-permitbaremultisig", strprintf("Relay non-P2SH multisig (default: %u)", DEFAULT_PERMIT_BAREMULTISIG), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-port=<port>", strprintf("Listen for connections on <port> (default: %u, testnet: %u, regtest: %u)", defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort(), regtestChainParams->GetDefaultPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-proxy=<ip:port>", "Connect through SOCKS5 proxy, set -noproxy to disable (default: disabled)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-proxyrandomize", strprintf("Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)", DEFAULT_PROXYRANDOMIZE), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-seednode=<ip>", "Connect to a node to retrieve peer addresses, and disconnect. This option can be specified multiple times to connect to multiple nodes.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-timeout=<n>", strprintf("Specify connection timeout in milliseconds (minimum: 1, default: %d)", DEFAULT_CONNECT_TIMEOUT), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-peertimeout=<n>", strprintf("Specify p2p connection timeout in seconds. This option determines the amount of time a peer may be inactive before the connection to it is dropped. (minimum: 1, default: %d)", DEFAULT_PEER_CONNECT_TIMEOUT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-torcontrol=<ip>:<port>", strprintf("Tor control port to use if onion listening enabled (default: %s)", DEFAULT_TOR_CONTROL), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-torpassword=<pass>", "Tor control port password (default: empty)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
#ifdef USE_UPNP
#if USE_UPNP
- gArgs.AddArg("-upnp", "Use UPnP to map the listening port (default: 1 when listening and no -proxy)", false, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-upnp", "Use UPnP to map the listening port (default: 1 when listening and no -proxy)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
#else
- gArgs.AddArg("-upnp", strprintf("Use UPnP to map the listening port (default: %u)", 0), false, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-upnp", strprintf("Use UPnP to map the listening port (default: %u)", 0), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
#endif
#else
hidden_args.emplace_back("-upnp");
#endif
- 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", false, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-whitebind=<[permissions@]addr>", "Bind to given address and whitelist peers connecting to it. "
+ "Use [host]:port notation for IPv6. Allowed permissions are bloomfilter (allow requesting BIP37 filtered blocks and transactions), "
+ "noban (do not ban for misbehavior), "
+ "forcerelay (relay even non-standard transactions), "
+ "relay (relay even in -blocksonly mode), "
+ "and mempool (allow requesting BIP35 mempool contents). "
+ "Specify multiple permissions separated by commas (default: noban,mempool,relay). Can be specified multiple times.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+
+ gArgs.AddArg("-whitelist=<[permissions@]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). Uses same permissions as "
+ "-whitebind. Can be specified multiple times." , ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
g_wallet_init_interface.AddWalletOptions();
#if ENABLE_ZMQ
- 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);
- gArgs.AddArg("-zmqpubhashblockhwm=<n>", strprintf("Set publish hash block outbound message high water mark (default: %d)", CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM), false, OptionsCategory::ZMQ);
- gArgs.AddArg("-zmqpubhashtxhwm=<n>", strprintf("Set publish hash transaction outbound message high water mark (default: %d)", CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM), false, OptionsCategory::ZMQ);
- gArgs.AddArg("-zmqpubrawblockhwm=<n>", strprintf("Set publish raw block outbound message high water mark (default: %d)", CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM), false, OptionsCategory::ZMQ);
- gArgs.AddArg("-zmqpubrawtxhwm=<n>", strprintf("Set publish raw transaction outbound message high water mark (default: %d)", CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM), false, OptionsCategory::ZMQ);
+ gArgs.AddArg("-zmqpubhashblock=<address>", "Enable publish hash block in <address>", ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
+ gArgs.AddArg("-zmqpubhashtx=<address>", "Enable publish hash transaction in <address>", ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
+ gArgs.AddArg("-zmqpubrawblock=<address>", "Enable publish raw block in <address>", ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
+ gArgs.AddArg("-zmqpubrawtx=<address>", "Enable publish raw transaction in <address>", ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
+ gArgs.AddArg("-zmqpubhashblockhwm=<n>", strprintf("Set publish hash block outbound message high water mark (default: %d)", CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM), ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
+ gArgs.AddArg("-zmqpubhashtxhwm=<n>", strprintf("Set publish hash transaction outbound message high water mark (default: %d)", CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM), ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
+ gArgs.AddArg("-zmqpubrawblockhwm=<n>", strprintf("Set publish raw block outbound message high water mark (default: %d)", CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM), ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
+ gArgs.AddArg("-zmqpubrawtxhwm=<n>", strprintf("Set publish raw transaction outbound message high water mark (default: %d)", CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM), ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
#else
hidden_args.emplace_back("-zmqpubhashblock=<address>");
hidden_args.emplace_back("-zmqpubhashtx=<address>");
@@ -461,7 +472,7 @@ void SetupServerArgs()
hidden_args.emplace_back("-zmqpubrawtxhwm=<n>");
#endif
- gArgs.AddArg("-checkblocks=<n>", strprintf("How many blocks to check at startup (default: %u, 0 = all)", DEFAULT_CHECKBLOCKS), true, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-checkblocks=<n>", strprintf("How many blocks to check at startup (default: %u, 0 = all)", DEFAULT_CHECKBLOCKS), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-checklevel=<n>", strprintf("How thorough the block verification of -checkblocks is: "
"level 0 reads the blocks from disk, "
"level 1 verifies block validity, "
@@ -469,68 +480,68 @@ void SetupServerArgs()
"level 3 checks disconnection of tip blocks, "
"and level 4 tries to reconnect the blocks, "
"each level includes the checks of the previous levels "
- "(0-4, default: %u)", DEFAULT_CHECKLEVEL), true, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("-checkblockindex", strprintf("Do a full consistency check for the block tree, setBlockIndexCandidates, ::ChainActive() and mapBlocksUnlinked occasionally. (default: %u, regtest: %u)", defaultChainParams->DefaultConsistencyChecks(), regtestChainParams->DefaultConsistencyChecks()), true, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("-checkmempool=<n>", strprintf("Run checks every <n> transactions (default: %u, regtest: %u)", defaultChainParams->DefaultConsistencyChecks(), regtestChainParams->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("-addrmantest", "Allows to test address relay on localhost", true, OptionsCategory::DEBUG_TEST);
+ "(0-4, default: %u)", DEFAULT_CHECKLEVEL), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-checkblockindex", strprintf("Do a full consistency check for the block tree, setBlockIndexCandidates, ::ChainActive() and mapBlocksUnlinked occasionally. (default: %u, regtest: %u)", defaultChainParams->DefaultConsistencyChecks(), regtestChainParams->DefaultConsistencyChecks()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-checkmempool=<n>", strprintf("Run checks every <n> transactions (default: %u, regtest: %u)", defaultChainParams->DefaultConsistencyChecks(), regtestChainParams->DefaultConsistencyChecks()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-checkpoints", strprintf("Disable expensive verification for known chain history (default: %u)", DEFAULT_CHECKPOINTS_ENABLED), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-deprecatedrpc=<method>", "Allows deprecated RPC method(s) to be used", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-dropmessagestest=<n>", "Randomly drop 1 of every <n> network messages", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-stopafterblockimport", strprintf("Stop running after importing blocks from disk (default: %u)", DEFAULT_STOPAFTERBLOCKIMPORT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-stopatheight", strprintf("Stop running after reaching the given height in the main chain (default: %u)", DEFAULT_STOPATHEIGHT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, 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), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, 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), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, 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), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, 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), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-addrmantest", "Allows to test address relay on localhost", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-debug=<category>", "Output debugging information (default: -nodebug, supplying <category> is optional). "
- "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("-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("-logthreadnames", strprintf("Prepend debug output with name of the originating thread (only available on platforms supporting thread_local) (default: %u)", DEFAULT_LOGTHREADNAMES), 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("-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 (default: 1 when no -daemon. To disable logging to file, set -nodebuglogfile)", 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);
+ "If <category> is not supplied or if <category> = 1, output all debugging information. <category> can be: " + ListLogCategories() + ".", ArgsManager::ALLOW_ANY, 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."), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-logips", strprintf("Include IP addresses in debug output (default: %u)", DEFAULT_LOGIPS), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-logtimestamps", strprintf("Prepend debug output with timestamp (default: %u)", DEFAULT_LOGTIMESTAMPS), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-logthreadnames", strprintf("Prepend debug output with name of the originating thread (only available on platforms supporting thread_local) (default: %u)", DEFAULT_LOGTHREADNAMES), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-logtimemicros", strprintf("Add microsecond precision to debug timestamps (default: %u)", DEFAULT_LOGTIMEMICROS), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-mocktime=<n>", "Replace actual time with <n> seconds since epoch (default: 0)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, 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), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, 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), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-printpriority", strprintf("Log transaction fee per kB when mining blocks (default: %u)", DEFAULT_PRINTPRIORITY), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-printtoconsole", "Send trace/debug info to console (default: 1 when no -daemon. To disable logging to file, set -nodebuglogfile)", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-shrinkdebugfile", "Shrink debug.log file on client startup (default: 1 when no -debug)", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-uacomment=<cmt>", "Append comment to the user agent string", ArgsManager::ALLOW_ANY, 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 define 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("-acceptnonstdtxn", strprintf("Relay and mine \"non-standard\" transactions (%sdefault: %u)", "testnet/regtest only; ", !testnetChainParams->RequireStandard()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, 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)), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::NODE_RELAY);
+ gArgs.AddArg("-dustrelayfee=<amt>", strprintf("Fee rate (in %s/kB) used to define 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)), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::NODE_RELAY);
+ gArgs.AddArg("-bytespersigop", strprintf("Equivalent bytes per sigop in transactions for relay and mining (default: %u)", DEFAULT_BYTES_PER_SIGOP), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
+ gArgs.AddArg("-datacarrier", strprintf("Relay and mine data carrier transactions (default: %u)", DEFAULT_ACCEPT_DATACARRIER), ArgsManager::ALLOW_ANY, 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), ArgsManager::ALLOW_ANY, 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 the transactions were already in the mempool or 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 HMAC-SHA-256 hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcauth. The client then connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> pair of arguments. This option can be specified multiple times", false, OptionsCategory::RPC);
- gArgs.AddArg("-rpcbind=<addr>[:port]", "Bind to given address to listen for JSON-RPC connections. Do not expose the RPC server to untrusted networks such as the public internet! This option is ignored unless -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)", 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, testnet: %u, regtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), regtestBaseParams->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);
+ CURRENCY_UNIT, FormatMoney(DEFAULT_MIN_RELAY_TX_FEE)), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
+ gArgs.AddArg("-whitelistforcerelay", strprintf("Add 'forcerelay' permission to whitelisted inbound peers with default permissions. This will relay transactions even if the transactions were already in the mempool or violate local relay policy. (default: %d)", DEFAULT_WHITELISTFORCERELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
+ gArgs.AddArg("-whitelistrelay", strprintf("Add 'relay' permission to whitelisted inbound peers with default permissions. The will accept relayed transactions even when not relaying transactions (default: %d)", DEFAULT_WHITELISTRELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
+
+
+ gArgs.AddArg("-blockmaxweight=<n>", strprintf("Set maximum BIP141 block weight (default: %d)", DEFAULT_BLOCK_MAX_WEIGHT), ArgsManager::ALLOW_ANY, 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)), ArgsManager::ALLOW_ANY, OptionsCategory::BLOCK_CREATION);
+ gArgs.AddArg("-blockversion=<n>", "Override block version to test forking scenarios", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::BLOCK_CREATION);
+
+ gArgs.AddArg("-rest", strprintf("Accept public REST requests (default: %u)", DEFAULT_REST_ENABLE), ArgsManager::ALLOW_ANY, 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", ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
+ gArgs.AddArg("-rpcauth=<userpw>", "Username and HMAC-SHA-256 hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcauth. The client then connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> pair of arguments. This option can be specified multiple times", ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
+ gArgs.AddArg("-rpcbind=<addr>[:port]", "Bind to given address to listen for JSON-RPC connections. Do not expose the RPC server to untrusted networks such as the public internet! This option is ignored unless -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)", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, 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)", ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
+ gArgs.AddArg("-rpcpassword=<pw>", "Password for JSON-RPC connections", ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
+ gArgs.AddArg("-rpcport=<port>", strprintf("Listen for JSON-RPC connections on <port> (default: %u, testnet: %u, regtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), regtestBaseParams->RPCPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, 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), ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
+ gArgs.AddArg("-rpcservertimeout=<n>", strprintf("Timeout during HTTP requests (default: %d)", DEFAULT_HTTP_SERVER_TIMEOUT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::RPC);
+ gArgs.AddArg("-rpcthreads=<n>", strprintf("Set the number of threads to service RPC calls (default: %d)", DEFAULT_HTTP_THREADS), ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
+ gArgs.AddArg("-rpcuser=<user>", "Username for JSON-RPC connections", ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
+ gArgs.AddArg("-rpcworkqueue=<n>", strprintf("Set the depth of the work queue to service RPC calls (default: %d)", DEFAULT_HTTP_WORKQUEUE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::RPC);
+ gArgs.AddArg("-server", "Accept command line and JSON-RPC commands", ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
#if HAVE_DECL_DAEMON
- gArgs.AddArg("-daemon", "Run in the background as a daemon and accept commands", false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-daemon", "Run in the background as a daemon and accept commands", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#else
hidden_args.emplace_back("-daemon");
#endif
@@ -821,11 +832,6 @@ void InitParameterInteraction()
}
}
-static std::string ResolveErrMsg(const char * const optname, const std::string& strBind)
-{
- return strprintf(_("Cannot resolve -%s address: '%s'").translated, optname, strBind);
-}
-
/**
* Initialize global loggers.
*
@@ -1054,7 +1060,7 @@ bool AppInitParameterInteraction()
{
CAmount n = 0;
if (!ParseMoney(gArgs.GetArg("-incrementalrelayfee", ""), n))
- return InitError(AmountErrMsg("incrementalrelayfee", gArgs.GetArg("-incrementalrelayfee", "")));
+ return InitError(AmountErrMsg("incrementalrelayfee", gArgs.GetArg("-incrementalrelayfee", "")).translated);
incrementalRelayFee = CFeeRate(n);
}
@@ -1098,9 +1104,9 @@ bool AppInitParameterInteraction()
if (gArgs.IsArgSet("-minrelaytxfee")) {
CAmount n = 0;
if (!ParseMoney(gArgs.GetArg("-minrelaytxfee", ""), n)) {
- return InitError(AmountErrMsg("minrelaytxfee", gArgs.GetArg("-minrelaytxfee", "")));
+ return InitError(AmountErrMsg("minrelaytxfee", gArgs.GetArg("-minrelaytxfee", "")).translated);
}
- // High fee check is done afterward in WalletParameterInteraction()
+ // High fee check is done afterward in CWallet::CreateWalletFromFile()
::minRelayTxFee = CFeeRate(n);
} else if (incrementalRelayFee > ::minRelayTxFee) {
// Allow only setting incrementalRelayFee to control both
@@ -1114,7 +1120,7 @@ bool AppInitParameterInteraction()
{
CAmount n = 0;
if (!ParseMoney(gArgs.GetArg("-blockmintxfee", ""), n))
- return InitError(AmountErrMsg("blockmintxfee", gArgs.GetArg("-blockmintxfee", "")));
+ return InitError(AmountErrMsg("blockmintxfee", gArgs.GetArg("-blockmintxfee", "")).translated);
}
// Feerate used to define dust. Shouldn't be changed lightly as old
@@ -1123,7 +1129,7 @@ bool AppInitParameterInteraction()
{
CAmount n = 0;
if (!ParseMoney(gArgs.GetArg("-dustrelayfee", ""), n))
- return InitError(AmountErrMsg("dustrelayfee", gArgs.GetArg("-dustrelayfee", "")));
+ return InitError(AmountErrMsg("dustrelayfee", gArgs.GetArg("-dustrelayfee", "")).translated);
dustRelayFee = CFeeRate(n);
}
@@ -1466,10 +1472,10 @@ bool AppInitMain(InitInterfaces& interfaces)
bool is_coinsview_empty;
try {
LOCK(cs_main);
+ // This statement makes ::ChainstateActive() usable.
+ g_chainstate = MakeUnique<CChainState>();
UnloadBlockIndex();
- pcoinsTip.reset();
- pcoinsdbview.reset();
- pcoinscatcher.reset();
+
// new CBlockTreeDB tries to delete the existing file, which
// fails if it's still open from the previous loop. Close it first:
pblocktree.reset();
@@ -1520,9 +1526,12 @@ bool AppInitMain(InitInterfaces& interfaces)
// At this point we're either in reindex or we've loaded a useful
// block tree into BlockIndex()!
- pcoinsdbview.reset(new CCoinsViewDB(nCoinDBCache, false, fReset || fReindexChainState));
- pcoinscatcher.reset(new CCoinsViewErrorCatcher(pcoinsdbview.get()));
- pcoinscatcher->AddReadErrCallback([]() {
+ ::ChainstateActive().InitCoinsDB(
+ /* cache_size_bytes */ nCoinDBCache,
+ /* in_memory */ false,
+ /* should_wipe */ fReset || fReindexChainState);
+
+ ::ChainstateActive().CoinsErrorCatcher().AddReadErrCallback([]() {
uiInterface.ThreadSafeMessageBox(
_("Error reading from database, shutting down.").translated,
"", CClientUIInterface::MSG_ERROR);
@@ -1530,23 +1539,25 @@ bool AppInitMain(InitInterfaces& interfaces)
// If necessary, upgrade from older database format.
// This is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate
- if (!pcoinsdbview->Upgrade()) {
+ if (!::ChainstateActive().CoinsDB().Upgrade()) {
strLoadError = _("Error upgrading chainstate database").translated;
break;
}
// ReplayBlocks is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate
- if (!ReplayBlocks(chainparams, pcoinsdbview.get())) {
+ if (!ReplayBlocks(chainparams, &::ChainstateActive().CoinsDB())) {
strLoadError = _("Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.").translated;
break;
}
// The on-disk coinsdb is now in a good state, create the cache
- pcoinsTip.reset(new CCoinsViewCache(pcoinscatcher.get()));
+ ::ChainstateActive().InitCoinsCache();
+ assert(::ChainstateActive().CanFlushToDisk());
- is_coinsview_empty = fReset || fReindexChainState || pcoinsTip->GetBestBlock().IsNull();
+ is_coinsview_empty = fReset || fReindexChainState ||
+ ::ChainstateActive().CoinsTip().GetBestBlock().IsNull();
if (!is_coinsview_empty) {
- // LoadChainTip sets ::ChainActive() based on pcoinsTip's best block
+ // LoadChainTip sets ::ChainActive() based on CoinsTip()'s best block
if (!LoadChainTip(chainparams)) {
strLoadError = _("Error initializing block database").translated;
break;
@@ -1588,7 +1599,7 @@ bool AppInitMain(InitInterfaces& interfaces)
break;
}
- if (!CVerifyDB().VerifyDB(chainparams, pcoinsdbview.get(), gArgs.GetArg("-checklevel", DEFAULT_CHECKLEVEL),
+ if (!CVerifyDB().VerifyDB(chainparams, &::ChainstateActive().CoinsDB(), gArgs.GetArg("-checklevel", DEFAULT_CHECKLEVEL),
gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS))) {
strLoadError = _("Corrupted block database detected").translated;
break;
@@ -1670,12 +1681,9 @@ bool AppInitMain(InitInterfaces& interfaces)
}
}
- if (chainparams.GetConsensus().vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout != 0) {
- // Only advertise witness capabilities if they have a reasonable start time.
- // This allows us to have the code merged without a defined softfork, by setting its
- // end time to 0.
- // Note that setting NODE_WITNESS is never required: the only downside from not
- // doing so is that after activation, no upgraded nodes will fetch from you.
+ if (chainparams.GetConsensus().SegwitHeight != std::numeric_limits<int>::max()) {
+ // Advertise witness capabilities.
+ // The option to not set NODE_WITNESS is only used in the tests and should be removed.
nLocalServices = ServiceFlags(nLocalServices | NODE_WITNESS);
}
@@ -1752,7 +1760,8 @@ bool AppInitMain(InitInterfaces& interfaces)
CConnman::Options connOptions;
connOptions.nLocalServices = nLocalServices;
connOptions.nMaxConnections = nMaxConnections;
- connOptions.nMaxOutbound = std::min(MAX_OUTBOUND_CONNECTIONS, connOptions.nMaxConnections);
+ connOptions.m_max_outbound_full_relay = std::min(MAX_OUTBOUND_FULL_RELAY_CONNECTIONS, connOptions.nMaxConnections);
+ connOptions.m_max_outbound_block_relay = std::min(MAX_BLOCKS_ONLY_CONNECTIONS, connOptions.nMaxConnections-connOptions.m_max_outbound_full_relay);
connOptions.nMaxAddnode = MAX_ADDNODE_CONNECTIONS;
connOptions.nMaxFeeler = 1;
connOptions.nBestHeight = chain_active_height;
@@ -1775,21 +1784,16 @@ bool AppInitMain(InitInterfaces& interfaces)
connOptions.vBinds.push_back(addrBind);
}
for (const std::string& strBind : gArgs.GetArgs("-whitebind")) {
- CService addrBind;
- if (!Lookup(strBind.c_str(), addrBind, 0, false)) {
- return InitError(ResolveErrMsg("whitebind", strBind));
- }
- if (addrBind.GetPort() == 0) {
- return InitError(strprintf(_("Need to specify a port with -whitebind: '%s'").translated, strBind));
- }
- connOptions.vWhiteBinds.push_back(addrBind);
+ NetWhitebindPermissions whitebind;
+ std::string error;
+ if (!NetWhitebindPermissions::TryParse(strBind, whitebind, error)) return InitError(error);
+ connOptions.vWhiteBinds.push_back(whitebind);
}
for (const auto& net : gArgs.GetArgs("-whitelist")) {
- CSubNet subnet;
- LookupSubNet(net.c_str(), subnet);
- if (!subnet.IsValid())
- return InitError(strprintf(_("Invalid netmask specified in -whitelist: '%s'").translated, net));
+ NetWhitelistPermissions subnet;
+ std::string error;
+ if (!NetWhitelistPermissions::TryParse(net, subnet, error)) return InitError(error);
connOptions.vWhitelistedRange.push_back(subnet);
}
diff --git a/src/interfaces/chain.cpp b/src/interfaces/chain.cpp
index 22e4aaedaf..b8b9ecded9 100644
--- a/src/interfaces/chain.cpp
+++ b/src/interfaces/chain.cpp
@@ -11,6 +11,7 @@
#include <net.h>
#include <net_processing.h>
#include <node/coin.h>
+#include <node/transaction.h>
#include <policy/fees.h>
#include <policy/policy.h>
#include <policy/rbf.h>
@@ -150,12 +151,6 @@ class LockImpl : public Chain::Lock, public UniqueLock<CCriticalSection>
LockAssertion lock(::cs_main);
return CheckFinalTx(tx);
}
- bool submitToMemoryPool(const CTransactionRef& tx, CAmount absurd_fee, CValidationState& state) override
- {
- LockAssertion lock(::cs_main);
- return AcceptToMemoryPool(::mempool, state, tx, nullptr /* missing inputs */, nullptr /* txn replaced */,
- false /* bypass limits */, absurd_fee);
- }
using UniqueLock::UniqueLock;
};
@@ -291,9 +286,13 @@ public:
auto it = ::mempool.GetIter(txid);
return it && (*it)->GetCountWithDescendants() > 1;
}
- void relayTransaction(const uint256& txid) override
+ bool broadcastTransaction(const CTransactionRef& tx, std::string& err_string, const CAmount& max_tx_fee, bool relay) override
{
- RelayTransaction(txid, *g_connman);
+ const TransactionError err = BroadcastTransaction(tx, err_string, max_tx_fee, relay, /*wait_callback*/ false);
+ // Chain clients only care about failures to accept the tx to the mempool. Disregard non-mempool related failures.
+ // Note: this will need to be updated if BroadcastTransactions() is updated to return other non-mempool failures
+ // that Chain clients do not need to know about.
+ return TransactionError::OK == err;
}
void getTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants) override
{
@@ -333,7 +332,6 @@ public:
LOCK(cs_main);
return ::fHavePruned;
}
- bool p2pEnabled() override { return g_connman != nullptr; }
bool isReadyToBroadcast() override { return !::fImporting && !::fReindex && !isInitialBlockDownload(); }
bool isInitialBlockDownload() override { return ::ChainstateActive().IsInitialBlockDownload(); }
bool shutdownRequested() override { return ShutdownRequested(); }
diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h
index e675defd47..da670a3370 100644
--- a/src/interfaces/chain.h
+++ b/src/interfaces/chain.h
@@ -43,10 +43,6 @@ class Wallet;
//! asynchronously
//! (https://github.com/bitcoin/bitcoin/pull/10973#issuecomment-380101269).
//!
-//! * The relayTransactions() and submitToMemoryPool() methods could be replaced
-//! with a higher-level broadcastTransaction method
-//! (https://github.com/bitcoin/bitcoin/pull/14978#issuecomment-459373984).
-//!
//! * The initMessages() and loadWallet() methods which the wallet uses to send
//! notifications to the GUI should go away when GUI and wallet can directly
//! communicate with each other without going through the node
@@ -127,11 +123,6 @@ public:
//! Check if transaction will be final given chain height current time.
virtual bool checkFinalTx(const CTransaction& tx) = 0;
-
- //! Add transaction to memory pool if the transaction fee is below the
- //! amount specified by absurd_fee. Returns false if the transaction
- //! could not be added due to the fee or for another reason.
- virtual bool submitToMemoryPool(const CTransactionRef& tx, CAmount absurd_fee, CValidationState& state) = 0;
};
//! Return Lock interface. Chain is locked when this is called, and
@@ -164,8 +155,10 @@ public:
//! Check if transaction has descendants in mempool.
virtual bool hasDescendantsInMempool(const uint256& txid) = 0;
- //! Relay transaction.
- virtual void relayTransaction(const uint256& txid) = 0;
+ //! Transaction is added to memory pool, if the transaction fee is below the
+ //! amount specified by max_tx_fee, and broadcast to all peers if relay is set to true.
+ //! Return false if the transaction could not be added due to the fee or for another reason.
+ virtual bool broadcastTransaction(const CTransactionRef& tx, std::string& err_string, const CAmount& max_tx_fee, bool relay) = 0;
//! Calculate mempool ancestor and descendant counts for the given transaction.
virtual void getTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants) = 0;
@@ -194,9 +187,6 @@ public:
//! Check if any block has been pruned.
virtual bool havePruned() = 0;
- //! Check if p2p enabled.
- virtual bool p2pEnabled() = 0;
-
//! Check if the node is ready to broadcast transactions.
virtual bool isReadyToBroadcast() = 0;
diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp
index fd2fb6531b..c80a8789fc 100644
--- a/src/interfaces/node.cpp
+++ b/src/interfaces/node.cpp
@@ -24,6 +24,7 @@
#include <primitives/block.h>
#include <rpc/server.h>
#include <shutdown.h>
+#include <support/allocators/secure.h>
#include <sync.h>
#include <txmempool.h>
#include <ui_interface.h>
@@ -43,6 +44,7 @@ fs::path GetWalletDir();
std::vector<fs::path> ListWalletDir();
std::vector<std::shared_ptr<CWallet>> GetWallets();
std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, std::string& error, std::string& warning);
+WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, std::string& error, std::string& warning, std::shared_ptr<CWallet>& result);
namespace interfaces {
@@ -60,6 +62,7 @@ public:
return gArgs.ParseParameters(argc, argv, error);
}
bool readConfigFiles(std::string& error) override { return gArgs.ReadConfigFiles(error, true); }
+ void forceSetArg(const std::string& arg, const std::string& value) override { gArgs.ForceSetArg(arg, value); }
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); }
@@ -198,6 +201,7 @@ public:
return GuessVerificationProgress(Params().TxData(), tip);
}
bool isInitialBlockDownload() override { return ::ChainstateActive().IsInitialBlockDownload(); }
+ bool isAddressTypeSet() override { return !::gArgs.GetArg("-addresstype", "").empty(); }
bool getReindex() override { return ::fReindex; }
bool getImporting() override { return ::fImporting; }
void setNetworkActive(bool active) override
@@ -231,7 +235,7 @@ public:
bool getUnspentOutput(const COutPoint& output, Coin& coin) override
{
LOCK(::cs_main);
- return ::pcoinsTip->GetCoin(output, coin);
+ return ::ChainstateActive().CoinsTip().GetCoin(output, coin);
}
std::string getWalletDir() override
{
@@ -257,6 +261,13 @@ public:
{
return MakeWallet(LoadWallet(*m_interfaces.chain, name, error, warning));
}
+ WalletCreationStatus createWallet(const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, std::string& error, std::string& warning, std::unique_ptr<Wallet>& result) override
+ {
+ std::shared_ptr<CWallet> wallet;
+ WalletCreationStatus status = CreateWallet(*m_interfaces.chain, passphrase, wallet_creation_flags, name, error, warning, wallet);
+ result = MakeWallet(wallet);
+ return status;
+ }
std::unique_ptr<Handler> handleInitMessage(InitMessageFn fn) override
{
return MakeHandler(::uiInterface.InitMessage_connect(fn));
diff --git a/src/interfaces/node.h b/src/interfaces/node.h
index bb4b3e1fae..2f4f396e72 100644
--- a/src/interfaces/node.h
+++ b/src/interfaces/node.h
@@ -9,6 +9,7 @@
#include <amount.h> // For CAmount
#include <net.h> // For CConnman::NumConnections
#include <netaddress.h> // For Network
+#include <support/allocators/secure.h> // For SecureString
#include <functional>
#include <memory>
@@ -27,6 +28,7 @@ class RPCTimerInterface;
class UniValue;
class proxyType;
struct CNodeStateStats;
+enum class WalletCreationStatus;
namespace interfaces {
class Handler;
@@ -44,6 +46,9 @@ public:
//! Set command line arguments.
virtual bool parseParameters(int argc, const char* const argv[], std::string& error) = 0;
+ //! Set a command line argument
+ virtual void forceSetArg(const std::string& arg, const std::string& value) = 0;
+
//! Set a command line argument if it doesn't already have a value
virtual bool softSetArg(const std::string& arg, const std::string& value) = 0;
@@ -150,6 +155,9 @@ public:
//! Is initial block download.
virtual bool isInitialBlockDownload() = 0;
+ //! Is -addresstype set.
+ virtual bool isAddressTypeSet() = 0;
+
//! Get reindex.
virtual bool getReindex() = 0;
@@ -197,6 +205,9 @@ public:
//! with handleLoadWallet.
virtual std::unique_ptr<Wallet> loadWallet(const std::string& name, std::string& error, std::string& warning) = 0;
+ //! Create a wallet from file
+ virtual WalletCreationStatus createWallet(const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, std::string& error, std::string& warning, std::unique_ptr<Wallet>& result) = 0;
+
//! Register handler for init messages.
using InitMessageFn = std::function<void(const std::string& message)>;
virtual std::unique_ptr<Handler> handleInitMessage(InitMessageFn fn) = 0;
diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp
index 077dc1ab4d..0c8d92eba5 100644
--- a/src/interfaces/wallet.cpp
+++ b/src/interfaces/wallet.cpp
@@ -65,7 +65,7 @@ WalletTx MakeWalletTx(interfaces::Chain::Lock& locked_chain, CWallet& wallet, co
WalletTxStatus MakeWalletTxStatus(interfaces::Chain::Lock& locked_chain, const CWalletTx& wtx)
{
WalletTxStatus result;
- result.block_height = locked_chain.getBlockHeight(wtx.hashBlock).get_value_or(std::numeric_limits<int>::max());
+ result.block_height = locked_chain.getBlockHeight(wtx.m_confirm.hashBlock).get_value_or(std::numeric_limits<int>::max());
result.blocks_to_maturity = wtx.GetBlocksToMaturity(locked_chain);
result.depth_in_main_chain = wtx.GetDepthInMainChain(locked_chain);
result.time_received = wtx.nTimeReceived;
diff --git a/src/net.cpp b/src/net.cpp
index 7d6eb31a7c..89f82aa3d2 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -16,6 +16,7 @@
#include <crypto/common.h>
#include <crypto/sha256.h>
#include <netbase.h>
+#include <net_permissions.h>
#include <primitives/transaction.h>
#include <scheduler.h>
#include <ui_interface.h>
@@ -67,7 +68,6 @@ enum BindFlags {
BF_NONE = 0,
BF_EXPLICIT = (1U << 0),
BF_REPORT_ERROR = (1U << 1),
- BF_WHITELIST = (1U << 2),
};
// The set of sockets cannot be modified while waiting
@@ -352,7 +352,7 @@ static CAddress GetBindAddress(SOCKET sock)
return addr_bind;
}
-CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, bool manual_connection)
+CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, bool manual_connection, bool block_relay_only)
{
if (pszDest == nullptr) {
if (IsLocal(addrConnect))
@@ -442,7 +442,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
NodeId id = GetNewNodeId();
uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize();
CAddress addr_bind = GetBindAddress(hSocket);
- CNode* pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addrConnect, CalculateKeyedNetGroup(addrConnect), nonce, addr_bind, pszDest ? pszDest : "", false);
+ CNode* pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addrConnect, CalculateKeyedNetGroup(addrConnect), nonce, addr_bind, pszDest ? pszDest : "", false, block_relay_only);
pnode->AddRef();
return pnode;
@@ -459,12 +459,10 @@ void CNode::CloseSocketDisconnect()
}
}
-bool CConnman::IsWhitelistedRange(const CNetAddr &addr) {
- for (const CSubNet& subnet : vWhitelistedRange) {
- if (subnet.Match(addr))
- return true;
+void CConnman::AddWhitelistPermissionFlags(NetPermissionFlags& flags, const CNetAddr &addr) const {
+ for (const auto& subnet : vWhitelistedRange) {
+ if (subnet.m_subnet.Match(addr)) NetPermissions::AddFlag(flags, subnet.m_flags);
}
- return false;
}
std::string CNode::GetAddrName() const {
@@ -501,9 +499,11 @@ void CNode::copyStats(CNodeStats &stats)
X(nServices);
X(addr);
X(addrBind);
- {
- LOCK(cs_filter);
- X(fRelayTxes);
+ if (m_tx_relay != nullptr) {
+ LOCK(m_tx_relay->cs_filter);
+ stats.fRelayTxes = m_tx_relay->fRelayTxes;
+ } else {
+ stats.fRelayTxes = false;
}
X(nLastSend);
X(nLastRecv);
@@ -528,10 +528,13 @@ void CNode::copyStats(CNodeStats &stats)
X(mapRecvBytesPerMsgCmd);
X(nRecvBytes);
}
- X(fWhitelisted);
- {
- LOCK(cs_feeFilter);
- X(minFeeFilter);
+ X(m_legacyWhitelisted);
+ X(m_permissionFlags);
+ if (m_tx_relay != nullptr) {
+ LOCK(m_tx_relay->cs_feeFilter);
+ stats.minFeeFilter = m_tx_relay->minFeeFilter;
+ } else {
+ stats.minFeeFilter = 0;
}
// It is common for nodes with good ping times to suddenly become lagged,
@@ -813,17 +816,23 @@ bool CConnman::AttemptToEvictConnection()
LOCK(cs_vNodes);
for (const CNode* node : vNodes) {
- if (node->fWhitelisted)
+ if (node->HasPermission(PF_NOBAN))
continue;
if (!node->fInbound)
continue;
if (node->fDisconnect)
continue;
- LOCK(node->cs_filter);
+ bool peer_relay_txes = false;
+ bool peer_filter_not_null = false;
+ if (node->m_tx_relay != nullptr) {
+ LOCK(node->m_tx_relay->cs_filter);
+ peer_relay_txes = node->m_tx_relay->fRelayTxes;
+ peer_filter_not_null = node->m_tx_relay->pfilter != nullptr;
+ }
NodeEvictionCandidate candidate = {node->GetId(), node->nTimeConnected, node->nMinPingUsecTime,
node->nLastBlockTime, node->nLastTXTime,
HasAllDesirableServiceFlags(node->nServices),
- node->fRelayTxes, node->pfilter != nullptr, node->addr, node->nKeyedNetGroup,
+ peer_relay_txes, peer_filter_not_null, node->addr, node->nKeyedNetGroup,
node->m_prefer_evict};
vEvictionCandidates.push_back(candidate);
}
@@ -896,7 +905,7 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
SOCKET hSocket = accept(hListenSocket.socket, (struct sockaddr*)&sockaddr, &len);
CAddress addr;
int nInbound = 0;
- int nMaxInbound = nMaxConnections - (nMaxOutbound + nMaxFeeler);
+ int nMaxInbound = nMaxConnections - m_max_outbound;
if (hSocket != INVALID_SOCKET) {
if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr)) {
@@ -904,7 +913,19 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
}
}
- bool whitelisted = hListenSocket.whitelisted || IsWhitelistedRange(addr);
+ NetPermissionFlags permissionFlags = NetPermissionFlags::PF_NONE;
+ hListenSocket.AddSocketPermissionFlags(permissionFlags);
+ AddWhitelistPermissionFlags(permissionFlags, addr);
+ bool legacyWhitelisted = false;
+ if (NetPermissions::HasFlag(permissionFlags, NetPermissionFlags::PF_ISIMPLICIT)) {
+ NetPermissions::ClearFlag(permissionFlags, PF_ISIMPLICIT);
+ if (gArgs.GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) NetPermissions::AddFlag(permissionFlags, PF_FORCERELAY);
+ if (gArgs.GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY)) NetPermissions::AddFlag(permissionFlags, PF_RELAY);
+ NetPermissions::AddFlag(permissionFlags, PF_MEMPOOL);
+ NetPermissions::AddFlag(permissionFlags, PF_NOBAN);
+ legacyWhitelisted = true;
+ }
+
{
LOCK(cs_vNodes);
for (const CNode* pnode : vNodes) {
@@ -941,7 +962,7 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
// Don't accept connections from banned peers, but if our inbound slots aren't almost full, accept
// if the only banning reason was an automatic misbehavior ban.
- if (!whitelisted && bannedlevel > ((nInbound + 1 < nMaxInbound) ? 1 : 0))
+ if (!NetPermissions::HasFlag(permissionFlags, NetPermissionFlags::PF_NOBAN) && bannedlevel > ((nInbound + 1 < nMaxInbound) ? 1 : 0))
{
LogPrint(BCLog::NET, "connection from %s dropped (banned)\n", addr.ToString());
CloseSocket(hSocket);
@@ -962,9 +983,15 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize();
CAddress addr_bind = GetBindAddress(hSocket);
- CNode* pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addr, CalculateKeyedNetGroup(addr), nonce, addr_bind, "", true);
+ ServiceFlags nodeServices = nLocalServices;
+ if (NetPermissions::HasFlag(permissionFlags, PF_BLOOMFILTER)) {
+ nodeServices = static_cast<ServiceFlags>(nodeServices | NODE_BLOOM);
+ }
+ CNode* pnode = new CNode(id, nodeServices, GetBestHeight(), hSocket, addr, CalculateKeyedNetGroup(addr), nonce, addr_bind, "", true);
pnode->AddRef();
- pnode->fWhitelisted = whitelisted;
+ pnode->m_permissionFlags = permissionFlags;
+ // If this flag is present, the user probably expect that RPC and QT report it as whitelisted (backward compatibility)
+ pnode->m_legacyWhitelisted = legacyWhitelisted;
pnode->m_prefer_evict = bannedlevel > 0;
m_msgproc->InitializeNode(pnode);
@@ -1638,7 +1665,7 @@ int CConnman::GetExtraOutboundCount()
}
}
}
- return std::max(nOutbound - nMaxOutbound, 0);
+ return std::max(nOutbound - m_max_outbound_full_relay - m_max_outbound_block_relay, 0);
}
void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
@@ -1698,7 +1725,8 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
CAddress addrConnect;
// Only connect out to one peer per network group (/16 for IPv4).
- int nOutbound = 0;
+ int nOutboundFullRelay = 0;
+ int nOutboundBlockRelay = 0;
std::set<std::vector<unsigned char> > setConnected;
{
LOCK(cs_vNodes);
@@ -1710,7 +1738,11 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
// also have the added issue that they're attacker controlled and could be used
// to prevent us from connecting to particular hosts if we used them here.
setConnected.insert(pnode->addr.GetGroup());
- nOutbound++;
+ if (pnode->m_tx_relay == nullptr) {
+ nOutboundBlockRelay++;
+ } else if (!pnode->fFeeler) {
+ nOutboundFullRelay++;
+ }
}
}
}
@@ -1729,7 +1761,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
//
bool fFeeler = false;
- if (nOutbound >= nMaxOutbound && !GetTryNewOutboundPeer()) {
+ if (nOutboundFullRelay >= m_max_outbound_full_relay && nOutboundBlockRelay >= m_max_outbound_block_relay && !GetTryNewOutboundPeer()) {
int64_t nTime = GetTimeMicros(); // The current time right now (in microseconds).
if (nTime > nNextFeeler) {
nNextFeeler = PoissonNextSend(nTime, FEELER_INTERVAL);
@@ -1803,7 +1835,14 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
LogPrint(BCLog::NET, "Making feeler connection to %s\n", addrConnect.ToString());
}
- OpenNetworkConnection(addrConnect, (int)setConnected.size() >= std::min(nMaxConnections - 1, 2), &grant, nullptr, false, fFeeler);
+ // Open this connection as block-relay-only if we're already at our
+ // full-relay capacity, but not yet at our block-relay peer limit.
+ // (It should not be possible for fFeeler to be set if we're not
+ // also at our block-relay peer limit, but check against that as
+ // well for sanity.)
+ bool block_relay_only = nOutboundBlockRelay < m_max_outbound_block_relay && !fFeeler && nOutboundFullRelay >= m_max_outbound_full_relay;
+
+ OpenNetworkConnection(addrConnect, (int)setConnected.size() >= std::min(nMaxConnections - 1, 2), &grant, nullptr, false, fFeeler, false, block_relay_only);
}
}
}
@@ -1890,7 +1929,7 @@ void CConnman::ThreadOpenAddedConnections()
}
// if successful, this moves the passed grant to the constructed node
-void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot, bool fFeeler, bool manual_connection)
+void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot, bool fFeeler, bool manual_connection, bool block_relay_only)
{
//
// Initiate outbound network connection
@@ -1909,7 +1948,7 @@ void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
} else if (FindNode(std::string(pszDest)))
return;
- CNode* pnode = ConnectNode(addrConnect, pszDest, fCountFailure, manual_connection);
+ CNode* pnode = ConnectNode(addrConnect, pszDest, fCountFailure, manual_connection, block_relay_only);
if (!pnode)
return;
@@ -1983,7 +2022,7 @@ void CConnman::ThreadMessageHandler()
-bool CConnman::BindListenPort(const CService &addrBind, std::string& strError, bool fWhitelisted)
+bool CConnman::BindListenPort(const CService& addrBind, std::string& strError, NetPermissionFlags permissions)
{
strError = "";
int nOne = 1;
@@ -2044,9 +2083,9 @@ bool CConnman::BindListenPort(const CService &addrBind, std::string& strError, b
return false;
}
- vhListenSocket.push_back(ListenSocket(hListenSocket, fWhitelisted));
+ vhListenSocket.push_back(ListenSocket(hListenSocket, permissions));
- if (addrBind.IsRoutable() && fDiscover && !fWhitelisted)
+ if (addrBind.IsRoutable() && fDiscover && (permissions & PF_NOBAN) == 0)
AddLocal(addrBind, LOCAL_BIND);
return true;
@@ -2130,11 +2169,11 @@ NodeId CConnman::GetNewNodeId()
}
-bool CConnman::Bind(const CService &addr, unsigned int flags) {
+bool CConnman::Bind(const CService &addr, unsigned int flags, NetPermissionFlags permissions) {
if (!(flags & BF_EXPLICIT) && !IsReachable(addr))
return false;
std::string strError;
- if (!BindListenPort(addr, strError, (flags & BF_WHITELIST) != 0)) {
+ if (!BindListenPort(addr, strError, permissions)) {
if ((flags & BF_REPORT_ERROR) && clientInterface) {
clientInterface->ThreadSafeMessageBox(strError, "", CClientUIInterface::MSG_ERROR);
}
@@ -2143,20 +2182,21 @@ bool CConnman::Bind(const CService &addr, unsigned int flags) {
return true;
}
-bool CConnman::InitBinds(const std::vector<CService>& binds, const std::vector<CService>& whiteBinds) {
+bool CConnman::InitBinds(const std::vector<CService>& binds, const std::vector<NetWhitebindPermissions>& whiteBinds)
+{
bool fBound = false;
for (const auto& addrBind : binds) {
- fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR));
+ fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR), NetPermissionFlags::PF_NONE);
}
for (const auto& addrBind : whiteBinds) {
- fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR | BF_WHITELIST));
+ fBound |= Bind(addrBind.m_service, (BF_EXPLICIT | BF_REPORT_ERROR), addrBind.m_flags);
}
if (binds.empty() && whiteBinds.empty()) {
struct in_addr inaddr_any;
inaddr_any.s_addr = INADDR_ANY;
struct in6_addr inaddr6_any = IN6ADDR_ANY_INIT;
- fBound |= Bind(CService(inaddr6_any, GetListenPort()), BF_NONE);
- fBound |= Bind(CService(inaddr_any, GetListenPort()), !fBound ? BF_REPORT_ERROR : BF_NONE);
+ fBound |= Bind(CService(inaddr6_any, GetListenPort()), BF_NONE, NetPermissionFlags::PF_NONE);
+ fBound |= Bind(CService(inaddr_any, GetListenPort()), !fBound ? BF_REPORT_ERROR : BF_NONE, NetPermissionFlags::PF_NONE);
}
return fBound;
}
@@ -2211,7 +2251,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
if (semOutbound == nullptr) {
// initialize semaphore
- semOutbound = MakeUnique<CSemaphore>(std::min((nMaxOutbound + nMaxFeeler), nMaxConnections));
+ semOutbound = MakeUnique<CSemaphore>(std::min(m_max_outbound, nMaxConnections));
}
if (semAddnode == nullptr) {
// initialize semaphore
@@ -2289,7 +2329,7 @@ void CConnman::Interrupt()
InterruptSocks5(true);
if (semOutbound) {
- for (int i=0; i<(nMaxOutbound + nMaxFeeler); i++) {
+ for (int i=0; i<m_max_outbound; i++) {
semOutbound->post();
}
}
@@ -2599,14 +2639,17 @@ int CConnman::GetBestHeight() const
unsigned int CConnman::GetReceiveFloodSize() const { return nReceiveFloodSize; }
-CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress& addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const CAddress& addrBindIn, const std::string& addrNameIn, bool fInboundIn)
+CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress& addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const CAddress& addrBindIn, const std::string& addrNameIn, bool fInboundIn, bool block_relay_only)
: nTimeConnected(GetSystemTimeInSeconds()),
addr(addrIn),
addrBind(addrBindIn),
fInbound(fInboundIn),
nKeyedNetGroup(nKeyedNetGroupIn),
addrKnown(5000, 0.001),
- filterInventoryKnown(50000, 0.000001),
+ // Don't relay addr messages to peers that we connect to as block-relay-only
+ // peers (to prevent adversaries from inferring these links from addr
+ // traffic).
+ m_addr_relay_peer(!block_relay_only),
id(idIn),
nLocalHostNonce(nLocalHostNonceIn),
nLocalServices(nLocalServicesIn),
@@ -2615,8 +2658,9 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn
hSocket = hSocketIn;
addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn;
hashContinue = uint256();
- filterInventoryKnown.reset();
- pfilter = MakeUnique<CBloomFilter>();
+ if (!block_relay_only) {
+ m_tx_relay = MakeUnique<TxRelay>();
+ }
for (const std::string &msg : getAllNetMessageTypes())
mapRecvBytesPerMsgCmd[msg] = 0;
diff --git a/src/net.h b/src/net.h
index 37aaf1a63b..f33147155a 100644
--- a/src/net.h
+++ b/src/net.h
@@ -15,6 +15,7 @@
#include <hash.h>
#include <limitedmap.h>
#include <netaddress.h>
+#include <net_permissions.h>
#include <policy/feerate.h>
#include <protocol.h>
#include <random.h>
@@ -39,6 +40,11 @@ class CScheduler;
class CNode;
class BanMan;
+/** Default for -whitelistrelay. */
+static const bool DEFAULT_WHITELISTRELAY = true;
+/** Default for -whitelistforcerelay. */
+static const bool DEFAULT_WHITELISTFORCERELAY = false;
+
/** Time between pings automatically sent out for latency probing and keepalive (in seconds). */
static const int PING_INTERVAL = 2 * 60;
/** Time after which to disconnect, after waiting for a ping response (or inactivity). */
@@ -55,10 +61,12 @@ static const unsigned int MAX_ADDR_TO_SEND = 1000;
static const unsigned int MAX_PROTOCOL_MESSAGE_LENGTH = 4 * 1000 * 1000;
/** Maximum length of the user agent string in `version` message */
static const unsigned int MAX_SUBVERSION_LENGTH = 256;
-/** Maximum number of automatic outgoing nodes */
-static const int MAX_OUTBOUND_CONNECTIONS = 8;
+/** Maximum number of automatic outgoing nodes over which we'll relay everything (blocks, tx, addrs, etc) */
+static const int MAX_OUTBOUND_FULL_RELAY_CONNECTIONS = 8;
/** Maximum number of addnode outgoing nodes */
static const int MAX_ADDNODE_CONNECTIONS = 8;
+/** Maximum number of block-relay-only outgoing connections */
+static const int MAX_BLOCKS_ONLY_CONNECTIONS = 2;
/** -listen default */
static const bool DEFAULT_LISTEN = true;
/** -upnp default */
@@ -125,7 +133,8 @@ public:
{
ServiceFlags nLocalServices = NODE_NONE;
int nMaxConnections = 0;
- int nMaxOutbound = 0;
+ int m_max_outbound_full_relay = 0;
+ int m_max_outbound_block_relay = 0;
int nMaxAddnode = 0;
int nMaxFeeler = 0;
int nBestHeight = 0;
@@ -138,8 +147,9 @@ public:
uint64_t nMaxOutboundLimit = 0;
int64_t m_peer_connect_timeout = DEFAULT_PEER_CONNECT_TIMEOUT;
std::vector<std::string> vSeedNodes;
- std::vector<CSubNet> vWhitelistedRange;
- std::vector<CService> vBinds, vWhiteBinds;
+ std::vector<NetWhitelistPermissions> vWhitelistedRange;
+ std::vector<NetWhitebindPermissions> vWhiteBinds;
+ std::vector<CService> vBinds;
bool m_use_addrman_outgoing = true;
std::vector<std::string> m_specified_outgoing;
std::vector<std::string> m_added_nodes;
@@ -148,10 +158,12 @@ public:
void Init(const Options& connOptions) {
nLocalServices = connOptions.nLocalServices;
nMaxConnections = connOptions.nMaxConnections;
- nMaxOutbound = std::min(connOptions.nMaxOutbound, connOptions.nMaxConnections);
+ m_max_outbound_full_relay = std::min(connOptions.m_max_outbound_full_relay, connOptions.nMaxConnections);
+ m_max_outbound_block_relay = connOptions.m_max_outbound_block_relay;
m_use_addrman_outgoing = connOptions.m_use_addrman_outgoing;
nMaxAddnode = connOptions.nMaxAddnode;
nMaxFeeler = connOptions.nMaxFeeler;
+ m_max_outbound = m_max_outbound_full_relay + m_max_outbound_block_relay + nMaxFeeler;
nBestHeight = connOptions.nBestHeight;
clientInterface = connOptions.uiInterface;
m_banman = connOptions.m_banman;
@@ -190,7 +202,7 @@ public:
bool GetNetworkActive() const { return fNetworkActive; };
bool GetUseAddrmanOutgoing() const { return m_use_addrman_outgoing; };
void SetNetworkActive(bool active);
- void OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound = nullptr, const char *strDest = nullptr, bool fOneShot = false, bool fFeeler = false, bool manual_connection = false);
+ void OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound = nullptr, const char *strDest = nullptr, bool fOneShot = false, bool fFeeler = false, bool manual_connection = false, bool block_relay_only = false);
bool CheckIncomingNonce(uint64_t nonce);
bool ForNode(NodeId id, std::function<bool(CNode* pnode)> func);
@@ -246,7 +258,7 @@ public:
void AddNewAddresses(const std::vector<CAddress>& vAddr, const CAddress& addrFrom, int64_t nTimePenalty = 0);
std::vector<CAddress> GetAddresses();
- // This allows temporarily exceeding nMaxOutbound, with the goal of finding
+ // This allows temporarily exceeding m_max_outbound_full_relay, with the goal of finding
// a peer that is better than all our current peers.
void SetTryNewOutboundPeer(bool flag);
bool GetTryNewOutboundPeer();
@@ -270,6 +282,12 @@ public:
bool DisconnectNode(const CNetAddr& addr);
bool DisconnectNode(NodeId id);
+ //! Used to convey which local services we are offering peers during node
+ //! connection.
+ //!
+ //! The data returned by this is used in CNode construction,
+ //! which is used to advertise which services we are offering
+ //! that peer during `net_processing.cpp:PushNodeVersion()`.
ServiceFlags GetLocalServices() const;
//!set the max outbound target in bytes
@@ -314,15 +332,17 @@ public:
private:
struct ListenSocket {
+ public:
SOCKET socket;
- bool whitelisted;
-
- ListenSocket(SOCKET socket_, bool whitelisted_) : socket(socket_), whitelisted(whitelisted_) {}
+ inline void AddSocketPermissionFlags(NetPermissionFlags& flags) const { NetPermissions::AddFlag(flags, m_permissions); }
+ ListenSocket(SOCKET socket_, NetPermissionFlags permissions_) : socket(socket_), m_permissions(permissions_) {}
+ private:
+ NetPermissionFlags m_permissions;
};
- bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false);
- bool Bind(const CService &addr, unsigned int flags);
- bool InitBinds(const std::vector<CService>& binds, const std::vector<CService>& whiteBinds);
+ bool BindListenPort(const CService& bindAddr, std::string& strError, NetPermissionFlags permissions);
+ bool Bind(const CService& addr, unsigned int flags, NetPermissionFlags permissions);
+ bool InitBinds(const std::vector<CService>& binds, const std::vector<NetWhitebindPermissions>& whiteBinds);
void ThreadOpenAddedConnections();
void AddOneShot(const std::string& strDest);
void ProcessOneShot();
@@ -346,8 +366,8 @@ private:
CNode* FindNode(const CService& addr);
bool AttemptToEvictConnection();
- CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, bool manual_connection);
- bool IsWhitelistedRange(const CNetAddr &addr);
+ CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, bool manual_connection, bool block_relay_only);
+ void AddWhitelistPermissionFlags(NetPermissionFlags& flags, const CNetAddr &addr) const;
void DeleteNode(CNode* pnode);
@@ -380,7 +400,7 @@ private:
// Whitelisted ranges. Any node connecting from these is automatically
// whitelisted (as well as those connecting to whitelisted binds).
- std::vector<CSubNet> vWhitelistedRange;
+ std::vector<NetWhitelistPermissions> vWhitelistedRange;
unsigned int nSendBufferMaxSize{0};
unsigned int nReceiveFloodSize{0};
@@ -399,15 +419,34 @@ private:
std::atomic<NodeId> nLastNodeId{0};
unsigned int nPrevNodeCount{0};
- /** Services this instance offers */
+ /**
+ * Services this instance offers.
+ *
+ * This data is replicated in each CNode instance we create during peer
+ * connection (in ConnectNode()) under a member also called
+ * nLocalServices.
+ *
+ * This data is not marked const, but after being set it should not
+ * change. See the note in CNode::nLocalServices documentation.
+ *
+ * \sa CNode::nLocalServices
+ */
ServiceFlags nLocalServices;
std::unique_ptr<CSemaphore> semOutbound;
std::unique_ptr<CSemaphore> semAddnode;
int nMaxConnections;
- int nMaxOutbound;
+
+ // How many full-relay (tx, block, addr) outbound peers we want
+ int m_max_outbound_full_relay;
+
+ // How many block-relay only outbound peers we want
+ // We do not relay tx or addr messages with these peers
+ int m_max_outbound_block_relay;
+
int nMaxAddnode;
int nMaxFeeler;
+ int m_max_outbound;
bool m_use_addrman_outgoing;
std::atomic<int> nBestHeight;
CClientUIInterface* clientInterface;
@@ -433,7 +472,7 @@ private:
std::thread threadMessageHandler;
/** flag for deciding to connect to an extra outbound peer,
- * in excess of nMaxOutbound
+ * in excess of m_max_outbound_full_relay
* This takes the place of a feeler connection */
std::atomic_bool m_try_another_outbound_peer;
@@ -448,7 +487,6 @@ void StartMapPort();
void InterruptMapPort();
void StopMapPort();
unsigned short GetListenPort();
-bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false);
struct CombinerAll
{
@@ -555,7 +593,8 @@ public:
mapMsgCmdSize mapSendBytesPerMsgCmd;
uint64_t nRecvBytes;
mapMsgCmdSize mapRecvBytesPerMsgCmd;
- bool fWhitelisted;
+ NetPermissionFlags m_permissionFlags;
+ bool m_legacyWhitelisted;
double dPingTime;
double dPingWait;
double dMinPing;
@@ -657,7 +696,11 @@ public:
*/
std::string cleanSubVer GUARDED_BY(cs_SubVer){};
bool m_prefer_evict{false}; // This peer is preferred for eviction.
- bool fWhitelisted{false}; // This peer can bypass DoS banning.
+ bool HasPermission(NetPermissionFlags permission) const {
+ return NetPermissions::HasFlag(m_permissionFlags, permission);
+ }
+ // This boolean is unusued in actual processing, only present for backward compatibility at RPC/QT level
+ bool m_legacyWhitelisted{false};
bool fFeeler{false}; // If true this node is being used as a short lived feeler.
bool fOneShot{false};
bool m_manual_connection{false};
@@ -668,15 +711,8 @@ public:
// Setting fDisconnect to true will cause the node to be disconnected the
// next time DisconnectNodes() runs
std::atomic_bool fDisconnect{false};
- // We use fRelayTxes for two purposes -
- // a) it allows us to not relay tx invs before receiving the peer's version message
- // b) the peer may tell us in its version message that we should not relay tx invs
- // unless it loads a bloom filter.
- bool fRelayTxes GUARDED_BY(cs_filter){false};
bool fSentAddr{false};
CSemaphoreGrant grantOutbound;
- mutable CCriticalSection cs_filter;
- std::unique_ptr<CBloomFilter> pfilter PT_GUARDED_BY(cs_filter);
std::atomic<int> nRefCount{0};
const uint64_t nKeyedNetGroup;
@@ -695,28 +731,51 @@ public:
std::vector<CAddress> vAddrToSend;
CRollingBloomFilter addrKnown;
bool fGetAddr{false};
- std::set<uint256> setKnown;
int64_t nNextAddrSend GUARDED_BY(cs_sendProcessing){0};
int64_t nNextLocalAddrSend GUARDED_BY(cs_sendProcessing){0};
- // inventory based relay
- CRollingBloomFilter filterInventoryKnown GUARDED_BY(cs_inventory);
- // Set of transaction ids we still have to announce.
- // They are sorted by the mempool before relay, so the order is not important.
- std::set<uint256> setInventoryTxToSend;
+ const bool m_addr_relay_peer;
+ bool IsAddrRelayPeer() const { return m_addr_relay_peer; }
+
// List of block ids we still have announce.
// There is no final sorting before sending, as they are always sent immediately
// and in the order requested.
std::vector<uint256> vInventoryBlockToSend GUARDED_BY(cs_inventory);
CCriticalSection cs_inventory;
- int64_t nNextInvSend{0};
+
+ struct TxRelay {
+ TxRelay() { pfilter = MakeUnique<CBloomFilter>(); }
+ mutable CCriticalSection cs_filter;
+ // We use fRelayTxes for two purposes -
+ // a) it allows us to not relay tx invs before receiving the peer's version message
+ // b) the peer may tell us in its version message that we should not relay tx invs
+ // unless it loads a bloom filter.
+ bool fRelayTxes GUARDED_BY(cs_filter){false};
+ std::unique_ptr<CBloomFilter> pfilter PT_GUARDED_BY(cs_filter) GUARDED_BY(cs_filter);
+
+ mutable CCriticalSection cs_tx_inventory;
+ CRollingBloomFilter filterInventoryKnown GUARDED_BY(cs_tx_inventory){50000, 0.000001};
+ // Set of transaction ids we still have to announce.
+ // They are sorted by the mempool before relay, so the order is not important.
+ std::set<uint256> setInventoryTxToSend;
+ // Used for BIP35 mempool sending
+ bool fSendMempool GUARDED_BY(cs_tx_inventory){false};
+ // Last time a "MEMPOOL" request was serviced.
+ std::atomic<int64_t> timeLastMempoolReq{0};
+ int64_t nNextInvSend{0};
+
+ CCriticalSection cs_feeFilter;
+ // Minimum fee rate with which to filter inv's to this node
+ CAmount minFeeFilter GUARDED_BY(cs_feeFilter){0};
+ CAmount lastSentFeeFilter{0};
+ int64_t nextSendTimeFeeFilter{0};
+ };
+
+ // m_tx_relay == nullptr if we're not relaying transactions with this peer
+ std::unique_ptr<TxRelay> m_tx_relay;
+
// Used for headers announcements - unfiltered blocks to relay
std::vector<uint256> vBlockHashesToAnnounce GUARDED_BY(cs_inventory);
- // Used for BIP35 mempool sending
- bool fSendMempool GUARDED_BY(cs_inventory){false};
-
- // Last time a "MEMPOOL" request was serviced.
- std::atomic<int64_t> timeLastMempoolReq{0};
// Block and TXN accept times
std::atomic<int64_t> nLastBlockTime{0};
@@ -733,15 +792,10 @@ public:
std::atomic<int64_t> nMinPingUsecTime{std::numeric_limits<int64_t>::max()};
// Whether a ping is requested.
std::atomic<bool> fPingQueued{false};
- // Minimum fee rate with which to filter inv's to this node
- CAmount minFeeFilter GUARDED_BY(cs_feeFilter){0};
- CCriticalSection cs_feeFilter;
- CAmount lastSentFeeFilter{0};
- int64_t nextSendTimeFeeFilter{0};
std::set<uint256> orphan_work_set;
- CNode(NodeId id, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress &addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const CAddress &addrBindIn, const std::string &addrNameIn = "", bool fInboundIn = false);
+ CNode(NodeId id, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress &addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const CAddress &addrBindIn, const std::string &addrNameIn = "", bool fInboundIn = false, bool block_relay_only = false);
~CNode();
CNode(const CNode&) = delete;
CNode& operator=(const CNode&) = delete;
@@ -749,10 +803,27 @@ public:
private:
const NodeId id;
const uint64_t nLocalHostNonce;
- // Services offered to this peer
+
+ //! Services offered to this peer.
+ //!
+ //! This is supplied by the parent CConnman during peer connection
+ //! (CConnman::ConnectNode()) from its attribute of the same name.
+ //!
+ //! This is const because there is no protocol defined for renegotiating
+ //! services initially offered to a peer. The set of local services we
+ //! offer should not change after initialization.
+ //!
+ //! An interesting example of this is NODE_NETWORK and initial block
+ //! download: a node which starts up from scratch doesn't have any blocks
+ //! to serve, but still advertises NODE_NETWORK because it will eventually
+ //! fulfill this role after IBD completes. P2P code is written in such a
+ //! way that it can gracefully handle peers who don't make good on their
+ //! service advertisements.
const ServiceFlags nLocalServices;
+
const int nMyStartingHeight;
int nSendVersion{0};
+ NetPermissionFlags m_permissionFlags{ PF_NONE };
std::list<CNetMessage> vRecvMsg; // Used only by SocketHandler thread
mutable CCriticalSection cs_addrName;
@@ -833,20 +904,21 @@ public:
void AddInventoryKnown(const CInv& inv)
{
- {
- LOCK(cs_inventory);
- filterInventoryKnown.insert(inv.hash);
+ if (m_tx_relay != nullptr) {
+ LOCK(m_tx_relay->cs_tx_inventory);
+ m_tx_relay->filterInventoryKnown.insert(inv.hash);
}
}
void PushInventory(const CInv& inv)
{
- LOCK(cs_inventory);
- if (inv.type == MSG_TX) {
- if (!filterInventoryKnown.contains(inv.hash)) {
- setInventoryTxToSend.insert(inv.hash);
+ if (inv.type == MSG_TX && m_tx_relay != nullptr) {
+ LOCK(m_tx_relay->cs_tx_inventory);
+ if (!m_tx_relay->filterInventoryKnown.contains(inv.hash)) {
+ m_tx_relay->setInventoryTxToSend.insert(inv.hash);
}
} else if (inv.type == MSG_BLOCK) {
+ LOCK(cs_inventory);
vInventoryBlockToSend.push_back(inv.hash);
}
}
diff --git a/src/net_permissions.cpp b/src/net_permissions.cpp
new file mode 100644
index 0000000000..ef6c40ce20
--- /dev/null
+++ b/src/net_permissions.cpp
@@ -0,0 +1,107 @@
+// 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 <net_permissions.h>
+#include <netbase.h>
+#include <util/error.h>
+#include <util/system.h>
+#include <util/translation.h>
+
+// The parse the following format "perm1,perm2@xxxxxx"
+bool TryParsePermissionFlags(const std::string str, NetPermissionFlags& output, size_t& readen, std::string& error)
+{
+ NetPermissionFlags flags = PF_NONE;
+ const auto atSeparator = str.find('@');
+
+ // if '@' is not found (ie, "xxxxx"), the caller should apply implicit permissions
+ if (atSeparator == std::string::npos) {
+ NetPermissions::AddFlag(flags, PF_ISIMPLICIT);
+ readen = 0;
+ }
+ // else (ie, "perm1,perm2@xxxxx"), let's enumerate the permissions by splitting by ',' and calculate the flags
+ else {
+ readen = 0;
+ // permissions == perm1,perm2
+ const auto permissions = str.substr(0, atSeparator);
+ while (readen < permissions.length()) {
+ const auto commaSeparator = permissions.find(',', readen);
+ const auto len = commaSeparator == std::string::npos ? permissions.length() - readen : commaSeparator - readen;
+ // permission == perm1
+ const auto permission = permissions.substr(readen, len);
+ readen += len; // We read "perm1"
+ if (commaSeparator != std::string::npos) readen++; // We read ","
+
+ if (permission == "bloomfilter" || permission == "bloom") NetPermissions::AddFlag(flags, PF_BLOOMFILTER);
+ else if (permission == "noban") NetPermissions::AddFlag(flags, PF_NOBAN);
+ else if (permission == "forcerelay") NetPermissions::AddFlag(flags, PF_FORCERELAY);
+ else if (permission == "mempool") NetPermissions::AddFlag(flags, PF_MEMPOOL);
+ else if (permission == "all") NetPermissions::AddFlag(flags, PF_ALL);
+ else if (permission == "relay") NetPermissions::AddFlag(flags, PF_RELAY);
+ else if (permission.length() == 0); // Allow empty entries
+ else {
+ error = strprintf(_("Invalid P2P permission: '%s'").translated, permission);
+ return false;
+ }
+ }
+ readen++;
+ }
+
+ output = flags;
+ error = "";
+ return true;
+}
+
+std::vector<std::string> NetPermissions::ToStrings(NetPermissionFlags flags)
+{
+ std::vector<std::string> strings;
+ if (NetPermissions::HasFlag(flags, PF_BLOOMFILTER)) strings.push_back("bloomfilter");
+ if (NetPermissions::HasFlag(flags, PF_NOBAN)) strings.push_back("noban");
+ if (NetPermissions::HasFlag(flags, PF_FORCERELAY)) strings.push_back("forcerelay");
+ if (NetPermissions::HasFlag(flags, PF_RELAY)) strings.push_back("relay");
+ if (NetPermissions::HasFlag(flags, PF_MEMPOOL)) strings.push_back("mempool");
+ return strings;
+}
+
+bool NetWhitebindPermissions::TryParse(const std::string str, NetWhitebindPermissions& output, std::string& error)
+{
+ NetPermissionFlags flags;
+ size_t offset;
+ if (!TryParsePermissionFlags(str, flags, offset, error)) return false;
+
+ const std::string strBind = str.substr(offset);
+ CService addrBind;
+ if (!Lookup(strBind.c_str(), addrBind, 0, false)) {
+ error = ResolveErrMsg("whitebind", strBind);
+ return false;
+ }
+ if (addrBind.GetPort() == 0) {
+ error = strprintf(_("Need to specify a port with -whitebind: '%s'").translated, strBind);
+ return false;
+ }
+
+ output.m_flags = flags;
+ output.m_service = addrBind;
+ error = "";
+ return true;
+}
+
+bool NetWhitelistPermissions::TryParse(const std::string str, NetWhitelistPermissions& output, std::string& error)
+{
+ NetPermissionFlags flags;
+ size_t offset;
+ if (!TryParsePermissionFlags(str, flags, offset, error)) return false;
+
+ const std::string net = str.substr(offset);
+ CSubNet subnet;
+ LookupSubNet(net.c_str(), subnet);
+ if (!subnet.IsValid()) {
+ error = strprintf(_("Invalid netmask specified in -whitelist: '%s'").translated, net);
+ return false;
+ }
+
+ output.m_flags = flags;
+ output.m_subnet = subnet;
+ error = "";
+ return true;
+}
diff --git a/src/net_permissions.h b/src/net_permissions.h
new file mode 100644
index 0000000000..b3987de65f
--- /dev/null
+++ b/src/net_permissions.h
@@ -0,0 +1,62 @@
+// 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 <string>
+#include <vector>
+#include <netaddress.h>
+
+#ifndef BITCOIN_NET_PERMISSIONS_H
+#define BITCOIN_NET_PERMISSIONS_H
+enum NetPermissionFlags
+{
+ PF_NONE = 0,
+ // Can query bloomfilter even if -peerbloomfilters is false
+ PF_BLOOMFILTER = (1U << 1),
+ // Relay and accept transactions from this peer, even if -blocksonly is true
+ PF_RELAY = (1U << 3),
+ // Always relay transactions from this peer, even if already in mempool or rejected from policy
+ // Keep parameter interaction: forcerelay implies relay
+ PF_FORCERELAY = (1U << 2) | PF_RELAY,
+ // Can't be banned for misbehavior
+ PF_NOBAN = (1U << 4),
+ // Can query the mempool
+ PF_MEMPOOL = (1U << 5),
+
+ // True if the user did not specifically set fine grained permissions
+ PF_ISIMPLICIT = (1U << 31),
+ PF_ALL = PF_BLOOMFILTER | PF_FORCERELAY | PF_RELAY | PF_NOBAN | PF_MEMPOOL,
+};
+class NetPermissions
+{
+public:
+ NetPermissionFlags m_flags;
+ static std::vector<std::string> ToStrings(NetPermissionFlags flags);
+ static inline bool HasFlag(const NetPermissionFlags& flags, NetPermissionFlags f)
+ {
+ return (flags & f) == f;
+ }
+ static inline void AddFlag(NetPermissionFlags& flags, NetPermissionFlags f)
+ {
+ flags = static_cast<NetPermissionFlags>(flags | f);
+ }
+ static inline void ClearFlag(NetPermissionFlags& flags, NetPermissionFlags f)
+ {
+ flags = static_cast<NetPermissionFlags>(flags & ~f);
+ }
+};
+class NetWhitebindPermissions : public NetPermissions
+{
+public:
+ static bool TryParse(const std::string str, NetWhitebindPermissions& output, std::string& error);
+ CService m_service;
+};
+
+class NetWhitelistPermissions : public NetPermissions
+{
+public:
+ static bool TryParse(const std::string str, NetWhitelistPermissions& output, std::string& error);
+ CSubNet m_subnet;
+};
+
+#endif // BITCOIN_NET_PERMISSIONS_H \ No newline at end of file
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 4e631fd00e..34d349e8e9 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -68,13 +68,13 @@ static constexpr int32_t MAX_PEER_TX_IN_FLIGHT = 100;
/** Maximum number of announced transactions from a peer */
static constexpr int32_t MAX_PEER_TX_ANNOUNCEMENTS = 2 * MAX_INV_SZ;
/** How many microseconds to delay requesting transactions from inbound peers */
-static constexpr int64_t INBOUND_PEER_TX_DELAY = 2 * 1000000; // 2 seconds
+static constexpr std::chrono::microseconds INBOUND_PEER_TX_DELAY{std::chrono::seconds{2}};
/** How long to wait (in microseconds) before downloading a transaction from an additional peer */
-static constexpr int64_t GETDATA_TX_INTERVAL = 60 * 1000000; // 1 minute
+static constexpr std::chrono::microseconds GETDATA_TX_INTERVAL{std::chrono::seconds{60}};
/** Maximum delay (in microseconds) for transaction requests to avoid biasing some peers over others. */
-static constexpr int64_t MAX_GETDATA_RANDOM_DELAY = 2 * 1000000; // 2 seconds
+static constexpr std::chrono::microseconds MAX_GETDATA_RANDOM_DELAY{std::chrono::seconds{2}};
/** How long to wait (in microseconds) before expiring an in-flight getdata request to a peer */
-static constexpr int64_t TX_EXPIRY_INTERVAL = 10 * GETDATA_TX_INTERVAL;
+static constexpr std::chrono::microseconds TX_EXPIRY_INTERVAL{GETDATA_TX_INTERVAL * 10};
static_assert(INBOUND_PEER_TX_DELAY >= MAX_GETDATA_RANDOM_DELAY,
"To preserve security, MAX_GETDATA_RANDOM_DELAY should not exceed INBOUND_PEER_DELAY");
/** Limit to avoid sending big packets. Not used in processing incoming GETDATA for compatibility */
@@ -262,7 +262,7 @@ struct CNodeState {
bool fSupportsDesiredCmpctVersion;
/** State used to enforce CHAIN_SYNC_TIMEOUT
- * Only in effect for outbound, non-manual connections, with
+ * Only in effect for outbound, non-manual, full-relay connections, with
* m_protect == false
* Algorithm: if a peer's best known block has less work than our tip,
* set a timeout CHAIN_SYNC_TIMEOUT seconds in the future:
@@ -340,16 +340,16 @@ struct CNodeState {
/* Track when to attempt download of announced transactions (process
* time in micros -> txid)
*/
- std::multimap<int64_t, uint256> m_tx_process_time;
+ std::multimap<std::chrono::microseconds, uint256> m_tx_process_time;
//! Store all the transactions a peer has recently announced
std::set<uint256> m_tx_announced;
//! Store transactions which were requested by us, with timestamp
- std::map<uint256, int64_t> m_tx_in_flight;
+ std::map<uint256, std::chrono::microseconds> m_tx_in_flight;
//! Periodically check for stuck getdata requests
- int64_t m_check_expiry_timer{0};
+ std::chrono::microseconds m_check_expiry_timer{0};
};
TxDownloadState m_tx_download;
@@ -391,7 +391,7 @@ struct CNodeState {
};
// Keeps track of the time (in microseconds) when transactions were requested last time
-limitedmap<uint256, int64_t> g_already_asked_for GUARDED_BY(cs_main)(MAX_INV_SZ);
+limitedmap<uint256, std::chrono::microseconds> g_already_asked_for GUARDED_BY(cs_main)(MAX_INV_SZ);
/** Map maintaining per-node state. */
static std::map<NodeId, CNodeState> mapNodeState GUARDED_BY(cs_main);
@@ -408,13 +408,16 @@ static void UpdatePreferredDownload(CNode* node, CNodeState* state) EXCLUSIVE_LO
nPreferredDownload -= state->fPreferredDownload;
// Whether this node should be marked as a preferred download node.
- state->fPreferredDownload = (!node->fInbound || node->fWhitelisted) && !node->fOneShot && !node->fClient;
+ state->fPreferredDownload = (!node->fInbound || node->HasPermission(PF_NOBAN)) && !node->fOneShot && !node->fClient;
nPreferredDownload += state->fPreferredDownload;
}
static void PushNodeVersion(CNode *pnode, CConnman* connman, int64_t nTime)
{
+ // Note that pnode->GetLocalServices() is a reflection of the local
+ // services we were offering when the CNode object was created for this
+ // peer.
ServiceFlags nLocalNodeServices = pnode->GetLocalServices();
uint64_t nonce = pnode->GetLocalNonce();
int nNodeStartingHeight = pnode->GetMyStartingHeight();
@@ -425,7 +428,7 @@ static void PushNodeVersion(CNode *pnode, CConnman* connman, int64_t nTime)
CAddress addrMe = CAddress(CService(), nLocalNodeServices);
connman->PushMessage(pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERSION, PROTOCOL_VERSION, (uint64_t)nLocalNodeServices, nTime, addrYou, addrMe,
- nonce, strSubVersion, nNodeStartingHeight, ::g_relay_txes));
+ nonce, strSubVersion, nNodeStartingHeight, ::g_relay_txes && pnode->m_tx_relay != nullptr));
if (fLogIPs) {
LogPrint(BCLog::NET, "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%d\n", PROTOCOL_VERSION, nNodeStartingHeight, addrMe.ToString(), addrYou.ToString(), nodeid);
@@ -688,16 +691,16 @@ void EraseTxRequest(const uint256& txid) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
g_already_asked_for.erase(txid);
}
-int64_t GetTxRequestTime(const uint256& txid) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+std::chrono::microseconds GetTxRequestTime(const uint256& txid) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
auto it = g_already_asked_for.find(txid);
if (it != g_already_asked_for.end()) {
return it->second;
}
- return 0;
+ return {};
}
-void UpdateTxRequestTime(const uint256& txid, int64_t request_time) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+void UpdateTxRequestTime(const uint256& txid, std::chrono::microseconds request_time) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
auto it = g_already_asked_for.find(txid);
if (it == g_already_asked_for.end()) {
@@ -707,17 +710,17 @@ void UpdateTxRequestTime(const uint256& txid, int64_t request_time) EXCLUSIVE_LO
}
}
-int64_t CalculateTxGetDataTime(const uint256& txid, int64_t current_time, bool use_inbound_delay) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+std::chrono::microseconds CalculateTxGetDataTime(const uint256& txid, std::chrono::microseconds current_time, bool use_inbound_delay) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
- int64_t process_time;
- int64_t last_request_time = GetTxRequestTime(txid);
+ std::chrono::microseconds process_time;
+ const auto last_request_time = GetTxRequestTime(txid);
// First time requesting this tx
- if (last_request_time == 0) {
+ if (last_request_time.count() == 0) {
process_time = current_time;
} else {
// Randomize the delay to avoid biasing some peers over others (such as due to
// fixed ordering of peer processing in ThreadMessageHandler)
- process_time = last_request_time + GETDATA_TX_INTERVAL + GetRand(MAX_GETDATA_RANDOM_DELAY);
+ process_time = last_request_time + GETDATA_TX_INTERVAL + GetRandMicros(MAX_GETDATA_RANDOM_DELAY);
}
// We delay processing announcements from inbound peers
@@ -726,7 +729,7 @@ int64_t CalculateTxGetDataTime(const uint256& txid, int64_t current_time, bool u
return process_time;
}
-void RequestTx(CNodeState* state, const uint256& txid, int64_t nNow) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+void RequestTx(CNodeState* state, const uint256& txid, std::chrono::microseconds current_time) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
CNodeState::TxDownloadState& peer_download_state = state->m_tx_download;
if (peer_download_state.m_tx_announced.size() >= MAX_PEER_TX_ANNOUNCEMENTS ||
@@ -740,7 +743,7 @@ void RequestTx(CNodeState* state, const uint256& txid, int64_t nNow) EXCLUSIVE_L
// Calculate the time to try requesting this transaction. Use
// fPreferredDownload as a proxy for outbound peers.
- int64_t process_time = CalculateTxGetDataTime(txid, nNow, !state->fPreferredDownload);
+ const auto process_time = CalculateTxGetDataTime(txid, current_time, !state->fPreferredDownload);
peer_download_state.m_tx_process_time.emplace(process_time, txid);
}
@@ -757,7 +760,7 @@ void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds)
}
// Returns true for outbound peers, excluding manual connections, feelers, and
-// one-shots
+// one-shots.
static bool IsOutboundDisconnectionCandidate(const CNode *node)
{
return !(node->fInbound || node->m_manual_connection || node->fFeeler || node->fOneShot);
@@ -1291,11 +1294,12 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
LOCK(g_cs_orphans);
if (mapOrphanTransactions.count(inv.hash)) return true;
}
+ const CCoinsViewCache& coins_cache = ::ChainstateActive().CoinsTip();
return recentRejects->contains(inv.hash) ||
mempool.exists(inv.hash) ||
- pcoinsTip->HaveCoinInCache(COutPoint(inv.hash, 0)) || // Best effort: only try output 0 and 1
- pcoinsTip->HaveCoinInCache(COutPoint(inv.hash, 1));
+ coins_cache.HaveCoinInCache(COutPoint(inv.hash, 0)) || // Best effort: only try output 0 and 1
+ coins_cache.HaveCoinInCache(COutPoint(inv.hash, 1));
}
case MSG_BLOCK:
case MSG_WITNESS_BLOCK:
@@ -1329,7 +1333,7 @@ static void RelayAddress(const CAddress& addr, bool fReachable, CConnman* connma
assert(nRelayNodes <= best.size());
auto sortfunc = [&best, &hasher, nRelayNodes](CNode* pnode) {
- if (pnode->nVersion >= CADDR_TIME_VERSION) {
+ if (pnode->nVersion >= CADDR_TIME_VERSION && pnode->IsAddrRelayPeer()) {
uint64_t hashKey = CSipHasher(hasher).Write(pnode->GetId()).Finalize();
for (unsigned int i = 0; i < nRelayNodes; i++) {
if (hashKey > best[i].first) {
@@ -1398,7 +1402,7 @@ void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, c
const CNetMsgMaker msgMaker(pfrom->GetSendVersion());
// disconnect node in case we have reached the outbound limit for serving historical blocks
// never disconnect whitelisted nodes
- if (send && connman->OutboundTargetReached(true) && ( ((pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() > HISTORICAL_BLOCK_AGE)) || inv.type == MSG_FILTERED_BLOCK) && !pfrom->fWhitelisted)
+ if (send && connman->OutboundTargetReached(true) && ( ((pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() > HISTORICAL_BLOCK_AGE)) || inv.type == MSG_FILTERED_BLOCK) && !pfrom->HasPermission(PF_NOBAN))
{
LogPrint(BCLog::NET, "historical block serving limit reached, disconnect peer=%d\n", pfrom->GetId());
@@ -1407,7 +1411,7 @@ void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, c
send = false;
}
// Avoid leaking prune-height by never sending blocks below the NODE_NETWORK_LIMITED threshold
- if (send && !pfrom->fWhitelisted && (
+ if (send && !pfrom->HasPermission(PF_NOBAN) && (
(((pfrom->GetLocalServices() & NODE_NETWORK_LIMITED) == NODE_NETWORK_LIMITED) && ((pfrom->GetLocalServices() & NODE_NETWORK) != NODE_NETWORK) && (::ChainActive().Tip()->nHeight - pindex->nHeight > (int)NODE_NETWORK_LIMITED_MIN_BLOCKS + 2 /* add two blocks buffer extension for possible races */) )
)) {
LogPrint(BCLog::NET, "Ignore block request below NODE_NETWORK_LIMITED threshold from peer=%d\n", pfrom->GetId());
@@ -1448,11 +1452,11 @@ void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, c
{
bool sendMerkleBlock = false;
CMerkleBlock merkleBlock;
- {
- LOCK(pfrom->cs_filter);
- if (pfrom->pfilter) {
+ if (pfrom->m_tx_relay != nullptr) {
+ LOCK(pfrom->m_tx_relay->cs_filter);
+ if (pfrom->m_tx_relay->pfilter) {
sendMerkleBlock = true;
- merkleBlock = CMerkleBlock(*pblock, *pfrom->pfilter);
+ merkleBlock = CMerkleBlock(*pblock, *pfrom->m_tx_relay->pfilter);
}
}
if (sendMerkleBlock) {
@@ -1512,7 +1516,12 @@ void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnm
std::deque<CInv>::iterator it = pfrom->vRecvGetData.begin();
std::vector<CInv> vNotFound;
const CNetMsgMaker msgMaker(pfrom->GetSendVersion());
- {
+
+ // Note that if we receive a getdata for a MSG_TX or MSG_WITNESS_TX from a
+ // block-relay-only outbound peer, we will stop processing further getdata
+ // messages from this peer (likely resulting in our peer eventually
+ // disconnecting us).
+ if (pfrom->m_tx_relay != nullptr) {
LOCK(cs_main);
while (it != pfrom->vRecvGetData.end() && (it->type == MSG_TX || it->type == MSG_WITNESS_TX)) {
@@ -1532,11 +1541,11 @@ void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnm
if (mi != mapRelay.end()) {
connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *mi->second));
push = true;
- } else if (pfrom->timeLastMempoolReq) {
+ } else if (pfrom->m_tx_relay->timeLastMempoolReq) {
auto txinfo = mempool.info(inv.hash);
// To protect privacy, do not answer getdata using the mempool when
// that TX couldn't have been INVed in reply to a MEMPOOL request.
- if (txinfo.tx && txinfo.nTime <= pfrom->timeLastMempoolReq) {
+ if (txinfo.tx && txinfo.nTime <= pfrom->m_tx_relay->timeLastMempoolReq) {
connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *txinfo.tx));
push = true;
}
@@ -1772,9 +1781,11 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve
}
}
- if (!pfrom->fDisconnect && IsOutboundDisconnectionCandidate(pfrom) && nodestate->pindexBestKnownBlock != nullptr) {
- // If this is an outbound peer, check to see if we should protect
+ if (!pfrom->fDisconnect && IsOutboundDisconnectionCandidate(pfrom) && nodestate->pindexBestKnownBlock != nullptr && pfrom->m_tx_relay != nullptr) {
+ // If this is an outbound full-relay peer, check to see if we should protect
// it from the bad/lagging chain logic.
+ // Note that block-relay-only peers are already implicitly protected, so we
+ // only consider setting m_protect for the full-relay peers.
if (g_outbound_peers_with_protect_from_disconnect < MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT && nodestate->pindexBestKnownBlock->nChainWork >= ::ChainActive().Tip()->nChainWork && !nodestate->m_chain_sync.m_protect) {
LogPrint(BCLog::NET, "Protecting outbound peer=%d from eviction\n", pfrom->GetId());
nodestate->m_chain_sync.m_protect = true;
@@ -1844,7 +1855,7 @@ void static ProcessOrphanTx(CConnman* connman, std::set<uint256>& orphan_work_se
EraseOrphanTx(orphanHash);
done = true;
}
- mempool.check(pcoinsTip.get());
+ mempool.check(&::ChainstateActive().CoinsTip());
}
}
@@ -1995,9 +2006,9 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// set nodes not capable of serving the complete blockchain history as "limited nodes"
pfrom->m_limited_node = (!(nServices & NODE_NETWORK) && (nServices & NODE_NETWORK_LIMITED));
- {
- LOCK(pfrom->cs_filter);
- pfrom->fRelayTxes = fRelay; // set to true after we get the first filter* message
+ if (pfrom->m_tx_relay != nullptr) {
+ LOCK(pfrom->m_tx_relay->cs_filter);
+ pfrom->m_tx_relay->fRelayTxes = fRelay; // set to true after we get the first filter* message
}
// Change version
@@ -2016,7 +2027,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
UpdatePreferredDownload(pfrom, State(pfrom->GetId()));
}
- if (!pfrom->fInbound)
+ if (!pfrom->fInbound && pfrom->IsAddrRelayPeer())
{
// Advertise our address
if (fListen && !::ChainstateActive().IsInitialBlockDownload())
@@ -2088,9 +2099,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// Mark this node as currently connected, so we update its timestamp later.
LOCK(cs_main);
State(pfrom->GetId())->fCurrentlyConnected = true;
- LogPrintf("New outbound peer connected: version: %d, blocks=%d, peer=%d%s\n",
- pfrom->nVersion.load(), pfrom->nStartingHeight, pfrom->GetId(),
- (fLogIPs ? strprintf(", peeraddr=%s", pfrom->addr.ToString()) : ""));
+ LogPrintf("New outbound peer connected: version: %d, blocks=%d, peer=%d%s (%s)\n",
+ pfrom->nVersion.load(), pfrom->nStartingHeight,
+ pfrom->GetId(), (fLogIPs ? strprintf(", peeraddr=%s", pfrom->addr.ToString()) : ""),
+ pfrom->m_tx_relay == nullptr ? "block-relay" : "full-relay");
}
if (pfrom->nVersion >= SENDHEADERS_VERSION) {
@@ -2131,6 +2143,9 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// Don't want addr from older versions unless seeding
if (pfrom->nVersion < CADDR_TIME_VERSION && connman->GetAddressCount() > 1000)
return true;
+ if (!pfrom->IsAddrRelayPeer()) {
+ return true;
+ }
if (vAddr.size() > 1000)
{
LOCK(cs_main);
@@ -2214,16 +2229,18 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
return false;
}
- bool fBlocksOnly = !g_relay_txes;
+ // We won't accept tx inv's if we're in blocks-only mode, or this is a
+ // block-relay-only peer
+ bool fBlocksOnly = !g_relay_txes || (pfrom->m_tx_relay == nullptr);
// Allow whitelisted peers to send data other than blocks in blocks only mode if whitelistrelay is true
- if (pfrom->fWhitelisted && gArgs.GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY))
+ if (pfrom->HasPermission(PF_RELAY))
fBlocksOnly = false;
LOCK(cs_main);
uint32_t nFetchFlags = GetFetchFlags(pfrom);
- int64_t nNow = GetTimeMicros();
+ const auto current_time = GetTime<std::chrono::microseconds>();
for (CInv &inv : vInv)
{
@@ -2253,9 +2270,11 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
{
pfrom->AddInventoryKnown(inv);
if (fBlocksOnly) {
- LogPrint(BCLog::NET, "transaction (%s) inv sent in violation of protocol peer=%d\n", inv.hash.ToString(), pfrom->GetId());
+ LogPrint(BCLog::NET, "transaction (%s) inv sent in violation of protocol, disconnecting peer=%d\n", inv.hash.ToString(), pfrom->GetId());
+ pfrom->fDisconnect = true;
+ return true;
} else if (!fAlreadyHave && !fImporting && !fReindex && !::ChainstateActive().IsInitialBlockDownload()) {
- RequestTx(State(pfrom->GetId()), inv.hash, nNow);
+ RequestTx(State(pfrom->GetId()), inv.hash, current_time);
}
}
}
@@ -2412,7 +2431,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
}
LOCK(cs_main);
- if (::ChainstateActive().IsInitialBlockDownload() && !pfrom->fWhitelisted) {
+ if (::ChainstateActive().IsInitialBlockDownload() && !pfrom->HasPermission(PF_NOBAN)) {
LogPrint(BCLog::NET, "Ignoring getheaders from peer=%d because node is in initial block download\n", pfrom->GetId());
return true;
}
@@ -2470,9 +2489,11 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (strCommand == NetMsgType::TX) {
// Stop processing the transaction early if
// We are in blocks only mode and peer is either not whitelisted or whitelistrelay is off
- if (!g_relay_txes && (!pfrom->fWhitelisted || !gArgs.GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY)))
+ // or if this peer is supposed to be a block-relay-only peer
+ if ((!g_relay_txes && !pfrom->HasPermission(PF_RELAY)) || (pfrom->m_tx_relay == nullptr))
{
LogPrint(BCLog::NET, "transaction sent in violation of protocol peer=%d\n", pfrom->GetId());
+ pfrom->fDisconnect = true;
return true;
}
@@ -2497,7 +2518,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (!AlreadyHave(inv) &&
AcceptToMemoryPool(mempool, state, ptx, &fMissingInputs, &lRemovedTxn, false /* bypass_limits */, 0 /* nAbsurdFee */)) {
- mempool.check(pcoinsTip.get());
+ mempool.check(&::ChainstateActive().CoinsTip());
RelayTransaction(tx.GetHash(), *connman);
for (unsigned int i = 0; i < tx.vout.size(); i++) {
auto it_by_prev = mapOrphanTransactionsByPrev.find(COutPoint(inv.hash, i));
@@ -2529,16 +2550,16 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
}
if (!fRejectedParents) {
uint32_t nFetchFlags = GetFetchFlags(pfrom);
- int64_t nNow = GetTimeMicros();
+ const auto current_time = GetTime<std::chrono::microseconds>();
for (const CTxIn& txin : tx.vin) {
CInv _inv(MSG_TX | nFetchFlags, txin.prevout.hash);
pfrom->AddInventoryKnown(_inv);
- if (!AlreadyHave(_inv)) RequestTx(State(pfrom->GetId()), _inv.hash, nNow);
+ if (!AlreadyHave(_inv)) RequestTx(State(pfrom->GetId()), _inv.hash, current_time);
}
AddOrphanTx(ptx, pfrom->GetId());
- // DoS prevention: do not allow mapOrphanTransactions to grow unbounded
+ // DoS prevention: do not allow mapOrphanTransactions to grow unbounded (see CVE-2012-3789)
unsigned int nMaxOrphanTx = (unsigned int)std::max((int64_t)0, gArgs.GetArg("-maxorphantx", DEFAULT_MAX_ORPHAN_TRANSACTIONS));
unsigned int nEvicted = LimitOrphanTxSize(nMaxOrphanTx);
if (nEvicted > 0) {
@@ -2565,7 +2586,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
AddToCompactExtraTransactions(ptx);
}
- if (pfrom->fWhitelisted && gArgs.GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) {
+ if (pfrom->HasPermission(PF_FORCERELAY)) {
// Always relay transactions received from whitelisted peers, even
// if they were already in the mempool or rejected from it due
// to policy, allowing the node to function as a gateway for
@@ -2989,6 +3010,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
LogPrint(BCLog::NET, "Ignoring \"getaddr\" from outbound connection. peer=%d\n", pfrom->GetId());
return true;
}
+ if (!pfrom->IsAddrRelayPeer()) {
+ LogPrint(BCLog::NET, "Ignoring \"getaddr\" from block-relay-only connection. peer=%d\n", pfrom->GetId());
+ return true;
+ }
// Only send one GetAddr response per connection to reduce resource waste
// and discourage addr stamping of INV announcements.
@@ -3010,22 +3035,30 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
}
if (strCommand == NetMsgType::MEMPOOL) {
- if (!(pfrom->GetLocalServices() & NODE_BLOOM) && !pfrom->fWhitelisted)
+ if (!(pfrom->GetLocalServices() & NODE_BLOOM) && !pfrom->HasPermission(PF_MEMPOOL))
{
- LogPrint(BCLog::NET, "mempool request with bloom filters disabled, disconnect peer=%d\n", pfrom->GetId());
- pfrom->fDisconnect = true;
+ if (!pfrom->HasPermission(PF_NOBAN))
+ {
+ LogPrint(BCLog::NET, "mempool request with bloom filters disabled, disconnect peer=%d\n", pfrom->GetId());
+ pfrom->fDisconnect = true;
+ }
return true;
}
- if (connman->OutboundTargetReached(false) && !pfrom->fWhitelisted)
+ if (connman->OutboundTargetReached(false) && !pfrom->HasPermission(PF_MEMPOOL))
{
- LogPrint(BCLog::NET, "mempool request with bandwidth limit reached, disconnect peer=%d\n", pfrom->GetId());
- pfrom->fDisconnect = true;
+ if (!pfrom->HasPermission(PF_NOBAN))
+ {
+ LogPrint(BCLog::NET, "mempool request with bandwidth limit reached, disconnect peer=%d\n", pfrom->GetId());
+ pfrom->fDisconnect = true;
+ }
return true;
}
- LOCK(pfrom->cs_inventory);
- pfrom->fSendMempool = true;
+ if (pfrom->m_tx_relay != nullptr) {
+ LOCK(pfrom->m_tx_relay->cs_tx_inventory);
+ pfrom->m_tx_relay->fSendMempool = true;
+ }
return true;
}
@@ -3116,12 +3149,12 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
LOCK(cs_main);
Misbehaving(pfrom->GetId(), 100);
}
- else
+ else if (pfrom->m_tx_relay != nullptr)
{
- LOCK(pfrom->cs_filter);
- pfrom->pfilter.reset(new CBloomFilter(filter));
- pfrom->pfilter->UpdateEmptyFull();
- pfrom->fRelayTxes = true;
+ LOCK(pfrom->m_tx_relay->cs_filter);
+ pfrom->m_tx_relay->pfilter.reset(new CBloomFilter(filter));
+ pfrom->m_tx_relay->pfilter->UpdateEmptyFull();
+ pfrom->m_tx_relay->fRelayTxes = true;
}
return true;
}
@@ -3135,10 +3168,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
bool bad = false;
if (vData.size() > MAX_SCRIPT_ELEMENT_SIZE) {
bad = true;
- } else {
- LOCK(pfrom->cs_filter);
- if (pfrom->pfilter) {
- pfrom->pfilter->insert(vData);
+ } else if (pfrom->m_tx_relay != nullptr) {
+ LOCK(pfrom->m_tx_relay->cs_filter);
+ if (pfrom->m_tx_relay->pfilter) {
+ pfrom->m_tx_relay->pfilter->insert(vData);
} else {
bad = true;
}
@@ -3151,11 +3184,14 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
}
if (strCommand == NetMsgType::FILTERCLEAR) {
- LOCK(pfrom->cs_filter);
+ if (pfrom->m_tx_relay == nullptr) {
+ return true;
+ }
+ LOCK(pfrom->m_tx_relay->cs_filter);
if (pfrom->GetLocalServices() & NODE_BLOOM) {
- pfrom->pfilter.reset(new CBloomFilter());
+ pfrom->m_tx_relay->pfilter.reset(new CBloomFilter());
}
- pfrom->fRelayTxes = true;
+ pfrom->m_tx_relay->fRelayTxes = true;
return true;
}
@@ -3163,9 +3199,9 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
CAmount newFeeFilter = 0;
vRecv >> newFeeFilter;
if (MoneyRange(newFeeFilter)) {
- {
- LOCK(pfrom->cs_feeFilter);
- pfrom->minFeeFilter = newFeeFilter;
+ if (pfrom->m_tx_relay != nullptr) {
+ LOCK(pfrom->m_tx_relay->cs_feeFilter);
+ pfrom->m_tx_relay->minFeeFilter = newFeeFilter;
}
LogPrint(BCLog::NET, "received: feefilter of %s from peer=%d\n", CFeeRate(newFeeFilter).ToString(), pfrom->GetId());
}
@@ -3216,7 +3252,7 @@ bool PeerLogicValidation::SendRejectsAndCheckIfBanned(CNode* pnode, bool enable_
if (state.fShouldBan) {
state.fShouldBan = false;
- if (pnode->fWhitelisted)
+ if (pnode->HasPermission(PF_NOBAN))
LogPrintf("Warning: not punishing whitelisted peer %s!\n", pnode->addr.ToString());
else if (pnode->m_manual_connection)
LogPrintf("Warning: not punishing manually-connected peer %s!\n", pnode->addr.ToString());
@@ -3442,6 +3478,8 @@ void PeerLogicValidation::EvictExtraOutboundPeers(int64_t time_in_seconds)
if (state == nullptr) return; // shouldn't be possible, but just in case
// Don't evict our protected peers
if (state->m_chain_sync.m_protect) return;
+ // Don't evict our block-relay-only peers.
+ if (pnode->m_tx_relay == nullptr) return;
if (state->m_last_block_announcement < oldest_block_announcement || (state->m_last_block_announcement == oldest_block_announcement && pnode->GetId() > worst_peer)) {
worst_peer = pnode->GetId();
oldest_block_announcement = state->m_last_block_announcement;
@@ -3569,7 +3607,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
// Address refresh broadcast
int64_t nNow = GetTimeMicros();
- if (!::ChainstateActive().IsInitialBlockDownload() && pto->nNextLocalAddrSend < nNow) {
+ if (pto->IsAddrRelayPeer() && !::ChainstateActive().IsInitialBlockDownload() && pto->nNextLocalAddrSend < nNow) {
AdvertiseLocal(pto);
pto->nNextLocalAddrSend = PoissonNextSend(nNow, AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL);
}
@@ -3577,7 +3615,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
//
// Message: addr
//
- if (pto->nNextAddrSend < nNow) {
+ if (pto->IsAddrRelayPeer() && pto->nNextAddrSend < nNow) {
pto->nNextAddrSend = PoissonNextSend(nNow, AVG_ADDRESS_BROADCAST_INTERVAL);
std::vector<CAddress> vAddr;
vAddr.reserve(pto->vAddrToSend.size());
@@ -3785,120 +3823,123 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
}
pto->vInventoryBlockToSend.clear();
- // Check whether periodic sends should happen
- bool fSendTrickle = pto->fWhitelisted;
- if (pto->nNextInvSend < nNow) {
- fSendTrickle = true;
- if (pto->fInbound) {
- pto->nNextInvSend = connman->PoissonNextSendInbound(nNow, INVENTORY_BROADCAST_INTERVAL);
- } else {
- // Use half the delay for outbound peers, as there is less privacy concern for them.
- pto->nNextInvSend = PoissonNextSend(nNow, INVENTORY_BROADCAST_INTERVAL >> 1);
+ if (pto->m_tx_relay != nullptr) {
+ LOCK(pto->m_tx_relay->cs_tx_inventory);
+ // Check whether periodic sends should happen
+ bool fSendTrickle = pto->HasPermission(PF_NOBAN);
+ if (pto->m_tx_relay->nNextInvSend < nNow) {
+ fSendTrickle = true;
+ if (pto->fInbound) {
+ pto->m_tx_relay->nNextInvSend = connman->PoissonNextSendInbound(nNow, INVENTORY_BROADCAST_INTERVAL);
+ } else {
+ // Use half the delay for outbound peers, as there is less privacy concern for them.
+ pto->m_tx_relay->nNextInvSend = PoissonNextSend(nNow, INVENTORY_BROADCAST_INTERVAL >> 1);
+ }
}
- }
-
- // Time to send but the peer has requested we not relay transactions.
- if (fSendTrickle) {
- LOCK(pto->cs_filter);
- if (!pto->fRelayTxes) pto->setInventoryTxToSend.clear();
- }
- // Respond to BIP35 mempool requests
- if (fSendTrickle && pto->fSendMempool) {
- auto vtxinfo = mempool.infoAll();
- pto->fSendMempool = false;
- CAmount filterrate = 0;
- {
- LOCK(pto->cs_feeFilter);
- filterrate = pto->minFeeFilter;
+ // Time to send but the peer has requested we not relay transactions.
+ if (fSendTrickle) {
+ LOCK(pto->m_tx_relay->cs_filter);
+ if (!pto->m_tx_relay->fRelayTxes) pto->m_tx_relay->setInventoryTxToSend.clear();
}
- LOCK(pto->cs_filter);
-
- for (const auto& txinfo : vtxinfo) {
- const uint256& hash = txinfo.tx->GetHash();
- CInv inv(MSG_TX, hash);
- pto->setInventoryTxToSend.erase(hash);
- if (filterrate) {
- if (txinfo.feeRate.GetFeePerK() < filterrate)
- continue;
- }
- if (pto->pfilter) {
- if (!pto->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue;
+ // Respond to BIP35 mempool requests
+ if (fSendTrickle && pto->m_tx_relay->fSendMempool) {
+ auto vtxinfo = mempool.infoAll();
+ pto->m_tx_relay->fSendMempool = false;
+ CAmount filterrate = 0;
+ {
+ LOCK(pto->m_tx_relay->cs_feeFilter);
+ filterrate = pto->m_tx_relay->minFeeFilter;
}
- pto->filterInventoryKnown.insert(hash);
- vInv.push_back(inv);
- if (vInv.size() == MAX_INV_SZ) {
- connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
- vInv.clear();
+
+ LOCK(pto->m_tx_relay->cs_filter);
+
+ for (const auto& txinfo : vtxinfo) {
+ const uint256& hash = txinfo.tx->GetHash();
+ CInv inv(MSG_TX, hash);
+ pto->m_tx_relay->setInventoryTxToSend.erase(hash);
+ if (filterrate) {
+ if (txinfo.feeRate.GetFeePerK() < filterrate)
+ continue;
+ }
+ if (pto->m_tx_relay->pfilter) {
+ if (!pto->m_tx_relay->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue;
+ }
+ pto->m_tx_relay->filterInventoryKnown.insert(hash);
+ vInv.push_back(inv);
+ if (vInv.size() == MAX_INV_SZ) {
+ connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
+ vInv.clear();
+ }
}
+ pto->m_tx_relay->timeLastMempoolReq = GetTime();
}
- pto->timeLastMempoolReq = GetTime();
- }
- // Determine transactions to relay
- if (fSendTrickle) {
- // Produce a vector with all candidates for sending
- std::vector<std::set<uint256>::iterator> vInvTx;
- vInvTx.reserve(pto->setInventoryTxToSend.size());
- for (std::set<uint256>::iterator it = pto->setInventoryTxToSend.begin(); it != pto->setInventoryTxToSend.end(); it++) {
- vInvTx.push_back(it);
- }
- CAmount filterrate = 0;
- {
- LOCK(pto->cs_feeFilter);
- filterrate = pto->minFeeFilter;
- }
- // Topologically and fee-rate sort the inventory we send for privacy and priority reasons.
- // A heap is used so that not all items need sorting if only a few are being sent.
- CompareInvMempoolOrder compareInvMempoolOrder(&mempool);
- std::make_heap(vInvTx.begin(), vInvTx.end(), compareInvMempoolOrder);
- // No reason to drain out at many times the network's capacity,
- // especially since we have many peers and some will draw much shorter delays.
- unsigned int nRelayedTransactions = 0;
- LOCK(pto->cs_filter);
- while (!vInvTx.empty() && nRelayedTransactions < INVENTORY_BROADCAST_MAX) {
- // Fetch the top element from the heap
- std::pop_heap(vInvTx.begin(), vInvTx.end(), compareInvMempoolOrder);
- std::set<uint256>::iterator it = vInvTx.back();
- vInvTx.pop_back();
- uint256 hash = *it;
- // Remove it from the to-be-sent set
- pto->setInventoryTxToSend.erase(it);
- // Check if not in the filter already
- if (pto->filterInventoryKnown.contains(hash)) {
- continue;
+ // Determine transactions to relay
+ if (fSendTrickle) {
+ // Produce a vector with all candidates for sending
+ std::vector<std::set<uint256>::iterator> vInvTx;
+ vInvTx.reserve(pto->m_tx_relay->setInventoryTxToSend.size());
+ for (std::set<uint256>::iterator it = pto->m_tx_relay->setInventoryTxToSend.begin(); it != pto->m_tx_relay->setInventoryTxToSend.end(); it++) {
+ vInvTx.push_back(it);
}
- // Not in the mempool anymore? don't bother sending it.
- auto txinfo = mempool.info(hash);
- if (!txinfo.tx) {
- continue;
- }
- if (filterrate && txinfo.feeRate.GetFeePerK() < filterrate) {
- continue;
- }
- if (pto->pfilter && !pto->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue;
- // Send
- vInv.push_back(CInv(MSG_TX, hash));
- nRelayedTransactions++;
+ CAmount filterrate = 0;
{
- // Expire old relay messages
- while (!vRelayExpiration.empty() && vRelayExpiration.front().first < nNow)
- {
- mapRelay.erase(vRelayExpiration.front().second);
- vRelayExpiration.pop_front();
+ LOCK(pto->m_tx_relay->cs_feeFilter);
+ filterrate = pto->m_tx_relay->minFeeFilter;
+ }
+ // Topologically and fee-rate sort the inventory we send for privacy and priority reasons.
+ // A heap is used so that not all items need sorting if only a few are being sent.
+ CompareInvMempoolOrder compareInvMempoolOrder(&mempool);
+ std::make_heap(vInvTx.begin(), vInvTx.end(), compareInvMempoolOrder);
+ // No reason to drain out at many times the network's capacity,
+ // especially since we have many peers and some will draw much shorter delays.
+ unsigned int nRelayedTransactions = 0;
+ LOCK(pto->m_tx_relay->cs_filter);
+ while (!vInvTx.empty() && nRelayedTransactions < INVENTORY_BROADCAST_MAX) {
+ // Fetch the top element from the heap
+ std::pop_heap(vInvTx.begin(), vInvTx.end(), compareInvMempoolOrder);
+ std::set<uint256>::iterator it = vInvTx.back();
+ vInvTx.pop_back();
+ uint256 hash = *it;
+ // Remove it from the to-be-sent set
+ pto->m_tx_relay->setInventoryTxToSend.erase(it);
+ // Check if not in the filter already
+ if (pto->m_tx_relay->filterInventoryKnown.contains(hash)) {
+ continue;
+ }
+ // Not in the mempool anymore? don't bother sending it.
+ auto txinfo = mempool.info(hash);
+ if (!txinfo.tx) {
+ continue;
}
+ if (filterrate && txinfo.feeRate.GetFeePerK() < filterrate) {
+ continue;
+ }
+ if (pto->m_tx_relay->pfilter && !pto->m_tx_relay->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue;
+ // Send
+ vInv.push_back(CInv(MSG_TX, hash));
+ nRelayedTransactions++;
+ {
+ // Expire old relay messages
+ while (!vRelayExpiration.empty() && vRelayExpiration.front().first < nNow)
+ {
+ mapRelay.erase(vRelayExpiration.front().second);
+ vRelayExpiration.pop_front();
+ }
- auto ret = mapRelay.insert(std::make_pair(hash, std::move(txinfo.tx)));
- if (ret.second) {
- vRelayExpiration.push_back(std::make_pair(nNow + 15 * 60 * 1000000, ret.first));
+ auto ret = mapRelay.insert(std::make_pair(hash, std::move(txinfo.tx)));
+ if (ret.second) {
+ vRelayExpiration.push_back(std::make_pair(nNow + 15 * 60 * 1000000, ret.first));
+ }
}
+ if (vInv.size() == MAX_INV_SZ) {
+ connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
+ vInv.clear();
+ }
+ pto->m_tx_relay->filterInventoryKnown.insert(hash);
}
- if (vInv.size() == MAX_INV_SZ) {
- connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
- vInv.clear();
- }
- pto->filterInventoryKnown.insert(hash);
}
}
}
@@ -3906,6 +3947,9 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
// Detect whether we're stalling
+ const auto current_time = GetTime<std::chrono::microseconds>();
+ // nNow is the current system time (GetTimeMicros is not mockable) and
+ // should be replaced by the mockable current_time eventually
nNow = GetTimeMicros();
if (state.nStallingSince && state.nStallingSince < nNow - 1000000 * BLOCK_STALLING_TIMEOUT) {
// Stalling only triggers when the block download window cannot move. During normal steady state,
@@ -3939,7 +3983,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
// Note: If all our peers are inbound, then we won't
// disconnect our sync peer for stalling; we have bigger
// problems if we can't get any outbound peers.
- if (!pto->fWhitelisted) {
+ if (!pto->HasPermission(PF_NOBAN)) {
LogPrintf("Timeout downloading headers from peer=%d, disconnecting\n", pto->GetId());
pto->fDisconnect = true;
return true;
@@ -3998,9 +4042,9 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
// were unresponsive in the past.
// Eventually we should consider disconnecting peers, but this is
// conservative.
- if (state.m_tx_download.m_check_expiry_timer <= nNow) {
+ if (state.m_tx_download.m_check_expiry_timer <= current_time) {
for (auto it=state.m_tx_download.m_tx_in_flight.begin(); it != state.m_tx_download.m_tx_in_flight.end();) {
- if (it->second <= nNow - TX_EXPIRY_INTERVAL) {
+ if (it->second <= current_time - TX_EXPIRY_INTERVAL) {
LogPrint(BCLog::NET, "timeout of inflight tx %s from peer=%d\n", it->first.ToString(), pto->GetId());
state.m_tx_download.m_tx_announced.erase(it->first);
state.m_tx_download.m_tx_in_flight.erase(it++);
@@ -4010,11 +4054,11 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
}
// On average, we do this check every TX_EXPIRY_INTERVAL. Randomize
// so that we're not doing this for all peers at the same time.
- state.m_tx_download.m_check_expiry_timer = nNow + TX_EXPIRY_INTERVAL/2 + GetRand(TX_EXPIRY_INTERVAL);
+ state.m_tx_download.m_check_expiry_timer = current_time + TX_EXPIRY_INTERVAL / 2 + GetRandMicros(TX_EXPIRY_INTERVAL);
}
auto& tx_process_time = state.m_tx_download.m_tx_process_time;
- while (!tx_process_time.empty() && tx_process_time.begin()->first <= nNow && state.m_tx_download.m_tx_in_flight.size() < MAX_PEER_TX_IN_FLIGHT) {
+ while (!tx_process_time.empty() && tx_process_time.begin()->first <= current_time && state.m_tx_download.m_tx_in_flight.size() < MAX_PEER_TX_IN_FLIGHT) {
const uint256 txid = tx_process_time.begin()->second;
// Erase this entry from tx_process_time (it may be added back for
// processing at a later time, see below)
@@ -4023,22 +4067,22 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
if (!AlreadyHave(inv)) {
// If this transaction was last requested more than 1 minute ago,
// then request.
- int64_t last_request_time = GetTxRequestTime(inv.hash);
- if (last_request_time <= nNow - GETDATA_TX_INTERVAL) {
+ const auto last_request_time = GetTxRequestTime(inv.hash);
+ if (last_request_time <= current_time - GETDATA_TX_INTERVAL) {
LogPrint(BCLog::NET, "Requesting %s peer=%d\n", inv.ToString(), pto->GetId());
vGetData.push_back(inv);
if (vGetData.size() >= MAX_GETDATA_SZ) {
connman->PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData));
vGetData.clear();
}
- UpdateTxRequestTime(inv.hash, nNow);
- state.m_tx_download.m_tx_in_flight.emplace(inv.hash, nNow);
+ UpdateTxRequestTime(inv.hash, current_time);
+ state.m_tx_download.m_tx_in_flight.emplace(inv.hash, current_time);
} else {
// This transaction is in flight from someone else; queue
// up processing to happen after the download times out
// (with a slight delay for inbound peers, to prefer
// requests to outbound peers).
- int64_t next_process_time = CalculateTxGetDataTime(txid, nNow, !state.fPreferredDownload);
+ const auto next_process_time = CalculateTxGetDataTime(txid, current_time, !state.fPreferredDownload);
tx_process_time.emplace(next_process_time, txid);
}
} else {
@@ -4056,27 +4100,27 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
// Message: feefilter
//
// We don't want white listed peers to filter txs to us if we have -whitelistforcerelay
- if (pto->nVersion >= FEEFILTER_VERSION && gArgs.GetBoolArg("-feefilter", DEFAULT_FEEFILTER) &&
- !(pto->fWhitelisted && gArgs.GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY))) {
+ if (pto->m_tx_relay != nullptr && pto->nVersion >= FEEFILTER_VERSION && gArgs.GetBoolArg("-feefilter", DEFAULT_FEEFILTER) &&
+ !pto->HasPermission(PF_FORCERELAY)) {
CAmount currentFilter = mempool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFeePerK();
int64_t timeNow = GetTimeMicros();
- if (timeNow > pto->nextSendTimeFeeFilter) {
+ if (timeNow > pto->m_tx_relay->nextSendTimeFeeFilter) {
static CFeeRate default_feerate(DEFAULT_MIN_RELAY_TX_FEE);
static FeeFilterRounder filterRounder(default_feerate);
CAmount filterToSend = filterRounder.round(currentFilter);
// We always have a fee filter of at least minRelayTxFee
filterToSend = std::max(filterToSend, ::minRelayTxFee.GetFeePerK());
- if (filterToSend != pto->lastSentFeeFilter) {
+ if (filterToSend != pto->m_tx_relay->lastSentFeeFilter) {
connman->PushMessage(pto, msgMaker.Make(NetMsgType::FEEFILTER, filterToSend));
- pto->lastSentFeeFilter = filterToSend;
+ pto->m_tx_relay->lastSentFeeFilter = filterToSend;
}
- pto->nextSendTimeFeeFilter = PoissonNextSend(timeNow, AVG_FEEFILTER_BROADCAST_INTERVAL);
+ pto->m_tx_relay->nextSendTimeFeeFilter = PoissonNextSend(timeNow, AVG_FEEFILTER_BROADCAST_INTERVAL);
}
// If the fee filter has changed substantially and it's still more than MAX_FEEFILTER_CHANGE_DELAY
// until scheduled broadcast, then move the broadcast to within MAX_FEEFILTER_CHANGE_DELAY.
- else if (timeNow + MAX_FEEFILTER_CHANGE_DELAY * 1000000 < pto->nextSendTimeFeeFilter &&
- (currentFilter < 3 * pto->lastSentFeeFilter / 4 || currentFilter > 4 * pto->lastSentFeeFilter / 3)) {
- pto->nextSendTimeFeeFilter = timeNow + GetRandInt(MAX_FEEFILTER_CHANGE_DELAY) * 1000000;
+ else if (timeNow + MAX_FEEFILTER_CHANGE_DELAY * 1000000 < pto->m_tx_relay->nextSendTimeFeeFilter &&
+ (currentFilter < 3 * pto->m_tx_relay->lastSentFeeFilter / 4 || currentFilter > 4 * pto->m_tx_relay->lastSentFeeFilter / 3)) {
+ pto->m_tx_relay->nextSendTimeFeeFilter = timeNow + GetRandInt(MAX_FEEFILTER_CHANGE_DELAY) * 1000000;
}
}
}
diff --git a/src/netbase.cpp b/src/netbase.cpp
index 6d4738c835..0148aea428 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -37,8 +37,8 @@ bool fNameLookup = DEFAULT_NAME_LOOKUP;
static const int SOCKS5_RECV_TIMEOUT = 20 * 1000;
static std::atomic<bool> interruptSocks5Recv(false);
-enum Network ParseNetwork(std::string net) {
- Downcase(net);
+enum Network ParseNetwork(const std::string& net_in) {
+ std::string net = ToLower(net_in);
if (net == "ipv4") return NET_IPV4;
if (net == "ipv6") return NET_IPV6;
if (net == "onion") return NET_ONION;
diff --git a/src/netbase.h b/src/netbase.h
index 708df5b8e2..313a575687 100644
--- a/src/netbase.h
+++ b/src/netbase.h
@@ -37,7 +37,7 @@ public:
bool randomize_credentials;
};
-enum Network ParseNetwork(std::string net);
+enum Network ParseNetwork(const std::string& net);
std::string GetNetworkName(enum Network net);
bool SetProxy(enum Network net, const proxyType &addrProxy);
bool GetProxy(enum Network net, proxyType &proxyInfoOut);
diff --git a/src/node/coin.cpp b/src/node/coin.cpp
index bb98e63f3a..ad8d1d3af4 100644
--- a/src/node/coin.cpp
+++ b/src/node/coin.cpp
@@ -10,8 +10,7 @@
void FindCoins(std::map<COutPoint, Coin>& coins)
{
LOCK2(cs_main, ::mempool.cs);
- assert(pcoinsTip);
- CCoinsViewCache& chain_view = *::pcoinsTip;
+ CCoinsViewCache& chain_view = ::ChainstateActive().CoinsTip();
CCoinsViewMemPool mempool_view(&chain_view, ::mempool);
for (auto& coin : coins) {
if (!mempool_view.GetCoin(coin.first, coin.second)) {
diff --git a/src/node/coinstats.cpp b/src/node/coinstats.cpp
new file mode 100644
index 0000000000..e1891b9898
--- /dev/null
+++ b/src/node/coinstats.cpp
@@ -0,0 +1,77 @@
+// Copyright (c) 2010 Satoshi Nakamoto
+// Copyright (c) 2009-2019 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 <node/coinstats.h>
+
+#include <amount.h>
+#include <coins.h>
+#include <chain.h>
+#include <hash.h>
+#include <serialize.h>
+#include <validation.h>
+#include <uint256.h>
+#include <util/system.h>
+
+#include <map>
+
+#include <boost/thread.hpp>
+
+
+static void ApplyStats(CCoinsStats &stats, CHashWriter& ss, const uint256& hash, const std::map<uint32_t, Coin>& outputs)
+{
+ assert(!outputs.empty());
+ ss << hash;
+ ss << VARINT(outputs.begin()->second.nHeight * 2 + outputs.begin()->second.fCoinBase ? 1u : 0u);
+ stats.nTransactions++;
+ for (const auto& output : outputs) {
+ ss << VARINT(output.first + 1);
+ ss << output.second.out.scriptPubKey;
+ ss << VARINT(output.second.out.nValue, VarIntMode::NONNEGATIVE_SIGNED);
+ stats.nTransactionOutputs++;
+ stats.nTotalAmount += output.second.out.nValue;
+ stats.nBogoSize += 32 /* txid */ + 4 /* vout index */ + 4 /* height + coinbase */ + 8 /* amount */ +
+ 2 /* scriptPubKey len */ + output.second.out.scriptPubKey.size() /* scriptPubKey */;
+ }
+ ss << VARINT(0u);
+}
+
+//! Calculate statistics about the unspent transaction output set
+bool GetUTXOStats(CCoinsView *view, CCoinsStats &stats)
+{
+ std::unique_ptr<CCoinsViewCursor> pcursor(view->Cursor());
+ assert(pcursor);
+
+ CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
+ stats.hashBlock = pcursor->GetBestBlock();
+ {
+ LOCK(cs_main);
+ stats.nHeight = LookupBlockIndex(stats.hashBlock)->nHeight;
+ }
+ ss << stats.hashBlock;
+ uint256 prevkey;
+ std::map<uint32_t, Coin> outputs;
+ while (pcursor->Valid()) {
+ boost::this_thread::interruption_point();
+ COutPoint key;
+ Coin coin;
+ if (pcursor->GetKey(key) && pcursor->GetValue(coin)) {
+ if (!outputs.empty() && key.hash != prevkey) {
+ ApplyStats(stats, ss, prevkey, outputs);
+ outputs.clear();
+ }
+ prevkey = key.hash;
+ outputs[key.n] = std::move(coin);
+ } else {
+ return error("%s: unable to read value", __func__);
+ }
+ pcursor->Next();
+ }
+ if (!outputs.empty()) {
+ ApplyStats(stats, ss, prevkey, outputs);
+ }
+ stats.hashSerialized = ss.GetHash();
+ stats.nDiskSize = view->EstimateSize();
+ return true;
+}
diff --git a/src/node/coinstats.h b/src/node/coinstats.h
new file mode 100644
index 0000000000..7c11aab8bd
--- /dev/null
+++ b/src/node/coinstats.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2010 Satoshi Nakamoto
+// Copyright (c) 2009-2019 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_NODE_COINSTATS_H
+#define BITCOIN_NODE_COINSTATS_H
+
+#include <amount.h>
+#include <uint256.h>
+
+#include <cstdint>
+
+class CCoinsView;
+
+struct CCoinsStats
+{
+ int nHeight;
+ uint256 hashBlock;
+ uint64_t nTransactions;
+ uint64_t nTransactionOutputs;
+ uint64_t nBogoSize;
+ uint256 hashSerialized;
+ uint64_t nDiskSize;
+ CAmount nTotalAmount;
+
+ CCoinsStats() : nHeight(0), nTransactions(0), nTransactionOutputs(0), nBogoSize(0), nDiskSize(0), nTotalAmount(0) {}
+};
+
+//! Calculate statistics about the unspent transaction output set
+bool GetUTXOStats(CCoinsView* view, CCoinsStats& stats);
+
+#endif // BITCOIN_NODE_COINSTATS_H
diff --git a/src/node/transaction.cpp b/src/node/transaction.cpp
index 5cd567a5c4..7e8291ddc8 100644
--- a/src/node/transaction.cpp
+++ b/src/node/transaction.cpp
@@ -14,26 +14,33 @@
#include <future>
-TransactionError BroadcastTransaction(const CTransactionRef tx, uint256& hashTx, std::string& err_string, const CAmount& highfee)
+TransactionError BroadcastTransaction(const CTransactionRef tx, std::string& err_string, const CAmount& max_tx_fee, bool relay, bool wait_callback)
{
+ // BroadcastTransaction can be called by either sendrawtransaction RPC or wallet RPCs.
+ // g_connman is assigned both before chain clients and before RPC server is accepting calls,
+ // and reset after chain clients and RPC sever are stopped. g_connman should never be null here.
+ assert(g_connman);
std::promise<void> promise;
- hashTx = tx->GetHash();
+ uint256 hashTx = tx->GetHash();
+ bool callback_set = false;
{ // cs_main scope
LOCK(cs_main);
- CCoinsViewCache &view = *pcoinsTip;
- bool fHaveChain = false;
- for (size_t o = 0; !fHaveChain && o < tx->vout.size(); o++) {
+ // If the transaction is already confirmed in the chain, don't do anything
+ // and return early.
+ CCoinsViewCache &view = ::ChainstateActive().CoinsTip();
+ for (size_t o = 0; o < tx->vout.size(); o++) {
const Coin& existingCoin = view.AccessCoin(COutPoint(hashTx, o));
- fHaveChain = !existingCoin.IsSpent();
+ // IsSpent doesnt mean the coin is spent, it means the output doesnt' exist.
+ // So if the output does exist, then this transaction exists in the chain.
+ if (!existingCoin.IsSpent()) return TransactionError::ALREADY_IN_CHAIN;
}
- bool fHaveMempool = mempool.exists(hashTx);
- if (!fHaveMempool && !fHaveChain) {
- // push to local node and sync with wallets
+ if (!mempool.exists(hashTx)) {
+ // Transaction is not already in the mempool. Submit it.
CValidationState state;
bool fMissingInputs;
if (!AcceptToMemoryPool(mempool, state, std::move(tx), &fMissingInputs,
- nullptr /* plTxnReplaced */, false /* bypass_limits */, highfee)) {
+ nullptr /* plTxnReplaced */, false /* bypass_limits */, max_tx_fee)) {
if (state.IsInvalid()) {
err_string = FormatStateMessage(state);
return TransactionError::MEMPOOL_REJECTED;
@@ -44,33 +51,37 @@ TransactionError BroadcastTransaction(const CTransactionRef tx, uint256& hashTx,
err_string = FormatStateMessage(state);
return TransactionError::MEMPOOL_ERROR;
}
- } else {
- // If wallet is enabled, ensure that the wallet has been made aware
- // of the new transaction prior to returning. This prevents a race
- // where a user might call sendrawtransaction with a transaction
- // to/from their wallet, immediately call some wallet RPC, and get
- // a stale result because callbacks have not yet been processed.
+ }
+
+ // Transaction was accepted to the mempool.
+
+ if (wait_callback) {
+ // For transactions broadcast from outside the wallet, make sure
+ // that the wallet has been notified of the transaction before
+ // continuing.
+ //
+ // This prevents a race where a user might call sendrawtransaction
+ // with a transaction to/from their wallet, immediately call some
+ // wallet RPC, and get a stale result because callbacks have not
+ // yet been processed.
CallFunctionInValidationInterfaceQueue([&promise] {
promise.set_value();
});
+ callback_set = true;
}
- } else if (fHaveChain) {
- return TransactionError::ALREADY_IN_CHAIN;
- } else {
- // Make sure we don't block forever if re-sending
- // a transaction already in mempool.
- promise.set_value();
}
} // cs_main
- promise.get_future().wait();
-
- if (!g_connman) {
- return TransactionError::P2P_DISABLED;
+ if (callback_set) {
+ // Wait until Validation Interface clients have been notified of the
+ // transaction entering the mempool.
+ promise.get_future().wait();
}
- RelayTransaction(hashTx, *g_connman);
+ if (relay) {
+ RelayTransaction(hashTx, *g_connman);
+ }
return TransactionError::OK;
}
diff --git a/src/node/transaction.h b/src/node/transaction.h
index 51033f94e5..cf64fc28d9 100644
--- a/src/node/transaction.h
+++ b/src/node/transaction.h
@@ -11,14 +11,21 @@
#include <util/error.h>
/**
- * Broadcast a transaction
+ * Submit a transaction to the mempool and (optionally) relay it to all P2P peers.
+ *
+ * Mempool submission can be synchronous (will await mempool entry notification
+ * over the CValidationInterface) or asynchronous (will submit and not wait for
+ * notification), depending on the value of wait_callback. wait_callback MUST
+ * NOT be set while cs_main, cs_mempool or cs_wallet are held to avoid
+ * deadlock.
*
* @param[in] tx the transaction to broadcast
- * @param[out] &txid the txid of the transaction, if successfully broadcast
* @param[out] &err_string reference to std::string to fill with error string if available
- * @param[in] highfee Reject txs with fees higher than this (if 0, accept any fee)
+ * @param[in] max_tx_fee reject txs with fees higher than this (if 0, accept any fee)
+ * @param[in] relay flag if both mempool insertion and p2p relay are requested
+ * @param[in] wait_callback, wait until callbacks have been processed to avoid stale result due to a sequentially RPC.
* return error
*/
-NODISCARD TransactionError BroadcastTransaction(CTransactionRef tx, uint256& txid, std::string& err_string, const CAmount& highfee);
+NODISCARD TransactionError BroadcastTransaction(CTransactionRef tx, std::string& err_string, const CAmount& max_tx_fee, bool relay, bool wait_callback);
#endif // BITCOIN_NODE_TRANSACTION_H
diff --git a/src/noui.cpp b/src/noui.cpp
index caab9f326e..c07939cc79 100644
--- a/src/noui.cpp
+++ b/src/noui.cpp
@@ -13,6 +13,12 @@
#include <string>
#include <boost/signals2/connection.hpp>
+#include <boost/signals2/signal.hpp>
+
+/** Store connections so we can disconnect them when suppressing output */
+boost::signals2::connection noui_ThreadSafeMessageBoxConn;
+boost::signals2::connection noui_ThreadSafeQuestionConn;
+boost::signals2::connection noui_InitMessageConn;
bool noui_ThreadSafeMessageBox(const std::string& message, const std::string& caption, unsigned int style)
{
@@ -57,7 +63,39 @@ void noui_InitMessage(const std::string& message)
void noui_connect()
{
- uiInterface.ThreadSafeMessageBox_connect(noui_ThreadSafeMessageBox);
- uiInterface.ThreadSafeQuestion_connect(noui_ThreadSafeQuestion);
- uiInterface.InitMessage_connect(noui_InitMessage);
+ noui_ThreadSafeMessageBoxConn = uiInterface.ThreadSafeMessageBox_connect(noui_ThreadSafeMessageBox);
+ noui_ThreadSafeQuestionConn = uiInterface.ThreadSafeQuestion_connect(noui_ThreadSafeQuestion);
+ noui_InitMessageConn = uiInterface.InitMessage_connect(noui_InitMessage);
+}
+
+bool noui_ThreadSafeMessageBoxSuppressed(const std::string& message, const std::string& caption, unsigned int style)
+{
+ return false;
+}
+
+bool noui_ThreadSafeQuestionSuppressed(const std::string& /* ignored interactive message */, const std::string& message, const std::string& caption, unsigned int style)
+{
+ return false;
}
+
+void noui_InitMessageSuppressed(const std::string& message)
+{
+}
+
+void noui_suppress()
+{
+ noui_ThreadSafeMessageBoxConn.disconnect();
+ noui_ThreadSafeQuestionConn.disconnect();
+ noui_InitMessageConn.disconnect();
+ noui_ThreadSafeMessageBoxConn = uiInterface.ThreadSafeMessageBox_connect(noui_ThreadSafeMessageBoxSuppressed);
+ noui_ThreadSafeQuestionConn = uiInterface.ThreadSafeQuestion_connect(noui_ThreadSafeQuestionSuppressed);
+ noui_InitMessageConn = uiInterface.InitMessage_connect(noui_InitMessageSuppressed);
+}
+
+void noui_reconnect()
+{
+ noui_ThreadSafeMessageBoxConn.disconnect();
+ noui_ThreadSafeQuestionConn.disconnect();
+ noui_InitMessageConn.disconnect();
+ noui_connect();
+} \ No newline at end of file
diff --git a/src/noui.h b/src/noui.h
index 79a79a9af2..854aeeacca 100644
--- a/src/noui.h
+++ b/src/noui.h
@@ -17,4 +17,10 @@ void noui_InitMessage(const std::string& message);
/** Connect all bitcoind signal handlers */
void noui_connect();
+/** Suppress all bitcoind signal handlers. Used to suppress output during test runs that produce expected errors */
+void noui_suppress();
+
+/** Reconnects the regular Non-GUI handlers after having used noui_suppress */
+void noui_reconnect();
+
#endif // BITCOIN_NOUI_H
diff --git a/src/obj-test/.gitignore b/src/obj-test/.gitignore
deleted file mode 100644
index d6b7ef32c8..0000000000
--- a/src/obj-test/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-*
-!.gitignore
diff --git a/src/obj/.gitignore b/src/obj/.gitignore
deleted file mode 100644
index d6b7ef32c8..0000000000
--- a/src/obj/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-*
-!.gitignore
diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp
index 29423db3d0..131cceccbe 100644
--- a/src/qt/addresstablemodel.cpp
+++ b/src/qt/addresstablemodel.cpp
@@ -10,6 +10,8 @@
#include <key_io.h>
#include <wallet/wallet.h>
+#include <algorithm>
+
#include <QFont>
#include <QDebug>
@@ -86,18 +88,18 @@ public:
QString::fromStdString(EncodeDestination(address.dest))));
}
}
- // qLowerBound() and qUpperBound() require our cachedAddressTable list to be sorted in asc order
+ // std::lower_bound() and std::upper_bound() require our cachedAddressTable list to be sorted in asc order
// Even though the map is already sorted this re-sorting step is needed because the originating map
// is sorted by binary address, not by base58() address.
- qSort(cachedAddressTable.begin(), cachedAddressTable.end(), AddressTableEntryLessThan());
+ std::sort(cachedAddressTable.begin(), cachedAddressTable.end(), AddressTableEntryLessThan());
}
void updateEntry(const QString &address, const QString &label, bool isMine, const QString &purpose, int status)
{
// Find address / label in model
- QList<AddressTableEntry>::iterator lower = qLowerBound(
+ QList<AddressTableEntry>::iterator lower = std::lower_bound(
cachedAddressTable.begin(), cachedAddressTable.end(), address, AddressTableEntryLessThan());
- QList<AddressTableEntry>::iterator upper = qUpperBound(
+ QList<AddressTableEntry>::iterator upper = std::upper_bound(
cachedAddressTable.begin(), cachedAddressTable.end(), address, AddressTableEntryLessThan());
int lowerIndex = (lower - cachedAddressTable.begin());
int upperIndex = (upper - cachedAddressTable.begin());
diff --git a/src/qt/askpassphrasedialog.cpp b/src/qt/askpassphrasedialog.cpp
index a89a15bc9d..2ababb5e1e 100644
--- a/src/qt/askpassphrasedialog.cpp
+++ b/src/qt/askpassphrasedialog.cpp
@@ -18,12 +18,13 @@
#include <QMessageBox>
#include <QPushButton>
-AskPassphraseDialog::AskPassphraseDialog(Mode _mode, QWidget *parent) :
+AskPassphraseDialog::AskPassphraseDialog(Mode _mode, QWidget *parent, SecureString* passphrase_out) :
QDialog(parent),
ui(new Ui::AskPassphraseDialog),
mode(_mode),
model(nullptr),
- fCapsLock(false)
+ fCapsLock(false),
+ m_passphrase_out(passphrase_out)
{
ui->setupUi(this);
@@ -43,7 +44,7 @@ AskPassphraseDialog::AskPassphraseDialog(Mode _mode, QWidget *parent) :
switch(mode)
{
case Encrypt: // Ask passphrase x2
- ui->warningLabel->setText(tr("Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>ten or more random characters</b>, or <b>eight or more words</b>."));
+ ui->warningLabel->setText(tr("Enter the new passphrase for the wallet.<br/>Please use a passphrase of <b>ten or more random characters</b>, or <b>eight or more words</b>."));
ui->passLabel1->hide();
ui->passEdit1->hide();
setWindowTitle(tr("Encrypt wallet"));
@@ -66,7 +67,7 @@ AskPassphraseDialog::AskPassphraseDialog(Mode _mode, QWidget *parent) :
break;
case ChangePass: // Ask old passphrase + new passphrase x2
setWindowTitle(tr("Change passphrase"));
- ui->warningLabel->setText(tr("Enter the old passphrase and new passphrase to the wallet."));
+ ui->warningLabel->setText(tr("Enter the old passphrase and new passphrase for the wallet."));
break;
}
textChanged();
@@ -90,7 +91,7 @@ void AskPassphraseDialog::setModel(WalletModel *_model)
void AskPassphraseDialog::accept()
{
SecureString oldpass, newpass1, newpass2;
- if(!model)
+ if (!model && mode != Encrypt)
return;
oldpass.reserve(MAX_PASSPHRASE_SIZE);
newpass1.reserve(MAX_PASSPHRASE_SIZE);
@@ -119,24 +120,33 @@ void AskPassphraseDialog::accept()
{
if(newpass1 == newpass2)
{
- if(model->setWalletEncrypted(true, newpass1))
- {
- QMessageBox::warning(this, tr("Wallet encrypted"),
+ QString encryption_reminder = tr("Remember that encrypting your wallet cannot fully protect "
+ "your bitcoins from being stolen by malware infecting your computer.");
+ if (m_passphrase_out) {
+ m_passphrase_out->assign(newpass1);
+ QMessageBox::warning(this, tr("Wallet to be encrypted"),
"<qt>" +
- tr("Your wallet is now encrypted. "
- "Remember that encrypting your wallet cannot fully protect "
- "your bitcoins from being stolen by malware infecting your computer.") +
- "<br><br><b>" +
- tr("IMPORTANT: Any previous backups you have made of your wallet file "
- "should be replaced with the newly generated, encrypted wallet file. "
- "For security reasons, previous backups of the unencrypted wallet file "
- "will become useless as soon as you start using the new, encrypted wallet.") +
+ tr("Your wallet is about to be encrypted. ") + encryption_reminder +
"</b></qt>");
- }
- else
- {
- QMessageBox::critical(this, tr("Wallet encryption failed"),
- tr("Wallet encryption failed due to an internal error. Your wallet was not encrypted."));
+ } else {
+ assert(model != nullptr);
+ if(model->setWalletEncrypted(true, newpass1))
+ {
+ QMessageBox::warning(this, tr("Wallet encrypted"),
+ "<qt>" +
+ tr("Your wallet is now encrypted. ") + encryption_reminder +
+ "<br><br><b>" +
+ tr("IMPORTANT: Any previous backups you have made of your wallet file "
+ "should be replaced with the newly generated, encrypted wallet file. "
+ "For security reasons, previous backups of the unencrypted wallet file "
+ "will become useless as soon as you start using the new, encrypted wallet.") +
+ "</b></qt>");
+ }
+ else
+ {
+ QMessageBox::critical(this, tr("Wallet encryption failed"),
+ tr("Wallet encryption failed due to an internal error. Your wallet was not encrypted."));
+ }
}
QDialog::accept(); // Success
}
diff --git a/src/qt/askpassphrasedialog.h b/src/qt/askpassphrasedialog.h
index ac31569f63..bdfd3fb9a0 100644
--- a/src/qt/askpassphrasedialog.h
+++ b/src/qt/askpassphrasedialog.h
@@ -7,6 +7,8 @@
#include <QDialog>
+#include <support/allocators/secure.h>
+
class WalletModel;
namespace Ui {
@@ -27,7 +29,7 @@ public:
Decrypt /**< Ask passphrase and decrypt wallet */
};
- explicit AskPassphraseDialog(Mode mode, QWidget *parent);
+ explicit AskPassphraseDialog(Mode mode, QWidget *parent, SecureString* passphrase_out = nullptr);
~AskPassphraseDialog();
void accept();
@@ -39,6 +41,7 @@ private:
Mode mode;
WalletModel *model;
bool fCapsLock;
+ SecureString* m_passphrase_out;
private Q_SLOTS:
void textChanged();
diff --git a/src/qt/bantablemodel.cpp b/src/qt/bantablemodel.cpp
index 8a6b205cd8..efc726e09e 100644
--- a/src/qt/bantablemodel.cpp
+++ b/src/qt/bantablemodel.cpp
@@ -10,6 +10,8 @@
#include <sync.h>
#include <util/time.h>
+#include <algorithm>
+
#include <QDebug>
#include <QList>
@@ -61,7 +63,7 @@ public:
if (sortColumn >= 0)
// sort cachedBanlist (use stable sort to prevent rows jumping around unnecessarily)
- qStableSort(cachedBanlist.begin(), cachedBanlist.end(), BannedNodeLessThan(sortColumn, sortOrder));
+ std::stable_sort(cachedBanlist.begin(), cachedBanlist.end(), BannedNodeLessThan(sortColumn, sortOrder));
}
int size() const
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index a3b6a92855..46f8deee57 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -169,8 +169,11 @@ void BitcoinCore::shutdown()
}
}
-BitcoinApplication::BitcoinApplication(interfaces::Node& node, int &argc, char **argv):
- QApplication(argc, argv),
+static int qt_argc = 1;
+static const char* qt_argv = "bitcoin-qt";
+
+BitcoinApplication::BitcoinApplication(interfaces::Node& node):
+ QApplication(qt_argc, const_cast<char **>(&qt_argv)),
coreThread(nullptr),
m_node(node),
optionsModel(nullptr),
@@ -279,6 +282,10 @@ void BitcoinApplication::parameterSetup()
m_node.initParameterInteraction();
}
+void BitcoinApplication::SetPrune(bool prune, bool force) {
+ optionsModel->SetPrune(prune, force);
+}
+
void BitcoinApplication::requestInitialize()
{
qDebug() << __func__ << ": Requesting initialize";
@@ -391,15 +398,15 @@ WId BitcoinApplication::getMainWinId() const
static void SetupUIArgs()
{
#if defined(ENABLE_WALLET) && defined(ENABLE_BIP70)
- gArgs.AddArg("-allowselfsignedrootcertificates", strprintf("Allow self signed root certificates (default: %u)", DEFAULT_SELFSIGNED_ROOTCERTS), true, OptionsCategory::GUI);
+ gArgs.AddArg("-allowselfsignedrootcertificates", strprintf("Allow self signed root certificates (default: %u)", DEFAULT_SELFSIGNED_ROOTCERTS), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::GUI);
#endif
- gArgs.AddArg("-choosedatadir", strprintf("Choose data directory on startup (default: %u)", DEFAULT_CHOOSE_DATADIR), false, OptionsCategory::GUI);
- gArgs.AddArg("-lang=<lang>", "Set language, for example \"de_DE\" (default: system locale)", false, OptionsCategory::GUI);
- gArgs.AddArg("-min", "Start minimized", false, OptionsCategory::GUI);
- gArgs.AddArg("-resetguisettings", "Reset all settings changed in the GUI", false, OptionsCategory::GUI);
- gArgs.AddArg("-rootcertificates=<file>", "Set SSL root certificates for payment request (default: -system-)", false, OptionsCategory::GUI);
- gArgs.AddArg("-splash", strprintf("Show splash screen on startup (default: %u)", 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);
+ gArgs.AddArg("-choosedatadir", strprintf("Choose data directory on startup (default: %u)", DEFAULT_CHOOSE_DATADIR), ArgsManager::ALLOW_ANY, OptionsCategory::GUI);
+ gArgs.AddArg("-lang=<lang>", "Set language, for example \"de_DE\" (default: system locale)", ArgsManager::ALLOW_ANY, OptionsCategory::GUI);
+ gArgs.AddArg("-min", "Start minimized", ArgsManager::ALLOW_ANY, OptionsCategory::GUI);
+ gArgs.AddArg("-resetguisettings", "Reset all settings changed in the GUI", ArgsManager::ALLOW_ANY, OptionsCategory::GUI);
+ gArgs.AddArg("-rootcertificates=<file>", "Set SSL root certificates for payment request (default: -system-)", ArgsManager::ALLOW_ANY, OptionsCategory::GUI);
+ gArgs.AddArg("-splash", strprintf("Show splash screen on startup (default: %u)", DEFAULT_SPLASHSCREEN), ArgsManager::ALLOW_ANY, OptionsCategory::GUI);
+ gArgs.AddArg("-uiplatform", strprintf("Select platform to customize UI for (one of windows, macosx, other; default: %s)", BitcoinGUI::DEFAULT_UIPLATFORM), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::GUI);
}
int GuiMain(int argc, char* argv[])
@@ -433,7 +440,7 @@ int GuiMain(int argc, char* argv[])
QApplication::setAttribute(Qt::AA_DontShowIconsInMenus);
#endif
- BitcoinApplication app(*node, argc, argv);
+ BitcoinApplication app(*node);
// Register meta types used for QMetaObject::invokeMethod
qRegisterMetaType< bool* >();
@@ -484,13 +491,14 @@ int GuiMain(int argc, char* argv[])
/// 5. Now that settings and translations are available, ask user for data directory
// User language is set up: pick a data directory
- if (!Intro::pickDataDirectory(*node))
- return EXIT_SUCCESS;
+ bool did_show_intro = false;
+ bool prune = false; // Intro dialog prune check box
+ // Gracefully exit if the user cancels
+ if (!Intro::showIfNeeded(*node, did_show_intro, prune)) return EXIT_SUCCESS;
- /// 6. Determine availability of data and blocks directory and parse bitcoin.conf
+ /// 6. Determine availability of data directory and parse bitcoin.conf
/// - Do not call GetDataDir(true) before this step finishes
- if (!fs::is_directory(GetDataDir(false)))
- {
+ if (!CheckDataDirOption()) {
node->initError(strprintf("Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "")));
QMessageBox::critical(nullptr, PACKAGE_NAME,
QObject::tr("Error: Specified data directory \"%1\" does not exist.").arg(QString::fromStdString(gArgs.GetArg("-datadir", ""))));
@@ -509,7 +517,7 @@ int GuiMain(int argc, char* argv[])
// - QSettings() will use the new application name after this, resulting in network-specific settings
// - Needs to be done before createOptionsModel
- // Check for -testnet or -regtest parameter (Params() calls are only valid after this clause)
+ // Check for -chain, -testnet or -regtest parameter (Params() calls are only valid after this clause)
try {
node->selectParams(gArgs.GetChainName());
} catch(std::exception &e) {
@@ -522,7 +530,7 @@ int GuiMain(int argc, char* argv[])
PaymentServer::ipcParseCommandLine(*node, argc, argv);
#endif
- QScopedPointer<const NetworkStyle> networkStyle(NetworkStyle::instantiate(QString::fromStdString(Params().NetworkIDString())));
+ QScopedPointer<const NetworkStyle> networkStyle(NetworkStyle::instantiate(Params().NetworkIDString()));
assert(!networkStyle.isNull());
// Allow for separate UI settings for testnets
QApplication::setApplicationName(networkStyle->getAppName());
@@ -560,6 +568,11 @@ int GuiMain(int argc, char* argv[])
// Load GUI settings from QSettings
app.createOptionsModel(gArgs.GetBoolArg("-resetguisettings", false));
+ if (did_show_intro) {
+ // Store intro dialog settings other than datadir (network specific)
+ app.SetPrune(prune, true);
+ }
+
if (gArgs.GetBoolArg("-splash", DEFAULT_SPLASHSCREEN) && !gArgs.GetBoolArg("-min", false))
app.createSplashScreen(networkStyle.data());
diff --git a/src/qt/bitcoin.h b/src/qt/bitcoin.h
index 40537c1813..8c77fd8a7d 100644
--- a/src/qt/bitcoin.h
+++ b/src/qt/bitcoin.h
@@ -56,7 +56,7 @@ class BitcoinApplication: public QApplication
{
Q_OBJECT
public:
- explicit BitcoinApplication(interfaces::Node& node, int &argc, char **argv);
+ explicit BitcoinApplication(interfaces::Node& node);
~BitcoinApplication();
#ifdef ENABLE_WALLET
@@ -67,6 +67,8 @@ public:
void parameterSetup();
/// Create options model
void createOptionsModel(bool resetSettings);
+ /// Update prune value
+ void SetPrune(bool prune, bool force = false);
/// Create main window
void createWindow(const NetworkStyle *networkStyle);
/// Create splash screen
diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc
index fddc2a5685..037b23e4b2 100644
--- a/src/qt/bitcoin.qrc
+++ b/src/qt/bitcoin.qrc
@@ -2,7 +2,6 @@
<qresource prefix="/icons">
<file alias="bitcoin">res/icons/bitcoin.png</file>
<file alias="address-book">res/icons/address-book.png</file>
- <file alias="quit">res/icons/quit.png</file>
<file alias="send">res/icons/send.png</file>
<file alias="connect_0">res/icons/connect0.png</file>
<file alias="connect_1">res/icons/connect1.png</file>
@@ -20,7 +19,6 @@
<file alias="eye">res/icons/eye.png</file>
<file alias="eye_minus">res/icons/eye_minus.png</file>
<file alias="eye_plus">res/icons/eye_plus.png</file>
- <file alias="options">res/icons/configure.png</file>
<file alias="receiving_addresses">res/icons/receive.png</file>
<file alias="editpaste">res/icons/editpaste.png</file>
<file alias="editcopy">res/icons/editcopy.png</file>
@@ -37,14 +35,6 @@
<file alias="tx_inout">res/icons/tx_inout.png</file>
<file alias="lock_closed">res/icons/lock_closed.png</file>
<file alias="lock_open">res/icons/lock_open.png</file>
- <file alias="key">res/icons/key.png</file>
- <file alias="filesave">res/icons/filesave.png</file>
- <file alias="debugwindow">res/icons/debugwindow.png</file>
- <file alias="open">res/icons/open.png</file>
- <file alias="info">res/icons/info.png</file>
- <file alias="about">res/icons/about.png</file>
- <file alias="about_qt">res/icons/about_qt.png</file>
- <file alias="verify">res/icons/verify.png</file>
<file alias="warning">res/icons/warning.png</file>
<file alias="fontbigger">res/icons/fontbigger.png</file>
<file alias="fontsmaller">res/icons/fontsmaller.png</file>
diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp
index 5854ade655..9fa49b87fa 100644
--- a/src/qt/bitcoinamountfield.cpp
+++ b/src/qt/bitcoinamountfield.cpp
@@ -6,6 +6,7 @@
#include <qt/bitcoinunits.h>
#include <qt/guiconstants.h>
+#include <qt/guiutil.h>
#include <qt/qvaluecombobox.h>
#include <QApplication>
@@ -121,7 +122,7 @@ public:
const QFontMetrics fm(fontMetrics());
int h = lineEdit()->minimumSizeHint().height();
- int w = fm.width(BitcoinUnits::format(BitcoinUnits::BTC, BitcoinUnits::maxMoney(), false, BitcoinUnits::separatorAlways));
+ int w = GUIUtil::TextWidth(fm, BitcoinUnits::format(BitcoinUnits::BTC, BitcoinUnits::maxMoney(), false, BitcoinUnits::separatorAlways));
w += 2; // cursor blinking space
QStyleOptionSpinBox opt;
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 3533227483..7671fde705 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -6,6 +6,7 @@
#include <qt/bitcoinunits.h>
#include <qt/clientmodel.h>
+#include <qt/createwalletdialog.h>
#include <qt/guiconstants.h>
#include <qt/guiutil.h>
#include <qt/modaloverlay.h>
@@ -40,7 +41,6 @@
#include <QApplication>
#include <QComboBox>
#include <QDateTime>
-#include <QDesktopWidget>
#include <QDragEnterEvent>
#include <QListWidget>
#include <QMenu>
@@ -48,6 +48,7 @@
#include <QMessageBox>
#include <QMimeData>
#include <QProgressDialog>
+#include <QScreen>
#include <QSettings>
#include <QShortcut>
#include <QStackedWidget>
@@ -81,7 +82,7 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty
QSettings settings;
if (!restoreGeometry(settings.value("MainWindowGeometry").toByteArray())) {
// Restore failed (perhaps missing setting), center the window
- move(QApplication::desktop()->availableGeometry().center() - frameGeometry().center());
+ move(QGuiApplication::primaryScreen()->availableGeometry().center() - frameGeometry().center());
}
#ifdef ENABLE_WALLET
@@ -248,7 +249,7 @@ void BitcoinGUI::createActions()
sendCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_2));
tabGroup->addAction(sendCoinsAction);
- sendCoinsMenuAction = new QAction(platformStyle->TextColorIcon(":/icons/send"), sendCoinsAction->text(), this);
+ sendCoinsMenuAction = new QAction(sendCoinsAction->text(), this);
sendCoinsMenuAction->setStatusTip(sendCoinsAction->statusTip());
sendCoinsMenuAction->setToolTip(sendCoinsMenuAction->statusTip());
@@ -259,7 +260,7 @@ void BitcoinGUI::createActions()
receiveCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_3));
tabGroup->addAction(receiveCoinsAction);
- receiveCoinsMenuAction = new QAction(platformStyle->TextColorIcon(":/icons/receiving_addresses"), receiveCoinsAction->text(), this);
+ receiveCoinsMenuAction = new QAction(receiveCoinsAction->text(), this);
receiveCoinsMenuAction->setStatusTip(receiveCoinsAction->statusTip());
receiveCoinsMenuAction->setToolTip(receiveCoinsMenuAction->statusTip());
@@ -287,48 +288,48 @@ void BitcoinGUI::createActions()
connect(historyAction, &QAction::triggered, this, &BitcoinGUI::gotoHistoryPage);
#endif // ENABLE_WALLET
- quitAction = new QAction(platformStyle->TextColorIcon(":/icons/quit"), tr("E&xit"), this);
+ quitAction = new QAction(tr("E&xit"), this);
quitAction->setStatusTip(tr("Quit application"));
quitAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q));
quitAction->setMenuRole(QAction::QuitRole);
- aboutAction = new QAction(platformStyle->TextColorIcon(":/icons/about"), tr("&About %1").arg(PACKAGE_NAME), this);
+ aboutAction = new QAction(tr("&About %1").arg(PACKAGE_NAME), this);
aboutAction->setStatusTip(tr("Show information about %1").arg(PACKAGE_NAME));
aboutAction->setMenuRole(QAction::AboutRole);
aboutAction->setEnabled(false);
- aboutQtAction = new QAction(platformStyle->TextColorIcon(":/icons/about_qt"), tr("About &Qt"), this);
+ aboutQtAction = new QAction(tr("About &Qt"), this);
aboutQtAction->setStatusTip(tr("Show information about Qt"));
aboutQtAction->setMenuRole(QAction::AboutQtRole);
- optionsAction = new QAction(platformStyle->TextColorIcon(":/icons/options"), tr("&Options..."), this);
+ optionsAction = new QAction(tr("&Options..."), this);
optionsAction->setStatusTip(tr("Modify configuration options for %1").arg(PACKAGE_NAME));
optionsAction->setMenuRole(QAction::PreferencesRole);
optionsAction->setEnabled(false);
- toggleHideAction = new QAction(platformStyle->TextColorIcon(":/icons/about"), tr("&Show / Hide"), this);
+ toggleHideAction = new QAction(tr("&Show / Hide"), this);
toggleHideAction->setStatusTip(tr("Show or hide the main Window"));
- encryptWalletAction = new QAction(platformStyle->TextColorIcon(":/icons/lock_closed"), tr("&Encrypt Wallet..."), this);
+ encryptWalletAction = new QAction(tr("&Encrypt Wallet..."), this);
encryptWalletAction->setStatusTip(tr("Encrypt the private keys that belong to your wallet"));
encryptWalletAction->setCheckable(true);
- backupWalletAction = new QAction(platformStyle->TextColorIcon(":/icons/filesave"), tr("&Backup Wallet..."), this);
+ backupWalletAction = new QAction(tr("&Backup Wallet..."), this);
backupWalletAction->setStatusTip(tr("Backup wallet to another location"));
- changePassphraseAction = new QAction(platformStyle->TextColorIcon(":/icons/key"), tr("&Change Passphrase..."), this);
+ changePassphraseAction = new QAction(tr("&Change Passphrase..."), this);
changePassphraseAction->setStatusTip(tr("Change the passphrase used for wallet encryption"));
- signMessageAction = new QAction(platformStyle->TextColorIcon(":/icons/edit"), tr("Sign &message..."), this);
+ signMessageAction = new QAction(tr("Sign &message..."), this);
signMessageAction->setStatusTip(tr("Sign messages with your Bitcoin addresses to prove you own them"));
- verifyMessageAction = new QAction(platformStyle->TextColorIcon(":/icons/verify"), tr("&Verify message..."), this);
+ verifyMessageAction = new QAction(tr("&Verify message..."), this);
verifyMessageAction->setStatusTip(tr("Verify messages to ensure they were signed with specified Bitcoin addresses"));
- openRPCConsoleAction = new QAction(platformStyle->TextColorIcon(":/icons/debugwindow"), tr("&Debug window"), this);
+ openRPCConsoleAction = new QAction(tr("&Debug window"), this);
openRPCConsoleAction->setStatusTip(tr("Open debugging and diagnostic console"));
// initially disable the debug window menu item
openRPCConsoleAction->setEnabled(false);
openRPCConsoleAction->setObjectName("openRPCConsoleAction");
- usedSendingAddressesAction = new QAction(platformStyle->TextColorIcon(":/icons/address-book"), tr("&Sending addresses"), this);
+ usedSendingAddressesAction = new QAction(tr("&Sending addresses"), this);
usedSendingAddressesAction->setStatusTip(tr("Show the list of used sending addresses and labels"));
- usedReceivingAddressesAction = new QAction(platformStyle->TextColorIcon(":/icons/address-book"), tr("&Receiving addresses"), this);
+ usedReceivingAddressesAction = new QAction(tr("&Receiving addresses"), this);
usedReceivingAddressesAction->setStatusTip(tr("Show the list of used receiving addresses and labels"));
- openAction = new QAction(platformStyle->TextColorIcon(":/icons/open"), tr("Open &URI..."), this);
+ openAction = new QAction(tr("Open &URI..."), this);
openAction->setStatusTip(tr("Open a bitcoin: URI or payment request"));
m_open_wallet_action = new QAction(tr("Open Wallet"), this);
@@ -339,7 +340,10 @@ void BitcoinGUI::createActions()
m_close_wallet_action = new QAction(tr("Close Wallet..."), this);
m_close_wallet_action->setStatusTip(tr("Close wallet"));
- showHelpMessageAction = new QAction(platformStyle->TextColorIcon(":/icons/info"), tr("&Command-line options"), this);
+ m_create_wallet_action = new QAction(tr("Create Wallet..."), this);
+ m_create_wallet_action->setStatusTip(tr("Create a new wallet"));
+
+ showHelpMessageAction = new QAction(tr("&Command-line options"), this);
showHelpMessageAction->setMenuRole(QAction::NoRole);
showHelpMessageAction->setStatusTip(tr("Show the %1 help message to get a list with possible Bitcoin command-line options").arg(PACKAGE_NAME));
@@ -371,6 +375,10 @@ void BitcoinGUI::createActions()
for (const std::pair<const std::string, bool>& i : m_wallet_controller->listWalletDir()) {
const std::string& path = i.first;
QString name = path.empty() ? QString("["+tr("default wallet")+"]") : QString::fromStdString(path);
+ // Menu items remove single &. Single & are shown when && is in
+ // the string, but only the first occurrence. So replace only
+ // the first & with &&.
+ name.replace(name.indexOf(QChar('&')), 1, QString("&&"));
QAction* action = m_open_wallet_menu->addAction(name);
if (i.second) {
@@ -379,31 +387,11 @@ void BitcoinGUI::createActions()
continue;
}
- connect(action, &QAction::triggered, [this, name, path] {
- OpenWalletActivity* activity = m_wallet_controller->openWallet(path);
-
- QProgressDialog* dialog = new QProgressDialog(this);
- dialog->setLabelText(tr("Opening Wallet <b>%1</b>...").arg(name.toHtmlEscaped()));
- dialog->setRange(0, 0);
- dialog->setCancelButton(nullptr);
- dialog->setWindowModality(Qt::ApplicationModal);
- dialog->show();
-
- connect(activity, &OpenWalletActivity::message, this, [this] (QMessageBox::Icon icon, QString text) {
- QMessageBox box;
- box.setIcon(icon);
- box.setText(tr("Open Wallet Failed"));
- box.setInformativeText(text);
- box.setStandardButtons(QMessageBox::Ok);
- box.setDefaultButton(QMessageBox::Ok);
- connect(this, &QObject::destroyed, &box, &QDialog::accept);
- box.exec();
- });
+ connect(action, &QAction::triggered, [this, path] {
+ auto activity = new OpenWalletActivity(m_wallet_controller, this);
connect(activity, &OpenWalletActivity::opened, this, &BitcoinGUI::setCurrentWallet);
connect(activity, &OpenWalletActivity::finished, activity, &QObject::deleteLater);
- connect(activity, &OpenWalletActivity::finished, dialog, &QObject::deleteLater);
- bool invoked = QMetaObject::invokeMethod(activity, "open");
- assert(invoked);
+ activity->open(path);
});
}
if (m_open_wallet_menu->isEmpty()) {
@@ -414,6 +402,12 @@ void BitcoinGUI::createActions()
connect(m_close_wallet_action, &QAction::triggered, [this] {
m_wallet_controller->closeWallet(walletFrame->currentWalletModel(), this);
});
+ connect(m_create_wallet_action, &QAction::triggered, [this] {
+ auto activity = new CreateWalletActivity(m_wallet_controller, this);
+ connect(activity, &CreateWalletActivity::created, this, &BitcoinGUI::setCurrentWallet);
+ connect(activity, &CreateWalletActivity::finished, activity, &QObject::deleteLater);
+ activity->create();
+ });
}
#endif // ENABLE_WALLET
@@ -435,6 +429,7 @@ void BitcoinGUI::createMenuBar()
QMenu *file = appMenuBar->addMenu(tr("&File"));
if(walletFrame)
{
+ file->addAction(m_create_wallet_action);
file->addAction(m_open_wallet_action);
file->addAction(m_close_wallet_action);
file->addSeparator();
@@ -480,24 +475,16 @@ void BitcoinGUI::createMenuBar()
connect(qApp, &QApplication::focusWindowChanged, [zoom_action] (QWindow* window) {
zoom_action->setEnabled(window != nullptr);
});
-#else
- QAction* restore_action = window_menu->addAction(tr("Restore"));
- connect(restore_action, &QAction::triggered, [] {
- qApp->focusWindow()->showNormal();
- });
-
- connect(qApp, &QApplication::focusWindowChanged, [restore_action] (QWindow* window) {
- restore_action->setEnabled(window != nullptr);
- });
#endif
if (walletFrame) {
+#ifdef Q_OS_MAC
window_menu->addSeparator();
QAction* main_window_action = window_menu->addAction(tr("Main Window"));
connect(main_window_action, &QAction::triggered, [this] {
GUIUtil::bringToFront(this);
});
-
+#endif
window_menu->addSeparator();
window_menu->addAction(usedSendingAddressesAction);
window_menu->addAction(usedReceivingAddressesAction);
@@ -1407,7 +1394,7 @@ UnitDisplayStatusBarControl::UnitDisplayStatusBarControl(const PlatformStyle *pl
const QFontMetrics fm(font());
for (const BitcoinUnits::Unit unit : units)
{
- max_width = qMax(max_width, fm.width(BitcoinUnits::longName(unit)));
+ max_width = qMax(max_width, GUIUtil::TextWidth(fm, BitcoinUnits::longName(unit)));
}
setMinimumSize(max_width, 0);
setAlignment(Qt::AlignRight | Qt::AlignVCenter);
diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h
index 46ced79007..809cf8b4ed 100644
--- a/src/qt/bitcoingui.h
+++ b/src/qt/bitcoingui.h
@@ -147,6 +147,7 @@ private:
QAction* openRPCConsoleAction = nullptr;
QAction* openAction = nullptr;
QAction* showHelpMessageAction = nullptr;
+ QAction* m_create_wallet_action{nullptr};
QAction* m_open_wallet_action{nullptr};
QMenu* m_open_wallet_menu{nullptr};
QAction* m_close_wallet_action{nullptr};
diff --git a/src/qt/bitcoinstrings.cpp b/src/qt/bitcoinstrings.cpp
index 87736cd185..3d40ee7823 100644
--- a/src/qt/bitcoinstrings.cpp
+++ b/src/qt/bitcoinstrings.cpp
@@ -131,12 +131,12 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Initialization sanity check failed. %s is shu
QT_TRANSLATE_NOOP("bitcoin-core", "Insufficient funds"),
QT_TRANSLATE_NOOP("bitcoin-core", "Invalid -onion address or hostname: '%s'"),
QT_TRANSLATE_NOOP("bitcoin-core", "Invalid -proxy address or hostname: '%s'"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Invalid P2P permission: '%s'"),
QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -%s=<amount>: '%s'"),
QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -discardfee=<amount>: '%s'"),
QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -fallbackfee=<amount>: '%s'"),
QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Invalid netmask specified in -whitelist: '%s'"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Keypool ran out, please call keypoolrefill first"),
QT_TRANSLATE_NOOP("bitcoin-core", "Loading P2P addresses..."),
QT_TRANSLATE_NOOP("bitcoin-core", "Loading banlist..."),
QT_TRANSLATE_NOOP("bitcoin-core", "Loading block index..."),
@@ -170,7 +170,6 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Transaction amounts must not be negative"),
QT_TRANSLATE_NOOP("bitcoin-core", "Transaction fee and change calculation failed"),
QT_TRANSLATE_NOOP("bitcoin-core", "Transaction has too long of a mempool chain"),
QT_TRANSLATE_NOOP("bitcoin-core", "Transaction must have at least one recipient"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Transaction too large for fee policy"),
QT_TRANSLATE_NOOP("bitcoin-core", "Transaction too large"),
QT_TRANSLATE_NOOP("bitcoin-core", "Unable to bind to %s on this computer (bind returned error %s)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Unable to bind to %s on this computer. %s is probably already running."),
@@ -179,6 +178,8 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Unable to generate initial keys"),
QT_TRANSLATE_NOOP("bitcoin-core", "Unable to generate keys"),
QT_TRANSLATE_NOOP("bitcoin-core", "Unable to start HTTP server. See debug log for details."),
QT_TRANSLATE_NOOP("bitcoin-core", "Unknown -blockfilterindex value %s."),
+QT_TRANSLATE_NOOP("bitcoin-core", "Unknown address type '%s'"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Unknown change type '%s'"),
QT_TRANSLATE_NOOP("bitcoin-core", "Unknown network specified in -onlynet: '%s'"),
QT_TRANSLATE_NOOP("bitcoin-core", "Unsupported logging category %s=%s."),
QT_TRANSLATE_NOOP("bitcoin-core", "Upgrading UTXO database"),
diff --git a/src/qt/createwalletdialog.cpp b/src/qt/createwalletdialog.cpp
new file mode 100644
index 0000000000..8e6474b0d4
--- /dev/null
+++ b/src/qt/createwalletdialog.cpp
@@ -0,0 +1,62 @@
+// Copyright (c) 2019 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 defined(HAVE_CONFIG_H)
+#include <config/bitcoin-config.h>
+#endif
+
+#include <qt/createwalletdialog.h>
+#include <qt/forms/ui_createwalletdialog.h>
+
+#include <QPushButton>
+
+CreateWalletDialog::CreateWalletDialog(QWidget* parent) :
+ QDialog(parent),
+ ui(new Ui::CreateWalletDialog)
+{
+ ui->setupUi(this);
+ ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Create"));
+ ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
+ ui->wallet_name_line_edit->setFocus(Qt::ActiveWindowFocusReason);
+
+ connect(ui->wallet_name_line_edit, &QLineEdit::textEdited, [this](const QString& text) {
+ ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!text.isEmpty());
+ });
+
+ connect(ui->encrypt_wallet_checkbox, &QCheckBox::toggled, [this](bool checked) {
+ // Disable the disable_privkeys_checkbox when isEncryptWalletChecked is
+ // set to true, enable it when isEncryptWalletChecked is false.
+ ui->disable_privkeys_checkbox->setEnabled(!checked);
+
+ // When the disable_privkeys_checkbox is disabled, uncheck it.
+ if (!ui->disable_privkeys_checkbox->isEnabled()) {
+ ui->disable_privkeys_checkbox->setChecked(false);
+ }
+ });
+}
+
+CreateWalletDialog::~CreateWalletDialog()
+{
+ delete ui;
+}
+
+QString CreateWalletDialog::walletName() const
+{
+ return ui->wallet_name_line_edit->text();
+}
+
+bool CreateWalletDialog::isEncryptWalletChecked() const
+{
+ return ui->encrypt_wallet_checkbox->isChecked();
+}
+
+bool CreateWalletDialog::isDisablePrivateKeysChecked() const
+{
+ return ui->disable_privkeys_checkbox->isChecked();
+}
+
+bool CreateWalletDialog::isMakeBlankWalletChecked() const
+{
+ return ui->blank_wallet_checkbox->isChecked();
+}
diff --git a/src/qt/createwalletdialog.h b/src/qt/createwalletdialog.h
new file mode 100644
index 0000000000..30766107b9
--- /dev/null
+++ b/src/qt/createwalletdialog.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2019 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_QT_CREATEWALLETDIALOG_H
+#define BITCOIN_QT_CREATEWALLETDIALOG_H
+
+#include <QDialog>
+
+class WalletModel;
+
+namespace Ui {
+ class CreateWalletDialog;
+}
+
+/** Dialog for creating wallets
+ */
+class CreateWalletDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit CreateWalletDialog(QWidget* parent);
+ virtual ~CreateWalletDialog();
+
+ QString walletName() const;
+ bool isEncryptWalletChecked() const;
+ bool isDisablePrivateKeysChecked() const;
+ bool isMakeBlankWalletChecked() const;
+
+private:
+ Ui::CreateWalletDialog *ui;
+};
+
+#endif // BITCOIN_QT_CREATEWALLETDIALOG_H
diff --git a/src/qt/forms/askpassphrasedialog.ui b/src/qt/forms/askpassphrasedialog.ui
index 69803989cd..e74d183818 100644
--- a/src/qt/forms/askpassphrasedialog.ui
+++ b/src/qt/forms/askpassphrasedialog.ui
@@ -95,7 +95,7 @@
<item row="3" column="1">
<widget class="QCheckBox" name="toggleShowPasswordButton">
<property name="text">
- <string>Show password</string>
+ <string>Show passphrase</string>
</property>
</widget>
</item>
diff --git a/src/qt/forms/createwalletdialog.ui b/src/qt/forms/createwalletdialog.ui
new file mode 100644
index 0000000000..e49bab8f3b
--- /dev/null
+++ b/src/qt/forms/createwalletdialog.ui
@@ -0,0 +1,151 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>CreateWalletDialog</class>
+ <widget class="QDialog" name="CreateWalletDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>364</width>
+ <height>185</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Create Wallet</string>
+ </property>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>140</y>
+ <width>341</width>
+ <height>32</height>
+ </rect>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ <widget class="QLineEdit" name="wallet_name_line_edit">
+ <property name="geometry">
+ <rect>
+ <x>120</x>
+ <y>20</y>
+ <width>231</width>
+ <height>24</height>
+ </rect>
+ </property>
+ </widget>
+ <widget class="QLabel" name="label">
+ <property name="geometry">
+ <rect>
+ <x>20</x>
+ <y>20</y>
+ <width>101</width>
+ <height>21</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>Wallet Name</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" name="encrypt_wallet_checkbox">
+ <property name="geometry">
+ <rect>
+ <x>20</x>
+ <y>50</y>
+ <width>171</width>
+ <height>22</height>
+ </rect>
+ </property>
+ <property name="toolTip">
+ <string>Encrypt the wallet. The wallet will be encrypted with a passphrase of your choice.</string>
+ </property>
+ <property name="text">
+ <string>Encrypt Wallet</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QCheckBox" name="disable_privkeys_checkbox">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>20</x>
+ <y>80</y>
+ <width>171</width>
+ <height>22</height>
+ </rect>
+ </property>
+ <property name="toolTip">
+ <string>Disable private keys for this wallet. Wallets with private keys disabled will have no private keys and cannot have an HD seed or imported private keys. This is ideal for watch-only wallets.</string>
+ </property>
+ <property name="text">
+ <string>Disable Private Keys</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" name="blank_wallet_checkbox">
+ <property name="geometry">
+ <rect>
+ <x>20</x>
+ <y>110</y>
+ <width>171</width>
+ <height>22</height>
+ </rect>
+ </property>
+ <property name="toolTip">
+ <string>Make a blank wallet. Blank wallets do not initially have private keys or scripts. Private keys and addresses can be imported, or an HD seed can be set, at a later time.</string>
+ </property>
+ <property name="text">
+ <string>Make Blank Wallet</string>
+ </property>
+ </widget>
+ </widget>
+ <tabstops>
+ <tabstop>wallet_name_line_edit</tabstop>
+ <tabstop>encrypt_wallet_checkbox</tabstop>
+ <tabstop>disable_privkeys_checkbox</tabstop>
+ <tabstop>blank_wallet_checkbox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>CreateWalletDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>CreateWalletDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui
index 6e52c5e477..be807b20c0 100644
--- a/src/qt/forms/debugwindow.ui
+++ b/src/qt/forms/debugwindow.ui
@@ -15,6 +15,25 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
+ <widget class="QLabel" name="label_alerts">
+ <property name="visible">
+ <bool>false</bool>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">QLabel { background-color: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop:0 #F0D0A0, stop:1 #F8D488); color:#000000; }</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="margin">
+ <number>3</number>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
diff --git a/src/qt/forms/intro.ui b/src/qt/forms/intro.ui
index cfdd8482e3..f27a4ebe44 100644
--- a/src/qt/forms/intro.ui
+++ b/src/qt/forms/intro.ui
@@ -211,6 +211,16 @@
</widget>
</item>
<item>
+ <widget class="QCheckBox" name="prune">
+ <property name="toolTip">
+ <string>Reverting this setting requires re-downloading the entire blockchain. It is faster to download the full chain first and prune it later. Disables some advanced features.</string>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ </item>
+ <item>
<widget class="QLabel" name="lblExplanation2">
<property name="text">
<string>This initial synchronisation is very demanding, and may expose hardware problems with your computer that had previously gone unnoticed. Each time you run %1, it will continue downloading where it left off.</string>
diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h
index d8f5594983..dcdb247977 100644
--- a/src/qt/guiconstants.h
+++ b/src/qt/guiconstants.h
@@ -5,6 +5,8 @@
#ifndef BITCOIN_QT_GUICONSTANTS_H
#define BITCOIN_QT_GUICONSTANTS_H
+#include <cstdint>
+
/* Milliseconds between model updates */
static const int MODEL_UPDATE_DELAY = 250;
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index dc1da7f8a9..c4e0321f28 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -39,7 +39,6 @@
#include <QClipboard>
#include <QDateTime>
#include <QDesktopServices>
-#include <QDesktopWidget>
#include <QDoubleValidator>
#include <QFileDialog>
#include <QFont>
@@ -58,9 +57,10 @@
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-#include <objc/objc-runtime.h>
#include <CoreServices/CoreServices.h>
#include <QProcess>
+
+void ForceActivation();
#endif
namespace GUIUtil {
@@ -360,10 +360,7 @@ bool isObscured(QWidget *w)
void bringToFront(QWidget* w)
{
#ifdef Q_OS_MAC
- // Force application activation on macOS. With Qt 5.4 this is required when
- // an action in the dock menu is triggered.
- id app = objc_msgSend((id) objc_getClass("NSApplication"), sel_registerName("sharedApplication"));
- objc_msgSend(app, sel_registerName("activateIgnoringOtherApps:"), YES);
+ ForceActivation();
#endif
if (w) {
@@ -591,7 +588,7 @@ bool SetStartOnSystemStartup(bool fAutoStart)
// Start client minimized
QString strArgs = "-min";
// Set -testnet /-regtest options
- strArgs += QString::fromStdString(strprintf(" -testnet=%d -regtest=%d", gArgs.GetBoolArg("-testnet", false), gArgs.GetBoolArg("-regtest", false)));
+ strArgs += QString::fromStdString(strprintf(" -chain=%s", gArgs.GetChainName()));
// Set the path to the shortcut target
psl->SetPath(pszExePath);
@@ -686,7 +683,7 @@ bool SetStartOnSystemStartup(bool fAutoStart)
optionFile << "Name=Bitcoin\n";
else
optionFile << strprintf("Name=Bitcoin (%s)\n", chain);
- optionFile << "Exec=" << pszExePath << strprintf(" -min -testnet=%d -regtest=%d\n", gArgs.GetBoolArg("-testnet", false), gArgs.GetBoolArg("-regtest", false));
+ optionFile << "Exec=" << pszExePath << strprintf(" -min -chain=%s\n", chain);
optionFile << "Terminal=false\n";
optionFile << "Hidden=false\n";
optionFile.close();
@@ -916,7 +913,7 @@ qreal calculateIdealFontSize(int width, const QString& text, QFont font, qreal m
while(font_size >= minPointSize) {
font.setPointSizeF(font_size);
QFontMetrics fm(font);
- if (fm.width(text) < width) {
+ if (TextWidth(fm, text) < width) {
break;
}
font_size -= 0.5;
@@ -948,7 +945,7 @@ void PolishProgressDialog(QProgressDialog* dialog)
{
#ifdef Q_OS_MAC
// Workaround for macOS-only Qt bug; see: QTBUG-65750, QTBUG-70357.
- const int margin = dialog->fontMetrics().width("X");
+ const int margin = TextWidth(dialog->fontMetrics(), ("X"));
dialog->resize(dialog->width() + 2 * margin, dialog->height());
dialog->show();
#else
@@ -956,4 +953,13 @@ void PolishProgressDialog(QProgressDialog* dialog)
#endif
}
+int TextWidth(const QFontMetrics& fm, const QString& text)
+{
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
+ return fm.horizontalAdvance(text);
+#else
+ return fm.width(text);
+#endif
+}
+
} // namespace GUIUtil
diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h
index bea4a83494..9db92f94d7 100644
--- a/src/qt/guiutil.h
+++ b/src/qt/guiutil.h
@@ -257,6 +257,14 @@ namespace GUIUtil
// Fix known bugs in QProgressDialog class.
void PolishProgressDialog(QProgressDialog* dialog);
+
+ /**
+ * Returns the distance in pixels appropriate for drawing a subsequent character after text.
+ *
+ * In Qt 5.12 and before the QFontMetrics::width() is used and it is deprecated since Qt 13.0.
+ * In Qt 5.11 the QFontMetrics::horizontalAdvance() was introduced.
+ */
+ int TextWidth(const QFontMetrics& fm, const QString& text);
} // namespace GUIUtil
#endif // BITCOIN_QT_GUIUTIL_H
diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp
index 102e37e471..9e05c63aa0 100644
--- a/src/qt/intro.cpp
+++ b/src/qt/intro.cpp
@@ -131,6 +131,11 @@ Intro::Intro(QWidget *parent, uint64_t blockchain_size, uint64_t chain_state_siz
ui->lblExplanation2->setText(ui->lblExplanation2->text().arg(PACKAGE_NAME));
uint64_t pruneTarget = std::max<int64_t>(0, gArgs.GetArg("-prune", 0));
+ if (pruneTarget > 1) { // -prune=1 means enabled, above that it's a size in MB
+ ui->prune->setChecked(true);
+ ui->prune->setEnabled(false);
+ }
+ ui->prune->setText(tr("Discard blocks after verification, except most recent %1 GB (prune)").arg(pruneTarget ? pruneTarget / 1000 : 2));
requiredSpace = m_blockchain_size;
QString storageRequiresMsg = tr("At least %1 GB of data will be stored in this directory, and it will grow over time.");
if (pruneTarget) {
@@ -180,8 +185,10 @@ void Intro::setDataDirectory(const QString &dataDir)
}
}
-bool Intro::pickDataDirectory(interfaces::Node& node)
+bool Intro::showIfNeeded(interfaces::Node& node, bool& did_show_intro, bool& prune)
{
+ did_show_intro = false;
+
QSettings settings;
/* If data directory provided on command line, no need to look at settings
or show a picking dialog */
@@ -205,6 +212,7 @@ bool Intro::pickDataDirectory(interfaces::Node& node)
Intro intro(0, node.getAssumedBlockchainSize(), node.getAssumedChainStateSize());
intro.setDataDirectory(dataDir);
intro.setWindowIcon(QIcon(":icons/bitcoin"));
+ did_show_intro = true;
while(true)
{
@@ -227,6 +235,9 @@ bool Intro::pickDataDirectory(interfaces::Node& node)
}
}
+ // Additional preferences:
+ prune = intro.ui->prune->isChecked();
+
settings.setValue("strDataDir", dataDir);
settings.setValue("fReset", false);
}
@@ -263,6 +274,11 @@ void Intro::setStatus(int status, const QString &message, quint64 bytesAvailable
{
freeString += " " + tr("(of %n GB needed)", "", requiredSpace);
ui->freeSpace->setStyleSheet("QLabel { color: #800000 }");
+ ui->prune->setChecked(true);
+ } else if (bytesAvailable / GB_BYTES - requiredSpace < 10) {
+ freeString += " " + tr("(%n GB needed for full chain)", "", requiredSpace);
+ ui->freeSpace->setStyleSheet("QLabel { color: #999900 }");
+ ui->prune->setChecked(true);
} else {
ui->freeSpace->setStyleSheet("");
}
diff --git a/src/qt/intro.h b/src/qt/intro.h
index c3b26808d4..aca7e71642 100644
--- a/src/qt/intro.h
+++ b/src/qt/intro.h
@@ -39,6 +39,7 @@ public:
/**
* Determine data directory. Let the user choose if the current one doesn't exist.
+ * Let the user configure additional preferences such as pruning.
*
* @returns true if a data directory was selected, false if the user cancelled the selection
* dialog.
@@ -46,7 +47,7 @@ public:
* @note do NOT call global GetDataDir() before calling this function, this
* will cause the wrong path to be cached.
*/
- static bool pickDataDirectory(interfaces::Node& node);
+ static bool showIfNeeded(interfaces::Node& node, bool& did_show_intro, bool& prune);
Q_SIGNALS:
void requestCheck();
diff --git a/src/qt/locale/bitcoin_en.ts b/src/qt/locale/bitcoin_en.ts
index bff7469071..7d9e7eab4e 100644
--- a/src/qt/locale/bitcoin_en.ts
+++ b/src/qt/locale/bitcoin_en.ts
@@ -132,7 +132,7 @@
<context>
<name>AddressTableModel</name>
<message>
- <location filename="../addresstablemodel.cpp" line="+163"/>
+ <location filename="../addresstablemodel.cpp" line="+165"/>
<source>Label</source>
<translation type="unfinished"></translation>
</message>
@@ -171,16 +171,11 @@
</message>
<message>
<location line="+14"/>
- <source>Show password</source>
+ <source>Show passphrase</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../askpassphrasedialog.cpp" line="+46"/>
- <source>Enter the new passphrase to the wallet.&lt;br/&gt;Please use a passphrase of &lt;b&gt;ten or more random characters&lt;/b&gt;, or &lt;b&gt;eight or more words&lt;/b&gt;.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+3"/>
+ <location filename="../askpassphrasedialog.cpp" line="+50"/>
<source>Encrypt wallet</source>
<translation type="unfinished"></translation>
</message>
@@ -210,12 +205,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
- <source>Enter the old passphrase and new passphrase to the wallet.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+45"/>
+ <location line="+46"/>
<source>Confirm wallet encryption</source>
<translation type="unfinished"></translation>
</message>
@@ -230,36 +220,61 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+9"/>
- <location line="+58"/>
+ <location line="+19"/>
+ <location line="+57"/>
<source>Wallet encrypted</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-56"/>
- <source>Your wallet is now encrypted. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source>
+ <location line="-145"/>
+ <source>Enter the new passphrase for the wallet.&lt;br/&gt;Please use a passphrase of &lt;b&gt;ten or more random characters&lt;/b&gt;, or &lt;b&gt;eight or more words&lt;/b&gt;.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+23"/>
+ <source>Enter the old passphrase and new passphrase for the wallet.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+53"/>
+ <source>Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+4"/>
+ <source>Wallet to be encrypted</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Your wallet is about to be encrypted. </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+8"/>
+ <source>Your wallet is now encrypted. </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
<source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+8"/>
- <location line="+7"/>
+ <location line="+8"/>
<location line="+43"/>
<location line="+6"/>
<source>Wallet encryption failed</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-55"/>
+ <location line="-56"/>
<source>Wallet encryption failed due to an internal error. Your wallet was not encrypted.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+7"/>
+ <location line="+8"/>
<location line="+49"/>
<source>The supplied passphrases do not match.</source>
<translation type="unfinished"></translation>
@@ -297,7 +312,7 @@
<context>
<name>BanTableModel</name>
<message>
- <location filename="../bantablemodel.cpp" line="+86"/>
+ <location filename="../bantablemodel.cpp" line="+88"/>
<source>IP/Netmask</source>
<translation type="unfinished"></translation>
</message>
@@ -310,17 +325,17 @@
<context>
<name>BitcoinGUI</name>
<message>
- <location filename="../bitcoingui.cpp" line="+318"/>
+ <location filename="../bitcoingui.cpp" line="+316"/>
<source>Sign &amp;message...</source>
<translation>Sign &amp;message...</translation>
</message>
<message>
- <location line="+638"/>
+ <location line="+623"/>
<source>Synchronizing with network...</source>
<translation>Synchronizing with network...</translation>
</message>
<message>
- <location line="-716"/>
+ <location line="-701"/>
<source>&amp;Overview</source>
<translation>&amp;Overview</translation>
</message>
@@ -400,7 +415,17 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+217"/>
+ <location line="+11"/>
+ <source>Create Wallet...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <source>Create a new wallet</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+190"/>
<source>Wallet:</source>
<translation type="unfinished"></translation>
</message>
@@ -435,7 +460,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-1036"/>
+ <location line="-1021"/>
<source>Send coins to a Bitcoin address</source>
<translation>Send coins to a Bitcoin address</translation>
</message>
@@ -500,17 +525,17 @@
<translation>Verify messages to ensure they were signed with specified Bitcoin addresses</translation>
</message>
<message>
- <location line="+118"/>
+ <location line="+110"/>
<source>&amp;File</source>
<translation>&amp;File</translation>
</message>
<message>
- <location line="+14"/>
+ <location line="+15"/>
<source>&amp;Settings</source>
<translation>&amp;Settings</translation>
</message>
<message>
- <location line="+66"/>
+ <location line="+58"/>
<source>&amp;Help</source>
<translation>&amp;Help</translation>
</message>
@@ -520,7 +545,7 @@
<translation>Tabs toolbar</translation>
</message>
<message>
- <location line="-271"/>
+ <location line="-256"/>
<source>Request payments (generates QR codes and bitcoin: URIs)</source>
<translation type="unfinished"></translation>
</message>
@@ -540,12 +565,12 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+10"/>
+ <location line="+13"/>
<source>&amp;Command-line options</source>
<translation type="unfinished"></translation>
</message>
<message numerus="yes">
- <location line="+540"/>
+ <location line="+522"/>
<source>%n active connection(s) to Bitcoin network</source>
<translation>
<numerusform>%n active connection to Bitcoin network</numerusform>
@@ -606,7 +631,7 @@
<translation>Up to date</translation>
</message>
<message>
- <location line="-657"/>
+ <location line="-642"/>
<source>&amp;Sending addresses</source>
<translation type="unfinished"></translation>
</message>
@@ -636,32 +661,22 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+4"/>
+ <location line="+7"/>
<source>Show the %1 help message to get a list with possible Bitcoin command-line options</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+30"/>
+ <location line="+29"/>
<source>default wallet</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+13"/>
- <source>Opening Wallet &lt;b&gt;%1&lt;/b&gt;...</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+9"/>
- <source>Open Wallet Failed</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+15"/>
+ <location line="+21"/>
<source>No wallets available</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+48"/>
+ <location line="+55"/>
<source>&amp;Window</source>
<translation type="unfinished">&amp;Window</translation>
</message>
@@ -676,12 +691,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+14"/>
- <source>Restore</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+12"/>
+ <location line="+18"/>
<source>Main Window</source>
<translation type="unfinished"></translation>
</message>
@@ -782,7 +792,7 @@
<translation>Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;locked&lt;/b&gt;</translation>
</message>
<message>
- <location filename="../bitcoin.cpp" line="+390"/>
+ <location filename="../bitcoin.cpp" line="+386"/>
<source>A fatal error occurred. Bitcoin can no longer continue safely and will quit.</source>
<translation type="unfinished"></translation>
</message>
@@ -941,7 +951,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+155"/>
+ <location line="+157"/>
<source>yes</source>
<translation type="unfinished"></translation>
</message>
@@ -978,6 +988,72 @@
</message>
</context>
<context>
+ <name>CreateWalletActivity</name>
+ <message>
+ <location filename="../walletcontroller.cpp" line="+201"/>
+ <source>Creating Wallet &lt;b&gt;%1&lt;/b&gt;...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+26"/>
+ <source>Create wallet failed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Create wallet warning</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>CreateWalletDialog</name>
+ <message>
+ <location filename="../forms/createwalletdialog.ui" line="+14"/>
+ <source>Create Wallet</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+38"/>
+ <source>Wallet Name</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+13"/>
+ <source>Encrypt the wallet. The wallet will be encrypted with a passphrase of your choice.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>Encrypt Wallet</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+19"/>
+ <source>Disable private keys for this wallet. Wallets with private keys disabled will have no private keys and cannot have an HD seed or imported private keys. This is ideal for watch-only wallets.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>Disable Private Keys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+13"/>
+ <source>Make a blank wallet. Blank wallets do not initially have private keys or scripts. Private keys and addresses can be imported, or an HD seed can be set, at a later time.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>Make Blank Wallet</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../createwalletdialog.cpp" line="+19"/>
+ <source>Create</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
<name>EditAddressDialog</name>
<message>
<location filename="../forms/editaddressdialog.ui" line="+14"/>
@@ -1121,6 +1197,11 @@
</message>
<message>
<location line="+10"/>
+ <source>Reverting this setting requires re-downloading the entire blockchain. It is faster to download the full chain first and prune it later. Disables some advanced features.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+10"/>
<source>This initial synchronisation is very demanding, and may expose hardware problems with your computer that had previously gone unnoticed. Each time you run %1, it will continue downloading where it left off.</source>
<translation type="unfinished"></translation>
</message>
@@ -1130,7 +1211,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-160"/>
+ <location line="-170"/>
<source>Use the default data directory</source>
<translation>Use the default data directory</translation>
</message>
@@ -1145,7 +1226,12 @@
<translation type="unfinished">Bitcoin</translation>
</message>
<message>
- <location line="+6"/>
+ <location line="+9"/>
+ <source>Discard blocks after verification, except most recent %1 GB (prune)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
<source>At least %1 GB of data will be stored in this directory, and it will grow over time.</source>
<translation type="unfinished"></translation>
</message>
@@ -1165,12 +1251,12 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+75"/>
+ <location line="+78"/>
<source>Error: Specified data directory &quot;%1&quot; cannot be created.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+27"/>
+ <location line="+30"/>
<source>Error</source>
<translation>Error</translation>
</message>
@@ -1190,6 +1276,14 @@
<numerusform>(of %n GB needed)</numerusform>
</translation>
</message>
+ <message numerus="yes">
+ <location line="+4"/>
+ <source>(%n GB needed for full chain)</source>
+ <translation type="unfinished">
+ <numerusform></numerusform>
+ <numerusform></numerusform>
+ </translation>
+ </message>
</context>
<context>
<name>ModalOverlay</name>
@@ -1286,6 +1380,29 @@
</message>
</context>
<context>
+ <name>OpenWalletActivity</name>
+ <message>
+ <location filename="../walletcontroller.cpp" line="+39"/>
+ <source>Open wallet failed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Open wallet warning</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+10"/>
+ <source>default wallet</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Opening Wallet &lt;b&gt;%1&lt;/b&gt;...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
<name>OptionsDialog</name>
<message>
<location filename="../forms/optionsdialog.ui" line="+14"/>
@@ -1734,16 +1851,16 @@
<name>PaymentServer</name>
<message>
<location filename="../paymentserver.cpp" line="+226"/>
- <location line="+346"/>
+ <location line="+350"/>
<location line="+42"/>
- <location line="+110"/>
+ <location line="+108"/>
<location line="+14"/>
<location line="+18"/>
<source>Payment request error</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-529"/>
+ <location line="-531"/>
<source>Cannot start bitcoin: click-to-pay handler</source>
<translation type="unfinished"></translation>
</message>
@@ -1752,13 +1869,13 @@
<location line="+9"/>
<location line="+16"/>
<location line="+16"/>
- <location line="+5"/>
+ <location line="+7"/>
<location line="+7"/>
<source>URI handling</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-53"/>
+ <location line="-55"/>
<source>&apos;bitcoin://&apos; is not a valid URI. Use &apos;bitcoin:&apos; instead.</source>
<translation type="unfinished"></translation>
</message>
@@ -1774,12 +1891,24 @@
</message>
<message>
<location line="+16"/>
- <location line="+36"/>
+ <location line="+38"/>
<source>Cannot process payment request because BIP70 support was not compiled in.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-32"/>
+ <location line="-37"/>
+ <location line="+38"/>
+ <source>Due to widespread security flaws in BIP70 it&apos;s strongly recommended that any merchant instructions to switch wallets be ignored.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="-37"/>
+ <location line="+38"/>
+ <source>If you are receiving this error you should request the merchant provide a BIP21 compatible URI.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="-34"/>
<source>Invalid payment address %1</source>
<translation type="unfinished"></translation>
</message>
@@ -1800,17 +1929,17 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+199"/>
+ <location line="+201"/>
<location line="+9"/>
<location line="+31"/>
<location line="+10"/>
<location line="+17"/>
- <location line="+85"/>
+ <location line="+83"/>
<source>Payment request rejected</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-152"/>
+ <location line="-150"/>
<source>Payment request network doesn&apos;t match client network.</source>
<translation type="unfinished"></translation>
</message>
@@ -1841,7 +1970,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+65"/>
+ <location line="+63"/>
<source>Refund from %1</source>
<translation type="unfinished"></translation>
</message>
@@ -1879,7 +2008,7 @@
<context>
<name>PeerTableModel</name>
<message>
- <location filename="../peertablemodel.cpp" line="+108"/>
+ <location filename="../peertablemodel.cpp" line="+110"/>
<source>User Agent</source>
<translation type="unfinished"></translation>
</message>
@@ -1922,7 +2051,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+702"/>
+ <location line="+699"/>
<source>%1 d</source>
<translation type="unfinished"></translation>
</message>
@@ -1938,7 +2067,7 @@
</message>
<message>
<location line="+2"/>
- <location line="+50"/>
+ <location line="+47"/>
<source>%1 s</source>
<translation type="unfinished"></translation>
</message>
@@ -2032,27 +2161,22 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../bitcoin.cpp" line="+74"/>
- <source>Error parsing command line arguments: %1.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+37"/>
+ <location filename="../bitcoin.cpp" line="+118"/>
<source>Error: Specified data directory &quot;%1&quot; does not exist.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+5"/>
+ <location line="+6"/>
<source>Error: Cannot parse configuration file: %1.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+14"/>
+ <location line="+15"/>
<source>Error: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+57"/>
+ <location line="+64"/>
<source>%1 didn&apos;t yet exit safely...</source>
<translation type="unfinished"></translation>
</message>
@@ -2103,7 +2227,7 @@
<context>
<name>RPCConsole</name>
<message>
- <location filename="../forms/debugwindow.ui" line="+56"/>
+ <location filename="../forms/debugwindow.ui" line="+75"/>
<location line="+26"/>
<location line="+26"/>
<location line="+26"/>
@@ -2147,12 +2271,12 @@
<translation>&amp;Information</translation>
</message>
<message>
- <location line="-10"/>
+ <location line="-29"/>
<source>Debug window</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+25"/>
+ <location line="+44"/>
<source>General</source>
<translation type="unfinished"></translation>
</message>
@@ -2265,8 +2389,8 @@
</message>
<message>
<location line="+65"/>
- <location filename="../rpcconsole.cpp" line="+498"/>
- <location line="+757"/>
+ <location filename="../rpcconsole.cpp" line="+497"/>
+ <location line="+759"/>
<source>Select a peer to view detailed information.</source>
<translation type="unfinished"></translation>
</message>
@@ -2417,7 +2541,7 @@
<translation>Clear console</translation>
</message>
<message>
- <location filename="../rpcconsole.cpp" line="-252"/>
+ <location filename="../rpcconsole.cpp" line="-243"/>
<source>1 &amp;hour</source>
<translation type="unfinished"></translation>
</message>
@@ -2450,7 +2574,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+47"/>
+ <location line="+38"/>
<source>&amp;Unban</source>
<translation type="unfinished"></translation>
</message>
@@ -2628,7 +2752,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../receivecoinsdialog.cpp" line="+45"/>
+ <location filename="../receivecoinsdialog.cpp" line="+46"/>
<source>Copy URI</source>
<translation type="unfinished"></translation>
</message>
@@ -2714,7 +2838,7 @@
<context>
<name>RecentRequestsTableModel</name>
<message>
- <location filename="../recentrequeststablemodel.cpp" line="+25"/>
+ <location filename="../recentrequeststablemodel.cpp" line="+27"/>
<source>Date</source>
<translation type="unfinished">Date</translation>
</message>
@@ -2753,7 +2877,7 @@
<name>SendCoinsDialog</name>
<message>
<location filename="../forms/sendcoinsdialog.ui" line="+14"/>
- <location filename="../sendcoinsdialog.cpp" line="+600"/>
+ <location filename="../sendcoinsdialog.cpp" line="+601"/>
<source>Send Coins</source>
<translation>Send Coins</translation>
</message>
@@ -2940,7 +3064,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation>S&amp;end</translation>
</message>
<message>
- <location filename="../sendcoinsdialog.cpp" line="-512"/>
+ <location filename="../sendcoinsdialog.cpp" line="-513"/>
<source>Copy quantity</source>
<translation type="unfinished"></translation>
</message>
@@ -2980,7 +3104,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+117"/>
+ <location line="+118"/>
<source> from wallet &apos;%1&apos;</source>
<translation type="unfinished"></translation>
</message>
@@ -3439,14 +3563,6 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
</message>
</context>
<context>
- <name>SplashScreen</name>
- <message>
- <location filename="../networkstyle.cpp" line="+19"/>
- <source>[testnet]</source>
- <translation>[testnet]</translation>
- </message>
-</context>
-<context>
<name>TrafficGraphWidget</name>
<message>
<location filename="../trafficgraphwidget.cpp" line="+81"/>
@@ -3698,7 +3814,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<context>
<name>TransactionTableModel</name>
<message>
- <location filename="../transactiontablemodel.cpp" line="+223"/>
+ <location filename="../transactiontablemodel.cpp" line="+225"/>
<source>Date</source>
<translation type="unfinished">Date</translation>
</message>
@@ -3834,7 +3950,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<context>
<name>TransactionView</name>
<message>
- <location filename="../transactionview.cpp" line="+70"/>
+ <location filename="../transactionview.cpp" line="+69"/>
<location line="+16"/>
<source>All</source>
<translation type="unfinished"></translation>
@@ -3955,7 +4071,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+199"/>
+ <location line="+194"/>
<source>Export Transaction History</source>
<translation type="unfinished"></translation>
</message>
@@ -4033,7 +4149,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<context>
<name>UnitDisplayStatusBarControl</name>
<message>
- <location filename="../bitcoingui.cpp" line="+155"/>
+ <location filename="../bitcoingui.cpp" line="+156"/>
<source>Unit to show amounts in. Click to select another unit.</source>
<translation type="unfinished"></translation>
</message>
@@ -4041,13 +4157,13 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<context>
<name>WalletController</name>
<message>
- <location filename="../walletcontroller.cpp" line="+70"/>
+ <location filename="../walletcontroller.cpp" line="-205"/>
<source>Close wallet</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Are you sure you wish to close wallet &lt;i&gt;%1&lt;/i&gt;?</source>
+ <source>Are you sure you wish to close the wallet &lt;i&gt;%1&lt;/i&gt;?</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -4072,7 +4188,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished">Send Coins</translation>
</message>
<message>
- <location line="+301"/>
+ <location line="+309"/>
<location line="+39"/>
<location line="+5"/>
<source>Fee bump error</source>
@@ -4205,12 +4321,12 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+31"/>
+ <location line="+30"/>
<source>Unable to start HTTP server. See debug log for details.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-168"/>
+ <location line="-167"/>
<source>The %s developers</source>
<translation type="unfinished"></translation>
</message>
@@ -4391,6 +4507,11 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
</message>
<message>
<location line="+4"/>
+ <source>Invalid P2P permission: &apos;%s&apos;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
<source>Invalid amount for -%s=&lt;amount&gt;: &apos;%s&apos;</source>
<translation type="unfinished"></translation>
</message>
@@ -4405,17 +4526,27 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+23"/>
+ <location line="+22"/>
<source>Specified blocks directory &quot;%s&quot; does not exist.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+26"/>
+ <location line="+22"/>
+ <source>Unknown address type &apos;%s&apos;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <source>Unknown change type &apos;%s&apos;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+4"/>
<source>Upgrading txindex database</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-45"/>
+ <location line="-46"/>
<source>Loading P2P addresses...</source>
<translation type="unfinished"></translation>
</message>
@@ -4465,7 +4596,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+6"/>
+ <location line="+5"/>
<source>Unable to bind to %s on this computer. %s is probably already running.</source>
<translation type="unfinished"></translation>
</message>
@@ -4475,7 +4606,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+4"/>
+ <location line="+6"/>
<source>Unsupported logging category %s=%s.</source>
<translation type="unfinished"></translation>
</message>
@@ -4500,7 +4631,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-155"/>
+ <location line="-156"/>
<source>Error: Listening for incoming connections failed (listen returned error %s)</source>
<translation type="unfinished"></translation>
</message>
@@ -4545,7 +4676,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+4"/>
+ <location line="+5"/>
<source>Invalid amount for -paytxfee=&lt;amount&gt;: &apos;%s&apos; (must be at least %s)</source>
<translation type="unfinished"></translation>
</message>
@@ -4555,7 +4686,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+6"/>
+ <location line="+5"/>
<source>Need to specify a port with -whitebind: &apos;%s&apos;</source>
<translation type="unfinished"></translation>
</message>
@@ -4617,11 +4748,6 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
</message>
<message>
<location line="+5"/>
- <source>Transaction too large for fee policy</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+1"/>
<source>Transaction too large</source>
<translation>Transaction too large</translation>
</message>
@@ -4646,7 +4772,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+7"/>
+ <location line="+9"/>
<source>Verifying wallet(s)...</source>
<translation type="unfinished"></translation>
</message>
@@ -4661,7 +4787,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-178"/>
+ <location line="-179"/>
<source>-maxtxfee is set very high! Fees this large could be paid on a single transaction.</source>
<translation type="unfinished"></translation>
</message>
@@ -4696,12 +4822,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+20"/>
- <source>Keypool ran out, please call keypoolrefill first</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+21"/>
+ <location line="+41"/>
<source>Starting network threads...</source>
<translation type="unfinished"></translation>
</message>
@@ -4736,12 +4857,12 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+10"/>
+ <location line="+11"/>
<source>Unknown network specified in -onlynet: &apos;%s&apos;</source>
<translation>Unknown network specified in -onlynet: &apos;%s&apos;</translation>
</message>
<message>
- <location line="-51"/>
+ <location line="-52"/>
<source>Insufficient funds</source>
<translation>Insufficient funds</translation>
</message>
diff --git a/src/qt/macdockiconhandler.mm b/src/qt/macdockiconhandler.mm
index 102adce6c5..5eb23c76e6 100644
--- a/src/qt/macdockiconhandler.mm
+++ b/src/qt/macdockiconhandler.mm
@@ -1,12 +1,11 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// Copyright (c) 2011-2019 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 "macdockiconhandler.h"
-#undef slots
-#include <objc/objc.h>
-#include <objc/message.h>
+#include <AppKit/AppKit.h>
+#include <objc/runtime.h>
static MacDockIconHandler *s_instance = nullptr;
@@ -21,9 +20,7 @@ bool dockClickHandler(id self, SEL _cmd, ...) {
}
void setupDockClickHandler() {
- id app = objc_msgSend((id)objc_getClass("NSApplication"), sel_registerName("sharedApplication"));
- id delegate = objc_msgSend(app, sel_registerName("delegate"));
- Class delClass = (Class)objc_msgSend(delegate, sel_registerName("class"));
+ Class delClass = (Class)[[[NSApplication sharedApplication] delegate] class];
SEL shouldHandle = sel_registerName("applicationShouldHandleReopen:hasVisibleWindows:");
class_replaceMethod(delClass, shouldHandle, (IMP)dockClickHandler, "B@:");
}
@@ -44,3 +41,13 @@ void MacDockIconHandler::cleanup()
{
delete s_instance;
}
+
+/**
+ * Force application activation on macOS. With Qt 5.5.1 this is required when
+ * an action in the Dock menu is triggered.
+ * TODO: Define a Qt version where it's no-longer necessary.
+ */
+void ForceActivation()
+{
+ [[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
+}
diff --git a/src/qt/networkstyle.cpp b/src/qt/networkstyle.cpp
index f0c860e669..5c039a939e 100644
--- a/src/qt/networkstyle.cpp
+++ b/src/qt/networkstyle.cpp
@@ -6,6 +6,9 @@
#include <qt/guiconstants.h>
+#include <chainparamsbase.h>
+#include <tinyformat.h>
+
#include <QApplication>
static const struct {
@@ -13,11 +16,10 @@ static const struct {
const char *appName;
const int iconColorHueShift;
const int iconColorSaturationReduction;
- const char *titleAddText;
} network_styles[] = {
- {"main", QAPP_APP_NAME_DEFAULT, 0, 0, ""},
- {"test", QAPP_APP_NAME_TESTNET, 70, 30, QT_TRANSLATE_NOOP("SplashScreen", "[testnet]")},
- {"regtest", QAPP_APP_NAME_REGTEST, 160, 30, "[regtest]"}
+ {"main", QAPP_APP_NAME_DEFAULT, 0, 0},
+ {"test", QAPP_APP_NAME_TESTNET, 70, 30},
+ {"regtest", QAPP_APP_NAME_REGTEST, 160, 30}
};
static const unsigned network_styles_count = sizeof(network_styles)/sizeof(*network_styles);
@@ -75,8 +77,9 @@ NetworkStyle::NetworkStyle(const QString &_appName, const int iconColorHueShift,
trayAndWindowIcon = QIcon(pixmap.scaled(QSize(256,256)));
}
-const NetworkStyle *NetworkStyle::instantiate(const QString &networkId)
+const NetworkStyle* NetworkStyle::instantiate(const std::string& networkId)
{
+ std::string titleAddText = networkId == CBaseChainParams::MAIN ? "" : strprintf("[%s]", networkId);
for (unsigned x=0; x<network_styles_count; ++x)
{
if (networkId == network_styles[x].networkId)
@@ -85,7 +88,7 @@ const NetworkStyle *NetworkStyle::instantiate(const QString &networkId)
network_styles[x].appName,
network_styles[x].iconColorHueShift,
network_styles[x].iconColorSaturationReduction,
- network_styles[x].titleAddText);
+ titleAddText.c_str());
}
}
return nullptr;
diff --git a/src/qt/networkstyle.h b/src/qt/networkstyle.h
index b78a9f5948..bb12dd1b6e 100644
--- a/src/qt/networkstyle.h
+++ b/src/qt/networkstyle.h
@@ -14,7 +14,7 @@ class NetworkStyle
{
public:
/** Get style associated with provided BIP70 network id, or 0 if not known */
- static const NetworkStyle *instantiate(const QString &networkId);
+ static const NetworkStyle* instantiate(const std::string& networkId);
const QString &getAppName() const { return appName; }
const QIcon &getAppIcon() const { return appIcon; }
diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp
index f3974b1c85..d047a82475 100644
--- a/src/qt/optionsmodel.cpp
+++ b/src/qt/optionsmodel.cpp
@@ -92,11 +92,7 @@ void OptionsModel::Init(bool resetSettings)
settings.setValue("bPrune", false);
if (!settings.contains("nPruneSize"))
settings.setValue("nPruneSize", 2);
- // Convert prune size from GB to MiB:
- const uint64_t nPruneSizeMiB = (settings.value("nPruneSize").toInt() * GB_BYTES) >> 20;
- if (!m_node.softSetArg("-prune", settings.value("bPrune").toBool() ? std::to_string(nPruneSizeMiB) : "0")) {
- addOverriddenOption("-prune");
- }
+ SetPrune(settings.value("bPrune").toBool());
if (!settings.contains("nDatabaseCache"))
settings.setValue("nDatabaseCache", (qint64)nDefaultDbCache);
@@ -240,6 +236,22 @@ static const QString GetDefaultProxyAddress()
return QString("%1:%2").arg(DEFAULT_GUI_PROXY_HOST).arg(DEFAULT_GUI_PROXY_PORT);
}
+void OptionsModel::SetPrune(bool prune, bool force)
+{
+ QSettings settings;
+ settings.setValue("bPrune", prune);
+ // Convert prune size from GB to MiB:
+ const uint64_t nPruneSizeMiB = (settings.value("nPruneSize").toInt() * GB_BYTES) >> 20;
+ std::string prune_val = prune ? std::to_string(nPruneSizeMiB) : "0";
+ if (force) {
+ m_node.forceSetArg("-prune", prune_val);
+ return;
+ }
+ if (!m_node.softSetArg("-prune", prune_val)) {
+ addOverriddenOption("-prune");
+ }
+}
+
// read QSettings values and return them
QVariant OptionsModel::data(const QModelIndex & index, int role) const
{
diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h
index 1af3a72b92..b1231b7c7d 100644
--- a/src/qt/optionsmodel.h
+++ b/src/qt/optionsmodel.h
@@ -77,6 +77,9 @@ public:
bool getCoinControlFeatures() const { return fCoinControlFeatures; }
const QString& getOverriddenByCommandLine() { return strOverriddenByCommandLine; }
+ /* Explicit setters */
+ void SetPrune(bool prune, bool force = false);
+
/* Restart flag helper */
void setRestartRequired(bool fRequired);
bool isRestartRequired() const;
diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp
index d8e48f350a..07ffff0126 100644
--- a/src/qt/overviewpage.cpp
+++ b/src/qt/overviewpage.cpp
@@ -204,9 +204,8 @@ void OverviewPage::updateWatchOnlyLabels(bool showWatchOnly)
void OverviewPage::setClientModel(ClientModel *model)
{
this->clientModel = model;
- if(model)
- {
- // Show warning if this is a prerelease version
+ if (model) {
+ // Show warning, for example if this is a prerelease version
connect(model, &ClientModel::alertsChanged, this, &OverviewPage::updateAlerts);
updateAlerts(model->getStatusBarWarnings());
}
diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp
index f3f5d28af9..00d83d23dd 100644
--- a/src/qt/paymentserver.cpp
+++ b/src/qt/paymentserver.cpp
@@ -41,8 +41,8 @@
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QSslCertificate>
+#include <QSslConfiguration>
#include <QSslError>
-#include <QSslSocket>
#include <QStringList>
#include <QTextDocument>
#include <QUrlQuery>
@@ -328,7 +328,9 @@ void PaymentServer::handleURIOrFile(const QString& s)
#ifndef ENABLE_BIP70
if (uri.hasQueryItem("r")) { // payment request
Q_EMIT message(tr("URI handling"),
- tr("Cannot process payment request because BIP70 support was not compiled in."),
+ tr("Cannot process payment request because BIP70 support was not compiled in.")+
+ tr("Due to widespread security flaws in BIP70 it's strongly recommended that any merchant instructions to switch wallets be ignored.")+
+ tr("If you are receiving this error you should request the merchant provide a BIP21 compatible URI."),
CClientUIInterface::ICON_WARNING);
}
#endif
@@ -364,7 +366,9 @@ void PaymentServer::handleURIOrFile(const QString& s)
return;
#else
Q_EMIT message(tr("Payment request file handling"),
- tr("Cannot process payment request because BIP70 support was not compiled in."),
+ tr("Cannot process payment request because BIP70 support was not compiled in.")+
+ tr("Due to widespread security flaws in BIP70 it's strongly recommended that any merchant instructions to switch wallets be ignored.")+
+ tr("If you are receiving this error you should request the merchant provide a BIP21 compatible URI."),
CClientUIInterface::ICON_WARNING);
#endif
}
@@ -448,9 +452,9 @@ void PaymentServer::LoadRootCAs(X509_STORE* _store)
certList = QSslCertificate::fromPath(certFile);
// Use those certificates when fetching payment requests, too:
- QSslSocket::setDefaultCaCertificates(certList);
+ QSslConfiguration::defaultConfiguration().setCaCertificates(certList);
} else
- certList = QSslSocket::systemCaCertificates();
+ certList = QSslConfiguration::systemCaCertificates();
int nRootCerts = 0;
const QDateTime currentTime = QDateTime::currentDateTime();
diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp
index 85b691c470..99a9a12fe2 100644
--- a/src/qt/peertablemodel.cpp
+++ b/src/qt/peertablemodel.cpp
@@ -11,6 +11,8 @@
#include <interfaces/node.h>
#include <sync.h>
+#include <algorithm>
+
#include <QDebug>
#include <QList>
#include <QTimer>
@@ -76,7 +78,7 @@ public:
if (sortColumn >= 0)
// sort cacheNodeStats (use stable sort to prevent rows jumping around unnecessarily)
- qStableSort(cachedNodeStats.begin(), cachedNodeStats.end(), NodeLessThan(sortColumn, sortOrder));
+ std::stable_sort(cachedNodeStats.begin(), cachedNodeStats.end(), NodeLessThan(sortColumn, sortOrder));
// build index map
mapNodeRows.clear();
diff --git a/src/qt/platformstyle.cpp b/src/qt/platformstyle.cpp
index fca2a4e8c5..08d692e44c 100644
--- a/src/qt/platformstyle.cpp
+++ b/src/qt/platformstyle.cpp
@@ -114,11 +114,6 @@ QIcon PlatformStyle::SingleColorIcon(const QIcon& icon) const
return ColorizeIcon(icon, SingleColor());
}
-QIcon PlatformStyle::TextColorIcon(const QString& filename) const
-{
- return ColorizeIcon(filename, TextColor());
-}
-
QIcon PlatformStyle::TextColorIcon(const QIcon& icon) const
{
return ColorizeIcon(icon, TextColor());
diff --git a/src/qt/platformstyle.h b/src/qt/platformstyle.h
index 4e763e760e..635aec4c93 100644
--- a/src/qt/platformstyle.h
+++ b/src/qt/platformstyle.h
@@ -33,9 +33,6 @@ public:
/** Colorize an icon (given object) with the icon color */
QIcon SingleColorIcon(const QIcon& icon) const;
- /** Colorize an icon (given filename) with the text color */
- QIcon TextColorIcon(const QString& filename) const;
-
/** Colorize an icon (given object) with the text color */
QIcon TextColorIcon(const QIcon& icon) const;
diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp
index c58717e21e..e8cf432131 100644
--- a/src/qt/receivecoinsdialog.cpp
+++ b/src/qt/receivecoinsdialog.cpp
@@ -7,6 +7,7 @@
#include <qt/receivecoinsdialog.h>
#include <qt/forms/ui_receivecoinsdialog.h>
+#include <interfaces/node.h>
#include <qt/addresstablemodel.h>
#include <qt/optionsmodel.h>
#include <qt/platformstyle.h>
@@ -92,10 +93,16 @@ void ReceiveCoinsDialog::setModel(WalletModel *_model)
// Last 2 columns are set by the columnResizingFixer, when the table geometry is ready.
columnResizingFixer = new GUIUtil::TableViewLastColumnResizingFixer(tableView, AMOUNT_MINIMUM_COLUMN_WIDTH, DATE_COLUMN_WIDTH, this);
- if (model->wallet().getDefaultAddressType() == OutputType::BECH32) {
- ui->useLegacyAddress->setCheckState(Qt::Unchecked);
+ if (model->node().isAddressTypeSet()) {
+ // user explicitly set the type, use it
+ if (model->wallet().getDefaultAddressType() == OutputType::BECH32) {
+ ui->useLegacyAddress->setCheckState(Qt::Unchecked);
+ } else {
+ ui->useLegacyAddress->setCheckState(Qt::Checked);
+ }
} else {
- ui->useLegacyAddress->setCheckState(Qt::Checked);
+ // Always fall back to bech32 in the gui
+ ui->useLegacyAddress->setCheckState(Qt::Unchecked);
}
// Set the button to be enabled or disabled based on whether the wallet can give out new addresses.
@@ -254,7 +261,7 @@ void ReceiveCoinsDialog::copyColumnToClipboard(int column)
if (!firstIndex.isValid()) {
return;
}
- GUIUtil::setClipboard(model->getRecentRequestsTableModel()->data(firstIndex.child(firstIndex.row(), column), Qt::EditRole).toString());
+ GUIUtil::setClipboard(model->getRecentRequestsTableModel()->index(firstIndex.row(), column).data(Qt::EditRole).toString());
}
// context menu
diff --git a/src/qt/recentrequeststablemodel.cpp b/src/qt/recentrequeststablemodel.cpp
index aa746017f3..1611ec823c 100644
--- a/src/qt/recentrequeststablemodel.cpp
+++ b/src/qt/recentrequeststablemodel.cpp
@@ -11,6 +11,8 @@
#include <clientversion.h>
#include <streams.h>
+#include <algorithm>
+
RecentRequestsTableModel::RecentRequestsTableModel(WalletModel *parent) :
QAbstractTableModel(parent), walletModel(parent)
@@ -202,7 +204,7 @@ void RecentRequestsTableModel::addNewRequest(RecentRequestEntry &recipient)
void RecentRequestsTableModel::sort(int column, Qt::SortOrder order)
{
- qSort(list.begin(), list.end(), RecentRequestEntryLessThan(column, order));
+ std::sort(list.begin(), list.end(), RecentRequestEntryLessThan(column, order));
Q_EMIT dataChanged(index(0, 0, QModelIndex()), index(list.size() - 1, NUMBER_OF_COLUMNS - 1, QModelIndex()));
}
diff --git a/src/qt/recentrequeststablemodel.h b/src/qt/recentrequeststablemodel.h
index 8a1140e952..130b709d46 100644
--- a/src/qt/recentrequeststablemodel.h
+++ b/src/qt/recentrequeststablemodel.h
@@ -76,7 +76,7 @@ public:
QVariant data(const QModelIndex &index, int role) const;
bool setData(const QModelIndex &index, const QVariant &value, int role);
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
- QModelIndex index(int row, int column, const QModelIndex &parent) const;
+ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex());
Qt::ItemFlags flags(const QModelIndex &index) const;
/*@}*/
diff --git a/src/qt/res/icons/about.png b/src/qt/res/icons/about.png
deleted file mode 100644
index 4143be8bac..0000000000
--- a/src/qt/res/icons/about.png
+++ /dev/null
Binary files differ
diff --git a/src/qt/res/icons/about_qt.png b/src/qt/res/icons/about_qt.png
deleted file mode 100644
index c40abfd3a6..0000000000
--- a/src/qt/res/icons/about_qt.png
+++ /dev/null
Binary files differ
diff --git a/src/qt/res/icons/configure.png b/src/qt/res/icons/configure.png
deleted file mode 100644
index 5333c83d5e..0000000000
--- a/src/qt/res/icons/configure.png
+++ /dev/null
Binary files differ
diff --git a/src/qt/res/icons/debugwindow.png b/src/qt/res/icons/debugwindow.png
deleted file mode 100644
index 290fe60864..0000000000
--- a/src/qt/res/icons/debugwindow.png
+++ /dev/null
Binary files differ
diff --git a/src/qt/res/icons/filesave.png b/src/qt/res/icons/filesave.png
deleted file mode 100644
index 779cca1d52..0000000000
--- a/src/qt/res/icons/filesave.png
+++ /dev/null
Binary files differ
diff --git a/src/qt/res/icons/info.png b/src/qt/res/icons/info.png
deleted file mode 100644
index 692b50c2a9..0000000000
--- a/src/qt/res/icons/info.png
+++ /dev/null
Binary files differ
diff --git a/src/qt/res/icons/key.png b/src/qt/res/icons/key.png
deleted file mode 100644
index f301c4f38c..0000000000
--- a/src/qt/res/icons/key.png
+++ /dev/null
Binary files differ
diff --git a/src/qt/res/icons/open.png b/src/qt/res/icons/open.png
deleted file mode 100644
index 4d958f0e18..0000000000
--- a/src/qt/res/icons/open.png
+++ /dev/null
Binary files differ
diff --git a/src/qt/res/icons/quit.png b/src/qt/res/icons/quit.png
deleted file mode 100644
index 55e34de4b8..0000000000
--- a/src/qt/res/icons/quit.png
+++ /dev/null
Binary files differ
diff --git a/src/qt/res/icons/verify.png b/src/qt/res/icons/verify.png
deleted file mode 100644
index 8e2cb2cc14..0000000000
--- a/src/qt/res/icons/verify.png
+++ /dev/null
Binary files differ
diff --git a/src/qt/res/src/verify.svg b/src/qt/res/src/verify.svg
deleted file mode 100644
index 1ff11b7f5e..0000000000
--- a/src/qt/res/src/verify.svg
+++ /dev/null
@@ -1,14 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
- viewBox="0 0 841.9 595.3" enable-background="new 0 0 841.9 595.3" xml:space="preserve">
-<path d="M654.1,317.5c-14.9-9.9-37.2-2.5-44.6,12.4l-62,111.6l-34.7-34.7c-12.4-12.4-34.7-12.4-47.1,0c-12.4,12.4-12.4,34.7,0,47.1
- l67,67c7.4,7.4,14.9,9.9,22.3,9.9h5c9.9-2.5,19.8-7.4,24.8-17.4l81.9-148.8C676.4,347.2,671.5,327.4,654.1,317.5z"/>
-<path d="M326.7,471.3H177.9V362.1l94.3-94.3c-5-14.9-7.4-29.8-7.4-44.6c0-81.9,67-148.8,148.8-148.8s148.8,67,148.8,148.8
- s-67,148.8-148.8,148.8h-37.2v49.6h-49.6L326.7,471.3L326.7,471.3z M227.5,421.7h49.6v-49.6h49.6v-49.6h86.8
- c54.6,0,99.2-44.6,99.2-99.2S468.1,124,413.5,124s-99.2,44.6-99.2,99.2c0,14.9,2.5,27.3,9.9,39.7l7.4,14.9L230,379.5v42.2H227.5z
- M413.5,198.4c14.9,0,24.8,9.9,24.8,24.8c0,14.9-9.9,24.8-24.8,24.8c-14.9,0-24.8-9.9-24.8-24.8
- C388.7,208.3,401.1,198.4,413.5,198.4 M413.5,173.6c-27.3,0-49.6,22.3-49.6,49.6c0,27.3,22.3,49.6,49.6,49.6
- c27.3,0,49.6-22.3,49.6-49.6C463.1,195.9,443.3,173.6,413.5,173.6z"/>
-</svg>
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index 84b4a2d0d8..eccc34e12f 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -28,13 +28,12 @@
#include <wallet/wallet.h>
#endif
-#include <QDesktopWidget>
#include <QKeyEvent>
#include <QMenu>
#include <QMessageBox>
#include <QScrollBar>
+#include <QScreen>
#include <QSettings>
-#include <QSignalMapper>
#include <QTime>
#include <QTimer>
#include <QStringList>
@@ -451,7 +450,7 @@ RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformSty
QSettings settings;
if (!restoreGeometry(settings.value("RPCConsoleWindowGeometry").toByteArray())) {
// Restore failed (perhaps missing setting), center the window
- move(QApplication::desktop()->availableGeometry().center() - frameGeometry().center());
+ move(QGuiApplication::primaryScreen()->availableGeometry().center() - frameGeometry().center());
}
QChar nonbreaking_hyphen(8209);
@@ -558,6 +557,17 @@ bool RPCConsole::eventFilter(QObject* obj, QEvent *event)
void RPCConsole::setClientModel(ClientModel *model)
{
clientModel = model;
+
+ bool wallet_enabled{false};
+#ifdef ENABLE_WALLET
+ wallet_enabled = WalletModel::isWalletEnabled();
+#endif // ENABLE_WALLET
+ if (model && !wallet_enabled) {
+ // Show warning, for example if this is a prerelease version
+ connect(model, &ClientModel::alertsChanged, this, &RPCConsole::updateAlerts);
+ updateAlerts(model->getStatusBarWarnings());
+ }
+
ui->trafficGraph->setClientModel(model);
if (model && clientModel->getPeerTableModel() && clientModel->getBanTableModel()) {
// Keep up to date with client
@@ -603,19 +613,10 @@ void RPCConsole::setClientModel(ClientModel *model)
peersTableContextMenu->addAction(banAction7d);
peersTableContextMenu->addAction(banAction365d);
- // Add a signal mapping to allow dynamic context menu arguments.
- // We need to use int (instead of int64_t), because signal mapper only supports
- // int or objects, which is okay because max bantime (1 year) is < int_max.
- QSignalMapper* signalMapper = new QSignalMapper(this);
- signalMapper->setMapping(banAction1h, 60*60);
- signalMapper->setMapping(banAction24h, 60*60*24);
- signalMapper->setMapping(banAction7d, 60*60*24*7);
- signalMapper->setMapping(banAction365d, 60*60*24*365);
- connect(banAction1h, &QAction::triggered, signalMapper, static_cast<void (QSignalMapper::*)()>(&QSignalMapper::map));
- connect(banAction24h, &QAction::triggered, signalMapper, static_cast<void (QSignalMapper::*)()>(&QSignalMapper::map));
- connect(banAction7d, &QAction::triggered, signalMapper, static_cast<void (QSignalMapper::*)()>(&QSignalMapper::map));
- connect(banAction365d, &QAction::triggered, signalMapper, static_cast<void (QSignalMapper::*)()>(&QSignalMapper::map));
- connect(signalMapper, static_cast<void (QSignalMapper::*)(int)>(&QSignalMapper::mapped), this, &RPCConsole::banSelectedNode);
+ connect(banAction1h, &QAction::triggered, [this] { banSelectedNode(60 * 60); });
+ connect(banAction24h, &QAction::triggered, [this] { banSelectedNode(60 * 60 * 24); });
+ connect(banAction7d, &QAction::triggered, [this] { banSelectedNode(60 * 60 * 24 * 7); });
+ connect(banAction365d, &QAction::triggered, [this] { banSelectedNode(60 * 60 * 24 * 365); });
// peer table context menu signals
connect(ui->peerWidget, &QTableView::customContextMenuRequested, this, &RPCConsole::showPeersTableContextMenu);
@@ -1120,7 +1121,7 @@ void RPCConsole::updateNodeDetail(const CNodeCombinedStats *stats)
ui->peerSubversion->setText(QString::fromStdString(stats->nodeStats.cleanSubVer));
ui->peerDirection->setText(stats->nodeStats.fInbound ? tr("Inbound") : tr("Outbound"));
ui->peerHeight->setText(QString("%1").arg(QString::number(stats->nodeStats.nStartingHeight)));
- ui->peerWhitelisted->setText(stats->nodeStats.fWhitelisted ? tr("Yes") : tr("No"));
+ ui->peerWhitelisted->setText(stats->nodeStats.m_legacyWhitelisted ? tr("Yes") : tr("No"));
// This check fails for example if the lock was busy and
// nodeStateStats couldn't be fetched.
@@ -1265,11 +1266,6 @@ void RPCConsole::showOrHideBanTableIfRequired()
ui->banHeading->setVisible(visible);
}
-RPCConsole::TabTypes RPCConsole::tabFocus() const
-{
- return (TabTypes) ui->tabWidget->currentIndex();
-}
-
void RPCConsole::setTabFocus(enum TabTypes tabType)
{
ui->tabWidget->setCurrentIndex(tabType);
@@ -1279,3 +1275,9 @@ QString RPCConsole::tabTitle(TabTypes tab_type) const
{
return ui->tabWidget->tabText(tab_type);
}
+
+void RPCConsole::updateAlerts(const QString& warnings)
+{
+ this->ui->label_alerts->setVisible(!warnings.isEmpty());
+ this->ui->label_alerts->setText(warnings);
+}
diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h
index 79b0f3b19c..3f7a74ba03 100644
--- a/src/qt/rpcconsole.h
+++ b/src/qt/rpcconsole.h
@@ -67,7 +67,6 @@ public:
std::vector<TabTypes> tabs() const { return {TAB_INFO, TAB_CONSOLE, TAB_GRAPH, TAB_PEERS}; }
- TabTypes tabFocus() const;
QString tabTitle(TabTypes tab_type) const;
protected:
@@ -168,6 +167,9 @@ private:
/** Update UI with latest network info from model. */
void updateNetworkState();
+
+private Q_SLOTS:
+ void updateAlerts(const QString& warnings);
};
#endif // BITCOIN_QT_RPCCONSOLE_H
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 193fba78b1..a88119d8c5 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -230,8 +230,9 @@ void SendCoinsDialog::on_sendButton_clicked()
{
recipients.append(entry->getValue());
}
- else
+ else if (valid)
{
+ ui->scrollArea->ensureWidgetVisible(entry);
valid = false;
}
}
@@ -282,7 +283,7 @@ void SendCoinsDialog::on_sendButton_clicked()
// generate amount string with wallet name in case of multiwallet
QString amount = BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount);
if (model->isMultiwallet()) {
- amount.append(tr(" from wallet '%1'").arg(model->getWalletName()));
+ amount.append(tr(" from wallet '%1'").arg(GUIUtil::HtmlEscape(model->getWalletName())));
}
// generate address string
@@ -296,7 +297,7 @@ void SendCoinsDialog::on_sendButton_clicked()
{
if(rcp.label.length() > 0) // label with address
{
- recipientElement.append(tr("%1 to '%2'").arg(amount, rcp.label));
+ recipientElement.append(tr("%1 to '%2'").arg(amount, GUIUtil::HtmlEscape(rcp.label)));
recipientElement.append(QString(" (%1)").arg(address));
}
else // just address
@@ -703,7 +704,7 @@ void SendCoinsDialog::updateSmartFeeLabel()
int lightness = ui->fallbackFeeWarningLabel->palette().color(QPalette::WindowText).lightness();
QColor warning_colour(255 - (lightness / 5), 176 - (lightness / 3), 48 - (lightness / 14));
ui->fallbackFeeWarningLabel->setStyleSheet("QLabel { color: " + warning_colour.name() + "; }");
- ui->fallbackFeeWarningLabel->setIndent(QFontMetrics(ui->fallbackFeeWarningLabel->font()).width("x"));
+ ui->fallbackFeeWarningLabel->setIndent(GUIUtil::TextWidth(QFontMetrics(ui->fallbackFeeWarningLabel->font()), "x"));
}
else
{
diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp
index 5bceb1f945..0e5abb89f3 100644
--- a/src/qt/splashscreen.cpp
+++ b/src/qt/splashscreen.cpp
@@ -8,12 +8,12 @@
#include <qt/splashscreen.h>
-#include <qt/networkstyle.h>
-
#include <clientversion.h>
#include <interfaces/handler.h>
#include <interfaces/node.h>
#include <interfaces/wallet.h>
+#include <qt/guiutil.h>
+#include <qt/networkstyle.h>
#include <ui_interface.h>
#include <util/system.h>
#include <util/translation.h>
@@ -21,9 +21,9 @@
#include <QApplication>
#include <QCloseEvent>
-#include <QDesktopWidget>
#include <QPainter>
#include <QRadialGradient>
+#include <QScreen>
SplashScreen::SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const NetworkStyle *networkStyle) :
@@ -75,21 +75,21 @@ SplashScreen::SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const Netw
// check font size and drawing with
pixPaint.setFont(QFont(font, 33*fontFactor));
QFontMetrics fm = pixPaint.fontMetrics();
- int titleTextWidth = fm.width(titleText);
+ int titleTextWidth = GUIUtil::TextWidth(fm, titleText);
if (titleTextWidth > 176) {
fontFactor = fontFactor * 176 / titleTextWidth;
}
pixPaint.setFont(QFont(font, 33*fontFactor));
fm = pixPaint.fontMetrics();
- titleTextWidth = fm.width(titleText);
+ titleTextWidth = GUIUtil::TextWidth(fm, titleText);
pixPaint.drawText(pixmap.width()/devicePixelRatio-titleTextWidth-paddingRight,paddingTop,titleText);
pixPaint.setFont(QFont(font, 15*fontFactor));
// if the version string is too long, reduce size
fm = pixPaint.fontMetrics();
- int versionTextWidth = fm.width(versionText);
+ int versionTextWidth = GUIUtil::TextWidth(fm, versionText);
if(versionTextWidth > titleTextWidth+paddingRight-10) {
pixPaint.setFont(QFont(font, 10*fontFactor));
titleVersionVSpace -= 5;
@@ -111,7 +111,7 @@ SplashScreen::SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const Netw
boldFont.setWeight(QFont::Bold);
pixPaint.setFont(boldFont);
fm = pixPaint.fontMetrics();
- int titleAddTextWidth = fm.width(titleAddText);
+ int titleAddTextWidth = GUIUtil::TextWidth(fm, titleAddText);
pixPaint.drawText(pixmap.width()/devicePixelRatio-titleAddTextWidth-10,15,titleAddText);
}
@@ -124,7 +124,7 @@ SplashScreen::SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const Netw
QRect r(QPoint(), QSize(pixmap.size().width()/devicePixelRatio,pixmap.size().height()/devicePixelRatio));
resize(r.size());
setFixedSize(r.size());
- move(QApplication::desktop()->screenGeometry().center() - r.center());
+ move(QGuiApplication::primaryScreen()->geometry().center() - r.center());
subscribeToCoreSignals();
installEventFilter(this);
diff --git a/src/qt/test/apptests.cpp b/src/qt/test/apptests.cpp
index 49e9e072a8..8ae01ac093 100644
--- a/src/qt/test/apptests.cpp
+++ b/src/qt/test/apptests.cpp
@@ -68,8 +68,7 @@ void AppTests::appTests()
m_app.parameterSetup();
m_app.createOptionsModel(true /* reset settings */);
- QScopedPointer<const NetworkStyle> style(
- NetworkStyle::instantiate(QString::fromStdString(Params().NetworkIDString())));
+ QScopedPointer<const NetworkStyle> style(NetworkStyle::instantiate(Params().NetworkIDString()));
m_app.setupPlatformStyle();
m_app.createWindow(style.data());
connect(&m_app, &BitcoinApplication::windowShown, this, &AppTests::guiTests);
diff --git a/src/qt/test/paymentservertests.cpp b/src/qt/test/paymentservertests.cpp
index 6cafe05461..eca468a6ab 100644
--- a/src/qt/test/paymentservertests.cpp
+++ b/src/qt/test/paymentservertests.cpp
@@ -16,6 +16,7 @@
#include <test/setup_common.h>
#include <util/strencodings.h>
+#include <openssl/ssl.h>
#include <openssl/x509.h>
#include <openssl/x509_vfy.h>
@@ -66,6 +67,7 @@ static SendCoinsRecipient handleRequest(PaymentServer* server, std::vector<unsig
void PaymentServerTests::paymentServerTests()
{
+ SSL_library_init();
BasicTestingSetup testing_setup(CBaseChainParams::MAIN);
auto node = interfaces::MakeNode();
OptionsModel optionsModel(*node);
diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp
index 6bda8dc6eb..796cf24b36 100644
--- a/src/qt/test/test_main.cpp
+++ b/src/qt/test/test_main.cpp
@@ -26,8 +26,6 @@
#include <QObject>
#include <QTest>
-#include <openssl/ssl.h>
-
#if defined(QT_STATICPLUGIN)
#include <QtPlugin>
#if defined(QT_QPA_PLATFORM_MINIMAL)
@@ -70,11 +68,9 @@ int main(int argc, char *argv[])
// Don't remove this, it's needed to access
// QApplication:: and QCoreApplication:: in the tests
- BitcoinApplication app(*node, argc, argv);
+ BitcoinApplication app(*node);
app.setApplicationName("Bitcoin-Qt-test");
- SSL_library_init();
-
AppTests app_tests(app);
if (QTest::qExec(&app_tests) != 0) {
fInvalid = true;
diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp
index 1064c60dfd..8d0cb54151 100644
--- a/src/qt/transactiontablemodel.cpp
+++ b/src/qt/transactiontablemodel.cpp
@@ -17,6 +17,8 @@
#include <interfaces/handler.h>
#include <uint256.h>
+#include <algorithm>
+
#include <QColor>
#include <QDateTime>
#include <QDebug>
@@ -93,9 +95,9 @@ public:
qDebug() << "TransactionTablePriv::updateWallet: " + QString::fromStdString(hash.ToString()) + " " + QString::number(status);
// Find bounds of this transaction in model
- QList<TransactionRecord>::iterator lower = qLowerBound(
+ QList<TransactionRecord>::iterator lower = std::lower_bound(
cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan());
- QList<TransactionRecord>::iterator upper = qUpperBound(
+ QList<TransactionRecord>::iterator upper = std::upper_bound(
cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan());
int lowerIndex = (lower - cachedWallet.begin());
int upperIndex = (upper - cachedWallet.begin());
diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp
index 17e174e57a..cbc4ab49f5 100644
--- a/src/qt/transactionview.cpp
+++ b/src/qt/transactionview.cpp
@@ -30,7 +30,6 @@
#include <QMenu>
#include <QPoint>
#include <QScrollBar>
-#include <QSignalMapper>
#include <QTableView>
#include <QTimer>
#include <QUrl>
@@ -176,11 +175,6 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
contextMenu->addAction(abandonAction);
contextMenu->addAction(editLabelAction);
- mapperThirdPartyTxUrls = new QSignalMapper(this);
-
- // Connect actions
- connect(mapperThirdPartyTxUrls, static_cast<void (QSignalMapper::*)(const QString&)>(&QSignalMapper::mapped), this, &TransactionView::openThirdPartyTxUrl);
-
connect(dateWidget, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this, &TransactionView::chooseDate);
connect(typeWidget, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this, &TransactionView::chooseType);
connect(watchOnlyWidget, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this, &TransactionView::chooseWatchonly);
@@ -246,15 +240,15 @@ void TransactionView::setModel(WalletModel *_model)
QStringList listUrls = _model->getOptionsModel()->getThirdPartyTxUrls().split("|", QString::SkipEmptyParts);
for (int i = 0; i < listUrls.size(); ++i)
{
- QString host = QUrl(listUrls[i].trimmed(), QUrl::StrictMode).host();
+ QString url = listUrls[i].trimmed();
+ QString host = QUrl(url, QUrl::StrictMode).host();
if (!host.isEmpty())
{
QAction *thirdPartyTxUrlAction = new QAction(host, this); // use host as menu item label
if (i == 0)
contextMenu->addSeparator();
contextMenu->addAction(thirdPartyTxUrlAction);
- connect(thirdPartyTxUrlAction, &QAction::triggered, mapperThirdPartyTxUrls, static_cast<void (QSignalMapper::*)()>(&QSignalMapper::map));
- mapperThirdPartyTxUrls->setMapping(thirdPartyTxUrlAction, listUrls[i].trimmed());
+ connect(thirdPartyTxUrlAction, &QAction::triggered, [this, url] { openThirdPartyTxUrl(url); });
}
}
}
diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h
index e07181d1c8..79347c371f 100644
--- a/src/qt/transactionview.h
+++ b/src/qt/transactionview.h
@@ -23,7 +23,6 @@ class QFrame;
class QLineEdit;
class QMenu;
class QModelIndex;
-class QSignalMapper;
class QTableView;
QT_END_NAMESPACE
@@ -72,7 +71,6 @@ private:
QLineEdit *amountWidget;
QMenu *contextMenu;
- QSignalMapper *mapperThirdPartyTxUrls;
QFrame *dateRangeWidget;
QDateTimeEdit *dateFrom;
diff --git a/src/qt/walletcontroller.cpp b/src/qt/walletcontroller.cpp
index 2aedb77798..fa6f9f3f16 100644
--- a/src/qt/walletcontroller.cpp
+++ b/src/qt/walletcontroller.cpp
@@ -2,8 +2,14 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <qt/askpassphrasedialog.h>
+#include <qt/createwalletdialog.h>
+#include <qt/guiconstants.h>
+#include <qt/guiutil.h>
#include <qt/walletcontroller.h>
+#include <wallet/wallet.h>
+
#include <interfaces/handler.h>
#include <interfaces/node.h>
@@ -13,10 +19,13 @@
#include <QMessageBox>
#include <QMutexLocker>
#include <QThread>
+#include <QTimer>
#include <QWindow>
WalletController::WalletController(interfaces::Node& node, const PlatformStyle* platform_style, OptionsModel* options_model, QObject* parent)
: QObject(parent)
+ , m_activity_thread(new QThread(this))
+ , m_activity_worker(new QObject)
, m_node(node)
, m_platform_style(platform_style)
, m_options_model(options_model)
@@ -29,15 +38,17 @@ WalletController::WalletController(interfaces::Node& node, const PlatformStyle*
getOrCreateWallet(std::move(wallet));
}
- m_activity_thread.start();
+ m_activity_worker->moveToThread(m_activity_thread);
+ m_activity_thread->start();
}
// Not using the default destructor because not all member types definitions are
// available in the header, just forward declared.
WalletController::~WalletController()
{
- m_activity_thread.quit();
- m_activity_thread.wait();
+ m_activity_thread->quit();
+ m_activity_thread->wait();
+ delete m_activity_worker;
}
std::vector<WalletModel*> WalletController::getOpenWallets() const
@@ -60,18 +71,11 @@ std::map<std::string, bool> WalletController::listWalletDir() const
return wallets;
}
-OpenWalletActivity* WalletController::openWallet(const std::string& name, QWidget* parent)
-{
- OpenWalletActivity* activity = new OpenWalletActivity(this, name);
- activity->moveToThread(&m_activity_thread);
- return activity;
-}
-
void WalletController::closeWallet(WalletModel* wallet_model, QWidget* parent)
{
QMessageBox box(parent);
box.setWindowTitle(tr("Close wallet"));
- box.setText(tr("Are you sure you wish to close wallet <i>%1</i>?").arg(wallet_model->getDisplayName()));
+ box.setText(tr("Are you sure you wish to close the wallet <i>%1</i>?").arg(GUIUtil::HtmlEscape(wallet_model->getDisplayName())));
box.setInformativeText(tr("Closing the wallet for too long can result in having to resync the entire chain if pruning is enabled."));
box.setStandardButtons(QMessageBox::Yes|QMessageBox::Cancel);
box.setDefaultButton(QMessageBox::Yes);
@@ -99,6 +103,9 @@ WalletModel* WalletController::getOrCreateWallet(std::unique_ptr<interfaces::Wal
// Instantiate model and register it.
WalletModel* wallet_model = new WalletModel(std::move(wallet), m_node, m_platform_style, m_options_model, nullptr);
+ // Handler callback runs in a different thread so fix wallet model thread affinity.
+ wallet_model->moveToThread(thread());
+ wallet_model->setParent(this);
m_wallets.push_back(wallet_model);
connect(wallet_model, &WalletModel::unload, [this, wallet_model] {
@@ -119,25 +126,11 @@ WalletModel* WalletController::getOrCreateWallet(std::unique_ptr<interfaces::Wal
connect(wallet_model, &WalletModel::coinsSent, this, &WalletController::coinsSent);
// Notify walletAdded signal on the GUI thread.
- if (QThread::currentThread() == thread()) {
- addWallet(wallet_model);
- } else {
- // Handler callback runs in a different thread so fix wallet model thread affinity.
- wallet_model->moveToThread(thread());
- bool invoked = QMetaObject::invokeMethod(this, "addWallet", Qt::QueuedConnection, Q_ARG(WalletModel*, wallet_model));
- assert(invoked);
- }
+ Q_EMIT walletAdded(wallet_model);
return wallet_model;
}
-void WalletController::addWallet(WalletModel* wallet_model)
-{
- // Take ownership of the wallet model and register it.
- wallet_model->setParent(this);
- Q_EMIT walletAdded(wallet_model);
-}
-
void WalletController::removeAndDeleteWallet(WalletModel* wallet_model)
{
// Unregister wallet model.
@@ -151,23 +144,148 @@ void WalletController::removeAndDeleteWallet(WalletModel* wallet_model)
delete wallet_model;
}
+WalletControllerActivity::WalletControllerActivity(WalletController* wallet_controller, QWidget* parent_widget)
+ : QObject(wallet_controller)
+ , m_wallet_controller(wallet_controller)
+ , m_parent_widget(parent_widget)
+{
+}
-OpenWalletActivity::OpenWalletActivity(WalletController* wallet_controller, const std::string& name)
- : m_wallet_controller(wallet_controller)
- , m_name(name)
-{}
+WalletControllerActivity::~WalletControllerActivity()
+{
+ delete m_progress_dialog;
+}
-void OpenWalletActivity::open()
+void WalletControllerActivity::showProgressDialog(const QString& label_text)
{
- std::string error, warning;
- std::unique_ptr<interfaces::Wallet> wallet = m_wallet_controller->m_node.loadWallet(m_name, error, warning);
- if (!warning.empty()) {
- Q_EMIT message(QMessageBox::Warning, QString::fromStdString(warning));
+ m_progress_dialog = new QProgressDialog(m_parent_widget);
+
+ m_progress_dialog->setLabelText(label_text);
+ m_progress_dialog->setRange(0, 0);
+ m_progress_dialog->setCancelButton(nullptr);
+ m_progress_dialog->setWindowModality(Qt::ApplicationModal);
+ GUIUtil::PolishProgressDialog(m_progress_dialog);
+}
+
+CreateWalletActivity::CreateWalletActivity(WalletController* wallet_controller, QWidget* parent_widget)
+ : WalletControllerActivity(wallet_controller, parent_widget)
+{
+ m_passphrase.reserve(MAX_PASSPHRASE_SIZE);
+}
+
+CreateWalletActivity::~CreateWalletActivity()
+{
+ delete m_create_wallet_dialog;
+ delete m_passphrase_dialog;
+}
+
+void CreateWalletActivity::askPassphrase()
+{
+ m_passphrase_dialog = new AskPassphraseDialog(AskPassphraseDialog::Encrypt, m_parent_widget, &m_passphrase);
+ m_passphrase_dialog->setWindowModality(Qt::ApplicationModal);
+ m_passphrase_dialog->show();
+
+ connect(m_passphrase_dialog, &QObject::destroyed, [this] {
+ m_passphrase_dialog = nullptr;
+ });
+ connect(m_passphrase_dialog, &QDialog::accepted, [this] {
+ createWallet();
+ });
+ connect(m_passphrase_dialog, &QDialog::rejected, [this] {
+ Q_EMIT finished();
+ });
+}
+
+void CreateWalletActivity::createWallet()
+{
+ showProgressDialog(tr("Creating Wallet <b>%1</b>...").arg(m_create_wallet_dialog->walletName().toHtmlEscaped()));
+
+ std::string name = m_create_wallet_dialog->walletName().toStdString();
+ uint64_t flags = 0;
+ if (m_create_wallet_dialog->isDisablePrivateKeysChecked()) {
+ flags |= WALLET_FLAG_DISABLE_PRIVATE_KEYS;
}
- if (wallet) {
- Q_EMIT opened(m_wallet_controller->getOrCreateWallet(std::move(wallet)));
- } else {
- Q_EMIT message(QMessageBox::Critical, QString::fromStdString(error));
+ if (m_create_wallet_dialog->isMakeBlankWalletChecked()) {
+ flags |= WALLET_FLAG_BLANK_WALLET;
}
+
+ QTimer::singleShot(500, worker(), [this, name, flags] {
+ std::unique_ptr<interfaces::Wallet> wallet;
+ WalletCreationStatus status = node().createWallet(m_passphrase, flags, name, m_error_message, m_warning_message, wallet);
+
+ if (status == WalletCreationStatus::SUCCESS) m_wallet_model = m_wallet_controller->getOrCreateWallet(std::move(wallet));
+
+ QTimer::singleShot(500, this, &CreateWalletActivity::finish);
+ });
+}
+
+void CreateWalletActivity::finish()
+{
+ m_progress_dialog->hide();
+
+ if (!m_error_message.empty()) {
+ QMessageBox::critical(m_parent_widget, tr("Create wallet failed"), QString::fromStdString(m_error_message));
+ } else if (!m_warning_message.empty()) {
+ QMessageBox::warning(m_parent_widget, tr("Create wallet warning"), QString::fromStdString(m_warning_message));
+ }
+
+ if (m_wallet_model) Q_EMIT created(m_wallet_model);
+
+ Q_EMIT finished();
+}
+
+void CreateWalletActivity::create()
+{
+ m_create_wallet_dialog = new CreateWalletDialog(m_parent_widget);
+ m_create_wallet_dialog->setWindowModality(Qt::ApplicationModal);
+ m_create_wallet_dialog->show();
+
+ connect(m_create_wallet_dialog, &QObject::destroyed, [this] {
+ m_create_wallet_dialog = nullptr;
+ });
+ connect(m_create_wallet_dialog, &QDialog::rejected, [this] {
+ Q_EMIT finished();
+ });
+ connect(m_create_wallet_dialog, &QDialog::accepted, [this] {
+ if (m_create_wallet_dialog->isEncryptWalletChecked()) {
+ askPassphrase();
+ } else {
+ createWallet();
+ }
+ });
+}
+
+OpenWalletActivity::OpenWalletActivity(WalletController* wallet_controller, QWidget* parent_widget)
+ : WalletControllerActivity(wallet_controller, parent_widget)
+{
+}
+
+void OpenWalletActivity::finish()
+{
+ m_progress_dialog->hide();
+
+ if (!m_error_message.empty()) {
+ QMessageBox::critical(m_parent_widget, tr("Open wallet failed"), QString::fromStdString(m_error_message));
+ } else if (!m_warning_message.empty()) {
+ QMessageBox::warning(m_parent_widget, tr("Open wallet warning"), QString::fromStdString(m_warning_message));
+ }
+
+ if (m_wallet_model) Q_EMIT opened(m_wallet_model);
+
Q_EMIT finished();
}
+
+void OpenWalletActivity::open(const std::string& path)
+{
+ QString name = path.empty() ? QString("["+tr("default wallet")+"]") : QString::fromStdString(path);
+
+ showProgressDialog(tr("Opening Wallet <b>%1</b>...").arg(name.toHtmlEscaped()));
+
+ QTimer::singleShot(0, worker(), [this, path] {
+ std::unique_ptr<interfaces::Wallet> wallet = node().loadWallet(path, m_error_message, m_warning_message);
+
+ if (wallet) m_wallet_model = m_wallet_controller->getOrCreateWallet(std::move(wallet));
+
+ QTimer::singleShot(0, this, &OpenWalletActivity::finish);
+ });
+}
diff --git a/src/qt/walletcontroller.h b/src/qt/walletcontroller.h
index 03039dd795..fb37b7292c 100644
--- a/src/qt/walletcontroller.h
+++ b/src/qt/walletcontroller.h
@@ -6,15 +6,20 @@
#define BITCOIN_QT_WALLETCONTROLLER_H
#include <qt/walletmodel.h>
+#include <support/allocators/secure.h>
#include <sync.h>
#include <map>
#include <memory>
+#include <string>
#include <vector>
#include <QMessageBox>
#include <QMutex>
+#include <QProgressDialog>
#include <QThread>
+#include <QTimer>
+#include <QString>
class OptionsModel;
class PlatformStyle;
@@ -24,7 +29,11 @@ class Handler;
class Node;
} // namespace interfaces
+class AskPassphraseDialog;
+class CreateWalletActivity;
+class CreateWalletDialog;
class OpenWalletActivity;
+class WalletControllerActivity;
/**
* Controller between interfaces::Node, WalletModel instances and the GUI.
@@ -33,7 +42,6 @@ class WalletController : public QObject
{
Q_OBJECT
- WalletModel* getOrCreateWallet(std::unique_ptr<interfaces::Wallet> wallet);
void removeAndDeleteWallet(WalletModel* wallet_model);
public:
@@ -43,16 +51,14 @@ public:
//! Returns wallet models currently open.
std::vector<WalletModel*> getOpenWallets() const;
+ WalletModel* getOrCreateWallet(std::unique_ptr<interfaces::Wallet> wallet);
+
//! Returns all wallet names in the wallet dir mapped to whether the wallet
//! is loaded.
std::map<std::string, bool> listWalletDir() const;
- OpenWalletActivity* openWallet(const std::string& name, QWidget* parent = nullptr);
void closeWallet(WalletModel* wallet_model, QWidget* parent = nullptr);
-private Q_SLOTS:
- void addWallet(WalletModel* wallet_model);
-
Q_SIGNALS:
void walletAdded(WalletModel* wallet_model);
void walletRemoved(WalletModel* wallet_model);
@@ -60,7 +66,8 @@ Q_SIGNALS:
void coinsSent(WalletModel* wallet_model, SendCoinsRecipient recipient, QByteArray transaction);
private:
- QThread m_activity_thread;
+ QThread* const m_activity_thread;
+ QObject* const m_activity_worker;
interfaces::Node& m_node;
const PlatformStyle* const m_platform_style;
OptionsModel* const m_options_model;
@@ -68,27 +75,72 @@ private:
std::vector<WalletModel*> m_wallets;
std::unique_ptr<interfaces::Handler> m_handler_load_wallet;
- friend class OpenWalletActivity;
+ friend class WalletControllerActivity;
};
-class OpenWalletActivity : public QObject
+class WalletControllerActivity : public QObject
{
Q_OBJECT
public:
- OpenWalletActivity(WalletController* wallet_controller, const std::string& name);
-
-public Q_SLOTS:
- void open();
+ WalletControllerActivity(WalletController* wallet_controller, QWidget* parent_widget);
+ virtual ~WalletControllerActivity();
Q_SIGNALS:
- void message(QMessageBox::Icon icon, const QString text);
void finished();
+
+protected:
+ interfaces::Node& node() const { return m_wallet_controller->m_node; }
+ QObject* worker() const { return m_wallet_controller->m_activity_worker; }
+
+ void showProgressDialog(const QString& label_text);
+
+ WalletController* const m_wallet_controller;
+ QWidget* const m_parent_widget;
+ QProgressDialog* m_progress_dialog{nullptr};
+ WalletModel* m_wallet_model{nullptr};
+ std::string m_error_message;
+ std::string m_warning_message;
+};
+
+
+class CreateWalletActivity : public WalletControllerActivity
+{
+ Q_OBJECT
+
+public:
+ CreateWalletActivity(WalletController* wallet_controller, QWidget* parent_widget);
+ virtual ~CreateWalletActivity();
+
+ void create();
+
+Q_SIGNALS:
+ void created(WalletModel* wallet_model);
+
+private:
+ void askPassphrase();
+ void createWallet();
+ void finish();
+
+ SecureString m_passphrase;
+ CreateWalletDialog* m_create_wallet_dialog{nullptr};
+ AskPassphraseDialog* m_passphrase_dialog{nullptr};
+};
+
+class OpenWalletActivity : public WalletControllerActivity
+{
+ Q_OBJECT
+
+public:
+ OpenWalletActivity(WalletController* wallet_controller, QWidget* parent_widget);
+
+ void open(const std::string& path);
+
+Q_SIGNALS:
void opened(WalletModel* wallet_model);
private:
- WalletController* const m_wallet_controller;
- std::string const m_name;
+ void finish();
};
#endif // BITCOIN_QT_WALLETCONTROLLER_H
diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp
index be47f67f95..8652827b59 100644
--- a/src/qt/walletview.cpp
+++ b/src/qt/walletview.cpp
@@ -170,9 +170,9 @@ void WalletView::processNewTransaction(const QModelIndex& parent, int start, int
QString type = ttm->index(start, TransactionTableModel::Type, parent).data().toString();
QModelIndex index = ttm->index(start, 0, parent);
QString address = ttm->data(index, TransactionTableModel::AddressRole).toString();
- QString label = ttm->data(index, TransactionTableModel::LabelRole).toString();
+ QString label = GUIUtil::HtmlEscape(ttm->data(index, TransactionTableModel::LabelRole).toString());
- Q_EMIT incomingTransaction(date, walletModel->getOptionsModel()->getDisplayUnit(), amount, type, address, label, walletModel->getWalletName());
+ Q_EMIT incomingTransaction(date, walletModel->getOptionsModel()->getDisplayUnit(), amount, type, address, label, GUIUtil::HtmlEscape(walletModel->getWalletName()));
}
void WalletView::gotoOverviewPage()
diff --git a/src/random.cpp b/src/random.cpp
index de26e6de1a..675b177af3 100644
--- a/src/random.cpp
+++ b/src/random.cpp
@@ -667,6 +667,11 @@ uint64_t GetRand(uint64_t nMax) noexcept
return FastRandomContext(g_mock_deterministic_tests).randrange(nMax);
}
+std::chrono::microseconds GetRandMicros(std::chrono::microseconds duration_max) noexcept
+{
+ return std::chrono::microseconds{GetRand(duration_max.count())};
+}
+
int GetRandInt(int nMax) noexcept
{
return GetRand(nMax);
diff --git a/src/random.h b/src/random.h
index 75d037738d..22801ec155 100644
--- a/src/random.h
+++ b/src/random.h
@@ -10,7 +10,8 @@
#include <crypto/common.h>
#include <uint256.h>
-#include <stdint.h>
+#include <chrono> // For std::chrono::microseconds
+#include <cstdint>
#include <limits>
/**
@@ -69,6 +70,7 @@
*/
void GetRandBytes(unsigned char* buf, int num) noexcept;
uint64_t GetRand(uint64_t nMax) noexcept;
+std::chrono::microseconds GetRandMicros(std::chrono::microseconds duration_max) noexcept;
int GetRandInt(int nMax) noexcept;
uint256 GetRandHash() noexcept;
diff --git a/src/rest.cpp b/src/rest.cpp
index eba7aae50f..2c4d475542 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -503,12 +503,12 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart)
if (fCheckMemPool) {
// use db+mempool as cache backend in case user likes to query mempool
LOCK2(cs_main, mempool.cs);
- CCoinsViewCache& viewChain = *pcoinsTip;
+ CCoinsViewCache& viewChain = ::ChainstateActive().CoinsTip();
CCoinsViewMemPool viewMempool(&viewChain, mempool);
process_utxos(viewMempool, mempool);
} else {
LOCK(cs_main); // no need to lock mempool!
- process_utxos(*pcoinsTip, CTxMemPool());
+ process_utxos(::ChainstateActive().CoinsTip(), CTxMemPool());
}
for (size_t i = 0; i < hits.size(); ++i) {
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index b7dcd59c6d..9513c2b9ac 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -10,6 +10,7 @@
#include <chain.h>
#include <chainparams.h>
#include <coins.h>
+#include <node/coinstats.h>
#include <consensus/validation.h>
#include <core_io.h>
#include <hash.h>
@@ -374,6 +375,7 @@ static std::string EntryDescriptionString()
return " \"vsize\" : 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"
" \"size\" : n, (numeric) (DEPRECATED) same as vsize. Only returned if bitcoind is started with -deprecatedrpc=size\n"
" size will be completely removed in v0.20.\n"
+ " \"weight\" : n, (numeric) transaction weight as defined in BIP 141.\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"
@@ -413,6 +415,7 @@ static void entryToJSON(const CTxMemPool& pool, UniValue& info, const CTxMemPool
info.pushKV("vsize", (int)e.GetTxSize());
if (IsDeprecatedRPCEnabled("size")) info.pushKV("size", (int)e.GetTxSize());
+ info.pushKV("weight", (int)e.GetTxWeight());
info.pushKV("fee", ValueFromAmount(e.GetFee()));
info.pushKV("modifiedfee", ValueFromAmount(e.GetModifiedFee()));
info.pushKV("time", e.GetTime());
@@ -907,77 +910,6 @@ static UniValue getblock(const JSONRPCRequest& request)
return blockToJSON(block, tip, pblockindex, verbosity >= 2);
}
-struct CCoinsStats
-{
- int nHeight;
- uint256 hashBlock;
- uint64_t nTransactions;
- uint64_t nTransactionOutputs;
- uint64_t nBogoSize;
- uint256 hashSerialized;
- uint64_t nDiskSize;
- CAmount nTotalAmount;
-
- CCoinsStats() : nHeight(0), nTransactions(0), nTransactionOutputs(0), nBogoSize(0), nDiskSize(0), nTotalAmount(0) {}
-};
-
-static void ApplyStats(CCoinsStats &stats, CHashWriter& ss, const uint256& hash, const std::map<uint32_t, Coin>& outputs)
-{
- assert(!outputs.empty());
- ss << hash;
- ss << VARINT(outputs.begin()->second.nHeight * 2 + outputs.begin()->second.fCoinBase ? 1u : 0u);
- stats.nTransactions++;
- for (const auto& output : outputs) {
- ss << VARINT(output.first + 1);
- ss << output.second.out.scriptPubKey;
- ss << VARINT(output.second.out.nValue, VarIntMode::NONNEGATIVE_SIGNED);
- stats.nTransactionOutputs++;
- stats.nTotalAmount += output.second.out.nValue;
- stats.nBogoSize += 32 /* txid */ + 4 /* vout index */ + 4 /* height + coinbase */ + 8 /* amount */ +
- 2 /* scriptPubKey len */ + output.second.out.scriptPubKey.size() /* scriptPubKey */;
- }
- ss << VARINT(0u);
-}
-
-//! Calculate statistics about the unspent transaction output set
-static bool GetUTXOStats(CCoinsView *view, CCoinsStats &stats)
-{
- std::unique_ptr<CCoinsViewCursor> pcursor(view->Cursor());
- assert(pcursor);
-
- CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
- stats.hashBlock = pcursor->GetBestBlock();
- {
- LOCK(cs_main);
- stats.nHeight = LookupBlockIndex(stats.hashBlock)->nHeight;
- }
- ss << stats.hashBlock;
- uint256 prevkey;
- std::map<uint32_t, Coin> outputs;
- while (pcursor->Valid()) {
- boost::this_thread::interruption_point();
- COutPoint key;
- Coin coin;
- if (pcursor->GetKey(key) && pcursor->GetValue(coin)) {
- if (!outputs.empty() && key.hash != prevkey) {
- ApplyStats(stats, ss, prevkey, outputs);
- outputs.clear();
- }
- prevkey = key.hash;
- outputs[key.n] = std::move(coin);
- } else {
- return error("%s: unable to read value", __func__);
- }
- pcursor->Next();
- }
- if (!outputs.empty()) {
- ApplyStats(stats, ss, prevkey, outputs);
- }
- stats.hashSerialized = ss.GetHash();
- stats.nDiskSize = view->EstimateSize();
- return true;
-}
-
static UniValue pruneblockchain(const JSONRPCRequest& request)
{
RPCHelpMan{"pruneblockchain", "",
@@ -1062,7 +994,9 @@ static UniValue gettxoutsetinfo(const JSONRPCRequest& request)
CCoinsStats stats;
::ChainstateActive().ForceFlushStateToDisk();
- if (GetUTXOStats(pcoinsdbview.get(), stats)) {
+
+ CCoinsView* coins_view = WITH_LOCK(cs_main, return &ChainstateActive().CoinsDB());
+ if (GetUTXOStats(coins_view, stats)) {
ret.pushKV("height", (int64_t)stats.nHeight);
ret.pushKV("bestblock", stats.hashBlock.GetHex());
ret.pushKV("transactions", (int64_t)stats.nTransactions);
@@ -1126,19 +1060,21 @@ UniValue gettxout(const JSONRPCRequest& request)
fMempool = request.params[2].get_bool();
Coin coin;
+ CCoinsViewCache* coins_view = &::ChainstateActive().CoinsTip();
+
if (fMempool) {
LOCK(mempool.cs);
- CCoinsViewMemPool view(pcoinsTip.get(), mempool);
+ CCoinsViewMemPool view(coins_view, mempool);
if (!view.GetCoin(out, coin) || mempool.isSpent(out)) {
return NullUniValue;
}
} else {
- if (!pcoinsTip->GetCoin(out, coin)) {
+ if (!coins_view->GetCoin(out, coin)) {
return NullUniValue;
}
}
- const CBlockIndex* pindex = LookupBlockIndex(pcoinsTip->GetBestBlock());
+ const CBlockIndex* pindex = LookupBlockIndex(coins_view->GetBestBlock());
ret.pushKV("bestblock", pindex->GetBlockHash().GetHex());
if (coin.nHeight == MEMPOOL_HEIGHT) {
ret.pushKV("confirmations", 0);
@@ -1180,57 +1116,53 @@ static UniValue verifychain(const JSONRPCRequest& request)
if (!request.params[1].isNull())
nCheckDepth = request.params[1].get_int();
- return CVerifyDB().VerifyDB(Params(), pcoinsTip.get(), nCheckLevel, nCheckDepth);
+ return CVerifyDB().VerifyDB(
+ Params(), &::ChainstateActive().CoinsTip(), nCheckLevel, nCheckDepth);
}
-/** Implementation of IsSuperMajority with better feedback */
-static UniValue SoftForkMajorityDesc(int version, const CBlockIndex* pindex, const Consensus::Params& consensusParams)
+static void BuriedForkDescPushBack(UniValue& softforks, const std::string &name, int height) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
- UniValue rv(UniValue::VOBJ);
- bool activated = false;
- switch(version)
- {
- case 2:
- activated = pindex->nHeight >= consensusParams.BIP34Height;
- break;
- case 3:
- activated = pindex->nHeight >= consensusParams.BIP66Height;
- break;
- case 4:
- activated = pindex->nHeight >= consensusParams.BIP65Height;
- break;
- }
- rv.pushKV("status", activated);
- return rv;
-}
+ // For buried deployments.
+ // A buried deployment is one where the height of the activation has been hardcoded into
+ // the client implementation long after the consensus change has activated. See BIP 90.
+ // Buried deployments with activation height value of
+ // std::numeric_limits<int>::max() are disabled and thus hidden.
+ if (height == std::numeric_limits<int>::max()) return;
-static UniValue SoftForkDesc(const std::string &name, int version, const CBlockIndex* pindex, const Consensus::Params& consensusParams)
-{
UniValue rv(UniValue::VOBJ);
- rv.pushKV("id", name);
- rv.pushKV("version", version);
- rv.pushKV("reject", SoftForkMajorityDesc(version, pindex, consensusParams));
- return rv;
+ rv.pushKV("type", "buried");
+ // getblockchaininfo reports the softfork as active from when the chain height is
+ // one below the activation height
+ rv.pushKV("active", ::ChainActive().Tip()->nHeight + 1 >= height);
+ rv.pushKV("height", height);
+ softforks.pushKV(name, rv);
}
-static UniValue BIP9SoftForkDesc(const Consensus::Params& consensusParams, Consensus::DeploymentPos id)
+static void BIP9SoftForkDescPushBack(UniValue& softforks, const std::string &name, const Consensus::Params& consensusParams, Consensus::DeploymentPos id) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
- UniValue rv(UniValue::VOBJ);
+ // For BIP9 deployments.
+ // Deployments (e.g. testdummy) with timeout value before Jan 1, 2009 are hidden.
+ // A timeout value of 0 guarantees a softfork will never be activated.
+ // This is used when merging logic to implement a proposed softfork without a specified deployment schedule.
+ if (consensusParams.vDeployments[id].nTimeout <= 1230768000) return;
+
+ UniValue bip9(UniValue::VOBJ);
const ThresholdState thresholdState = VersionBitsTipState(consensusParams, id);
switch (thresholdState) {
- case ThresholdState::DEFINED: rv.pushKV("status", "defined"); break;
- case ThresholdState::STARTED: rv.pushKV("status", "started"); break;
- case ThresholdState::LOCKED_IN: rv.pushKV("status", "locked_in"); break;
- case ThresholdState::ACTIVE: rv.pushKV("status", "active"); break;
- case ThresholdState::FAILED: rv.pushKV("status", "failed"); break;
+ case ThresholdState::DEFINED: bip9.pushKV("status", "defined"); break;
+ case ThresholdState::STARTED: bip9.pushKV("status", "started"); break;
+ case ThresholdState::LOCKED_IN: bip9.pushKV("status", "locked_in"); break;
+ case ThresholdState::ACTIVE: bip9.pushKV("status", "active"); break;
+ case ThresholdState::FAILED: bip9.pushKV("status", "failed"); break;
}
if (ThresholdState::STARTED == thresholdState)
{
- rv.pushKV("bit", consensusParams.vDeployments[id].bit);
+ bip9.pushKV("bit", consensusParams.vDeployments[id].bit);
}
- rv.pushKV("startTime", consensusParams.vDeployments[id].nStartTime);
- rv.pushKV("timeout", consensusParams.vDeployments[id].nTimeout);
- rv.pushKV("since", VersionBitsTipStateSinceHeight(consensusParams, id));
+ bip9.pushKV("startTime", consensusParams.vDeployments[id].nStartTime);
+ bip9.pushKV("timeout", consensusParams.vDeployments[id].nTimeout);
+ int64_t since_height = VersionBitsTipStateSinceHeight(consensusParams, id);
+ bip9.pushKV("since", since_height);
if (ThresholdState::STARTED == thresholdState)
{
UniValue statsUV(UniValue::VOBJ);
@@ -1240,18 +1172,18 @@ static UniValue BIP9SoftForkDesc(const Consensus::Params& consensusParams, Conse
statsUV.pushKV("elapsed", statsStruct.elapsed);
statsUV.pushKV("count", statsStruct.count);
statsUV.pushKV("possible", statsStruct.possible);
- rv.pushKV("statistics", statsUV);
+ bip9.pushKV("statistics", statsUV);
}
- return rv;
-}
-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.
- // This is used when softfork codes are merged without specifying the deployment schedule.
- if (consensusParams.vDeployments[id].nTimeout > 0)
- bip9_softforks.pushKV(VersionBitsDeploymentInfo[id].name, BIP9SoftForkDesc(consensusParams, id));
+ UniValue rv(UniValue::VOBJ);
+ rv.pushKV("type", "bip9");
+ rv.pushKV("bip9", bip9);
+ if (ThresholdState::ACTIVE == thresholdState) {
+ rv.pushKV("height", since_height);
+ }
+ rv.pushKV("active", ThresholdState::ACTIVE == thresholdState);
+
+ softforks.pushKV(name, rv);
}
UniValue getblockchaininfo(const JSONRPCRequest& request)
@@ -1275,29 +1207,25 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
" \"pruneheight\": xxxxxx, (numeric) lowest-height complete block stored (only present if pruning is enabled)\n"
" \"automatic_pruning\": xx, (boolean) whether automatic pruning is enabled (only present if pruning is enabled)\n"
" \"prune_target_size\": xxxxxx, (numeric) the target size used by pruning (only present if automatic pruning is enabled)\n"
- " \"softforks\": [ (array) status of softforks in progress\n"
- " {\n"
- " \"id\": \"xxxx\", (string) name of softfork\n"
- " \"version\": xx, (numeric) block version\n"
- " \"reject\": { (object) progress toward rejecting pre-softfork blocks\n"
- " \"status\": xx, (boolean) true if threshold reached\n"
- " },\n"
- " }, ...\n"
- " ],\n"
- " \"bip9_softforks\": { (object) status of BIP9 softforks in progress\n"
+ " \"softforks\": { (object) status of softforks\n"
" \"xxxx\" : { (string) name of the softfork\n"
- " \"status\": \"xxxx\", (string) one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\"\n"
- " \"bit\": xx, (numeric) the bit (0-28) in the block version field used to signal this softfork (only for \"started\" status)\n"
- " \"startTime\": xx, (numeric) the minimum median time past of a block at which the bit gains its meaning\n"
- " \"timeout\": xx, (numeric) the median time past of a block at which the deployment is considered failed if not yet locked in\n"
- " \"since\": xx, (numeric) height of the first block to which the status applies\n"
- " \"statistics\": { (object) numeric statistics about BIP9 signalling for a softfork (only for \"started\" status)\n"
- " \"period\": xx, (numeric) the length in blocks of the BIP9 signalling period \n"
- " \"threshold\": xx, (numeric) the number of blocks with the version bit set required to activate the feature \n"
- " \"elapsed\": xx, (numeric) the number of blocks elapsed since the beginning of the current period \n"
- " \"count\": xx, (numeric) the number of blocks with the version bit set in the current period \n"
- " \"possible\": xx (boolean) returns false if there are not enough blocks left in this period to pass activation threshold \n"
- " }\n"
+ " \"type\": \"xxxx\", (string) one of \"buried\", \"bip9\"\n"
+ " \"bip9\": { (object) status of bip9 softforks (only for \"bip9\" type)\n"
+ " \"status\": \"xxxx\", (string) one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\"\n"
+ " \"bit\": xx, (numeric) the bit (0-28) in the block version field used to signal this softfork (only for \"started\" status)\n"
+ " \"startTime\": xx, (numeric) the minimum median time past of a block at which the bit gains its meaning\n"
+ " \"timeout\": xx, (numeric) the median time past of a block at which the deployment is considered failed if not yet locked in\n"
+ " \"since\": xx, (numeric) height of the first block to which the status applies\n"
+ " \"statistics\": { (object) numeric statistics about BIP9 signalling for a softfork\n"
+ " \"period\": xx, (numeric) the length in blocks of the BIP9 signalling period \n"
+ " \"threshold\": xx, (numeric) the number of blocks with the version bit set required to activate the feature \n"
+ " \"elapsed\": xx, (numeric) the number of blocks elapsed since the beginning of the current period \n"
+ " \"count\": xx, (numeric) the number of blocks with the version bit set in the current period \n"
+ " \"possible\": xx (boolean) returns false if there are not enough blocks left in this period to pass activation threshold \n"
+ " }\n"
+ " },\n"
+ " \"height\": \"xxxxxx\", (numeric) height of the first block which the rules are or will be enforced (only for \"buried\" type, or \"bip9\" type with \"active\" status)\n"
+ " \"active\": xx, (boolean) true if the rules are enforced for the mempool and the next block\n"
" }\n"
" }\n"
" \"warnings\" : \"...\", (string) any network and blockchain warnings.\n"
@@ -1342,16 +1270,14 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
}
const Consensus::Params& consensusParams = Params().GetConsensus();
- UniValue softforks(UniValue::VARR);
- UniValue bip9_softforks(UniValue::VOBJ);
- softforks.push_back(SoftForkDesc("bip34", 2, tip, consensusParams));
- softforks.push_back(SoftForkDesc("bip66", 3, tip, consensusParams));
- softforks.push_back(SoftForkDesc("bip65", 4, tip, consensusParams));
- for (int pos = Consensus::DEPLOYMENT_CSV; pos != Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++pos) {
- BIP9SoftForkDescPushBack(bip9_softforks, consensusParams, static_cast<Consensus::DeploymentPos>(pos));
- }
+ UniValue softforks(UniValue::VOBJ);
+ BuriedForkDescPushBack(softforks, "bip34", consensusParams.BIP34Height);
+ BuriedForkDescPushBack(softforks, "bip66", consensusParams.BIP66Height);
+ BuriedForkDescPushBack(softforks, "bip65", consensusParams.BIP65Height);
+ BuriedForkDescPushBack(softforks, "csv", consensusParams.CSVHeight);
+ BuriedForkDescPushBack(softforks, "segwit", consensusParams.SegwitHeight);
+ BIP9SoftForkDescPushBack(softforks, "testdummy", consensusParams, Consensus::DEPLOYMENT_TESTDUMMY);
obj.pushKV("softforks", softforks);
- obj.pushKV("bip9_softforks", bip9_softforks);
obj.pushKV("warnings", GetWarnings("statusbar"));
return obj;
@@ -1643,6 +1569,7 @@ static UniValue getchaintxstats(const JSONRPCRequest& request)
" \"time\": xxxxx, (numeric) The timestamp for the final block in the window in UNIX format.\n"
" \"txcount\": xxxxx, (numeric) The total number of transactions in the chain up to that point.\n"
" \"window_final_block_hash\": \"...\", (string) The hash of the final block in the window.\n"
+ " \"window_final_block_height\": xxxxx, (numeric) The height of the final block in the window.\n"
" \"window_block_count\": xxxxx, (numeric) Size of the window in number of blocks.\n"
" \"window_tx_count\": xxxxx, (numeric) The number of transactions in the window. Only returned if \"window_block_count\" is > 0.\n"
" \"window_interval\": xxxxx, (numeric) The elapsed time in the window in seconds. Only returned if \"window_block_count\" is > 0.\n"
@@ -1693,6 +1620,7 @@ static UniValue getchaintxstats(const JSONRPCRequest& request)
ret.pushKV("time", (int64_t)pindex->nTime);
ret.pushKV("txcount", (int64_t)pindex->nChainTx);
ret.pushKV("window_final_block_hash", pindex->GetBlockHash().GetHex());
+ ret.pushKV("window_final_block_height", pindex->nHeight);
ret.pushKV("window_block_count", blockcount);
if (blockcount > 0) {
ret.pushKV("window_tx_count", nTxDiff);
@@ -2136,17 +2064,21 @@ UniValue scantxoutset(const JSONRPCRequest& request)
},
RPCResult{
"{\n"
+ " \"success\": true|false, (boolean) Whether the scan was completed\n"
+ " \"txouts\": n, (numeric) The number of unspent transaction outputs scanned\n"
+ " \"height\": n, (numeric) The current block height (index)\n"
+ " \"bestblock\": \"hex\", (string) The hash of the block at the tip of the chain\n"
" \"unspents\": [\n"
- " {\n"
- " \"txid\" : \"transactionid\", (string) The transaction id\n"
- " \"vout\": n, (numeric) the vout value\n"
- " \"scriptPubKey\" : \"script\", (string) the script key\n"
- " \"desc\" : \"descriptor\", (string) A specialized descriptor for the matched scriptPubKey\n"
- " \"amount\" : x.xxx, (numeric) The total amount in " + CURRENCY_UNIT + " of the unspent output\n"
- " \"height\" : n, (numeric) Height of the unspent transaction output\n"
+ " {\n"
+ " \"txid\": \"hash\", (string) The transaction id\n"
+ " \"vout\": n, (numeric) The vout value\n"
+ " \"scriptPubKey\": \"script\", (string) The script key\n"
+ " \"desc\": \"descriptor\", (string) A specialized descriptor for the matched scriptPubKey\n"
+ " \"amount\": x.xxx, (numeric) The total amount in " + CURRENCY_UNIT + " of the unspent output\n"
+ " \"height\": n, (numeric) Height of the unspent transaction output\n"
" }\n"
- " ,...], \n"
- " \"total_amount\" : x.xxx, (numeric) The total amount of all found unspent outputs in " + CURRENCY_UNIT + "\n"
+ " ,...],\n"
+ " \"total_amount\": x.xxx, (numeric) The total amount of all found unspent outputs in " + CURRENCY_UNIT + "\n"
"]\n"
},
RPCExamples{""},
@@ -2200,15 +2132,20 @@ UniValue scantxoutset(const JSONRPCRequest& request)
g_scan_progress = 0;
int64_t count = 0;
std::unique_ptr<CCoinsViewCursor> pcursor;
+ CBlockIndex* tip;
{
LOCK(cs_main);
::ChainstateActive().ForceFlushStateToDisk();
- pcursor = std::unique_ptr<CCoinsViewCursor>(pcoinsdbview->Cursor());
+ pcursor = std::unique_ptr<CCoinsViewCursor>(::ChainstateActive().CoinsDB().Cursor());
assert(pcursor);
+ tip = ::ChainActive().Tip();
+ assert(tip);
}
bool res = FindScriptPubKey(g_scan_progress, g_should_abort_scan, count, pcursor.get(), needles, coins);
result.pushKV("success", res);
- result.pushKV("searched_items", count);
+ result.pushKV("txouts", count);
+ result.pushKV("height", tip->nHeight);
+ result.pushKV("bestblock", tip->GetBlockHash().GetHex());
for (const auto& it : coins) {
const COutPoint& outpoint = it.first;
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index 3cd661e067..c2714f9c83 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -85,6 +85,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "getblockheader", 1, "verbose" },
{ "getchaintxstats", 0, "nblocks" },
{ "gettransaction", 1, "include_watchonly" },
+ { "gettransaction", 2, "verbose" },
{ "getrawtransaction", 1, "verbose" },
{ "createrawtransaction", 0, "inputs" },
{ "createrawtransaction", 1, "outputs" },
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 48bc88823a..07c2958635 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -352,7 +352,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
"}\n"
},
RPCExamples{
- HelpExampleCli("getblocktemplate", "{\"rules\": [\"segwit\"]}")
+ HelpExampleCli("getblocktemplate", "'{\"rules\": [\"segwit\"]}'")
+ HelpExampleRpc("getblocktemplate", "{\"rules\": [\"segwit\"]}")
},
}.Check(request);
@@ -482,9 +482,8 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
// TODO: Maybe recheck connections/IBD and (if something wrong) send an expires-immediately template to stop miners?
}
- const struct VBDeploymentInfo& segwit_info = VersionBitsDeploymentInfo[Consensus::DEPLOYMENT_SEGWIT];
// GBT must be called with 'segwit' set in the rules
- if (setClientRules.count(segwit_info.name) != 1) {
+ if (setClientRules.count("segwit") != 1) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "getblocktemplate must be called with the segwit rule set (call with {\"rules\": [\"segwit\"]})");
}
@@ -521,7 +520,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
pblock->nNonce = 0;
// NOTE: If at some point we support pre-segwit miners post-segwit-activation, this needs to take segwit support into consideration
- const bool fPreSegWit = (ThresholdState::ACTIVE != VersionBitsState(pindexPrev, consensusParams, Consensus::DEPLOYMENT_SEGWIT, versionbitscache));
+ const bool fPreSegWit = (pindexPrev->nHeight + 1 < consensusParams.SegwitHeight);
UniValue aCaps(UniValue::VARR); aCaps.push_back("proposal");
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index 6be4057366..1516007201 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -136,6 +136,7 @@ UniValue getdescriptorinfo(const JSONRPCRequest& request)
RPCResult{
"{\n"
" \"descriptor\" : \"desc\", (string) The descriptor in canonical form, without private keys\n"
+ " \"checksum\" : \"chksum\", (string) The checksum for the input descriptor\n"
" \"isrange\" : true|false, (boolean) Whether the descriptor is ranged\n"
" \"issolvable\" : true|false, (boolean) Whether the descriptor is solvable\n"
" \"hasprivatekeys\" : true|false, (boolean) Whether the input descriptor contained at least one private key\n"
@@ -149,13 +150,15 @@ UniValue getdescriptorinfo(const JSONRPCRequest& request)
RPCTypeCheck(request.params, {UniValue::VSTR});
FlatSigningProvider provider;
- auto desc = Parse(request.params[0].get_str(), provider);
+ std::string error;
+ auto desc = Parse(request.params[0].get_str(), provider, error);
if (!desc) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Invalid descriptor"));
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error);
}
UniValue result(UniValue::VOBJ);
result.pushKV("descriptor", desc->ToString());
+ result.pushKV("checksum", GetDescriptorChecksum(request.params[0].get_str()));
result.pushKV("isrange", desc->IsRange());
result.pushKV("issolvable", desc->IsSolvable());
result.pushKV("hasprivatekeys", provider.keys.size() > 0);
@@ -197,9 +200,10 @@ UniValue deriveaddresses(const JSONRPCRequest& request)
}
FlatSigningProvider key_provider;
- auto desc = Parse(desc_str, key_provider, /* require_checksum = */ true);
+ std::string error;
+ auto desc = Parse(desc_str, key_provider, error, /* require_checksum = */ true);
if (!desc) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Invalid descriptor"));
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error);
}
if (!desc->IsRange() && request.params.size() > 1) {
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index 16b59e3d58..7c4b3d0cc6 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -9,6 +9,7 @@
#include <core_io.h>
#include <net.h>
#include <net_processing.h>
+#include <net_permissions.h>
#include <netbase.h>
#include <policy/policy.h>
#include <policy/settings.h>
@@ -81,6 +82,10 @@ static UniValue getpeerinfo(const JSONRPCRequest& request)
" \"addrbind\":\"ip:port\", (string) Bind address of the connection to the peer\n"
" \"addrlocal\":\"ip:port\", (string) Local address as reported by the peer\n"
" \"services\":\"xxxxxxxxxxxxxxxx\", (string) The services offered\n"
+ " \"servicesnames\":[ (array) the services offered, in human-readable form\n"
+ " \"SERVICE_NAME\", (string) the service name if it is recognised\n"
+ " ...\n"
+ " ],\n"
" \"relaytxes\":true|false, (boolean) Whether peer has asked us to relay transactions to it\n"
" \"lastsend\": ttt, (numeric) The time in seconds since epoch (Jan 1 1970 GMT) of the last send\n"
" \"lastrecv\": ttt, (numeric) The time in seconds since epoch (Jan 1 1970 GMT) of the last receive\n"
@@ -146,6 +151,7 @@ static UniValue getpeerinfo(const JSONRPCRequest& request)
if (stats.addrBind.IsValid())
obj.pushKV("addrbind", stats.addrBind.ToString());
obj.pushKV("services", strprintf("%016x", stats.nServices));
+ obj.pushKV("servicesnames", GetServicesNames(stats.nServices));
obj.pushKV("relaytxes", stats.fRelayTxes);
obj.pushKV("lastsend", stats.nLastSend);
obj.pushKV("lastrecv", stats.nLastRecv);
@@ -177,7 +183,12 @@ static UniValue getpeerinfo(const JSONRPCRequest& request)
}
obj.pushKV("inflight", heights);
}
- obj.pushKV("whitelisted", stats.fWhitelisted);
+ obj.pushKV("whitelisted", stats.m_legacyWhitelisted);
+ UniValue permissions(UniValue::VARR);
+ for (const auto& permission : NetPermissions::ToStrings(stats.m_permissionFlags)) {
+ permissions.push_back(permission);
+ }
+ obj.pushKV("permissions", permissions);
obj.pushKV("minfeefilter", ValueFromAmount(stats.minFeeFilter));
UniValue sendPerMsgCmd(UniValue::VOBJ);
@@ -440,6 +451,10 @@ static UniValue getnetworkinfo(const JSONRPCRequest& request)
" \"subversion\": \"/Satoshi:x.x.x/\", (string) the server subversion string\n"
" \"protocolversion\": xxxxx, (numeric) the protocol version\n"
" \"localservices\": \"xxxxxxxxxxxxxxxx\", (string) the services we offer to the network\n"
+ " \"localservicesnames\": [ (array) the services we offer to the network, in human-readable form\n"
+ " \"SERVICE_NAME\", (string) the service name\n"
+ " ...\n"
+ " ],\n"
" \"localrelay\": true|false, (bool) true if transaction relay is requested from peers\n"
" \"timeoffset\": xxxxx, (numeric) the time offset\n"
" \"connections\": xxxxx, (numeric) the number of connections\n"
@@ -478,8 +493,11 @@ static UniValue getnetworkinfo(const JSONRPCRequest& request)
obj.pushKV("version", CLIENT_VERSION);
obj.pushKV("subversion", strSubVersion);
obj.pushKV("protocolversion",PROTOCOL_VERSION);
- if(g_connman)
- obj.pushKV("localservices", strprintf("%016x", g_connman->GetLocalServices()));
+ if (g_connman) {
+ ServiceFlags services = g_connman->GetLocalServices();
+ obj.pushKV("localservices", strprintf("%016x", services));
+ obj.pushKV("localservicesnames", GetServicesNames(services));
+ }
obj.pushKV("localrelay", g_relay_txes);
obj.pushKV("timeoffset", GetTimeOffset());
if (g_connman) {
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index a263043124..4003299662 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -260,7 +260,7 @@ static UniValue gettxoutproof(const JSONRPCRequest& request)
// 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);
+ const Coin& coin = AccessByTxid(::ChainstateActive().CoinsTip(), tx);
if (!coin.IsSpent()) {
pblockindex = ::ChainActive()[coin.nHeight];
break;
@@ -407,7 +407,11 @@ static UniValue createrawtransaction(const JSONRPCRequest& request)
}, true
);
- CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], request.params[3]);
+ bool rbf = false;
+ if (!request.params[3].isNull()) {
+ rbf = request.params[3].isTrue();
+ }
+ CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], rbf);
return EncodeHexTx(CTransaction(rawTx));
}
@@ -633,7 +637,7 @@ static UniValue combinerawtransaction(const JSONRPCRequest& request)
{
LOCK(cs_main);
LOCK(mempool.cs);
- CCoinsViewCache &viewChain = *pcoinsTip;
+ CCoinsViewCache &viewChain = ::ChainstateActive().CoinsTip();
CCoinsViewMemPool viewMempool(&viewChain, mempool);
view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view
@@ -755,7 +759,10 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
}
FindCoins(coins);
- return SignTransaction(mtx, request.params[2], &keystore, coins, true, request.params[3]);
+ // Parse the prevtxs array
+ ParsePrevouts(request.params[2], &keystore, coins);
+
+ return SignTransaction(mtx, &keystore, coins, request.params[3]);
}
static UniValue sendrawtransaction(const JSONRPCRequest& request)
@@ -811,14 +818,14 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request)
max_raw_tx_fee = fr.GetFee((weight+3)/4);
}
- uint256 txid;
std::string err_string;
- const TransactionError err = BroadcastTransaction(tx, txid, err_string, max_raw_tx_fee);
+ AssertLockNotHeld(cs_main);
+ const TransactionError err = BroadcastTransaction(tx, err_string, max_raw_tx_fee, /*relay*/ true, /*wait_callback*/ true);
if (TransactionError::OK != err) {
throw JSONRPCTransactionError(err, err_string);
}
- return txid.GetHex();
+ return tx->GetHash().GetHex();
}
static UniValue testmempoolaccept(const JSONRPCRequest& request)
@@ -1366,7 +1373,11 @@ UniValue createpsbt(const JSONRPCRequest& request)
}, true
);
- CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], request.params[3]);
+ bool rbf = false;
+ if (!request.params[3].isNull()) {
+ rbf = request.params[3].isTrue();
+ }
+ CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], rbf);
// Make a blank psbt
PartiallySignedTransaction psbtx;
@@ -1498,7 +1509,7 @@ UniValue utxoupdatepsbt(const JSONRPCRequest& request)
CCoinsViewCache view(&viewDummy);
{
LOCK2(cs_main, mempool.cs);
- CCoinsViewCache &viewChain = *pcoinsTip;
+ CCoinsViewCache &viewChain = ::ChainstateActive().CoinsTip();
CCoinsViewMemPool viewMempool(&viewChain, mempool);
view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view
diff --git a/src/rpc/rawtransaction_util.cpp b/src/rpc/rawtransaction_util.cpp
index 1c96d01232..f1d176ba4d 100644
--- a/src/rpc/rawtransaction_util.cpp
+++ b/src/rpc/rawtransaction_util.cpp
@@ -19,7 +19,7 @@
#include <util/rbf.h>
#include <util/strencodings.h>
-CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf)
+CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, bool rbf)
{
if (inputs_in.isNull() || outputs_in.isNull())
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, arguments 1 and 2 must be non-null");
@@ -37,8 +37,6 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
rawTx.nLockTime = nLockTime;
}
- bool rbfOptIn = rbf.isTrue();
-
for (unsigned int idx = 0; idx < inputs.size(); idx++) {
const UniValue& input = inputs[idx];
const UniValue& o = input.get_obj();
@@ -53,7 +51,7 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive");
uint32_t nSequence;
- if (rbfOptIn) {
+ if (rbf) {
nSequence = MAX_BIP125_RBF_SEQUENCE; /* CTxIn::SEQUENCE_FINAL - 2 */
} else if (rawTx.nLockTime) {
nSequence = CTxIn::SEQUENCE_FINAL - 1;
@@ -125,7 +123,7 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
}
}
- if (!rbf.isNull() && rawTx.vin.size() > 0 && rbfOptIn != SignalsOptInRBF(CTransaction(rawTx))) {
+ if (rbf && rawTx.vin.size() > 0 && !SignalsOptInRBF(CTransaction(rawTx))) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter combination: Sequence number(s) contradict replaceable option");
}
@@ -149,9 +147,8 @@ static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::
vErrorsRet.push_back(entry);
}
-UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival, FillableSigningProvider* keystore, std::map<COutPoint, Coin>& coins, bool is_temp_keystore, const UniValue& hashType)
+void ParsePrevouts(const UniValue& prevTxsUnival, FillableSigningProvider* keystore, std::map<COutPoint, Coin>& coins)
{
- // Add previous txouts given in the RPC call:
if (!prevTxsUnival.isNull()) {
UniValue prevTxs = prevTxsUnival.get_array();
for (unsigned int idx = 0; idx < prevTxs.size(); ++idx) {
@@ -199,36 +196,80 @@ UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival
}
// if redeemScript and private keys were given, add redeemScript to the keystore so it can be signed
- if (is_temp_keystore && (scriptPubKey.IsPayToScriptHash() || scriptPubKey.IsPayToWitnessScriptHash())) {
+ const bool is_p2sh = scriptPubKey.IsPayToScriptHash();
+ const bool is_p2wsh = scriptPubKey.IsPayToWitnessScriptHash();
+ if (keystore && (is_p2sh || is_p2wsh)) {
RPCTypeCheckObj(prevOut,
{
{"redeemScript", UniValueType(UniValue::VSTR)},
{"witnessScript", UniValueType(UniValue::VSTR)},
}, true);
UniValue rs = find_value(prevOut, "redeemScript");
- if (!rs.isNull()) {
- std::vector<unsigned char> rsData(ParseHexV(rs, "redeemScript"));
- CScript redeemScript(rsData.begin(), rsData.end());
- keystore->AddCScript(redeemScript);
- // Automatically also add the P2WSH wrapped version of the script (to deal with P2SH-P2WSH).
- // This is only for compatibility, it is encouraged to use the explicit witnessScript field instead.
- keystore->AddCScript(GetScriptForWitness(redeemScript));
- }
UniValue ws = find_value(prevOut, "witnessScript");
- if (!ws.isNull()) {
- std::vector<unsigned char> wsData(ParseHexV(ws, "witnessScript"));
- CScript witnessScript(wsData.begin(), wsData.end());
- keystore->AddCScript(witnessScript);
- // Automatically also add the P2WSH wrapped version of the script (to deal with P2SH-P2WSH).
- keystore->AddCScript(GetScriptForWitness(witnessScript));
- }
if (rs.isNull() && ws.isNull()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Missing redeemScript/witnessScript");
}
+
+ // work from witnessScript when possible
+ std::vector<unsigned char> scriptData(!ws.isNull() ? ParseHexV(ws, "witnessScript") : ParseHexV(rs, "redeemScript"));
+ CScript script(scriptData.begin(), scriptData.end());
+ keystore->AddCScript(script);
+ // Automatically also add the P2WSH wrapped version of the script (to deal with P2SH-P2WSH).
+ // This is done for redeemScript only for compatibility, it is encouraged to use the explicit witnessScript field instead.
+ CScript witness_output_script{GetScriptForWitness(script)};
+ keystore->AddCScript(witness_output_script);
+
+ if (!ws.isNull() && !rs.isNull()) {
+ // if both witnessScript and redeemScript are provided,
+ // they should either be the same (for backwards compat),
+ // or the redeemScript should be the encoded form of
+ // the witnessScript (ie, for p2sh-p2wsh)
+ if (ws.get_str() != rs.get_str()) {
+ std::vector<unsigned char> redeemScriptData(ParseHexV(rs, "redeemScript"));
+ CScript redeemScript(redeemScriptData.begin(), redeemScriptData.end());
+ if (redeemScript != witness_output_script) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "redeemScript does not correspond to witnessScript");
+ }
+ }
+ }
+
+ if (is_p2sh) {
+ const CTxDestination p2sh{ScriptHash(script)};
+ const CTxDestination p2sh_p2wsh{ScriptHash(witness_output_script)};
+ if (scriptPubKey == GetScriptForDestination(p2sh)) {
+ // traditional p2sh; arguably an error if
+ // we got here with rs.IsNull(), because
+ // that means the p2sh script was specified
+ // via witnessScript param, but for now
+ // we'll just quietly accept it
+ } else if (scriptPubKey == GetScriptForDestination(p2sh_p2wsh)) {
+ // p2wsh encoded as p2sh; ideally the witness
+ // script was specified in the witnessScript
+ // param, but also support specifying it via
+ // redeemScript param for backwards compat
+ // (in which case ws.IsNull() == true)
+ } else {
+ // otherwise, can't generate scriptPubKey from
+ // either script, so we got unusable parameters
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "redeemScript/witnessScript does not match scriptPubKey");
+ }
+ } else if (is_p2wsh) {
+ // plain p2wsh; could throw an error if script
+ // was specified by redeemScript rather than
+ // witnessScript (ie, ws.IsNull() == true), but
+ // accept it for backwards compat
+ const CTxDestination p2wsh{WitnessV0ScriptHash(script)};
+ if (scriptPubKey != GetScriptForDestination(p2wsh)) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "redeemScript/witnessScript does not match scriptPubKey");
+ }
+ }
}
}
}
+}
+UniValue SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, std::map<COutPoint, Coin>& coins, const UniValue& hashType)
+{
int nHashType = ParseSighashString(hashType);
bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
@@ -268,6 +309,9 @@ UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival
if (serror == SCRIPT_ERR_INVALID_STACK_OPERATION) {
// Unable to sign input and verification failed (possible attempt to partially sign).
TxInErrorToJSON(txin, vErrors, "Unable to sign input, invalid stack size (possibly missing key)");
+ } else if (serror == SCRIPT_ERR_SIG_NULLFAIL) {
+ // Verification failed (possibly due to insufficient signatures).
+ TxInErrorToJSON(txin, vErrors, "CHECK(MULTI)SIG failing with non-zero signature (possibly need more signatures)");
} else {
TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror));
}
diff --git a/src/rpc/rawtransaction_util.h b/src/rpc/rawtransaction_util.h
index d198887b93..b35e6da4ca 100644
--- a/src/rpc/rawtransaction_util.h
+++ b/src/rpc/rawtransaction_util.h
@@ -12,21 +12,29 @@ class UniValue;
struct CMutableTransaction;
class Coin;
class COutPoint;
+class SigningProvider;
/**
* Sign a transaction with the given keystore and previous transactions
*
* @param mtx The transaction to-be-signed
- * @param prevTxs Array of previous txns outputs that tx depends on but may not yet be in the block chain
* @param keystore Temporary keystore containing signing keys
* @param coins Map of unspent outputs - coins in mempool and current chain UTXO set, may be extended by previous txns outputs after call
- * @param tempKeystore Whether to use temporary keystore
* @param hashType The signature hash type
* @returns JSON object with details of signed transaction
*/
-UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxs, FillableSigningProvider* keystore, std::map<COutPoint, Coin>& coins, bool tempKeystore, const UniValue& hashType);
+UniValue SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, std::map<COutPoint, Coin>& coins, const UniValue& hashType);
+
+/**
+ * Parse a prevtxs UniValue array and get the map of coins from it
+ *
+ * @param prevTxs Array of previous txns outputs that tx depends on but may not yet be in the block chain
+ * @param keystore A pointer to the temprorary keystore if there is one
+ * @param coins Map of unspent outputs - coins in mempool and current chain UTXO set, may be extended by previous txns outputs after call
+ */
+void ParsePrevouts(const UniValue& prevTxsUnival, FillableSigningProvider* keystore, std::map<COutPoint, Coin>& coins);
/** Create a transaction from univalue parameters */
-CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf);
+CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, bool rbf);
#endif // BITCOIN_RPC_RAWTRANSACTION_UTIL_H
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
index 18f7426bcf..3e5bb85c1c 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -162,7 +162,7 @@ UniValue stop(const JSONRPCRequest& jsonRequest)
if (jsonRequest.fHelp || jsonRequest.params.size() > 1)
throw std::runtime_error(
RPCHelpMan{"stop",
- "\nStop Bitcoin server.",
+ "\nRequest a graceful shutdown of " PACKAGE_NAME ".",
{},
RPCResults{},
RPCExamples{""},
@@ -173,7 +173,7 @@ UniValue stop(const JSONRPCRequest& jsonRequest)
if (jsonRequest.params[0].isNum()) {
MilliSleep(jsonRequest.params[0].get_int());
}
- return "Bitcoin server stopping";
+ return PACKAGE_NAME " stopping";
}
static UniValue uptime(const JSONRPCRequest& jsonRequest)
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index de90276677..adda90c104 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -4,11 +4,12 @@
#include <key_io.h>
#include <outputtype.h>
-#include <script/signingprovider.h>
#include <rpc/util.h>
#include <script/descriptor.h>
+#include <script/signingprovider.h>
#include <tinyformat.h>
#include <util/strencodings.h>
+#include <util/string.h>
#include <tuple>
@@ -645,11 +646,7 @@ std::string RPCArg::ToString(const bool oneline) const
}
case Type::OBJ:
case Type::OBJ_USER_KEYS: {
- std::string res;
- for (size_t i = 0; i < m_inner.size();) {
- res += m_inner[i].ToStringObj(oneline);
- if (++i < m_inner.size()) res += ",";
- }
+ const std::string res = Join(m_inner, ",", [&](const RPCArg& i) { return i.ToStringObj(oneline); });
if (m_type == Type::OBJ) {
return "{" + res + "}";
} else {
@@ -717,9 +714,10 @@ std::vector<CScript> EvalDescriptorStringOrObject(const UniValue& scanobject, Fl
throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan object needs to be either a string or an object");
}
- auto desc = Parse(desc_str, provider);
+ std::string error;
+ auto desc = Parse(desc_str, provider, error);
if (!desc) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Invalid descriptor '%s'", desc_str));
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error);
}
if (!desc->IsRange()) {
range.first = 0;
@@ -735,3 +733,21 @@ std::vector<CScript> EvalDescriptorStringOrObject(const UniValue& scanobject, Fl
}
return ret;
}
+
+UniValue GetServicesNames(ServiceFlags services)
+{
+ UniValue servicesNames(UniValue::VARR);
+
+ if (services & NODE_NETWORK)
+ servicesNames.push_back("NETWORK");
+ if (services & NODE_GETUTXO)
+ servicesNames.push_back("GETUTXO");
+ if (services & NODE_BLOOM)
+ servicesNames.push_back("BLOOM");
+ if (services & NODE_WITNESS)
+ servicesNames.push_back("WITNESS");
+ if (services & NODE_NETWORK_LIMITED)
+ servicesNames.push_back("NETWORK_LIMITED");
+
+ return servicesNames;
+}
diff --git a/src/rpc/util.h b/src/rpc/util.h
index 4c3322b879..72fc7b6286 100644
--- a/src/rpc/util.h
+++ b/src/rpc/util.h
@@ -8,6 +8,7 @@
#include <node/transaction.h>
#include <outputtype.h>
#include <pubkey.h>
+#include <protocol.h>
#include <rpc/protocol.h>
#include <rpc/request.h>
#include <script/script.h>
@@ -90,6 +91,9 @@ std::pair<int64_t, int64_t> ParseDescriptorRange(const UniValue& value);
/** Evaluate a descriptor given as a string, or as a {"desc":...,"range":...} object, with default range of 1000. */
std::vector<CScript> EvalDescriptorStringOrObject(const UniValue& scanobject, FlatSigningProvider& provider);
+/** Returns, given services flags, a list of humanly readable (known) network services */
+UniValue GetServicesNames(ServiceFlags services);
+
struct RPCArg {
enum class Type {
OBJ,
diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp
index 50119ba184..b782ebbd1f 100644
--- a/src/script/descriptor.cpp
+++ b/src/script/descriptor.cpp
@@ -335,10 +335,12 @@ public:
/** Base class for all Descriptor implementations. */
class DescriptorImpl : public Descriptor
{
- //! Public key arguments for this descriptor (size 1 for PK, PKH, WPKH; any size of Multisig).
+ //! Public key arguments for this descriptor (size 1 for PK, PKH, WPKH; any size for Multisig).
const std::vector<std::unique_ptr<PubkeyProvider>> m_pubkey_args;
//! The sub-descriptor argument (nullptr for everything but SH and WSH).
- const std::unique_ptr<DescriptorImpl> m_script_arg;
+ //! In doc/descriptors.m this is referred to as SCRIPT expressions sh(SCRIPT)
+ //! and wsh(SCRIPT), and distinct from KEY expressions and ADDR expressions.
+ const std::unique_ptr<DescriptorImpl> m_subdescriptor_arg;
//! The string name of the descriptor function.
const std::string m_name;
@@ -349,10 +351,10 @@ protected:
/** A helper function to construct the scripts for this descriptor.
*
* This function is invoked once for every CScript produced by evaluating
- * m_script_arg, or just once in case m_script_arg is nullptr.
+ * m_subdescriptor_arg, or just once in case m_subdescriptor_arg is nullptr.
* @param pubkeys The evaluations of the m_pubkey_args field.
- * @param script The evaluation of m_script_arg (or nullptr when m_script_arg is nullptr).
+ * @param script The evaluation of m_subdescriptor_arg (or nullptr when m_subdescriptor_arg is nullptr).
* @param out A FlatSigningProvider to put scripts or public keys in that are necessary to the solver.
* The script arguments to this function are automatically added, as is the origin info of the provided pubkeys.
* @return A vector with scriptPubKeys for this descriptor.
@@ -360,12 +362,12 @@ protected:
virtual std::vector<CScript> MakeScripts(const std::vector<CPubKey>& pubkeys, const CScript* script, FlatSigningProvider& out) const = 0;
public:
- DescriptorImpl(std::vector<std::unique_ptr<PubkeyProvider>> pubkeys, std::unique_ptr<DescriptorImpl> script, const std::string& name) : m_pubkey_args(std::move(pubkeys)), m_script_arg(std::move(script)), m_name(name) {}
+ DescriptorImpl(std::vector<std::unique_ptr<PubkeyProvider>> pubkeys, std::unique_ptr<DescriptorImpl> script, const std::string& name) : m_pubkey_args(std::move(pubkeys)), m_subdescriptor_arg(std::move(script)), m_name(name) {}
bool IsSolvable() const override
{
- if (m_script_arg) {
- if (!m_script_arg->IsSolvable()) return false;
+ if (m_subdescriptor_arg) {
+ if (!m_subdescriptor_arg->IsSolvable()) return false;
}
return true;
}
@@ -375,8 +377,8 @@ public:
for (const auto& pubkey : m_pubkey_args) {
if (pubkey->IsRange()) return true;
}
- if (m_script_arg) {
- if (m_script_arg->IsRange()) return true;
+ if (m_subdescriptor_arg) {
+ if (m_subdescriptor_arg->IsRange()) return true;
}
return false;
}
@@ -396,10 +398,10 @@ public:
}
ret += std::move(tmp);
}
- if (m_script_arg) {
+ if (m_subdescriptor_arg) {
if (pos++) ret += ",";
std::string tmp;
- if (!m_script_arg->ToStringHelper(arg, tmp, priv)) return false;
+ if (!m_subdescriptor_arg->ToStringHelper(arg, tmp, priv)) return false;
ret += std::move(tmp);
}
out = std::move(ret) + ")";
@@ -428,6 +430,8 @@ public:
// Construct temporary data in `entries` and `subscripts`, to avoid producing output in case of failure.
for (const auto& p : m_pubkey_args) {
entries.emplace_back();
+ // If we have a cache, we don't need GetPubKey to compute the public key.
+ // Pass in nullptr to signify only origin info is desired.
if (!p->GetPubKey(pos, arg, cache_read ? nullptr : &entries.back().first, entries.back().second)) return false;
if (cache_read) {
// Cached expanded public key exists, use it.
@@ -444,9 +448,9 @@ public:
}
}
std::vector<CScript> subscripts;
- if (m_script_arg) {
+ if (m_subdescriptor_arg) {
FlatSigningProvider subprovider;
- if (!m_script_arg->ExpandHelper(pos, arg, cache_read, subscripts, subprovider, cache_write)) return false;
+ if (!m_subdescriptor_arg->ExpandHelper(pos, arg, cache_read, subscripts, subprovider, cache_write)) return false;
out = Merge(out, subprovider);
}
@@ -456,7 +460,7 @@ public:
pubkeys.push_back(entry.first);
out.origins.emplace(entry.first.GetID(), std::make_pair<CPubKey, KeyOriginInfo>(CPubKey(entry.first), std::move(entry.second)));
}
- if (m_script_arg) {
+ if (m_subdescriptor_arg) {
for (const auto& subscript : subscripts) {
out.scripts.emplace(CScriptID(subscript), subscript);
std::vector<CScript> addscripts = MakeScripts(pubkeys, &subscript, out);
@@ -488,9 +492,9 @@ public:
if (!p->GetPrivKey(pos, provider, key)) continue;
out.keys.emplace(key.GetPubKey().GetID(), key);
}
- if (m_script_arg) {
+ if (m_subdescriptor_arg) {
FlatSigningProvider subprovider;
- m_script_arg->ExpandPrivate(pos, provider, subprovider);
+ m_subdescriptor_arg->ExpandPrivate(pos, provider, subprovider);
out = Merge(out, subprovider);
}
}
@@ -686,7 +690,7 @@ std::vector<Span<const char>> Split(const Span<const char>& sp, char sep)
}
/** Parse a key path, being passed a split list of elements (the first element is ignored). */
-NODISCARD bool ParseKeyPath(const std::vector<Span<const char>>& split, KeyPath& out)
+NODISCARD bool ParseKeyPath(const std::vector<Span<const char>>& split, KeyPath& out, std::string& error)
{
for (size_t i = 1; i < split.size(); ++i) {
Span<const char> elem = split[i];
@@ -696,33 +700,60 @@ NODISCARD bool ParseKeyPath(const std::vector<Span<const char>>& split, KeyPath&
hardened = true;
}
uint32_t p;
- if (!ParseUInt32(std::string(elem.begin(), elem.end()), &p) || p > 0x7FFFFFFFUL) return false;
+ if (!ParseUInt32(std::string(elem.begin(), elem.end()), &p)) {
+ error = strprintf("Key path value '%s' is not a valid uint32", std::string(elem.begin(), elem.end()).c_str());
+ return false;
+ } else if (p > 0x7FFFFFFFUL) {
+ error = strprintf("Key path value %u is out of range", p);
+ return false;
+ }
out.push_back(p | (((uint32_t)hardened) << 31));
}
return true;
}
/** Parse a public key that excludes origin information. */
-std::unique_ptr<PubkeyProvider> ParsePubkeyInner(const Span<const char>& sp, bool permit_uncompressed, FlatSigningProvider& out)
+std::unique_ptr<PubkeyProvider> ParsePubkeyInner(const Span<const char>& sp, bool permit_uncompressed, FlatSigningProvider& out, std::string& error)
{
auto split = Split(sp, '/');
std::string str(split[0].begin(), split[0].end());
+ if (str.size() == 0) {
+ error = "No key provided";
+ return nullptr;
+ }
if (split.size() == 1) {
if (IsHex(str)) {
std::vector<unsigned char> data = ParseHex(str);
CPubKey pubkey(data);
- if (pubkey.IsFullyValid() && (permit_uncompressed || pubkey.IsCompressed())) return MakeUnique<ConstPubkeyProvider>(pubkey);
+ if (pubkey.IsFullyValid()) {
+ if (permit_uncompressed || pubkey.IsCompressed()) {
+ return MakeUnique<ConstPubkeyProvider>(pubkey);
+ } else {
+ error = "Uncompressed keys are not allowed";
+ return nullptr;
+ }
+ }
+ error = strprintf("Pubkey '%s' is invalid", str);
+ return nullptr;
}
CKey key = DecodeSecret(str);
- if (key.IsValid() && (permit_uncompressed || key.IsCompressed())) {
- CPubKey pubkey = key.GetPubKey();
- out.keys.emplace(pubkey.GetID(), key);
- return MakeUnique<ConstPubkeyProvider>(pubkey);
+ if (key.IsValid()) {
+ if (permit_uncompressed || key.IsCompressed()) {
+ CPubKey pubkey = key.GetPubKey();
+ out.keys.emplace(pubkey.GetID(), key);
+ return MakeUnique<ConstPubkeyProvider>(pubkey);
+ } else {
+ error = "Uncompressed keys are not allowed";
+ return nullptr;
+ }
}
}
CExtKey extkey = DecodeExtKey(str);
CExtPubKey extpubkey = DecodeExtPubKey(str);
- if (!extkey.key.IsValid() && !extpubkey.pubkey.IsValid()) return nullptr;
+ if (!extkey.key.IsValid() && !extpubkey.pubkey.IsValid()) {
+ error = strprintf("key '%s' is not valid", str);
+ return nullptr;
+ }
KeyPath path;
DeriveType type = DeriveType::NO;
if (split.back() == MakeSpan("*").first(1)) {
@@ -732,7 +763,7 @@ std::unique_ptr<PubkeyProvider> ParsePubkeyInner(const Span<const char>& sp, boo
split.pop_back();
type = DeriveType::HARDENED;
}
- if (!ParseKeyPath(split, path)) return nullptr;
+ if (!ParseKeyPath(split, path, error)) return nullptr;
if (extkey.key.IsValid()) {
extpubkey = extkey.Neuter();
out.keys.emplace(extpubkey.pubkey.GetID(), extkey.key);
@@ -741,95 +772,154 @@ std::unique_ptr<PubkeyProvider> ParsePubkeyInner(const Span<const char>& sp, boo
}
/** Parse a public key including origin information (if enabled). */
-std::unique_ptr<PubkeyProvider> ParsePubkey(const Span<const char>& sp, bool permit_uncompressed, FlatSigningProvider& out)
+std::unique_ptr<PubkeyProvider> ParsePubkey(const Span<const char>& sp, bool permit_uncompressed, FlatSigningProvider& out, std::string& error)
{
auto origin_split = Split(sp, ']');
- if (origin_split.size() > 2) return nullptr;
- if (origin_split.size() == 1) return ParsePubkeyInner(origin_split[0], permit_uncompressed, out);
- if (origin_split[0].size() < 1 || origin_split[0][0] != '[') return nullptr;
+ if (origin_split.size() > 2) {
+ error = "Multiple ']' characters found for a single pubkey";
+ return nullptr;
+ }
+ if (origin_split.size() == 1) return ParsePubkeyInner(origin_split[0], permit_uncompressed, out, error);
+ if (origin_split[0].size() < 1 || origin_split[0][0] != '[') {
+ error = strprintf("Key origin start '[ character expected but not found, got '%c' instead", origin_split[0][0]);
+ return nullptr;
+ }
auto slash_split = Split(origin_split[0].subspan(1), '/');
- if (slash_split[0].size() != 8) return nullptr;
+ if (slash_split[0].size() != 8) {
+ error = strprintf("Fingerprint is not 4 bytes (%u characters instead of 8 characters)", slash_split[0].size());
+ return nullptr;
+ }
std::string fpr_hex = std::string(slash_split[0].begin(), slash_split[0].end());
- if (!IsHex(fpr_hex)) return nullptr;
+ if (!IsHex(fpr_hex)) {
+ error = strprintf("Fingerprint '%s' is not hex", fpr_hex);
+ return nullptr;
+ }
auto fpr_bytes = ParseHex(fpr_hex);
KeyOriginInfo info;
static_assert(sizeof(info.fingerprint) == 4, "Fingerprint must be 4 bytes");
assert(fpr_bytes.size() == 4);
std::copy(fpr_bytes.begin(), fpr_bytes.end(), info.fingerprint);
- if (!ParseKeyPath(slash_split, info.path)) return nullptr;
- auto provider = ParsePubkeyInner(origin_split[1], permit_uncompressed, out);
+ if (!ParseKeyPath(slash_split, info.path, error)) return nullptr;
+ auto provider = ParsePubkeyInner(origin_split[1], permit_uncompressed, out, error);
if (!provider) return nullptr;
return MakeUnique<OriginPubkeyProvider>(std::move(info), std::move(provider));
}
/** Parse a script in a particular context. */
-std::unique_ptr<DescriptorImpl> ParseScript(Span<const char>& sp, ParseScriptContext ctx, FlatSigningProvider& out)
+std::unique_ptr<DescriptorImpl> ParseScript(Span<const char>& sp, ParseScriptContext ctx, FlatSigningProvider& out, std::string& error)
{
auto expr = Expr(sp);
if (Func("pk", expr)) {
- auto pubkey = ParsePubkey(expr, ctx != ParseScriptContext::P2WSH, out);
+ auto pubkey = ParsePubkey(expr, ctx != ParseScriptContext::P2WSH, out, error);
if (!pubkey) return nullptr;
return MakeUnique<PKDescriptor>(std::move(pubkey));
}
if (Func("pkh", expr)) {
- auto pubkey = ParsePubkey(expr, ctx != ParseScriptContext::P2WSH, out);
+ auto pubkey = ParsePubkey(expr, ctx != ParseScriptContext::P2WSH, out, error);
if (!pubkey) return nullptr;
return MakeUnique<PKHDescriptor>(std::move(pubkey));
}
if (ctx == ParseScriptContext::TOP && Func("combo", expr)) {
- auto pubkey = ParsePubkey(expr, true, out);
+ auto pubkey = ParsePubkey(expr, true, out, error);
if (!pubkey) return nullptr;
return MakeUnique<ComboDescriptor>(std::move(pubkey));
+ } else if (ctx != ParseScriptContext::TOP && Func("combo", expr)) {
+ error = "Cannot have combo in non-top level";
+ return nullptr;
}
if (Func("multi", expr)) {
auto threshold = Expr(expr);
uint32_t thres;
std::vector<std::unique_ptr<PubkeyProvider>> providers;
- if (!ParseUInt32(std::string(threshold.begin(), threshold.end()), &thres)) return nullptr;
+ if (!ParseUInt32(std::string(threshold.begin(), threshold.end()), &thres)) {
+ error = strprintf("Multi threshold '%s' is not valid", std::string(threshold.begin(), threshold.end()).c_str());
+ return nullptr;
+ }
size_t script_size = 0;
while (expr.size()) {
- if (!Const(",", expr)) return nullptr;
+ if (!Const(",", expr)) {
+ error = strprintf("Multi: expected ',', got '%c'", expr[0]);
+ return nullptr;
+ }
auto arg = Expr(expr);
- auto pk = ParsePubkey(arg, ctx != ParseScriptContext::P2WSH, out);
+ auto pk = ParsePubkey(arg, ctx != ParseScriptContext::P2WSH, out, error);
if (!pk) return nullptr;
script_size += pk->GetSize() + 1;
providers.emplace_back(std::move(pk));
}
- if (providers.size() < 1 || providers.size() > 16 || thres < 1 || thres > providers.size()) return nullptr;
+ if (providers.size() < 1 || providers.size() > 16) {
+ error = strprintf("Cannot have %u keys in multisig; must have between 1 and 16 keys, inclusive", providers.size());
+ return nullptr;
+ } else if (thres < 1) {
+ error = strprintf("Multisig threshold cannot be %d, must be at least 1", thres);
+ return nullptr;
+ } else if (thres > providers.size()) {
+ error = strprintf("Multisig threshold cannot be larger than the number of keys; threshold is %d but only %u keys specified", thres, providers.size());
+ return nullptr;
+ }
if (ctx == ParseScriptContext::TOP) {
- if (providers.size() > 3) return nullptr; // Not more than 3 pubkeys for raw multisig
+ if (providers.size() > 3) {
+ error = strprintf("Cannot have %u pubkeys in bare multisig; only at most 3 pubkeys", providers.size());
+ return nullptr;
+ }
}
if (ctx == ParseScriptContext::P2SH) {
- if (script_size + 3 > 520) return nullptr; // Enforce P2SH script size limit
+ if (script_size + 3 > 520) {
+ error = strprintf("P2SH script is too large, %d bytes is larger than 520 bytes", script_size + 3);
+ return nullptr;
+ }
}
return MakeUnique<MultisigDescriptor>(thres, std::move(providers));
}
if (ctx != ParseScriptContext::P2WSH && Func("wpkh", expr)) {
- auto pubkey = ParsePubkey(expr, false, out);
+ auto pubkey = ParsePubkey(expr, false, out, error);
if (!pubkey) return nullptr;
return MakeUnique<WPKHDescriptor>(std::move(pubkey));
+ } else if (ctx == ParseScriptContext::P2WSH && Func("wpkh", expr)) {
+ error = "Cannot have wpkh within wsh";
+ return nullptr;
}
if (ctx == ParseScriptContext::TOP && Func("sh", expr)) {
- auto desc = ParseScript(expr, ParseScriptContext::P2SH, out);
+ auto desc = ParseScript(expr, ParseScriptContext::P2SH, out, error);
if (!desc || expr.size()) return nullptr;
return MakeUnique<SHDescriptor>(std::move(desc));
+ } else if (ctx != ParseScriptContext::TOP && Func("sh", expr)) {
+ error = "Cannot have sh in non-top level";
+ return nullptr;
}
if (ctx != ParseScriptContext::P2WSH && Func("wsh", expr)) {
- auto desc = ParseScript(expr, ParseScriptContext::P2WSH, out);
+ auto desc = ParseScript(expr, ParseScriptContext::P2WSH, out, error);
if (!desc || expr.size()) return nullptr;
return MakeUnique<WSHDescriptor>(std::move(desc));
+ } else if (ctx == ParseScriptContext::P2WSH && Func("wsh", expr)) {
+ error = "Cannot have wsh within wsh";
+ return nullptr;
}
if (ctx == ParseScriptContext::TOP && Func("addr", expr)) {
CTxDestination dest = DecodeDestination(std::string(expr.begin(), expr.end()));
- if (!IsValidDestination(dest)) return nullptr;
+ if (!IsValidDestination(dest)) {
+ error = "Address is not valid";
+ return nullptr;
+ }
return MakeUnique<AddressDescriptor>(std::move(dest));
}
if (ctx == ParseScriptContext::TOP && Func("raw", expr)) {
std::string str(expr.begin(), expr.end());
- if (!IsHex(str)) return nullptr;
+ if (!IsHex(str)) {
+ error = "Raw script is not hex";
+ return nullptr;
+ }
auto bytes = ParseHex(str);
return MakeUnique<RawDescriptor>(CScript(bytes.begin(), bytes.end()));
}
+ if (ctx == ParseScriptContext::P2SH) {
+ error = "A function is needed within P2SH";
+ return nullptr;
+ } else if (ctx == ParseScriptContext::P2WSH) {
+ error = "A function is needed within P2WSH";
+ return nullptr;
+ }
+ error = strprintf("%s is not a valid descriptor function", std::string(expr.begin(), expr.end()));
return nullptr;
}
@@ -910,27 +1000,58 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo
} // namespace
-std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out, bool require_checksum)
+/** Check a descriptor checksum, and update desc to be the checksum-less part. */
+bool CheckChecksum(Span<const char>& sp, bool require_checksum, std::string& error, std::string* out_checksum = nullptr)
{
- Span<const char> sp(descriptor.data(), descriptor.size());
-
- // Checksum checks
auto check_split = Split(sp, '#');
- if (check_split.size() > 2) return nullptr; // Multiple '#' symbols
- if (check_split.size() == 1 && require_checksum) return nullptr; // Missing checksum
+ if (check_split.size() > 2) {
+ error = "Multiple '#' symbols";
+ return false;
+ }
+ if (check_split.size() == 1 && require_checksum){
+ error = "Missing checksum";
+ return false;
+ }
+ if (check_split.size() == 2) {
+ if (check_split[1].size() != 8) {
+ error = strprintf("Expected 8 character checksum, not %u characters", check_split[1].size());
+ return false;
+ }
+ }
+ auto checksum = DescriptorChecksum(check_split[0]);
+ if (checksum.empty()) {
+ error = "Invalid characters in payload";
+ return false;
+ }
if (check_split.size() == 2) {
- if (check_split[1].size() != 8) return nullptr; // Unexpected length for checksum
- auto checksum = DescriptorChecksum(check_split[0]);
- if (checksum.empty()) return nullptr; // Invalid characters in payload
- if (!std::equal(checksum.begin(), checksum.end(), check_split[1].begin())) return nullptr; // Checksum mismatch
+ if (!std::equal(checksum.begin(), checksum.end(), check_split[1].begin())) {
+ error = strprintf("Provided checksum '%s' does not match computed checksum '%s'", std::string(check_split[1].begin(), check_split[1].end()), checksum);
+ return false;
+ }
}
+ if (out_checksum) *out_checksum = std::move(checksum);
sp = check_split[0];
+ return true;
+}
- auto ret = ParseScript(sp, ParseScriptContext::TOP, out);
+std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out, std::string& error, bool require_checksum)
+{
+ Span<const char> sp(descriptor.data(), descriptor.size());
+ if (!CheckChecksum(sp, require_checksum, error)) return nullptr;
+ auto ret = ParseScript(sp, ParseScriptContext::TOP, out, error);
if (sp.size() == 0 && ret) return std::unique_ptr<Descriptor>(std::move(ret));
return nullptr;
}
+std::string GetDescriptorChecksum(const std::string& descriptor)
+{
+ std::string ret;
+ std::string error;
+ Span<const char> sp(descriptor.data(), descriptor.size());
+ if (!CheckChecksum(sp, false, error, &ret)) return "";
+ return ret;
+}
+
std::unique_ptr<Descriptor> InferDescriptor(const CScript& script, const SigningProvider& provider)
{
return InferScript(script, ParseScriptContext::TOP, provider);
diff --git a/src/script/descriptor.h b/src/script/descriptor.h
index 29915c6c92..0195ca0939 100644
--- a/src/script/descriptor.h
+++ b/src/script/descriptor.h
@@ -47,9 +47,9 @@ struct Descriptor {
*
* pos: the position at which to expand the descriptor. If IsRange() is false, this is ignored.
* provider: the provider to query for private keys in case of hardened derivation.
- * output_script: the expanded scriptPubKeys will be put here.
+ * output_scripts: the expanded scriptPubKeys will be put here.
* out: scripts and public keys necessary for solving the expanded scriptPubKeys will be put here (may be equal to provider).
- * cache: vector which will be overwritten with cache data necessary to-evaluate the descriptor at this point without access to private keys.
+ * cache: vector which will be overwritten with cache data necessary to evaluate the descriptor at this point without access to private keys.
*/
virtual bool Expand(int pos, const SigningProvider& provider, std::vector<CScript>& output_scripts, FlatSigningProvider& out, std::vector<unsigned char>* cache = nullptr) const = 0;
@@ -57,7 +57,7 @@ struct Descriptor {
*
* pos: the position at which to expand the descriptor. If IsRange() is false, this is ignored.
* cache: vector from which cached expansion data will be read.
- * output_script: the expanded scriptPubKeys will be put here.
+ * output_scripts: the expanded scriptPubKeys will be put here.
* out: scripts and public keys necessary for solving the expanded scriptPubKeys will be put here (may be equal to provider).
*/
virtual bool ExpandFromCache(int pos, const std::vector<unsigned char>& cache, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const = 0;
@@ -79,7 +79,15 @@ struct Descriptor {
* If a parse error occurs, or the checksum is missing/invalid, or anything
* else is wrong, nullptr is returned.
*/
-std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out, bool require_checksum = false);
+std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out, std::string& error, bool require_checksum = false);
+
+/** Get the checksum for a descriptor.
+ *
+ * If it already has one, and it is correct, return the checksum in the input.
+ * If it already has one that is wrong, return "".
+ * If it does not already have one, return the checksum that would need to be added.
+ */
+std::string GetDescriptorChecksum(const std::string& descriptor);
/** Find a descriptor for the specified script, using information from provider where possible.
*
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index f8701b6d01..20fae2eebf 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -334,7 +334,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
opcode == OP_MOD ||
opcode == OP_LSHIFT ||
opcode == OP_RSHIFT)
- return set_error(serror, SCRIPT_ERR_DISABLED_OPCODE); // Disabled opcodes.
+ return set_error(serror, SCRIPT_ERR_DISABLED_OPCODE); // Disabled opcodes (CVE-2010-5137).
// 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))
@@ -1483,6 +1483,8 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C
return set_error(serror, SCRIPT_ERR_SIG_PUSHONLY);
}
+ // scriptSig and scriptPubKey must be evaluated sequentially on the same stack
+ // rather than being simply concatenated (see CVE-2010-5141)
std::vector<std::vector<unsigned char> > stack, stackCopy;
if (!EvalScript(stack, scriptSig, flags, checker, SigVersion::BASE, serror))
// serror is set
diff --git a/src/serialize.h b/src/serialize.h
index 1dc27d84eb..a38d76fc18 100644
--- a/src/serialize.h
+++ b/src/serialize.h
@@ -555,6 +555,7 @@ template<typename Stream, unsigned int N, typename T> inline void Unserialize(St
* vectors of unsigned char are a special case and are intended to be serialized as a single opaque blob.
*/
template<typename Stream, typename T, typename A> void Serialize_impl(Stream& os, const std::vector<T, A>& v, const unsigned char&);
+template<typename Stream, typename T, typename A> void Serialize_impl(Stream& os, const std::vector<T, A>& v, const bool&);
template<typename Stream, typename T, typename A, typename V> void Serialize_impl(Stream& os, const std::vector<T, A>& v, const V&);
template<typename Stream, typename T, typename A> inline void Serialize(Stream& os, const std::vector<T, A>& v);
template<typename Stream, typename T, typename A> void Unserialize_impl(Stream& is, std::vector<T, A>& v, const unsigned char&);
@@ -713,6 +714,18 @@ void Serialize_impl(Stream& os, const std::vector<T, A>& v, const unsigned char&
os.write((char*)v.data(), v.size() * sizeof(T));
}
+template<typename Stream, typename T, typename A>
+void Serialize_impl(Stream& os, const std::vector<T, A>& v, const bool&)
+{
+ // A special case for std::vector<bool>, as dereferencing
+ // std::vector<bool>::const_iterator does not result in a const bool&
+ // due to std::vector's special casing for bool arguments.
+ WriteCompactSize(os, v.size());
+ for (bool elem : v) {
+ ::Serialize(os, elem);
+ }
+}
+
template<typename Stream, typename T, typename A, typename V>
void Serialize_impl(Stream& os, const std::vector<T, A>& v, const V&)
{
diff --git a/src/test/README.md b/src/test/README.md
index 0017e3de26..8901fae7bd 100644
--- a/src/test/README.md
+++ b/src/test/README.md
@@ -49,7 +49,3 @@ unit tests. The file naming convention is `<source_filename>_tests.cpp`
and such files should wrap their tests in a test suite
called `<source_filename>_tests`. For an example of this pattern,
examine `uint256_tests.cpp`.
-
-For further reading, I found the following website to be helpful in
-explaining how the boost unit test framework works:
-[http://www.alittlemadness.com/2009/03/31/c-unit-testing-with-boosttest/](http://archive.is/dRBGf).
diff --git a/src/test/data/script_tests.json b/src/test/data/script_tests.json
index 9b320b6943..3241f32f56 100644
--- a/src/test/data/script_tests.json
+++ b/src/test/data/script_tests.json
@@ -829,15 +829,16 @@
["NOP", "2SWAP 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"],
["1", "2 3 2SWAP 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"],
+
+["NOP", "SIZE 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"],
+
+["TEST DISABLED OP CODES (CVE-2010-5137)"],
["'a' 'b'", "CAT", "P2SH,STRICTENC", "DISABLED_OPCODE", "CAT disabled"],
["'a' 'b' 0", "IF CAT ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "CAT disabled"],
["'abc' 1 1", "SUBSTR", "P2SH,STRICTENC", "DISABLED_OPCODE", "SUBSTR disabled"],
["'abc' 1 1 0", "IF SUBSTR ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "SUBSTR disabled"],
["'abc' 2 0", "IF LEFT ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "LEFT disabled"],
["'abc' 2 0", "IF RIGHT ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "RIGHT disabled"],
-
-["NOP", "SIZE 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"],
-
["'abc'", "IF INVERT ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "INVERT disabled"],
["1 2 0 IF AND ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "DISABLED_OPCODE", "AND disabled"],
["1 2 0 IF OR ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "DISABLED_OPCODE", "OR disabled"],
diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp
index a50d6854f8..b0a613372f 100644
--- a/src/test/denialofservice_tests.cpp
+++ b/src/test/denialofservice_tests.cpp
@@ -151,17 +151,17 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), nullptr, scheduler, false);
const Consensus::Params& consensusParams = Params().GetConsensus();
- constexpr int nMaxOutbound = 8;
+ constexpr int max_outbound_full_relay = 8;
CConnman::Options options;
options.nMaxConnections = 125;
- options.nMaxOutbound = nMaxOutbound;
+ options.m_max_outbound_full_relay = max_outbound_full_relay;
options.nMaxFeeler = 1;
connman->Init(options);
std::vector<CNode *> vNodes;
// Mock some outbound peers
- for (int i=0; i<nMaxOutbound; ++i) {
+ for (int i=0; i<max_outbound_full_relay; ++i) {
AddRandomOutboundPeer(vNodes, *peerLogic, connman.get());
}
@@ -190,7 +190,7 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
AddRandomOutboundPeer(vNodes, *peerLogic, connman.get());
peerLogic->CheckForStaleTipAndEvictPeers(consensusParams);
- for (int i=0; i<nMaxOutbound; ++i) {
+ for (int i=0; i<max_outbound_full_relay; ++i) {
BOOST_CHECK(vNodes[i]->fDisconnect == false);
}
// Last added node should get marked for eviction
@@ -203,10 +203,10 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
UpdateLastBlockAnnounceTime(vNodes.back()->GetId(), GetTime());
peerLogic->CheckForStaleTipAndEvictPeers(consensusParams);
- for (int i=0; i<nMaxOutbound-1; ++i) {
+ for (int i=0; i<max_outbound_full_relay-1; ++i) {
BOOST_CHECK(vNodes[i]->fDisconnect == false);
}
- BOOST_CHECK(vNodes[nMaxOutbound-1]->fDisconnect == true);
+ BOOST_CHECK(vNodes[max_outbound_full_relay-1]->fDisconnect == true);
BOOST_CHECK(vNodes.back()->fDisconnect == false);
bool dummy;
diff --git a/src/test/descriptor_tests.cpp b/src/test/descriptor_tests.cpp
index f5bda7d5e6..50ac0bd7b8 100644
--- a/src/test/descriptor_tests.cpp
+++ b/src/test/descriptor_tests.cpp
@@ -13,13 +13,15 @@
namespace {
-void CheckUnparsable(const std::string& prv, const std::string& pub)
+void CheckUnparsable(const std::string& prv, const std::string& pub, const std::string& expected_error)
{
FlatSigningProvider keys_priv, keys_pub;
- auto parse_priv = Parse(prv, keys_priv);
- auto parse_pub = Parse(pub, keys_pub);
+ std::string error;
+ auto parse_priv = Parse(prv, keys_priv, error);
+ auto parse_pub = Parse(pub, keys_pub, error);
BOOST_CHECK_MESSAGE(!parse_priv, prv);
BOOST_CHECK_MESSAGE(!parse_pub, pub);
+ BOOST_CHECK(error == expected_error);
}
constexpr int DEFAULT = 0;
@@ -40,32 +42,47 @@ bool EqualDescriptor(std::string a, std::string b)
return a == b;
}
-std::string MaybeUseHInsteadOfApostrophy(std::string ret)
+std::string UseHInsteadOfApostrophe(const std::string& desc)
{
- if (InsecureRandBool()) {
- while (true) {
- auto it = ret.find("'");
- if (it != std::string::npos) {
- ret[it] = 'h';
- if (ret.size() > 9 && ret[ret.size() - 9] == '#') ret = ret.substr(0, ret.size() - 9); // Changing apostrophe to h breaks the checksum
- } else {
- break;
- }
- }
+ std::string ret = desc;
+ while (true) {
+ auto it = ret.find('\'');
+ if (it == std::string::npos) break;
+ ret[it] = 'h';
+ }
+
+ // GetDescriptorChecksum returns "" if the checksum exists but is bad.
+ // Switching apostrophes with 'h' breaks the checksum if it exists - recalculate it and replace the broken one.
+ if (GetDescriptorChecksum(ret) == "") {
+ ret = ret.substr(0, desc.size() - 9);
+ ret += std::string("#") + GetDescriptorChecksum(ret);
}
return ret;
}
const std::set<std::vector<uint32_t>> ONLY_EMPTY{{}};
-void Check(const std::string& prv, const std::string& pub, int flags, const std::vector<std::vector<std::string>>& scripts, const std::set<std::vector<uint32_t>>& paths = ONLY_EMPTY)
+void DoCheck(const std::string& prv, const std::string& pub, int flags, const std::vector<std::vector<std::string>>& scripts, const std::set<std::vector<uint32_t>>& paths = ONLY_EMPTY,
+ bool replace_apostrophe_with_h_in_prv=false, bool replace_apostrophe_with_h_in_pub=false)
{
FlatSigningProvider keys_priv, keys_pub;
std::set<std::vector<uint32_t>> left_paths = paths;
+ std::string error;
+ std::unique_ptr<Descriptor> parse_priv;
+ std::unique_ptr<Descriptor> parse_pub;
// Check that parsing succeeds.
- auto parse_priv = Parse(MaybeUseHInsteadOfApostrophy(prv), keys_priv);
- auto parse_pub = Parse(MaybeUseHInsteadOfApostrophy(pub), keys_pub);
+ if (replace_apostrophe_with_h_in_prv) {
+ parse_priv = Parse(UseHInsteadOfApostrophe(prv), keys_priv, error);
+ } else {
+ parse_priv = Parse(prv, keys_priv, error);
+ }
+ if (replace_apostrophe_with_h_in_pub) {
+ parse_pub = Parse(UseHInsteadOfApostrophe(pub), keys_pub, error);
+ } else {
+ parse_pub = Parse(pub, keys_pub, error);
+ }
+
BOOST_CHECK(parse_priv);
BOOST_CHECK(parse_pub);
@@ -164,6 +181,32 @@ void Check(const std::string& prv, const std::string& pub, int flags, const std:
BOOST_CHECK_MESSAGE(left_paths.empty(), "Not all expected key paths found: " + prv);
}
+void Check(const std::string& prv, const std::string& pub, int flags, const std::vector<std::vector<std::string>>& scripts, const std::set<std::vector<uint32_t>>& paths = ONLY_EMPTY)
+{
+ bool found_apostrophes_in_prv = false;
+ bool found_apostrophes_in_pub = false;
+
+ // Do not replace apostrophes with 'h' in prv and pub
+ DoCheck(prv, pub, flags, scripts, paths);
+
+ // Replace apostrophes with 'h' in prv but not in pub, if apostrophes are found in prv
+ if (prv.find('\'') != std::string::npos) {
+ found_apostrophes_in_prv = true;
+ DoCheck(prv, pub, flags, scripts, paths, /* replace_apostrophe_with_h_in_prv = */true, /*replace_apostrophe_with_h_in_pub = */false);
+ }
+
+ // Replace apostrophes with 'h' in pub but not in prv, if apostrophes are found in pub
+ if (pub.find('\'') != std::string::npos) {
+ found_apostrophes_in_pub = true;
+ DoCheck(prv, pub, flags, scripts, paths, /* replace_apostrophe_with_h_in_prv = */false, /*replace_apostrophe_with_h_in_pub = */true);
+ }
+
+ // Replace apostrophes with 'h' both in prv and in pub, if apostrophes are found in both
+ if (found_apostrophes_in_prv && found_apostrophes_in_pub) {
+ DoCheck(prv, pub, flags, scripts, paths, /* replace_apostrophe_with_h_in_prv = */true, /*replace_apostrophe_with_h_in_pub = */true);
+ }
+}
+
}
BOOST_FIXTURE_TEST_SUITE(descriptor_tests, BasicTestingSetup)
@@ -176,14 +219,17 @@ BOOST_AUTO_TEST_CASE(descriptor_test)
Check("pkh([deadbeef/1/2'/3/4']L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pkh([deadbeef/1/2'/3/4']03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"76a9149a1c78a507689f6f54b847ad1cef1e614ee23f1e88ac"}}, {{1,0x80000002UL,3,0x80000004UL}});
Check("wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"00149a1c78a507689f6f54b847ad1cef1e614ee23f1e"}});
Check("sh(wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"a91484ab21b1b2fd065d4504ff693d832434b6108d7b87"}});
+ CheckUnparsable("sh(wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY2))", "sh(wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5))", "Pubkey '03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5' is invalid"); // Invalid pubkey
+ CheckUnparsable("pkh(deadbeef/1/2'/3/4']L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pkh(deadbeef/1/2'/3/4']03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "Key origin start '[ character expected but not found, got 'd' instead"); // Missing start bracket in key origin
+ CheckUnparsable("pkh([deadbeef]/1/2'/3/4']L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pkh([deadbeef]/1/2'/3/4']03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "Multiple ']' characters found for a single pubkey"); // Multiple end brackets in key origin
// Basic single-key uncompressed
Check("combo(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "combo(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235ac","76a914b5bd079c4d57cc7fc28ecf8213a6b791625b818388ac"}});
Check("pk(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "pk(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235ac"}});
Check("pkh(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "pkh(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"76a914b5bd079c4d57cc7fc28ecf8213a6b791625b818388ac"}});
- CheckUnparsable("wpkh(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "wpkh(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)"); // No uncompressed keys in witness
- CheckUnparsable("wsh(pk(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss))", "wsh(pk(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235))"); // No uncompressed keys in witness
- CheckUnparsable("sh(wpkh(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss))", "sh(wpkh(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235))"); // No uncompressed keys in witness
+ CheckUnparsable("wpkh(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "wpkh(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "Uncompressed keys are not allowed"); // No uncompressed keys in witness
+ CheckUnparsable("wsh(pk(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss))", "wsh(pk(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235))", "Uncompressed keys are not allowed"); // No uncompressed keys in witness
+ CheckUnparsable("sh(wpkh(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss))", "sh(wpkh(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235))", "Uncompressed keys are not allowed"); // No uncompressed keys in witness
// Some unconventional single-key constructions
Check("sh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"a9141857af51a5e516552b3086430fd8ce55f7c1a52487"}});
@@ -200,38 +246,50 @@ BOOST_AUTO_TEST_CASE(descriptor_test)
Check("wpkh([ffffffff/13']xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*)", "wpkh([ffffffff/13']xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*)", RANGE, {{"0014326b2249e3a25d5dc60935f044ee835d090ba859"},{"0014af0bd98abc2f2cae66e36896a39ffe2d32984fb7"},{"00141fa798efd1cbf95cebf912c031b8a4a6e9fb9f27"}}, {{0x8000000DUL, 1, 2, 0}, {0x8000000DUL, 1, 2, 1}, {0x8000000DUL, 1, 2, 2}});
Check("sh(wpkh(xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "sh(wpkh(xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", RANGE | HARDENED, {{"a9149a4d9901d6af519b2a23d4a2f51650fcba87ce7b87"},{"a914bed59fc0024fae941d6e20a3b44a109ae740129287"},{"a9148483aa1116eb9c05c482a72bada4b1db24af654387"}}, {{10, 20, 30, 40, 0x80000000UL}, {10, 20, 30, 40, 0x80000001UL}, {10, 20, 30, 40, 0x80000002UL}});
Check("combo(xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/*)", "combo(xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/*)", RANGE, {{"2102df12b7035bdac8e3bab862a3a83d06ea6b17b6753d52edecba9be46f5d09e076ac","76a914f90e3178ca25f2c808dc76624032d352fdbdfaf288ac","0014f90e3178ca25f2c808dc76624032d352fdbdfaf2","a91408f3ea8c68d4a7585bf9e8bda226723f70e445f087"},{"21032869a233c9adff9a994e4966e5b821fd5bac066da6c3112488dc52383b4a98ecac","76a914a8409d1b6dfb1ed2a3e8aa5e0ef2ff26b15b75b788ac","0014a8409d1b6dfb1ed2a3e8aa5e0ef2ff26b15b75b7","a91473e39884cb71ae4e5ac9739e9225026c99763e6687"}}, {{0}, {1}});
- CheckUnparsable("combo([012345678]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)", "combo([012345678]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)"); // Too long key fingerprint
- CheckUnparsable("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483648)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483648)"); // BIP 32 path element overflow
+ CheckUnparsable("combo([012345678]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)", "combo([012345678]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)", "Fingerprint is not 4 bytes (9 characters instead of 8 characters)"); // Too long key fingerprint
+ CheckUnparsable("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483648)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483648)", "Key path value 2147483648 is out of range"); // BIP 32 path element overflow
+ CheckUnparsable("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/1aa)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/1aa)", "Key path value '1aa' is not a valid uint32"); // Path is not valid uint
// Multisig constructions
Check("multi(1,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}});
Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, {{0x8000006FUL,222},{0}});
Check("wsh(multi(2,xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", HARDENED | RANGE, {{"0020b92623201f3bb7c3771d45b2ad1d0351ea8fbf8cfe0a0e570264e1075fa1948f"},{"002036a08bbe4923af41cf4316817c93b8d37e2f635dd25cfff06bd50df6ae7ea203"},{"0020a96e7ab4607ca6b261bfe3245ffda9c746b28d3f59e83d34820ec0e2b36c139c"}}, {{0xFFFFFFFFUL,0}, {1,2,0}, {1,2,1}, {1,2,2}, {10, 20, 30, 40, 0x80000000UL}, {10, 20, 30, 40, 0x80000001UL}, {10, 20, 30, 40, 0x80000002UL}});
Check("sh(wsh(multi(16,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9)))","sh(wsh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232)))", SIGNABLE, {{"a9147fc63e13dc25e8a95a3cee3d9a714ac3afd96f1e87"}});
- CheckUnparsable("sh(multi(16,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9))","sh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232))"); // P2SH does not fit 16 compressed pubkeys in a redeemscript
- CheckUnparsable("wsh(multi(2,[aaaaaaaa][aaaaaaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaaaaaaa][aaaaaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))"); // Double key origin descriptor
- CheckUnparsable("wsh(multi(2,[aaaagaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaagaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))"); // Non hex fingerprint
- CheckUnparsable("wsh(multi(2,[aaaaaaaa],xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaaaaaaa],xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))"); // No public key with origin
- CheckUnparsable("wsh(multi(2,[aaaaaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaaaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))"); // Too short fingerprint
- CheckUnparsable("wsh(multi(2,[aaaaaaaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaaaaaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))"); // Too long fingerprint
+ CheckUnparsable("sh(multi(16,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9))","sh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232))", "P2SH script is too large, 547 bytes is larger than 520 bytes"); // P2SH does not fit 16 compressed pubkeys in a redeemscript
+ CheckUnparsable("wsh(multi(2,[aaaaaaaa][aaaaaaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaaaaaaa][aaaaaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", "Multiple ']' characters found for a single pubkey"); // Double key origin descriptor
+ CheckUnparsable("wsh(multi(2,[aaaagaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaagaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", "Fingerprint 'aaagaaaa' is not hex"); // Non hex fingerprint
+ CheckUnparsable("wsh(multi(2,[aaaaaaaa],xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaaaaaaa],xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", "No key provided"); // No public key with origin
+ CheckUnparsable("wsh(multi(2,[aaaaaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaaaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", "Fingerprint is not 4 bytes (7 characters instead of 8 characters)"); // Too short fingerprint
+ CheckUnparsable("wsh(multi(2,[aaaaaaaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaaaaaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", "Fingerprint is not 4 bytes (9 characters instead of 8 characters)"); // Too long fingerprint
+ CheckUnparsable("multi(a,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(a,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "Multi threshold 'a' is not valid"); // Invalid threshold
+ CheckUnparsable("multi(0,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(0,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "Multisig threshold cannot be 0, must be at least 1"); // Threshold of 0
+ CheckUnparsable("multi(3,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(3,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "Multisig threshold cannot be larger than the number of keys; threshold is 3 but only 2 keys specified"); // Threshold larger than number of keys
+ CheckUnparsable("multi(3,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f)", "multi(3,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8)", "Cannot have 4 pubkeys in bare multisig; only at most 3 pubkeys"); // Threshold larger than number of keys
+ CheckUnparsable("sh(multi(16,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))","sh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "Cannot have 17 keys in multisig; must have between 1 and 16 keys, inclusive"); // Cannot have more than 16 keys in a multisig
// Check for invalid nesting of structures
- CheckUnparsable("sh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "sh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)"); // P2SH needs a script, not a key
- CheckUnparsable("sh(combo(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(combo(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))"); // Old must be top level
- CheckUnparsable("wsh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "wsh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)"); // P2WSH needs a script, not a key
- CheckUnparsable("wsh(wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "wsh(wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))"); // Cannot embed witness inside witness
- CheckUnparsable("wsh(sh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "wsh(sh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))"); // Cannot embed P2SH inside P2WSH
- CheckUnparsable("sh(sh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "sh(sh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))"); // Cannot embed P2SH inside P2SH
- CheckUnparsable("wsh(wsh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "wsh(wsh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))"); // Cannot embed P2WSH inside P2WSH
+ CheckUnparsable("sh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "sh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "A function is needed within P2SH"); // P2SH needs a script, not a key
+ CheckUnparsable("sh(combo(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(combo(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "Cannot have combo in non-top level"); // Old must be top level
+ CheckUnparsable("wsh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "wsh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "A function is needed within P2WSH"); // P2WSH needs a script, not a key
+ CheckUnparsable("wsh(wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "wsh(wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "Cannot have wpkh within wsh"); // Cannot embed witness inside witness
+ CheckUnparsable("wsh(sh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "wsh(sh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", "Cannot have sh in non-top level"); // Cannot embed P2SH inside P2WSH
+ CheckUnparsable("sh(sh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "sh(sh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", "Cannot have sh in non-top level"); // Cannot embed P2SH inside P2SH
+ CheckUnparsable("wsh(wsh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "wsh(wsh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", "Cannot have wsh within wsh"); // Cannot embed P2WSH inside P2WSH
// Checksums
Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxfy", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5t", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, {{0x8000006FUL,222},{0}});
Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, {{0x8000006FUL,222},{0}});
- CheckUnparsable("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#"); // Empty checksum
- CheckUnparsable("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxfyq", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5tq"); // Too long checksum
- CheckUnparsable("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxf", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5"); // Too short checksum
- CheckUnparsable("sh(multi(3,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxfy", "sh(multi(3,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5t"); // Error in payload
- CheckUnparsable("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggssrxfy", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjq09x4t"); // Error in checksum
+ CheckUnparsable("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#", "Expected 8 character checksum, not 0 characters"); // Empty checksum
+ CheckUnparsable("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxfyq", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5tq", "Expected 8 character checksum, not 9 characters"); // Too long checksum
+ CheckUnparsable("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxf", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5", "Expected 8 character checksum, not 7 characters"); // Too short checksum
+ CheckUnparsable("sh(multi(3,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxfy", "sh(multi(3,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5t", "Provided checksum 'tjg09x5t' does not match computed checksum 'd4x0uxyv'"); // Error in payload
+ CheckUnparsable("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggssrxfy", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjq09x4t", "Provided checksum 'tjq09x4t' does not match computed checksum 'tjg09x5t'"); // Error in checksum
+ CheckUnparsable("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))##ggssrxfy", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))##tjq09x4t", "Multiple '#' symbols"); // Error in checksum
+
+ // Addr and raw tests
+ CheckUnparsable("", "addr(asdf)", "Address is not valid"); // Invalid address
+ CheckUnparsable("", "raw(asdf)", "Raw script is not hex"); // Invalid script
+ CheckUnparsable("", "raw(Ü)#00000000", "Invalid characters in payload"); // Invalid chars
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp
index 8a42344642..77304fe918 100644
--- a/src/test/getarg_tests.cpp
+++ b/src/test/getarg_tests.cpp
@@ -7,6 +7,7 @@
#include <test/setup_common.h>
#include <string>
+#include <utility>
#include <vector>
#include <boost/algorithm/string.hpp>
@@ -32,17 +33,18 @@ static void ResetArgs(const std::string& strArg)
BOOST_CHECK(gArgs.ParseParameters(vecChar.size(), vecChar.data(), error));
}
-static void SetupArgs(const std::vector<std::string>& args)
+static void SetupArgs(const std::vector<std::pair<std::string, unsigned int>>& args)
{
gArgs.ClearArgs();
- for (const std::string& arg : args) {
- gArgs.AddArg(arg, "", false, OptionsCategory::OPTIONS);
+ for (const auto& arg : args) {
+ gArgs.AddArg(arg.first, "", arg.second, OptionsCategory::OPTIONS);
}
}
BOOST_AUTO_TEST_CASE(boolarg)
{
- SetupArgs({"-foo"});
+ const auto foo = std::make_pair("-foo", ArgsManager::ALLOW_BOOL);
+ SetupArgs({foo});
ResetArgs("-foo");
BOOST_CHECK(gArgs.GetBoolArg("-foo", false));
BOOST_CHECK(gArgs.GetBoolArg("-foo", true));
@@ -95,7 +97,9 @@ BOOST_AUTO_TEST_CASE(boolarg)
BOOST_AUTO_TEST_CASE(stringarg)
{
- SetupArgs({"-foo", "-bar"});
+ const auto foo = std::make_pair("-foo", ArgsManager::ALLOW_STRING);
+ const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_STRING);
+ SetupArgs({foo, bar});
ResetArgs("");
BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", ""), "");
BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", "eleven"), "eleven");
@@ -120,7 +124,9 @@ BOOST_AUTO_TEST_CASE(stringarg)
BOOST_AUTO_TEST_CASE(intarg)
{
- SetupArgs({"-foo", "-bar"});
+ const auto foo = std::make_pair("-foo", ArgsManager::ALLOW_INT);
+ const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_INT);
+ SetupArgs({foo, bar});
ResetArgs("");
BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", 11), 11);
BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", 0), 0);
@@ -140,7 +146,9 @@ BOOST_AUTO_TEST_CASE(intarg)
BOOST_AUTO_TEST_CASE(doubledash)
{
- SetupArgs({"-foo", "-bar"});
+ const auto foo = std::make_pair("-foo", ArgsManager::ALLOW_ANY);
+ const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_ANY);
+ SetupArgs({foo, bar});
ResetArgs("--foo");
BOOST_CHECK_EQUAL(gArgs.GetBoolArg("-foo", false), true);
@@ -151,7 +159,9 @@ BOOST_AUTO_TEST_CASE(doubledash)
BOOST_AUTO_TEST_CASE(boolargno)
{
- SetupArgs({"-foo", "-bar"});
+ const auto foo = std::make_pair("-foo", ArgsManager::ALLOW_BOOL);
+ const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_BOOL);
+ SetupArgs({foo, bar});
ResetArgs("-nofoo");
BOOST_CHECK(!gArgs.GetBoolArg("-foo", true));
BOOST_CHECK(!gArgs.GetBoolArg("-foo", false));
diff --git a/src/test/merkle_tests.cpp b/src/test/merkle_tests.cpp
index 1684258c9f..dc38a1a818 100644
--- a/src/test/merkle_tests.cpp
+++ b/src/test/merkle_tests.cpp
@@ -249,4 +249,104 @@ BOOST_AUTO_TEST_CASE(merkle_test)
}
}
+
+BOOST_AUTO_TEST_CASE(merkle_test_empty_block)
+{
+ bool mutated = false;
+ CBlock block;
+ uint256 root = BlockMerkleRoot(block, &mutated);
+
+ BOOST_CHECK_EQUAL(root.IsNull(), true);
+ BOOST_CHECK_EQUAL(mutated, false);
+}
+
+BOOST_AUTO_TEST_CASE(merkle_test_oneTx_block)
+{
+ bool mutated = false;
+ CBlock block;
+
+ block.vtx.resize(1);
+ CMutableTransaction mtx;
+ mtx.nLockTime = 0;
+ block.vtx[0] = MakeTransactionRef(std::move(mtx));
+ uint256 root = BlockMerkleRoot(block, &mutated);
+ BOOST_CHECK_EQUAL(root, block.vtx[0]->GetHash());
+ BOOST_CHECK_EQUAL(mutated, false);
+}
+
+BOOST_AUTO_TEST_CASE(merkle_test_OddTxWithRepeatedLastTx_block)
+{
+ bool mutated;
+ CBlock block, blockWithRepeatedLastTx;
+
+ block.vtx.resize(3);
+
+ for (std::size_t pos = 0; pos < block.vtx.size(); pos++) {
+ CMutableTransaction mtx;
+ mtx.nLockTime = pos;
+ block.vtx[pos] = MakeTransactionRef(std::move(mtx));
+ }
+
+ blockWithRepeatedLastTx = block;
+ blockWithRepeatedLastTx.vtx.push_back(blockWithRepeatedLastTx.vtx.back());
+
+ uint256 rootofBlock = BlockMerkleRoot(block, &mutated);
+ BOOST_CHECK_EQUAL(mutated, false);
+
+ uint256 rootofBlockWithRepeatedLastTx = BlockMerkleRoot(blockWithRepeatedLastTx, &mutated);
+ BOOST_CHECK_EQUAL(rootofBlock, rootofBlockWithRepeatedLastTx);
+ BOOST_CHECK_EQUAL(mutated, true);
+}
+
+BOOST_AUTO_TEST_CASE(merkle_test_LeftSubtreeRightSubtree)
+{
+ CBlock block, leftSubtreeBlock, rightSubtreeBlock;
+
+ block.vtx.resize(4);
+ std::size_t pos;
+ for (pos = 0; pos < block.vtx.size(); pos++) {
+ CMutableTransaction mtx;
+ mtx.nLockTime = pos;
+ block.vtx[pos] = MakeTransactionRef(std::move(mtx));
+ }
+
+ for (pos = 0; pos < block.vtx.size() / 2; pos++)
+ leftSubtreeBlock.vtx.push_back(block.vtx[pos]);
+
+ for (pos = block.vtx.size() / 2; pos < block.vtx.size(); pos++)
+ rightSubtreeBlock.vtx.push_back(block.vtx[pos]);
+
+ uint256 root = BlockMerkleRoot(block);
+ uint256 rootOfLeftSubtree = BlockMerkleRoot(leftSubtreeBlock);
+ uint256 rootOfRightSubtree = BlockMerkleRoot(rightSubtreeBlock);
+ std::vector<uint256> leftRight;
+ leftRight.push_back(rootOfLeftSubtree);
+ leftRight.push_back(rootOfRightSubtree);
+ uint256 rootOfLR = ComputeMerkleRoot(leftRight);
+
+ BOOST_CHECK_EQUAL(root, rootOfLR);
+}
+
+BOOST_AUTO_TEST_CASE(merkle_test_BlockWitness)
+{
+ CBlock block;
+
+ block.vtx.resize(2);
+ for (std::size_t pos = 0; pos < block.vtx.size(); pos++) {
+ CMutableTransaction mtx;
+ mtx.nLockTime = pos;
+ block.vtx[pos] = MakeTransactionRef(std::move(mtx));
+ }
+
+ uint256 blockWitness = BlockWitnessMerkleRoot(block);
+
+ std::vector<uint256> hashes;
+ hashes.resize(block.vtx.size());
+ hashes[0].SetNull();
+ hashes[1] = block.vtx[1]->GetHash();
+
+ uint256 merkelRootofHashes = ComputeMerkleRoot(hashes);
+
+ BOOST_CHECK_EQUAL(merkelRootofHashes, blockWitness);
+}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index 05d7f76983..c9661b730d 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -372,7 +372,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
CBlockIndex* prev = ::ChainActive().Tip();
CBlockIndex* next = new CBlockIndex();
next->phashBlock = new uint256(InsecureRand256());
- pcoinsTip->SetBestBlock(next->GetBlockHash());
+ ::ChainstateActive().CoinsTip().SetBestBlock(next->GetBlockHash());
next->pprev = prev;
next->nHeight = prev->nHeight + 1;
next->BuildSkip();
@@ -384,7 +384,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
CBlockIndex* prev = ::ChainActive().Tip();
CBlockIndex* next = new CBlockIndex();
next->phashBlock = new uint256(InsecureRand256());
- pcoinsTip->SetBestBlock(next->GetBlockHash());
+ ::ChainstateActive().CoinsTip().SetBestBlock(next->GetBlockHash());
next->pprev = prev;
next->nHeight = prev->nHeight + 1;
next->BuildSkip();
@@ -414,7 +414,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
while (::ChainActive().Tip()->nHeight > nHeight) {
CBlockIndex* del = ::ChainActive().Tip();
::ChainActive().SetTip(del->pprev);
- pcoinsTip->SetBestBlock(del->pprev->GetBlockHash());
+ ::ChainstateActive().CoinsTip().SetBestBlock(del->pprev->GetBlockHash());
delete del->phashBlock;
delete del;
}
diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp
index 86c0cecbf1..a3d0831624 100644
--- a/src/test/netbase_tests.cpp
+++ b/src/test/netbase_tests.cpp
@@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <netbase.h>
+#include <net_permissions.h>
#include <test/setup_common.h>
#include <util/strencodings.h>
@@ -321,4 +322,82 @@ BOOST_AUTO_TEST_CASE(netbase_parsenetwork)
BOOST_CHECK_EQUAL(ParseNetwork(""), NET_UNROUTABLE);
}
+BOOST_AUTO_TEST_CASE(netpermissions_test)
+{
+ std::string error;
+ NetWhitebindPermissions whitebindPermissions;
+ NetWhitelistPermissions whitelistPermissions;
+
+ // Detect invalid white bind
+ BOOST_CHECK(!NetWhitebindPermissions::TryParse("", whitebindPermissions, error));
+ BOOST_CHECK(error.find("Cannot resolve -whitebind address") != std::string::npos);
+ BOOST_CHECK(!NetWhitebindPermissions::TryParse("127.0.0.1", whitebindPermissions, error));
+ BOOST_CHECK(error.find("Need to specify a port with -whitebind") != std::string::npos);
+ BOOST_CHECK(!NetWhitebindPermissions::TryParse("", whitebindPermissions, error));
+
+ // If no permission flags, assume backward compatibility
+ BOOST_CHECK(NetWhitebindPermissions::TryParse("1.2.3.4:32", whitebindPermissions, error));
+ BOOST_CHECK(error.empty());
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_ISIMPLICIT);
+ BOOST_CHECK(NetPermissions::HasFlag(whitebindPermissions.m_flags, PF_ISIMPLICIT));
+ NetPermissions::ClearFlag(whitebindPermissions.m_flags, PF_ISIMPLICIT);
+ BOOST_CHECK(!NetPermissions::HasFlag(whitebindPermissions.m_flags, PF_ISIMPLICIT));
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_NONE);
+ NetPermissions::AddFlag(whitebindPermissions.m_flags, PF_ISIMPLICIT);
+ BOOST_CHECK(NetPermissions::HasFlag(whitebindPermissions.m_flags, PF_ISIMPLICIT));
+
+ // Can set one permission
+ BOOST_CHECK(NetWhitebindPermissions::TryParse("bloom@1.2.3.4:32", whitebindPermissions, error));
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_BLOOMFILTER);
+ BOOST_CHECK(NetWhitebindPermissions::TryParse("@1.2.3.4:32", whitebindPermissions, error));
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_NONE);
+
+ // Happy path, can parse flags
+ BOOST_CHECK(NetWhitebindPermissions::TryParse("bloom,forcerelay@1.2.3.4:32", whitebindPermissions, error));
+ // forcerelay should also activate the relay permission
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_BLOOMFILTER | PF_FORCERELAY | PF_RELAY);
+ BOOST_CHECK(NetWhitebindPermissions::TryParse("bloom,relay,noban@1.2.3.4:32", whitebindPermissions, error));
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_BLOOMFILTER | PF_RELAY | PF_NOBAN);
+ BOOST_CHECK(NetWhitebindPermissions::TryParse("bloom,forcerelay,noban@1.2.3.4:32", whitebindPermissions, error));
+ BOOST_CHECK(NetWhitebindPermissions::TryParse("all@1.2.3.4:32", whitebindPermissions, error));
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_ALL);
+
+ // Allow dups
+ BOOST_CHECK(NetWhitebindPermissions::TryParse("bloom,relay,noban,noban@1.2.3.4:32", whitebindPermissions, error));
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_BLOOMFILTER | PF_RELAY | PF_NOBAN);
+
+ // Allow empty
+ BOOST_CHECK(NetWhitebindPermissions::TryParse("bloom,relay,,noban@1.2.3.4:32", whitebindPermissions, error));
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_BLOOMFILTER | PF_RELAY | PF_NOBAN);
+ BOOST_CHECK(NetWhitebindPermissions::TryParse(",@1.2.3.4:32", whitebindPermissions, error));
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_NONE);
+ BOOST_CHECK(NetWhitebindPermissions::TryParse(",,@1.2.3.4:32", whitebindPermissions, error));
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_NONE);
+
+ // Detect invalid flag
+ BOOST_CHECK(!NetWhitebindPermissions::TryParse("bloom,forcerelay,oopsie@1.2.3.4:32", whitebindPermissions, error));
+ BOOST_CHECK(error.find("Invalid P2P permission") != std::string::npos);
+
+ // Check whitelist error
+ BOOST_CHECK(!NetWhitelistPermissions::TryParse("bloom,forcerelay,noban@1.2.3.4:32", whitelistPermissions, error));
+ BOOST_CHECK(error.find("Invalid netmask specified in -whitelist") != std::string::npos);
+
+ // Happy path for whitelist parsing
+ BOOST_CHECK(NetWhitelistPermissions::TryParse("noban@1.2.3.4", whitelistPermissions, error));
+ BOOST_CHECK_EQUAL(whitelistPermissions.m_flags, PF_NOBAN);
+ BOOST_CHECK(NetWhitelistPermissions::TryParse("bloom,forcerelay,noban,relay@1.2.3.4/32", whitelistPermissions, error));
+ BOOST_CHECK_EQUAL(whitelistPermissions.m_flags, PF_BLOOMFILTER | PF_FORCERELAY | PF_NOBAN | PF_RELAY);
+ BOOST_CHECK(error.empty());
+ BOOST_CHECK_EQUAL(whitelistPermissions.m_subnet.ToString(), "1.2.3.4/32");
+ BOOST_CHECK(NetWhitelistPermissions::TryParse("bloom,forcerelay,noban,relay,mempool@1.2.3.4/32", whitelistPermissions, error));
+
+ const auto strings = NetPermissions::ToStrings(PF_ALL);
+ BOOST_CHECK_EQUAL(strings.size(), 5);
+ BOOST_CHECK(std::find(strings.begin(), strings.end(), "bloomfilter") != strings.end());
+ BOOST_CHECK(std::find(strings.begin(), strings.end(), "forcerelay") != strings.end());
+ BOOST_CHECK(std::find(strings.begin(), strings.end(), "relay") != strings.end());
+ BOOST_CHECK(std::find(strings.begin(), strings.end(), "noban") != strings.end());
+ BOOST_CHECK(std::find(strings.begin(), strings.end(), "mempool") != strings.end());
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/serialize_tests.cpp b/src/test/serialize_tests.cpp
index 8a8620938e..b90be15fba 100644
--- a/src/test/serialize_tests.cpp
+++ b/src/test/serialize_tests.cpp
@@ -258,6 +258,14 @@ static bool isCanonicalException(const std::ios_base::failure& ex)
return strcmp(expectedException.what(), ex.what()) == 0;
}
+BOOST_AUTO_TEST_CASE(vector_bool)
+{
+ std::vector<uint8_t> vec1{1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1};
+ std::vector<bool> vec2{1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1};
+
+ BOOST_CHECK(vec1 == std::vector<uint8_t>(vec2.begin(), vec2.end()));
+ BOOST_CHECK(SerializeHash(vec1) == SerializeHash(vec2));
+}
BOOST_AUTO_TEST_CASE(noncanonical)
{
diff --git a/src/test/setup_common.cpp b/src/test/setup_common.cpp
index de877fd167..bbdf1ef830 100644
--- a/src/test/setup_common.cpp
+++ b/src/test/setup_common.cpp
@@ -85,8 +85,12 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
mempool.setSanityCheck(1.0);
pblocktree.reset(new CBlockTreeDB(1 << 20, true));
- pcoinsdbview.reset(new CCoinsViewDB(1 << 23, true));
- pcoinsTip.reset(new CCoinsViewCache(pcoinsdbview.get()));
+ g_chainstate = MakeUnique<CChainState>();
+ ::ChainstateActive().InitCoinsDB(
+ /* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
+ assert(!::ChainstateActive().CanFlushToDisk());
+ ::ChainstateActive().InitCoinsCache();
+ assert(::ChainstateActive().CanFlushToDisk());
if (!LoadGenesisBlock(chainparams)) {
throw std::runtime_error("LoadGenesisBlock failed.");
}
@@ -113,8 +117,7 @@ TestingSetup::~TestingSetup()
g_connman.reset();
g_banman.reset();
UnloadBlockIndex();
- pcoinsTip.reset();
- pcoinsdbview.reset();
+ g_chainstate.reset();
pblocktree.reset();
}
@@ -122,7 +125,7 @@ TestChain100Setup::TestChain100Setup() : TestingSetup(CBaseChainParams::REGTEST)
{
// CreateAndProcessBlock() does not support building SegWit blocks, so don't activate in these tests.
// TODO: fix the code to support SegWit blocks.
- gArgs.ForceSetArg("-vbparams", strprintf("segwit:0:%d", (int64_t)Consensus::BIP9Deployment::NO_TIMEOUT));
+ gArgs.ForceSetArg("-segwitheight", "432");
SelectParams(CBaseChainParams::REGTEST);
// Generate a 100-block chain:
diff --git a/src/test/timedata_tests.cpp b/src/test/timedata_tests.cpp
index b4c0e6a0f4..7b00222ab7 100644
--- a/src/test/timedata_tests.cpp
+++ b/src/test/timedata_tests.cpp
@@ -2,8 +2,14 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
//
-#include <timedata.h>
+
+#include <netaddress.h>
+#include <noui.h>
#include <test/setup_common.h>
+#include <timedata.h>
+#include <warnings.h>
+
+#include <string>
#include <boost/test/unit_test.hpp>
@@ -34,4 +40,61 @@ BOOST_AUTO_TEST_CASE(util_MedianFilter)
BOOST_CHECK_EQUAL(filter.median(), 7);
}
+static void MultiAddTimeData(int n, int64_t offset)
+{
+ static int cnt = 0;
+ for (int i = 0; i < n; ++i) {
+ CNetAddr addr;
+ addr.SetInternal(std::to_string(++cnt));
+ AddTimeData(addr, offset);
+ }
+}
+
+
+BOOST_AUTO_TEST_CASE(addtimedata)
+{
+ BOOST_CHECK_EQUAL(GetTimeOffset(), 0);
+
+ //Part 1: Add large offsets to test a warning message that our clock may be wrong.
+ MultiAddTimeData(3, DEFAULT_MAX_TIME_ADJUSTMENT + 1);
+ // Filter size is 1 + 3 = 4: It is always initialized with a single element (offset 0)
+
+ noui_suppress();
+ MultiAddTimeData(1, DEFAULT_MAX_TIME_ADJUSTMENT + 1); //filter size 5
+ noui_reconnect();
+
+ BOOST_CHECK(GetWarnings("gui").find("clock is wrong") != std::string::npos);
+
+ // nTimeOffset is not changed if the median of offsets exceeds DEFAULT_MAX_TIME_ADJUSTMENT
+ BOOST_CHECK_EQUAL(GetTimeOffset(), 0);
+
+ // Part 2: Test positive and negative medians by adding more offsets
+ MultiAddTimeData(4, 100); // filter size 9
+ BOOST_CHECK_EQUAL(GetTimeOffset(), 100);
+ MultiAddTimeData(10, -100); //filter size 19
+ BOOST_CHECK_EQUAL(GetTimeOffset(), -100);
+
+ // Part 3: Test behaviour when filter has reached maximum number of offsets
+ const int MAX_SAMPLES = 200;
+ int nfill = (MAX_SAMPLES - 3 - 19) / 2; //89
+ MultiAddTimeData(nfill, 100);
+ MultiAddTimeData(nfill, -100); //filter size MAX_SAMPLES - 3
+ BOOST_CHECK_EQUAL(GetTimeOffset(), -100);
+
+ MultiAddTimeData(2, 100);
+ //filter size MAX_SAMPLES -1, median is the initial 0 offset
+ //since we added same number of positive/negative offsets
+
+ BOOST_CHECK_EQUAL(GetTimeOffset(), 0);
+
+ // After the number of offsets has reached MAX_SAMPLES -1 (=199), nTimeOffset will never change
+ // because it is only updated when the number of elements in the filter becomes odd. It was decided
+ // not to fix this because it prevents possible attacks. See the comment in AddTimeData() or issue #4521
+ // for a more detailed explanation.
+ MultiAddTimeData(2, 100); // filter median is 100 now, but nTimeOffset will not change
+ BOOST_CHECK_EQUAL(GetTimeOffset(), 0);
+
+ // We want this test to end with nTimeOffset==0, otherwise subsequent tests of the suite will fail.
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp
index f99a3748c9..193858cca9 100644
--- a/src/test/txvalidationcache_tests.cpp
+++ b/src/test/txvalidationcache_tests.cpp
@@ -13,7 +13,7 @@
#include <boost/test/unit_test.hpp>
-bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks);
+bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks);
BOOST_AUTO_TEST_SUITE(tx_validationcache_tests)
@@ -97,7 +97,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
BOOST_CHECK_EQUAL(mempool.size(), 0U);
}
-// Run CheckInputs (using pcoinsTip) on the given transaction, for all script
+// Run CheckInputs (using CoinsTip()) on the given transaction, for all script
// flags. Test that CheckInputs passes for all flags that don't overlap with
// the failing_flags argument, but otherwise fails.
// CHECKLOCKTIMEVERIFY and CHECKSEQUENCEVERIFY (and future NOP codes that may
@@ -125,7 +125,7 @@ static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t fail
// WITNESS requires P2SH
test_flags |= SCRIPT_VERIFY_P2SH;
}
- bool ret = CheckInputs(tx, state, pcoinsTip.get(), true, test_flags, true, add_to_cache, txdata, nullptr);
+ bool ret = CheckInputs(tx, state, &::ChainstateActive().CoinsTip(), test_flags, true, add_to_cache, txdata, nullptr);
// CheckInputs should succeed iff test_flags doesn't intersect with
// failing_flags
bool expected_return_value = !(test_flags & failing_flags);
@@ -135,13 +135,13 @@ static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t fail
if (ret && add_to_cache) {
// Check that we get a cache hit if the tx was valid
std::vector<CScriptCheck> scriptchecks;
- BOOST_CHECK(CheckInputs(tx, state, pcoinsTip.get(), true, test_flags, true, add_to_cache, txdata, &scriptchecks));
+ BOOST_CHECK(CheckInputs(tx, state, &::ChainstateActive().CoinsTip(), test_flags, true, add_to_cache, txdata, &scriptchecks));
BOOST_CHECK(scriptchecks.empty());
} else {
// Check that we get script executions to check, if the transaction
// was invalid, or we didn't add to cache.
std::vector<CScriptCheck> scriptchecks;
- BOOST_CHECK(CheckInputs(tx, state, pcoinsTip.get(), true, test_flags, true, add_to_cache, txdata, &scriptchecks));
+ BOOST_CHECK(CheckInputs(tx, state, &::ChainstateActive().CoinsTip(), test_flags, true, add_to_cache, txdata, &scriptchecks));
BOOST_CHECK_EQUAL(scriptchecks.size(), tx.vin.size());
}
}
@@ -204,13 +204,13 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
CValidationState state;
PrecomputedTransactionData ptd_spend_tx(spend_tx);
- BOOST_CHECK(!CheckInputs(CTransaction(spend_tx), state, pcoinsTip.get(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, nullptr));
+ BOOST_CHECK(!CheckInputs(CTransaction(spend_tx), state, &::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, nullptr));
// If we call again asking for scriptchecks (as happens in
// ConnectBlock), we should add a script check object for this -- we're
// not caching invalidity (if that changes, delete this test case).
std::vector<CScriptCheck> scriptchecks;
- BOOST_CHECK(CheckInputs(CTransaction(spend_tx), state, pcoinsTip.get(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, &scriptchecks));
+ BOOST_CHECK(CheckInputs(CTransaction(spend_tx), state, &::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, &scriptchecks));
BOOST_CHECK_EQUAL(scriptchecks.size(), 1U);
// Test that CheckInputs returns true iff DERSIG-enforcing flags are
@@ -227,7 +227,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
block = CreateAndProcessBlock({spend_tx}, p2pk_scriptPubKey);
LOCK(cs_main);
BOOST_CHECK(::ChainActive().Tip()->GetBlockHash() == block.GetHash());
- BOOST_CHECK(pcoinsTip->GetBestBlock() == block.GetHash());
+ BOOST_CHECK(::ChainstateActive().CoinsTip().GetBestBlock() == block.GetHash());
// Test P2SH: construct a transaction that is valid without P2SH, and
// then test validity with P2SH.
@@ -272,7 +272,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 100;
CValidationState state;
PrecomputedTransactionData txdata(invalid_with_cltv_tx);
- BOOST_CHECK(CheckInputs(CTransaction(invalid_with_cltv_tx), state, pcoinsTip.get(), true, SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true, txdata, nullptr));
+ BOOST_CHECK(CheckInputs(CTransaction(invalid_with_cltv_tx), state, ::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true, txdata, nullptr));
}
// TEST CHECKSEQUENCEVERIFY
@@ -300,7 +300,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 100;
CValidationState state;
PrecomputedTransactionData txdata(invalid_with_csv_tx);
- BOOST_CHECK(CheckInputs(CTransaction(invalid_with_csv_tx), state, pcoinsTip.get(), true, SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true, txdata, nullptr));
+ BOOST_CHECK(CheckInputs(CTransaction(invalid_with_csv_tx), state, &::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true, txdata, nullptr));
}
// TODO: add tests for remaining script flags
@@ -362,12 +362,12 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
CValidationState state;
PrecomputedTransactionData txdata(tx);
// This transaction is now invalid under segwit, because of the second input.
- BOOST_CHECK(!CheckInputs(CTransaction(tx), state, pcoinsTip.get(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, nullptr));
+ BOOST_CHECK(!CheckInputs(CTransaction(tx), state, &::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, nullptr));
std::vector<CScriptCheck> scriptchecks;
// Make sure this transaction was not cached (ie because the first
// input was valid)
- BOOST_CHECK(CheckInputs(CTransaction(tx), state, pcoinsTip.get(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, &scriptchecks));
+ BOOST_CHECK(CheckInputs(CTransaction(tx), state, &::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, &scriptchecks));
// Should get 2 script checks back -- caching is on a whole-transaction basis.
BOOST_CHECK_EQUAL(scriptchecks.size(), 2U);
}
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index 9960573b33..d0cd4b0a03 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -6,14 +6,16 @@
#include <clientversion.h>
#include <sync.h>
+#include <test/setup_common.h>
#include <test/util.h>
-#include <util/strencodings.h>
#include <util/moneystr.h>
+#include <util/strencodings.h>
+#include <util/string.h>
#include <util/time.h>
-#include <test/setup_common.h>
#include <stdint.h>
#include <thread>
+#include <utility>
#include <vector>
#ifndef WIN32
#include <signal.h>
@@ -122,6 +124,19 @@ BOOST_AUTO_TEST_CASE(util_HexStr)
);
}
+BOOST_AUTO_TEST_CASE(util_Join)
+{
+ // Normal version
+ BOOST_CHECK_EQUAL(Join({}, ", "), "");
+ BOOST_CHECK_EQUAL(Join({"foo"}, ", "), "foo");
+ BOOST_CHECK_EQUAL(Join({"foo", "bar"}, ", "), "foo, bar");
+
+ // Version with unary operator
+ const auto op_upper = [](const std::string& s) { return ToUpper(s); };
+ BOOST_CHECK_EQUAL(Join<std::string>({}, ", ", op_upper), "");
+ BOOST_CHECK_EQUAL(Join<std::string>({"foo"}, ", ", op_upper), "FOO");
+ BOOST_CHECK_EQUAL(Join<std::string>({"foo", "bar"}, ", ", op_upper), "FOO, BAR");
+}
BOOST_AUTO_TEST_CASE(util_FormatISO8601DateTime)
{
@@ -154,10 +169,10 @@ struct TestArgsManager : public ArgsManager
LOCK(cs_args);
m_network_only_args.insert(arg);
}
- void SetupArgs(int argv, const char* args[])
+ void SetupArgs(const std::vector<std::pair<std::string, unsigned int>>& args)
{
- for (int i = 0; i < argv; ++i) {
- AddArg(args[i], "", false, OptionsCategory::OPTIONS);
+ for (const auto& arg : args) {
+ AddArg(arg.first, "", arg.second, OptionsCategory::OPTIONS);
}
}
using ArgsManager::ReadConfigStream;
@@ -168,11 +183,15 @@ struct TestArgsManager : public ArgsManager
BOOST_AUTO_TEST_CASE(util_ParseParameters)
{
TestArgsManager testArgs;
- const char* avail_args[] = {"-a", "-b", "-ccc", "-d"};
+ const auto a = std::make_pair("-a", ArgsManager::ALLOW_ANY);
+ const auto b = std::make_pair("-b", ArgsManager::ALLOW_ANY);
+ const auto ccc = std::make_pair("-ccc", ArgsManager::ALLOW_ANY);
+ const auto d = std::make_pair("-d", ArgsManager::ALLOW_ANY);
+
const char *argv_test[] = {"-ignored", "-a", "-b", "-ccc=argument", "-ccc=multiple", "f", "-d=e"};
std::string error;
- testArgs.SetupArgs(4, avail_args);
+ testArgs.SetupArgs({a, b, ccc, d});
BOOST_CHECK(testArgs.ParseParameters(0, (char**)argv_test, error));
BOOST_CHECK(testArgs.GetOverrideArgs().empty() && testArgs.GetConfigArgs().empty());
@@ -200,11 +219,17 @@ BOOST_AUTO_TEST_CASE(util_ParseParameters)
BOOST_AUTO_TEST_CASE(util_GetBoolArg)
{
TestArgsManager testArgs;
- const char* avail_args[] = {"-a", "-b", "-c", "-d", "-e", "-f"};
+ const auto a = std::make_pair("-a", ArgsManager::ALLOW_BOOL);
+ const auto b = std::make_pair("-b", ArgsManager::ALLOW_BOOL);
+ const auto c = std::make_pair("-c", ArgsManager::ALLOW_BOOL);
+ const auto d = std::make_pair("-d", ArgsManager::ALLOW_BOOL);
+ const auto e = std::make_pair("-e", ArgsManager::ALLOW_BOOL);
+ const auto f = std::make_pair("-f", ArgsManager::ALLOW_BOOL);
+
const char *argv_test[] = {
"ignored", "-a", "-nob", "-c=0", "-d=1", "-e=false", "-f=true"};
std::string error;
- testArgs.SetupArgs(6, avail_args);
+ testArgs.SetupArgs({a, b, c, d, e, f});
BOOST_CHECK(testArgs.ParseParameters(7, (char**)argv_test, error));
// Each letter should be set.
@@ -237,9 +262,10 @@ BOOST_AUTO_TEST_CASE(util_GetBoolArgEdgeCases)
TestArgsManager testArgs;
// Params test
- const char* avail_args[] = {"-foo", "-bar"};
+ const auto foo = std::make_pair("-foo", ArgsManager::ALLOW_BOOL);
+ const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_BOOL);
const char *argv_test[] = {"ignored", "-nofoo", "-foo", "-nobar=0"};
- testArgs.SetupArgs(2, avail_args);
+ testArgs.SetupArgs({foo, bar});
std::string error;
BOOST_CHECK(testArgs.ParseParameters(4, (char**)argv_test, error));
@@ -308,8 +334,17 @@ BOOST_AUTO_TEST_CASE(util_ReadConfigStream)
"iii=2\n";
TestArgsManager test_args;
- const char* avail_args[] = {"-a", "-b", "-ccc", "-d", "-e", "-fff", "-ggg", "-h", "-i", "-iii"};
- test_args.SetupArgs(10, avail_args);
+ const auto a = std::make_pair("-a", ArgsManager::ALLOW_BOOL);
+ const auto b = std::make_pair("-b", ArgsManager::ALLOW_BOOL);
+ const auto ccc = std::make_pair("-ccc", ArgsManager::ALLOW_STRING);
+ const auto d = std::make_pair("-d", ArgsManager::ALLOW_STRING);
+ const auto e = std::make_pair("-e", ArgsManager::ALLOW_ANY);
+ const auto fff = std::make_pair("-fff", ArgsManager::ALLOW_BOOL);
+ const auto ggg = std::make_pair("-ggg", ArgsManager::ALLOW_BOOL);
+ const auto h = std::make_pair("-h", ArgsManager::ALLOW_BOOL);
+ const auto i = std::make_pair("-i", ArgsManager::ALLOW_BOOL);
+ const auto iii = std::make_pair("-iii", ArgsManager::ALLOW_INT);
+ test_args.SetupArgs({a, b, ccc, d, e, fff, ggg, h, i, iii});
test_args.ReadConfigString(str_config);
// expectation: a, b, ccc, d, fff, ggg, h, i end up in map
@@ -507,8 +542,9 @@ BOOST_AUTO_TEST_CASE(util_GetArg)
BOOST_AUTO_TEST_CASE(util_GetChainName)
{
TestArgsManager test_args;
- const char* avail_args[] = {"-testnet", "-regtest"};
- test_args.SetupArgs(2, avail_args);
+ const auto testnet = std::make_pair("-testnet", ArgsManager::ALLOW_BOOL);
+ const auto regtest = std::make_pair("-regtest", ArgsManager::ALLOW_BOOL);
+ test_args.SetupArgs({testnet, regtest});
const char* argv_testnet[] = {"cmd", "-testnet"};
const char* argv_regtest[] = {"cmd", "-regtest"};
@@ -682,7 +718,7 @@ BOOST_FIXTURE_TEST_CASE(util_ArgsMerge, ArgsMergeTestingSetup)
const std::string& name = net_specific ? "wallet" : "server";
const std::string key = "-" + name;
- parser.AddArg(key, name, false, OptionsCategory::OPTIONS);
+ parser.AddArg(key, name, ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
if (net_specific) parser.SetNetworkOnlyArg(key);
auto args = GetValues(arg_actions, section, name, "a");
@@ -809,8 +845,8 @@ BOOST_FIXTURE_TEST_CASE(util_ChainMerge, ChainMergeTestingSetup)
ForEachMergeSetup([&](const ActionList& arg_actions, const ActionList& conf_actions) {
TestArgsManager parser;
LOCK(parser.cs_args);
- parser.AddArg("-regtest", "regtest", false, OptionsCategory::OPTIONS);
- parser.AddArg("-testnet", "testnet", false, OptionsCategory::OPTIONS);
+ parser.AddArg("-regtest", "regtest", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ parser.AddArg("-testnet", "testnet", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
auto arg = [](Action action) { return action == ENABLE_TEST ? "-testnet=1" :
action == DISABLE_TEST ? "-testnet=0" :
@@ -877,7 +913,7 @@ BOOST_FIXTURE_TEST_CASE(util_ChainMerge, ChainMergeTestingSetup)
// Results file is formatted like:
//
// <input> || <output>
- BOOST_CHECK_EQUAL(out_sha_hex, "b284f4b4a15dd6bf8c06213a69a004b1960388e1d9917173927db52ac220927f");
+ BOOST_CHECK_EQUAL(out_sha_hex, "94b4ad55c8ac639a56b93e36f7e32e4c611fd7d7dd7b2be6a71707b1eadcaec7");
}
BOOST_AUTO_TEST_CASE(util_FormatMoney)
@@ -1510,17 +1546,9 @@ BOOST_AUTO_TEST_CASE(test_ToLower)
BOOST_CHECK_EQUAL(ToLower(0), 0);
BOOST_CHECK_EQUAL(ToLower('\xff'), '\xff');
- std::string testVector;
- Downcase(testVector);
- BOOST_CHECK_EQUAL(testVector, "");
-
- testVector = "#HODL";
- Downcase(testVector);
- BOOST_CHECK_EQUAL(testVector, "#hodl");
-
- testVector = "\x00\xfe\xff";
- Downcase(testVector);
- BOOST_CHECK_EQUAL(testVector, "\x00\xfe\xff");
+ BOOST_CHECK_EQUAL(ToLower(""), "");
+ BOOST_CHECK_EQUAL(ToLower("#HODL"), "#hodl");
+ BOOST_CHECK_EQUAL(ToLower("\x00\xfe\xff"), "\x00\xfe\xff");
}
BOOST_AUTO_TEST_CASE(test_ToUpper)
@@ -1531,6 +1559,10 @@ BOOST_AUTO_TEST_CASE(test_ToUpper)
BOOST_CHECK_EQUAL(ToUpper('{'), '{');
BOOST_CHECK_EQUAL(ToUpper(0), 0);
BOOST_CHECK_EQUAL(ToUpper('\xff'), '\xff');
+
+ BOOST_CHECK_EQUAL(ToUpper(""), "");
+ BOOST_CHECK_EQUAL(ToUpper("#hodl"), "#HODL");
+ BOOST_CHECK_EQUAL(ToUpper("\x00\xfe\xff"), "\x00\xfe\xff");
}
BOOST_AUTO_TEST_CASE(test_Capitalize)
diff --git a/src/txdb.cpp b/src/txdb.cpp
index df9851396e..18be07e6db 100644
--- a/src/txdb.cpp
+++ b/src/txdb.cpp
@@ -52,7 +52,7 @@ struct CoinEntry {
}
-CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / "chainstate", nCacheSize, fMemory, fWipe, true)
+CCoinsViewDB::CCoinsViewDB(fs::path ldb_path, size_t nCacheSize, bool fMemory, bool fWipe) : db(ldb_path, nCacheSize, fMemory, fWipe, true)
{
}
diff --git a/src/txdb.h b/src/txdb.h
index c4ece11503..140ce2c7ff 100644
--- a/src/txdb.h
+++ b/src/txdb.h
@@ -48,7 +48,10 @@ class CCoinsViewDB final : public CCoinsView
protected:
CDBWrapper db;
public:
- explicit CCoinsViewDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false);
+ /**
+ * @param[in] ldb_path Location in the filesystem where leveldb data will be stored.
+ */
+ explicit CCoinsViewDB(fs::path ldb_path, size_t nCacheSize, bool fMemory, bool fWipe);
bool GetCoin(const COutPoint &outpoint, Coin &coin) const override;
bool HaveCoin(const COutPoint &outpoint) const override;
diff --git a/src/txmempool.h b/src/txmempool.h
index 7169e80da2..6e5ba445d3 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -497,7 +497,7 @@ public:
*
* 1. Locking both `cs_main` and `mempool.cs` will give a view of mempool
* that is consistent with current chain tip (`::ChainActive()` and
- * `pcoinsTip`) and is fully populated. Fully populated means that if the
+ * `CoinsTip()`) and is fully populated. Fully populated means that if the
* current active chain is missing transactions that were present in a
* previously active chain, all the missing transactions will have been
* re-added to the mempool and should be present if they meet size and
diff --git a/src/util/error.cpp b/src/util/error.cpp
index 9edb7dc533..aa44ed3e3a 100644
--- a/src/util/error.cpp
+++ b/src/util/error.cpp
@@ -36,12 +36,17 @@ std::string TransactionErrorString(const TransactionError err)
assert(false);
}
-std::string AmountHighWarn(const std::string& optname)
+std::string ResolveErrMsg(const std::string& optname, const std::string& strBind)
{
- return strprintf(_("%s is set very high!").translated, optname);
+ return strprintf(_("Cannot resolve -%s address: '%s'").translated, optname, strBind);
}
-std::string AmountErrMsg(const char* const optname, const std::string& strValue)
+bilingual_str AmountHighWarn(const std::string& optname)
{
- return strprintf(_("Invalid amount for -%s=<amount>: '%s'").translated, optname, strValue);
+ return strprintf(_("%s is set very high!"), optname);
+}
+
+bilingual_str AmountErrMsg(const std::string& optname, const std::string& strValue)
+{
+ return strprintf(_("Invalid amount for -%s=<amount>: '%s'"), optname, strValue);
}
diff --git a/src/util/error.h b/src/util/error.h
index 0fd474b962..f540b0020d 100644
--- a/src/util/error.h
+++ b/src/util/error.h
@@ -10,13 +10,15 @@
* string functions. Types and functions defined here should not require any
* outside dependencies.
*
- * Error types defined here can be used in different parts of the bitcoin
+ * Error types defined here can be used in different parts of the
* codebase, to avoid the need to write boilerplate code catching and
* translating errors passed across wallet/node/rpc/gui code boundaries.
*/
#include <string>
+struct bilingual_str;
+
enum class TransactionError {
OK, //!< No error
MISSING_INPUTS,
@@ -32,8 +34,10 @@ enum class TransactionError {
std::string TransactionErrorString(const TransactionError error);
-std::string AmountHighWarn(const std::string& optname);
+std::string ResolveErrMsg(const std::string& optname, const std::string& strBind);
+
+bilingual_str AmountHighWarn(const std::string& optname);
-std::string AmountErrMsg(const char* const optname, const std::string& strValue);
+bilingual_str AmountErrMsg(const std::string& optname, const std::string& strValue);
#endif // BITCOIN_UTIL_ERROR_H
diff --git a/src/util/strencodings.cpp b/src/util/strencodings.cpp
index 0acbb4f117..1e7d24c71c 100644
--- a/src/util/strencodings.cpp
+++ b/src/util/strencodings.cpp
@@ -546,9 +546,18 @@ bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out)
return true;
}
-void Downcase(std::string& str)
+std::string ToLower(const std::string& str)
{
- std::transform(str.begin(), str.end(), str.begin(), [](char c){return ToLower(c);});
+ std::string r;
+ for (auto ch : str) r += ToLower((unsigned char)ch);
+ return r;
+}
+
+std::string ToUpper(const std::string& str)
+{
+ std::string r;
+ for (auto ch : str) r += ToUpper((unsigned char)ch);
+ return r;
}
std::string Capitalize(std::string str)
diff --git a/src/util/strencodings.h b/src/util/strencodings.h
index 7c4364a082..e35b2ab857 100644
--- a/src/util/strencodings.h
+++ b/src/util/strencodings.h
@@ -199,6 +199,8 @@ bool ConvertBits(const O& outfn, I it, I end) {
* Converts the given character to its lowercase equivalent.
* This function is locale independent. It only converts uppercase
* characters in the standard 7-bit ASCII range.
+ * This is a feature, not a limitation.
+ *
* @param[in] c the character to convert to lowercase.
* @return the lowercase equivalent of c; or the argument
* if no conversion is possible.
@@ -209,17 +211,22 @@ constexpr char ToLower(char c)
}
/**
- * Converts the given string to its lowercase equivalent.
+ * Returns the lowercase equivalent of the given string.
* This function is locale independent. It only converts uppercase
* characters in the standard 7-bit ASCII range.
- * @param[in,out] str the string to convert to lowercase.
+ * This is a feature, not a limitation.
+ *
+ * @param[in] str the string to convert to lowercase.
+ * @returns lowercased equivalent of str
*/
-void Downcase(std::string& str);
+std::string ToLower(const std::string& str);
/**
* Converts the given character to its uppercase equivalent.
* This function is locale independent. It only converts lowercase
* characters in the standard 7-bit ASCII range.
+ * This is a feature, not a limitation.
+ *
* @param[in] c the character to convert to uppercase.
* @return the uppercase equivalent of c; or the argument
* if no conversion is possible.
@@ -230,12 +237,24 @@ constexpr char ToUpper(char c)
}
/**
+ * Returns the uppercase equivalent of the given string.
+ * This function is locale independent. It only converts lowercase
+ * characters in the standard 7-bit ASCII range.
+ * This is a feature, not a limitation.
+ *
+ * @param[in] str the string to convert to uppercase.
+ * @returns UPPERCASED EQUIVALENT OF str
+ */
+std::string ToUpper(const std::string& str);
+
+/**
* Capitalizes the first character of the given string.
- * This function is locale independent. It only capitalizes the
- * first character of the argument if it has an uppercase equivalent
- * in the standard 7-bit ASCII range.
+ * This function is locale independent. It only converts lowercase
+ * characters in the standard 7-bit ASCII range.
+ * This is a feature, not a limitation.
+ *
* @param[in] str the string to capitalize.
- * @return string with the first letter capitalized.
+ * @returns string with the first letter capitalized.
*/
std::string Capitalize(std::string str);
diff --git a/src/util/string.cpp b/src/util/string.cpp
new file mode 100644
index 0000000000..8ea3a1afc6
--- /dev/null
+++ b/src/util/string.cpp
@@ -0,0 +1,5 @@
+// Copyright (c) 2019 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 <util/string.h>
diff --git a/src/util/string.h b/src/util/string.h
new file mode 100644
index 0000000000..dec0c19b08
--- /dev/null
+++ b/src/util/string.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2019 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_UTIL_STRING_H
+#define BITCOIN_UTIL_STRING_H
+
+#include <functional>
+#include <string>
+#include <vector>
+
+/**
+ * Join a list of items
+ *
+ * @param list The list to join
+ * @param separator The separator
+ * @param unary_op Apply this operator to each item in the list
+ */
+template <typename T, typename UnaryOp>
+std::string Join(const std::vector<T>& list, const std::string& separator, UnaryOp unary_op)
+{
+ std::string ret;
+ for (size_t i = 0; i < list.size(); ++i) {
+ if (i > 0) ret += separator;
+ ret += unary_op(list.at(i));
+ }
+ return ret;
+}
+
+inline std::string Join(const std::vector<std::string>& list, const std::string& separator)
+{
+ return Join(list, separator, [](const std::string& i) { return i; });
+}
+
+#endif // BITCOIN_UTIL_STRENCODINGS_H
diff --git a/src/util/system.cpp b/src/util/system.cpp
index c27b0cc105..8098cde093 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -268,22 +268,24 @@ public:
* This method also tracks when the -no form was supplied, and if so,
* checks whether there was a double-negative (-nofoo=0 -> -foo=1).
*
- * If there was not a double negative, it removes the "no" from the key,
- * and returns true, indicating the caller should clear the args vector
- * to indicate a negated option.
+ * If there was not a double negative, it removes the "no" from the key
+ * and clears the args vector to indicate a negated option.
*
* If there was a double negative, it removes "no" from the key, sets the
- * value to "1" and returns false.
+ * value to "1" and pushes the key and the updated value to the args vector.
*
- * If there was no "no", it leaves key and value untouched and returns
- * false.
+ * If there was no "no", it leaves key and value untouched and pushes them
+ * to the args vector.
*
* Where an option was negated can be later checked using the
* IsArgNegated() method. One use case for this is to have a way to disable
* options that are not normally boolean (e.g. using -nodebuglogfile to request
* that debug log output is not sent to any file at all).
*/
-static bool InterpretNegatedOption(std::string& key, std::string& val)
+
+NODISCARD static bool InterpretOption(std::string key, std::string val, unsigned int flags,
+ std::map<std::string, std::vector<std::string>>& args,
+ std::string& error)
{
assert(key[0] == '-');
@@ -294,31 +296,25 @@ static bool InterpretNegatedOption(std::string& key, std::string& val)
++option_index;
}
if (key.substr(option_index, 2) == "no") {
- bool bool_val = InterpretBool(val);
key.erase(option_index, 2);
- if (!bool_val ) {
+ if (flags & ArgsManager::ALLOW_BOOL) {
+ if (InterpretBool(val)) {
+ args[key].clear();
+ return true;
+ }
// Double negatives like -nofoo=0 are supported (but discouraged)
LogPrintf("Warning: parsed potentially confusing double-negative %s=%s\n", key, val);
val = "1";
} else {
- return true;
+ error = strprintf("Negating of %s is meaningless and therefore forbidden", key.c_str());
+ return false;
}
}
- return false;
+ args[key].push_back(val);
+ return true;
}
-ArgsManager::ArgsManager() :
- /* These options would cause cross-contamination if values for
- * mainnet were used while running on regtest/testnet (or vice-versa).
- * Setting them as section_only_args ensures that sharing a config file
- * between mainnet and regtest/testnet won't cause problems due to these
- * parameters by accident. */
- m_network_only_args{
- "-addnode", "-connect",
- "-port", "-bind",
- "-rpcport", "-rpcbind",
- "-wallet",
- }
+ArgsManager::ArgsManager()
{
// nothing to do
}
@@ -384,6 +380,7 @@ bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::strin
for (int i = 1; i < argc; i++) {
std::string key(argv[i]);
+ if (key == "-") break; //bitcoin-tx using stdin
std::string val;
size_t is_index = key.find('=');
if (is_index != std::string::npos) {
@@ -391,7 +388,7 @@ bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::strin
key.erase(is_index);
}
#ifdef WIN32
- std::transform(key.begin(), key.end(), key.begin(), ToLower);
+ key = ToLower(key);
if (key[0] == '/')
key[0] = '-';
#endif
@@ -403,19 +400,14 @@ bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::strin
if (key.length() > 1 && key[1] == '-')
key.erase(0, 1);
- // Check for -nofoo
- if (InterpretNegatedOption(key, val)) {
- m_override_args[key].clear();
- } else {
- m_override_args[key].push_back(val);
- }
-
- // Check that the arg is known
- if (!(IsSwitchChar(key[0]) && key.size() == 1)) {
- if (!IsArgKnown(key)) {
- error = strprintf("Invalid parameter %s", key.c_str());
+ const unsigned int flags = FlagsOfKnownArg(key);
+ if (flags) {
+ if (!InterpretOption(key, val, flags, m_override_args, error)) {
return false;
}
+ } else {
+ error = strprintf("Invalid parameter %s", key.c_str());
+ return false;
}
}
@@ -432,21 +424,30 @@ bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::strin
return true;
}
-bool ArgsManager::IsArgKnown(const std::string& key) const
+unsigned int ArgsManager::FlagsOfKnownArg(const std::string& key) const
{
+ assert(key[0] == '-');
+
size_t option_index = key.find('.');
- std::string arg_no_net;
if (option_index == std::string::npos) {
- arg_no_net = key;
+ option_index = 1;
} else {
- arg_no_net = std::string("-") + key.substr(option_index + 1, std::string::npos);
+ ++option_index;
+ }
+ if (key.substr(option_index, 2) == "no") {
+ option_index += 2;
}
+ const std::string base_arg_name = '-' + key.substr(option_index);
+
LOCK(cs_args);
for (const auto& arg_map : m_available_args) {
- if (arg_map.second.count(arg_no_net)) return true;
+ const auto search = arg_map.second.find(base_arg_name);
+ if (search != arg_map.second.end()) {
+ return search->second.m_flags;
+ }
}
- return false;
+ return ArgsManager::NONE;
}
std::vector<std::string> ArgsManager::GetArgs(const std::string& strArg) const
@@ -538,24 +539,29 @@ 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)
+void ArgsManager::AddArg(const std::string& name, const std::string& help, unsigned int flags, const OptionsCategory& cat)
{
// Split arg name from its help param
size_t eq_index = name.find('=');
if (eq_index == std::string::npos) {
eq_index = name.size();
}
+ std::string arg_name = name.substr(0, eq_index);
LOCK(cs_args);
std::map<std::string, Arg>& arg_map = m_available_args[cat];
- auto ret = arg_map.emplace(name.substr(0, eq_index), Arg(name.substr(eq_index, name.size() - eq_index), help, debug_only));
+ auto ret = arg_map.emplace(arg_name, Arg{name.substr(eq_index, name.size() - eq_index), help, flags});
assert(ret.second); // Make sure an insertion actually happened
+
+ if (flags & ArgsManager::NETWORK_ONLY) {
+ m_network_only_args.emplace(arg_name);
+ }
}
void ArgsManager::AddHiddenArgs(const std::vector<std::string>& names)
{
for (const std::string& name : names) {
- AddArg(name, "", false, OptionsCategory::HIDDEN);
+ AddArg(name, "", ArgsManager::ALLOW_ANY, OptionsCategory::HIDDEN);
}
}
@@ -614,7 +620,7 @@ std::string ArgsManager::GetHelpMessage() const
if (arg_map.first == OptionsCategory::HIDDEN) break;
for (const auto& arg : arg_map.second) {
- if (show_debug || !arg.second.m_debug_only) {
+ if (show_debug || !(arg.second.m_flags & ArgsManager::DEBUG_ONLY)) {
std::string name;
if (arg.second.m_help_param.empty()) {
name = arg.first;
@@ -635,7 +641,7 @@ bool HelpRequested(const ArgsManager& args)
void SetupHelpOptions(ArgsManager& args)
{
- args.AddArg("-?", "Print this help message and exit", false, OptionsCategory::OPTIONS);
+ args.AddArg("-?", "Print this help message and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
args.AddHiddenArgs({"-h", "-help"});
}
@@ -742,8 +748,9 @@ const fs::path &GetDataDir(bool fNetSpecific)
// this function
if (!path.empty()) return path;
- if (gArgs.IsArgSet("-datadir")) {
- path = fs::system_complete(gArgs.GetArg("-datadir", ""));
+ std::string datadir = gArgs.GetArg("-datadir", "");
+ if (!datadir.empty()) {
+ path = fs::system_complete(datadir);
if (!fs::is_directory(path)) {
path = "";
return path;
@@ -762,6 +769,12 @@ const fs::path &GetDataDir(bool fNetSpecific)
return path;
}
+bool CheckDataDirOption()
+{
+ std::string datadir = gArgs.GetArg("-datadir", "");
+ return datadir.empty() || fs::is_directory(fs::system_complete(datadir));
+}
+
void ClearDatadirCache()
{
LOCK(csPathCached);
@@ -839,22 +852,18 @@ bool ArgsManager::ReadConfigStream(std::istream& stream, const std::string& file
return false;
}
for (const std::pair<std::string, std::string>& option : options) {
- std::string strKey = std::string("-") + option.first;
- std::string strValue = option.second;
-
- if (InterpretNegatedOption(strKey, strValue)) {
- m_config_args[strKey].clear();
+ const std::string strKey = std::string("-") + option.first;
+ const unsigned int flags = FlagsOfKnownArg(strKey);
+ if (flags) {
+ if (!InterpretOption(strKey, option.second, flags, m_config_args, error)) {
+ return false;
+ }
} else {
- m_config_args[strKey].push_back(strValue);
- }
-
- // Check that the arg is known
- if (!IsArgKnown(strKey)) {
- if (!ignore_invalid_keys) {
+ if (ignore_invalid_keys) {
+ LogPrintf("Ignoring unknown configuration value %s\n", option.first);
+ } else {
error = strprintf("Invalid configuration value %s", option.first.c_str());
return false;
- } else {
- LogPrintf("Ignoring unknown configuration value %s\n", option.first);
}
}
}
@@ -935,7 +944,7 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
// If datadir is changed in .conf file:
ClearDatadirCache();
- if (!fs::is_directory(GetDataDir(false))) {
+ if (!CheckDataDirOption()) {
error = strprintf("specified data directory \"%s\" does not exist.", gArgs.GetArg("-datadir", "").c_str());
return false;
}
@@ -945,16 +954,18 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
std::string ArgsManager::GetChainName() const
{
LOCK(cs_args);
- bool fRegTest = ArgsManagerHelper::GetNetBoolArg(*this, "-regtest");
- bool fTestNet = ArgsManagerHelper::GetNetBoolArg(*this, "-testnet");
+ const bool fRegTest = ArgsManagerHelper::GetNetBoolArg(*this, "-regtest");
+ const bool fTestNet = ArgsManagerHelper::GetNetBoolArg(*this, "-testnet");
+ const bool is_chain_arg_set = IsArgSet("-chain");
- if (fTestNet && fRegTest)
- throw std::runtime_error("Invalid combination of -regtest and -testnet.");
+ if ((int)is_chain_arg_set + (int)fRegTest + (int)fTestNet > 1) {
+ throw std::runtime_error("Invalid combination of -regtest, -testnet and -chain. Can use at most one.");
+ }
if (fRegTest)
return CBaseChainParams::REGTEST;
if (fTestNet)
return CBaseChainParams::TESTNET;
- return CBaseChainParams::MAIN;
+ return GetArg("-chain", CBaseChainParams::MAIN);
}
bool RenameOver(fs::path src, fs::path dest)
@@ -1203,6 +1214,9 @@ int64_t GetStartupTime()
fs::path AbsPathForConfigVal(const fs::path& path, bool net_specific)
{
+ if (path.is_absolute()) {
+ return path;
+ }
return fs::absolute(path, GetDataDir(net_specific));
}
diff --git a/src/util/system.h b/src/util/system.h
index 66a9eb4612..908a3c407d 100644
--- a/src/util/system.h
+++ b/src/util/system.h
@@ -71,6 +71,8 @@ fs::path GetDefaultDataDir();
// The blocks directory is always net specific.
const fs::path &GetBlocksDir();
const fs::path &GetDataDir(bool fNetSpecific = true);
+// Return true if -datadir option points to a valid directory or is not specified.
+bool CheckDataDirOption();
/** Tests only */
void ClearDatadirCache();
fs::path GetConfigFile(const std::string& confPath);
@@ -127,6 +129,23 @@ struct SectionInfo
class ArgsManager
{
+public:
+ enum Flags {
+ NONE = 0x00,
+ // Boolean options can accept negation syntax -noOPTION or -noOPTION=1
+ ALLOW_BOOL = 0x01,
+ ALLOW_INT = 0x02,
+ ALLOW_STRING = 0x04,
+ ALLOW_ANY = ALLOW_BOOL | ALLOW_INT | ALLOW_STRING,
+ DEBUG_ONLY = 0x100,
+ /* Some options would cause cross-contamination if values for
+ * mainnet were used while running on regtest/testnet (or vice-versa).
+ * Setting them as NETWORK_ONLY ensures that sharing a config file
+ * between mainnet and regtest/testnet won't cause problems due to these
+ * parameters by accident. */
+ NETWORK_ONLY = 0x200,
+ };
+
protected:
friend class ArgsManagerHelper;
@@ -134,9 +153,7 @@ protected:
{
std::string m_help_param;
std::string m_help_text;
- bool m_debug_only;
-
- Arg(const std::string& help_param, const std::string& help_text, bool debug_only) : m_help_param(help_param), m_help_text(help_text), m_debug_only(debug_only) {};
+ unsigned int m_flags;
};
mutable CCriticalSection cs_args;
@@ -256,7 +273,7 @@ public:
/**
* Add argument
*/
- void AddArg(const std::string& name, const std::string& help, const bool debug_only, const OptionsCategory& cat);
+ void AddArg(const std::string& name, const std::string& help, unsigned int flags, const OptionsCategory& cat);
/**
* Add many hidden arguments
@@ -269,6 +286,7 @@ public:
void ClearArgs() {
LOCK(cs_args);
m_available_args.clear();
+ m_network_only_args.clear();
}
/**
@@ -277,9 +295,10 @@ public:
std::string GetHelpMessage() const;
/**
- * Check whether we know of this arg
+ * Return Flags for known arg.
+ * Return ArgsManager::NONE for unknown arg.
*/
- bool IsArgKnown(const std::string& key) const;
+ unsigned int FlagsOfKnownArg(const std::string& key) const;
};
extern ArgsManager gArgs;
diff --git a/src/validation.cpp b/src/validation.cpp
index b4677df62f..9696b65ea7 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -82,11 +82,17 @@ namespace {
BlockManager g_blockman;
} // anon namespace
-static CChainState g_chainstate(g_blockman);
+std::unique_ptr<CChainState> g_chainstate;
-CChainState& ChainstateActive() { return g_chainstate; }
+CChainState& ChainstateActive() {
+ assert(g_chainstate);
+ return *g_chainstate;
+}
-CChain& ChainActive() { return g_chainstate.m_chain; }
+CChain& ChainActive() {
+ assert(g_chainstate);
+ return g_chainstate->m_chain;
+}
/**
* Mutex to guard access to validation specific variables, such as reading
@@ -173,14 +179,12 @@ CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& loc
return chain.Genesis();
}
-std::unique_ptr<CCoinsViewDB> pcoinsdbview;
-std::unique_ptr<CCoinsViewCache> pcoinsTip;
std::unique_ptr<CBlockTreeDB> pblocktree;
// See definition for documentation
static void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight);
static void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight);
-bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks = nullptr);
+bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks = nullptr);
static FILE* OpenUndoFile(const FlatFilePos &pos, bool fReadOnly = false);
static FlatFileSeq BlockFileSeq();
static FlatFileSeq UndoFileSeq();
@@ -260,8 +264,8 @@ bool CheckSequenceLocks(const CTxMemPool& pool, const CTransaction& tx, int flag
lockPair.second = lp->time;
}
else {
- // pcoinsTip contains the UTXO set for ::ChainActive().Tip()
- CCoinsViewMemPool viewMemPool(pcoinsTip.get(), pool);
+ // CoinsTip() contains the UTXO set for ::ChainActive().Tip()
+ CCoinsViewMemPool viewMemPool(&::ChainstateActive().CoinsTip(), pool);
std::vector<int> prevheights;
prevheights.resize(tx.vin.size());
for (size_t txinIndex = 0; txinIndex < tx.vin.size(); txinIndex++) {
@@ -310,7 +314,8 @@ bool CheckSequenceLocks(const CTxMemPool& pool, const CTransaction& tx, int flag
// Returns the script flags which should be checked for a given block
static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consensus::Params& chainparams);
-static void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age) EXCLUSIVE_LOCKS_REQUIRED(pool.cs)
+static void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age)
+ EXCLUSIVE_LOCKS_REQUIRED(pool.cs, ::cs_main)
{
int expired = pool.Expire(GetTime() - age);
if (expired != 0) {
@@ -320,7 +325,7 @@ static void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age)
std::vector<COutPoint> vNoSpendsRemaining;
pool.TrimToSize(limit, &vNoSpendsRemaining);
for (const COutPoint& removed : vNoSpendsRemaining)
- pcoinsTip->Uncache(removed);
+ ::ChainstateActive().CoinsTip().Uncache(removed);
}
static bool IsCurrentForFeeEstimation() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
@@ -382,7 +387,7 @@ static void UpdateMempoolForReorg(DisconnectedBlockTransactions& disconnectpool,
mempool.UpdateTransactionsFromBlock(vHashUpdate);
// We also need to remove any now-immature transactions
- mempool.removeForReorg(pcoinsTip.get(), ::ChainActive().Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS);
+ mempool.removeForReorg(&::ChainstateActive().CoinsTip(), ::ChainActive().Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS);
// Re-limit mempool size, in case we added any transactions
LimitMempoolSize(mempool, gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60);
}
@@ -414,30 +419,143 @@ static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, CValidationSt
assert(txFrom->vout.size() > txin.prevout.n);
assert(txFrom->vout[txin.prevout.n] == coin.out);
} else {
- const Coin& coinFromDisk = pcoinsTip->AccessCoin(txin.prevout);
+ const Coin& coinFromDisk = ::ChainstateActive().CoinsTip().AccessCoin(txin.prevout);
assert(!coinFromDisk.IsSpent());
assert(coinFromDisk.out == coin.out);
}
}
- return CheckInputs(tx, state, view, true, flags, cacheSigStore, true, txdata);
+ return CheckInputs(tx, state, view, flags, cacheSigStore, true, txdata);
}
-/**
- * @param[out] coins_to_uncache Return any outpoints which were not previously present in the
- * coins cache, but were added as a result of validating the tx
- * for mempool acceptance. This allows the caller to optionally
- * remove the cache additions if the associated transaction ends
- * up being rejected by the mempool.
- */
-static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool& pool, CValidationState& state, const CTransactionRef& ptx,
- bool* pfMissingInputs, int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced,
- bool bypass_limits, const CAmount& nAbsurdFee, std::vector<COutPoint>& coins_to_uncache, bool test_accept) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+namespace {
+
+class MemPoolAccept
{
- const CTransaction& tx = *ptx;
- const uint256 hash = tx.GetHash();
- AssertLockHeld(cs_main);
- LOCK(pool.cs); // mempool "read lock" (held through GetMainSignals().TransactionAddedToMempool())
+public:
+ MemPoolAccept(CTxMemPool& mempool) : m_pool(mempool), m_view(&m_dummy), m_viewmempool(&::ChainstateActive().CoinsTip(), m_pool),
+ m_limit_ancestors(gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT)),
+ m_limit_ancestor_size(gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000),
+ m_limit_descendants(gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT)),
+ m_limit_descendant_size(gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT)*1000) {}
+
+ // We put the arguments we're handed into a struct, so we can pass them
+ // around easier.
+ struct ATMPArgs {
+ const CChainParams& m_chainparams;
+ CValidationState &m_state;
+ bool* m_missing_inputs;
+ const int64_t m_accept_time;
+ std::list<CTransactionRef>* m_replaced_transactions;
+ const bool m_bypass_limits;
+ const CAmount& m_absurd_fee;
+ /*
+ * Return any outpoints which were not previously present in the coins
+ * cache, but were added as a result of validating the tx for mempool
+ * acceptance. This allows the caller to optionally remove the cache
+ * additions if the associated transaction ends up being rejected by
+ * the mempool.
+ */
+ std::vector<COutPoint>& m_coins_to_uncache;
+ const bool m_test_accept;
+ };
+
+ // Single transaction acceptance
+ bool AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs& args) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+private:
+ // All the intermediate state that gets passed between the various levels
+ // of checking a given transaction.
+ struct Workspace {
+ Workspace(const CTransactionRef& ptx) : m_ptx(ptx), m_hash(ptx->GetHash()) {}
+ std::set<uint256> m_conflicts;
+ CTxMemPool::setEntries m_all_conflicting;
+ CTxMemPool::setEntries m_ancestors;
+ std::unique_ptr<CTxMemPoolEntry> m_entry;
+
+ bool m_replacement_transaction;
+ CAmount m_modified_fees;
+ CAmount m_conflicting_fees;
+ size_t m_conflicting_size;
+
+ const CTransactionRef& m_ptx;
+ const uint256& m_hash;
+ };
+
+ // Run the policy checks on a given transaction, excluding any script checks.
+ // Looks up inputs, calculates feerate, considers replacement, evaluates
+ // package limits, etc. As this function can be invoked for "free" by a peer,
+ // only tests that are fast should be done here (to avoid CPU DoS).
+ bool PreChecks(ATMPArgs& args, Workspace& ws) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
+
+ // Run the script checks using our policy flags. As this can be slow, we should
+ // only invoke this on transactions that have otherwise passed policy checks.
+ bool PolicyScriptChecks(ATMPArgs& args, Workspace& ws, PrecomputedTransactionData& txdata) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+ // Re-run the script checks, using consensus flags, and try to cache the
+ // result in the scriptcache. This should be done after
+ // PolicyScriptChecks(). This requires that all inputs either be in our
+ // utxo set or in the mempool.
+ bool ConsensusScriptChecks(ATMPArgs& args, Workspace& ws, PrecomputedTransactionData &txdata) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+ // Try to add the transaction to the mempool, removing any conflicts first.
+ // Returns true if the transaction is in the mempool after any size
+ // limiting is performed, false otherwise.
+ bool Finalize(ATMPArgs& args, Workspace& ws) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
+
+ // Compare a package's feerate against minimum allowed.
+ bool CheckFeeRate(size_t package_size, CAmount package_fee, CValidationState& state)
+ {
+ CAmount mempoolRejectFee = m_pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(package_size);
+ if (mempoolRejectFee > 0 && package_fee < mempoolRejectFee) {
+ return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", strprintf("%d < %d", package_fee, mempoolRejectFee));
+ }
+
+ if (package_fee < ::minRelayTxFee.GetFee(package_size)) {
+ return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "min relay fee not met", strprintf("%d < %d", package_fee, ::minRelayTxFee.GetFee(package_size)));
+ }
+ return true;
+ }
+
+private:
+ CTxMemPool& m_pool;
+ CCoinsViewCache m_view;
+ CCoinsViewMemPool m_viewmempool;
+ CCoinsView m_dummy;
+
+ // The package limits in effect at the time of invocation.
+ const size_t m_limit_ancestors;
+ const size_t m_limit_ancestor_size;
+ // These may be modified while evaluating a transaction (eg to account for
+ // in-mempool conflicts; see below).
+ size_t m_limit_descendants;
+ size_t m_limit_descendant_size;
+};
+
+bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
+{
+ const CTransactionRef& ptx = ws.m_ptx;
+ const CTransaction& tx = *ws.m_ptx;
+ const uint256& hash = ws.m_hash;
+
+ // Copy/alias what we need out of args
+ CValidationState &state = args.m_state;
+ bool* pfMissingInputs = args.m_missing_inputs;
+ const int64_t nAcceptTime = args.m_accept_time;
+ const bool bypass_limits = args.m_bypass_limits;
+ const CAmount& nAbsurdFee = args.m_absurd_fee;
+ std::vector<COutPoint>& coins_to_uncache = args.m_coins_to_uncache;
+
+ // Alias what we need out of ws
+ std::set<uint256>& setConflicts = ws.m_conflicts;
+ CTxMemPool::setEntries& allConflicting = ws.m_all_conflicting;
+ CTxMemPool::setEntries& setAncestors = ws.m_ancestors;
+ std::unique_ptr<CTxMemPoolEntry>& entry = ws.m_entry;
+ bool& fReplacementTransaction = ws.m_replacement_transaction;
+ CAmount& nModifiedFees = ws.m_modified_fees;
+ CAmount& nConflictingFees = ws.m_conflicting_fees;
+ size_t& nConflictingSize = ws.m_conflicting_size;
+
if (pfMissingInputs) {
*pfMissingInputs = false;
}
@@ -467,15 +585,14 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
return state.Invalid(ValidationInvalidReason::TX_PREMATURE_SPEND, false, REJECT_NONSTANDARD, "non-final");
// is it already in the memory pool?
- if (pool.exists(hash)) {
+ if (m_pool.exists(hash)) {
return state.Invalid(ValidationInvalidReason::TX_CONFLICT, false, REJECT_DUPLICATE, "txn-already-in-mempool");
}
// Check for conflicts with in-memory transactions
- std::set<uint256> setConflicts;
for (const CTxIn &txin : tx.vin)
{
- const CTransaction* ptxConflicting = pool.GetConflictTx(txin.prevout);
+ const CTransaction* ptxConflicting = m_pool.GetConflictTx(txin.prevout);
if (ptxConflicting) {
if (!setConflicts.count(ptxConflicting->GetHash()))
{
@@ -509,350 +626,436 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
}
}
- {
- CCoinsView dummy;
- CCoinsViewCache view(&dummy);
-
- LockPoints lp;
- CCoinsViewMemPool viewMemPool(pcoinsTip.get(), pool);
- view.SetBackend(viewMemPool);
+ LockPoints lp;
+ m_view.SetBackend(m_viewmempool);
- // do all inputs exist?
- for (const CTxIn& txin : tx.vin) {
- if (!pcoinsTip->HaveCoinInCache(txin.prevout)) {
- coins_to_uncache.push_back(txin.prevout);
- }
-
- // Note: this call may add txin.prevout to the coins cache
- // (pcoinsTip.cacheCoins) by way of FetchCoin(). It should be removed
- // later (via coins_to_uncache) if this tx turns out to be invalid.
- if (!view.HaveCoin(txin.prevout)) {
- // Are inputs missing because we already have the tx?
- for (size_t out = 0; out < tx.vout.size(); out++) {
- // Optimistically just do efficient check of cache for outputs
- if (pcoinsTip->HaveCoinInCache(COutPoint(hash, out))) {
- return state.Invalid(ValidationInvalidReason::TX_CONFLICT, false, REJECT_DUPLICATE, "txn-already-known");
- }
- }
- // Otherwise assume this might be an orphan tx for which we just haven't seen parents yet
- if (pfMissingInputs) {
- *pfMissingInputs = true;
+ CCoinsViewCache& coins_cache = ::ChainstateActive().CoinsTip();
+ // do all inputs exist?
+ for (const CTxIn& txin : tx.vin) {
+ if (!coins_cache.HaveCoinInCache(txin.prevout)) {
+ coins_to_uncache.push_back(txin.prevout);
+ }
+
+ // Note: this call may add txin.prevout to the coins cache
+ // (coins_cache.cacheCoins) by way of FetchCoin(). It should be removed
+ // later (via coins_to_uncache) if this tx turns out to be invalid.
+ if (!m_view.HaveCoin(txin.prevout)) {
+ // Are inputs missing because we already have the tx?
+ for (size_t out = 0; out < tx.vout.size(); out++) {
+ // Optimistically just do efficient check of cache for outputs
+ if (coins_cache.HaveCoinInCache(COutPoint(hash, out))) {
+ return state.Invalid(ValidationInvalidReason::TX_CONFLICT, false, REJECT_DUPLICATE, "txn-already-known");
}
- return false; // fMissingInputs and !state.IsInvalid() is used to detect this condition, don't set state.Invalid()
}
+ // Otherwise assume this might be an orphan tx for which we just haven't seen parents yet
+ if (pfMissingInputs) {
+ *pfMissingInputs = true;
+ }
+ return false; // fMissingInputs and !state.IsInvalid() is used to detect this condition, don't set state.Invalid()
}
+ }
- // Bring the best block into scope
- view.GetBestBlock();
+ // Bring the best block into scope
+ m_view.GetBestBlock();
- // we have all inputs cached now, so switch back to dummy, so we don't need to keep lock on mempool
- view.SetBackend(dummy);
+ // we have all inputs cached now, so switch back to dummy (to protect
+ // against bugs where we pull more inputs from disk that miss being added
+ // to coins_to_uncache)
+ m_view.SetBackend(m_dummy);
- // Only accept BIP68 sequence locked 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.
- // Must keep pool.cs for this unless we change CheckSequenceLocks to take a
- // CoinsViewCache instead of create its own
- if (!CheckSequenceLocks(pool, tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp))
- return state.Invalid(ValidationInvalidReason::TX_PREMATURE_SPEND, false, REJECT_NONSTANDARD, "non-BIP68-final");
+ // Only accept BIP68 sequence locked 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.
+ // Must keep pool.cs for this unless we change CheckSequenceLocks to take a
+ // CoinsViewCache instead of create its own
+ if (!CheckSequenceLocks(m_pool, tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp))
+ return state.Invalid(ValidationInvalidReason::TX_PREMATURE_SPEND, false, REJECT_NONSTANDARD, "non-BIP68-final");
- CAmount nFees = 0;
- if (!Consensus::CheckTxInputs(tx, state, view, GetSpendHeight(view), nFees)) {
- return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, tx.GetHash().ToString(), FormatStateMessage(state));
- }
+ CAmount nFees = 0;
+ if (!Consensus::CheckTxInputs(tx, state, m_view, GetSpendHeight(m_view), nFees)) {
+ return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, tx.GetHash().ToString(), FormatStateMessage(state));
+ }
- // Check for non-standard pay-to-script-hash in inputs
- if (fRequireStandard && !AreInputsStandard(tx, view))
- return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, "bad-txns-nonstandard-inputs");
+ // Check for non-standard pay-to-script-hash in inputs
+ if (fRequireStandard && !AreInputsStandard(tx, m_view))
+ return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, "bad-txns-nonstandard-inputs");
- // Check for non-standard witness in P2WSH
- if (tx.HasWitness() && fRequireStandard && !IsWitnessStandard(tx, view))
- return state.Invalid(ValidationInvalidReason::TX_WITNESS_MUTATED, false, REJECT_NONSTANDARD, "bad-witness-nonstandard");
+ // Check for non-standard witness in P2WSH
+ if (tx.HasWitness() && fRequireStandard && !IsWitnessStandard(tx, m_view))
+ return state.Invalid(ValidationInvalidReason::TX_WITNESS_MUTATED, false, REJECT_NONSTANDARD, "bad-witness-nonstandard");
- int64_t nSigOpsCost = GetTransactionSigOpCost(tx, view, STANDARD_SCRIPT_VERIFY_FLAGS);
+ int64_t nSigOpsCost = GetTransactionSigOpCost(tx, m_view, STANDARD_SCRIPT_VERIFY_FLAGS);
- // nModifiedFees includes any fee deltas from PrioritiseTransaction
- CAmount nModifiedFees = nFees;
- pool.ApplyDelta(hash, nModifiedFees);
+ // nModifiedFees includes any fee deltas from PrioritiseTransaction
+ nModifiedFees = nFees;
+ m_pool.ApplyDelta(hash, nModifiedFees);
- // Keep track of transactions that spend a coinbase, which we re-scan
- // during reorgs to ensure COINBASE_MATURITY is still met.
- bool fSpendsCoinbase = false;
- for (const CTxIn &txin : tx.vin) {
- const Coin &coin = view.AccessCoin(txin.prevout);
- if (coin.IsCoinBase()) {
- fSpendsCoinbase = true;
- break;
- }
+ // Keep track of transactions that spend a coinbase, which we re-scan
+ // during reorgs to ensure COINBASE_MATURITY is still met.
+ bool fSpendsCoinbase = false;
+ for (const CTxIn &txin : tx.vin) {
+ const Coin &coin = m_view.AccessCoin(txin.prevout);
+ if (coin.IsCoinBase()) {
+ fSpendsCoinbase = true;
+ break;
}
+ }
- CTxMemPoolEntry entry(ptx, nFees, nAcceptTime, ::ChainActive().Height(),
- fSpendsCoinbase, nSigOpsCost, lp);
- unsigned int nSize = entry.GetTxSize();
+ entry.reset(new CTxMemPoolEntry(ptx, nFees, nAcceptTime, ::ChainActive().Height(),
+ fSpendsCoinbase, nSigOpsCost, lp));
+ unsigned int nSize = entry->GetTxSize();
- if (nSigOpsCost > MAX_STANDARD_TX_SIGOPS_COST)
- return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, "bad-txns-too-many-sigops",
+ if (nSigOpsCost > MAX_STANDARD_TX_SIGOPS_COST)
+ return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, "bad-txns-too-many-sigops",
strprintf("%d", nSigOpsCost));
- CAmount mempoolRejectFee = pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize);
- if (!bypass_limits && mempoolRejectFee > 0 && nModifiedFees < mempoolRejectFee) {
- return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", strprintf("%d < %d", nModifiedFees, mempoolRejectFee));
- }
-
- // No transactions are allowed below minRelayTxFee except from disconnected blocks
- if (!bypass_limits && nModifiedFees < ::minRelayTxFee.GetFee(nSize)) {
- return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "min relay fee not met", strprintf("%d < %d", nModifiedFees, ::minRelayTxFee.GetFee(nSize)));
- }
+ // No transactions are allowed below minRelayTxFee except from disconnected
+ // blocks
+ if (!bypass_limits && !CheckFeeRate(nSize, nModifiedFees, state)) return false;
- if (nAbsurdFee && nFees > nAbsurdFee)
- return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false,
+ if (nAbsurdFee && nFees > nAbsurdFee)
+ return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false,
REJECT_HIGHFEE, "absurdly-high-fee",
strprintf("%d > %d", nFees, nAbsurdFee));
- // Calculate in-mempool ancestors, up to a limit.
- CTxMemPool::setEntries setAncestors;
- size_t nLimitAncestors = gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT);
- size_t nLimitAncestorSize = gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000;
- size_t nLimitDescendants = gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT);
- size_t nLimitDescendantSize = gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT)*1000;
- std::string errString;
- if (!pool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) {
- setAncestors.clear();
- // If CalculateMemPoolAncestors fails second time, we want the original error string.
- std::string dummy_err_string;
- // If the new transaction is relatively small (up to 40k weight)
- // and has at most one ancestor (ie ancestor limit of 2, including
- // the new transaction), allow it if its parent has exactly the
- // descendant limit descendants.
- //
- // This allows protocols which rely on distrusting counterparties
- // being able to broadcast descendants of an unconfirmed transaction
- // to be secure by simply only having two immediately-spendable
- // outputs - one for each counterparty. For more info on the uses for
- // this, see https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2018-November/016518.html
- if (nSize > EXTRA_DESCENDANT_TX_SIZE_LIMIT ||
- !pool.CalculateMemPoolAncestors(entry, setAncestors, 2, nLimitAncestorSize, nLimitDescendants + 1, nLimitDescendantSize + EXTRA_DESCENDANT_TX_SIZE_LIMIT, dummy_err_string)) {
- return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_NONSTANDARD, "too-long-mempool-chain", errString);
- }
- }
-
- // A transaction that spends outputs that would be replaced by it is invalid. Now
- // that we have the set of all ancestors we can detect this
- // pathological case by making sure setConflicts and setAncestors don't
- // intersect.
- for (CTxMemPool::txiter ancestorIt : setAncestors)
+ const CTxMemPool::setEntries setIterConflicting = m_pool.GetIterSet(setConflicts);
+ // Calculate in-mempool ancestors, up to a limit.
+ if (setConflicts.size() == 1) {
+ // In general, when we receive an RBF transaction with mempool conflicts, we want to know whether we
+ // would meet the chain limits after the conflicts have been removed. However, there isn't a practical
+ // way to do this short of calculating the ancestor and descendant sets with an overlay cache of
+ // changed mempool entries. Due to both implementation and runtime complexity concerns, this isn't
+ // very realistic, thus we only ensure a limited set of transactions are RBF'able despite mempool
+ // conflicts here. Importantly, we need to ensure that some transactions which were accepted using
+ // the below carve-out are able to be RBF'ed, without impacting the security the carve-out provides
+ // for off-chain contract systems (see link in the comment below).
+ //
+ // Specifically, the subset of RBF transactions which we allow despite chain limits are those which
+ // conflict directly with exactly one other transaction (but may evict children of said transaction),
+ // and which are not adding any new mempool dependencies. Note that the "no new mempool dependencies"
+ // check is accomplished later, so we don't bother doing anything about it here, but if BIP 125 is
+ // amended, we may need to move that check to here instead of removing it wholesale.
+ //
+ // Such transactions are clearly not merging any existing packages, so we are only concerned with
+ // ensuring that (a) no package is growing past the package size (not count) limits and (b) we are
+ // not allowing something to effectively use the (below) carve-out spot when it shouldn't be allowed
+ // to.
+ //
+ // To check these we first check if we meet the RBF criteria, above, and increment the descendant
+ // limits by the direct conflict and its descendants (as these are recalculated in
+ // CalculateMempoolAncestors by assuming the new transaction being added is a new descendant, with no
+ // removals, of each parent's existing dependant set). The ancestor count limits are unmodified (as
+ // the ancestor limits should be the same for both our new transaction and any conflicts).
+ // We don't bother incrementing m_limit_descendants by the full removal count as that limit never comes
+ // into force here (as we're only adding a single transaction).
+ assert(setIterConflicting.size() == 1);
+ CTxMemPool::txiter conflict = *setIterConflicting.begin();
+
+ m_limit_descendants += 1;
+ m_limit_descendant_size += conflict->GetSizeWithDescendants();
+ }
+
+ std::string errString;
+ if (!m_pool.CalculateMemPoolAncestors(*entry, setAncestors, m_limit_ancestors, m_limit_ancestor_size, m_limit_descendants, m_limit_descendant_size, errString)) {
+ setAncestors.clear();
+ // If CalculateMemPoolAncestors fails second time, we want the original error string.
+ std::string dummy_err_string;
+ // Contracting/payment channels CPFP carve-out:
+ // If the new transaction is relatively small (up to 40k weight)
+ // and has at most one ancestor (ie ancestor limit of 2, including
+ // the new transaction), allow it if its parent has exactly the
+ // descendant limit descendants.
+ //
+ // This allows protocols which rely on distrusting counterparties
+ // being able to broadcast descendants of an unconfirmed transaction
+ // to be secure by simply only having two immediately-spendable
+ // outputs - one for each counterparty. For more info on the uses for
+ // this, see https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2018-November/016518.html
+ if (nSize > EXTRA_DESCENDANT_TX_SIZE_LIMIT ||
+ !m_pool.CalculateMemPoolAncestors(*entry, setAncestors, 2, m_limit_ancestor_size, m_limit_descendants + 1, m_limit_descendant_size + EXTRA_DESCENDANT_TX_SIZE_LIMIT, dummy_err_string)) {
+ return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_NONSTANDARD, "too-long-mempool-chain", errString);
+ }
+ }
+
+ // A transaction that spends outputs that would be replaced by it is invalid. Now
+ // that we have the set of all ancestors we can detect this
+ // pathological case by making sure setConflicts and setAncestors don't
+ // intersect.
+ for (CTxMemPool::txiter ancestorIt : setAncestors)
+ {
+ const uint256 &hashAncestor = ancestorIt->GetTx().GetHash();
+ if (setConflicts.count(hashAncestor))
{
- const uint256 &hashAncestor = ancestorIt->GetTx().GetHash();
- if (setConflicts.count(hashAncestor))
- {
- return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-spends-conflicting-tx",
- strprintf("%s spends conflicting transaction %s",
- hash.ToString(),
- hashAncestor.ToString()));
- }
+ return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-spends-conflicting-tx",
+ strprintf("%s spends conflicting transaction %s",
+ hash.ToString(),
+ hashAncestor.ToString()));
}
+ }
- // Check if it's economically rational to mine this transaction rather
- // than the ones it replaces.
- CAmount nConflictingFees = 0;
- size_t nConflictingSize = 0;
- uint64_t nConflictingCount = 0;
- CTxMemPool::setEntries allConflicting;
-
- // If we don't hold the lock allConflicting might be incomplete; the
- // subsequent RemoveStaged() and addUnchecked() calls don't guarantee
- // mempool consistency for us.
- const bool fReplacementTransaction = setConflicts.size();
- if (fReplacementTransaction)
- {
- CFeeRate newFeeRate(nModifiedFees, nSize);
- std::set<uint256> setConflictsParents;
- const int maxDescendantsToVisit = 100;
- const CTxMemPool::setEntries setIterConflicting = pool.GetIterSet(setConflicts);
- for (const auto& mi : setIterConflicting) {
- // Don't allow the replacement to reduce the feerate of the
- // mempool.
- //
- // We usually don't want to accept replacements with lower
- // feerates than what they replaced as that would lower the
- // feerate of the next block. Requiring that the feerate always
- // be increased is also an easy-to-reason about way to prevent
- // DoS attacks via replacements.
- //
- // We only consider the feerates of transactions being directly
- // replaced, not their indirect descendants. While that does
- // mean high feerate children are ignored when deciding whether
- // or not to replace, we do require the replacement to pay more
- // overall fees too, mitigating most cases.
- CFeeRate oldFeeRate(mi->GetModifiedFee(), mi->GetTxSize());
- if (newFeeRate <= oldFeeRate)
- {
- return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "insufficient fee",
- strprintf("rejecting replacement %s; new feerate %s <= old feerate %s",
- hash.ToString(),
- newFeeRate.ToString(),
- oldFeeRate.ToString()));
- }
-
- for (const CTxIn &txin : mi->GetTx().vin)
- {
- setConflictsParents.insert(txin.prevout.hash);
- }
+ // Check if it's economically rational to mine this transaction rather
+ // than the ones it replaces.
+ nConflictingFees = 0;
+ nConflictingSize = 0;
+ uint64_t nConflictingCount = 0;
- nConflictingCount += mi->GetCountWithDescendants();
- }
- // This potentially overestimates the number of actual descendants
- // but we just want to be conservative to avoid doing too much
- // work.
- if (nConflictingCount <= maxDescendantsToVisit) {
- // If not too many to replace, then calculate the set of
- // transactions that would have to be evicted
- for (CTxMemPool::txiter it : setIterConflicting) {
- pool.CalculateDescendants(it, allConflicting);
- }
- for (CTxMemPool::txiter it : allConflicting) {
- nConflictingFees += it->GetModifiedFee();
- nConflictingSize += it->GetTxSize();
- }
- } else {
- return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_NONSTANDARD, "too many potential replacements",
- strprintf("rejecting replacement %s; too many potential replacements (%d > %d)\n",
+ // If we don't hold the lock allConflicting might be incomplete; the
+ // subsequent RemoveStaged() and addUnchecked() calls don't guarantee
+ // mempool consistency for us.
+ fReplacementTransaction = setConflicts.size();
+ if (fReplacementTransaction)
+ {
+ CFeeRate newFeeRate(nModifiedFees, nSize);
+ std::set<uint256> setConflictsParents;
+ const int maxDescendantsToVisit = 100;
+ for (const auto& mi : setIterConflicting) {
+ // Don't allow the replacement to reduce the feerate of the
+ // mempool.
+ //
+ // We usually don't want to accept replacements with lower
+ // feerates than what they replaced as that would lower the
+ // feerate of the next block. Requiring that the feerate always
+ // be increased is also an easy-to-reason about way to prevent
+ // DoS attacks via replacements.
+ //
+ // We only consider the feerates of transactions being directly
+ // replaced, not their indirect descendants. While that does
+ // mean high feerate children are ignored when deciding whether
+ // or not to replace, we do require the replacement to pay more
+ // overall fees too, mitigating most cases.
+ CFeeRate oldFeeRate(mi->GetModifiedFee(), mi->GetTxSize());
+ if (newFeeRate <= oldFeeRate)
+ {
+ return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "insufficient fee",
+ strprintf("rejecting replacement %s; new feerate %s <= old feerate %s",
hash.ToString(),
- nConflictingCount,
- maxDescendantsToVisit));
+ newFeeRate.ToString(),
+ oldFeeRate.ToString()));
}
- for (unsigned int j = 0; j < tx.vin.size(); j++)
+ for (const CTxIn &txin : mi->GetTx().vin)
{
- // We don't want to accept replacements that require low
- // feerate junk to be mined first. Ideally we'd keep track of
- // the ancestor feerates and make the decision based on that,
- // but for now requiring all new inputs to be confirmed works.
- if (!setConflictsParents.count(tx.vin[j].prevout.hash))
- {
- // Rather than check the UTXO set - potentially expensive -
- // it's cheaper to just check if the new input refers to a
- // tx that's in the mempool.
- if (pool.exists(tx.vin[j].prevout.hash)) {
- return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_NONSTANDARD, "replacement-adds-unconfirmed",
- strprintf("replacement %s adds unconfirmed input, idx %d",
- hash.ToString(), j));
- }
- }
+ setConflictsParents.insert(txin.prevout.hash);
}
- // The replacement must pay greater fees than the transactions it
- // replaces - if we did the bandwidth used by those conflicting
- // transactions would not be paid for.
- if (nModifiedFees < nConflictingFees)
- {
- return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "insufficient fee",
- strprintf("rejecting replacement %s, less fees than conflicting txs; %s < %s",
- hash.ToString(), FormatMoney(nModifiedFees), FormatMoney(nConflictingFees)));
+ nConflictingCount += mi->GetCountWithDescendants();
+ }
+ // This potentially overestimates the number of actual descendants
+ // but we just want to be conservative to avoid doing too much
+ // work.
+ if (nConflictingCount <= maxDescendantsToVisit) {
+ // If not too many to replace, then calculate the set of
+ // transactions that would have to be evicted
+ for (CTxMemPool::txiter it : setIterConflicting) {
+ m_pool.CalculateDescendants(it, allConflicting);
}
-
- // Finally in addition to paying more fees than the conflicts the
- // new transaction must pay for its own bandwidth.
- CAmount nDeltaFees = nModifiedFees - nConflictingFees;
- if (nDeltaFees < ::incrementalRelayFee.GetFee(nSize))
- {
- return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "insufficient fee",
- strprintf("rejecting replacement %s, not enough additional fees to relay; %s < %s",
- hash.ToString(),
- FormatMoney(nDeltaFees),
- FormatMoney(::incrementalRelayFee.GetFee(nSize))));
+ for (CTxMemPool::txiter it : allConflicting) {
+ nConflictingFees += it->GetModifiedFee();
+ nConflictingSize += it->GetTxSize();
}
+ } else {
+ return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_NONSTANDARD, "too many potential replacements",
+ strprintf("rejecting replacement %s; too many potential replacements (%d > %d)\n",
+ hash.ToString(),
+ nConflictingCount,
+ maxDescendantsToVisit));
}
- constexpr unsigned int scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS;
-
- // Check against previous transactions
- // This is done last to help prevent CPU exhaustion denial-of-service attacks.
- PrecomputedTransactionData txdata(tx);
- if (!CheckInputs(tx, state, view, true, scriptVerifyFlags, true, false, txdata)) {
- // SCRIPT_VERIFY_CLEANSTACK requires SCRIPT_VERIFY_WITNESS, so we
- // need to turn both off, and compare against just turning off CLEANSTACK
- // to see if the failure is specifically due to witness validation.
- CValidationState stateDummy; // Want reported failures to be from first CheckInputs
- if (!tx.HasWitness() && CheckInputs(tx, stateDummy, view, true, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, false, txdata) &&
- !CheckInputs(tx, stateDummy, view, true, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true, false, txdata)) {
- // Only the witness is missing, so the transaction itself may be fine.
- state.Invalid(ValidationInvalidReason::TX_WITNESS_MUTATED, false,
- state.GetRejectCode(), state.GetRejectReason(), state.GetDebugMessage());
+ for (unsigned int j = 0; j < tx.vin.size(); j++)
+ {
+ // We don't want to accept replacements that require low
+ // feerate junk to be mined first. Ideally we'd keep track of
+ // the ancestor feerates and make the decision based on that,
+ // but for now requiring all new inputs to be confirmed works.
+ //
+ // Note that if you relax this to make RBF a little more useful,
+ // this may break the CalculateMempoolAncestors RBF relaxation,
+ // above. See the comment above the first CalculateMempoolAncestors
+ // call for more info.
+ if (!setConflictsParents.count(tx.vin[j].prevout.hash))
+ {
+ // Rather than check the UTXO set - potentially expensive -
+ // it's cheaper to just check if the new input refers to a
+ // tx that's in the mempool.
+ if (m_pool.exists(tx.vin[j].prevout.hash)) {
+ return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_NONSTANDARD, "replacement-adds-unconfirmed",
+ strprintf("replacement %s adds unconfirmed input, idx %d",
+ hash.ToString(), j));
+ }
}
- assert(IsTransactionReason(state.GetReason()));
- return false; // state filled in by CheckInputs
}
- // Check again against the current block tip's script verification
- // flags to cache our script execution flags. This is, of course,
- // useless if the next block has different script flags from the
- // previous one, but because the cache tracks script flags for us it
- // will auto-invalidate and we'll just have a few blocks of extra
- // misses on soft-fork activation.
- //
- // This is also useful in case of bugs in the standard flags that cause
- // transactions to pass as valid when they're actually invalid. For
- // instance the STRICTENC flag was incorrectly allowing certain
- // CHECKSIG NOT scripts to pass, even though they were invalid.
- //
- // There is a similar check in CreateNewBlock() to prevent creating
- // invalid blocks (using TestBlockValidity), however allowing such
- // transactions into the mempool can be exploited as a DoS attack.
- unsigned int currentBlockScriptVerifyFlags = GetBlockScriptFlags(::ChainActive().Tip(), chainparams.GetConsensus());
- if (!CheckInputsFromMempoolAndCache(tx, state, view, pool, currentBlockScriptVerifyFlags, true, txdata)) {
- return error("%s: BUG! PLEASE REPORT THIS! CheckInputs failed against latest-block but not STANDARD flags %s, %s",
- __func__, hash.ToString(), FormatStateMessage(state));
- }
-
- if (test_accept) {
- // Tx was accepted, but not added
- return true;
+ // The replacement must pay greater fees than the transactions it
+ // replaces - if we did the bandwidth used by those conflicting
+ // transactions would not be paid for.
+ if (nModifiedFees < nConflictingFees)
+ {
+ return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "insufficient fee",
+ strprintf("rejecting replacement %s, less fees than conflicting txs; %s < %s",
+ hash.ToString(), FormatMoney(nModifiedFees), FormatMoney(nConflictingFees)));
}
- // Remove conflicting transactions from the mempool
- for (CTxMemPool::txiter it : allConflicting)
+ // Finally in addition to paying more fees than the conflicts the
+ // new transaction must pay for its own bandwidth.
+ CAmount nDeltaFees = nModifiedFees - nConflictingFees;
+ if (nDeltaFees < ::incrementalRelayFee.GetFee(nSize))
{
- LogPrint(BCLog::MEMPOOL, "replacing tx %s with %s for %s BTC additional fees, %d delta bytes\n",
- it->GetTx().GetHash().ToString(),
- hash.ToString(),
- FormatMoney(nModifiedFees - nConflictingFees),
- (int)nSize - (int)nConflictingSize);
- if (plTxnReplaced)
- plTxnReplaced->push_back(it->GetSharedTx());
+ return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "insufficient fee",
+ strprintf("rejecting replacement %s, not enough additional fees to relay; %s < %s",
+ hash.ToString(),
+ FormatMoney(nDeltaFees),
+ FormatMoney(::incrementalRelayFee.GetFee(nSize))));
}
- pool.RemoveStaged(allConflicting, false, MemPoolRemovalReason::REPLACED);
+ }
+ return true;
+}
+
+bool MemPoolAccept::PolicyScriptChecks(ATMPArgs& args, Workspace& ws, PrecomputedTransactionData& txdata)
+{
+ const CTransaction& tx = *ws.m_ptx;
- // This transaction should only count for fee estimation if:
- // - it isn't a BIP 125 replacement transaction (may not be widely supported)
- // - it's not being re-added during a reorg which bypasses typical mempool fee limits
- // - the node is not behind
- // - the transaction is not dependent on any other transactions in the mempool
- bool validForFeeEstimation = !fReplacementTransaction && !bypass_limits && IsCurrentForFeeEstimation() && pool.HasNoInputsOf(tx);
+ CValidationState &state = args.m_state;
- // Store transaction in memory
- pool.addUnchecked(entry, setAncestors, validForFeeEstimation);
+ constexpr unsigned int scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS;
- // trim mempool and check if tx was trimmed
- if (!bypass_limits) {
- LimitMempoolSize(pool, gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60);
- if (!pool.exists(hash))
- return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "mempool full");
+ // Check against previous transactions
+ // This is done last to help prevent CPU exhaustion denial-of-service attacks.
+ if (!CheckInputs(tx, state, m_view, scriptVerifyFlags, true, false, txdata)) {
+ // SCRIPT_VERIFY_CLEANSTACK requires SCRIPT_VERIFY_WITNESS, so we
+ // need to turn both off, and compare against just turning off CLEANSTACK
+ // to see if the failure is specifically due to witness validation.
+ CValidationState stateDummy; // Want reported failures to be from first CheckInputs
+ if (!tx.HasWitness() && CheckInputs(tx, stateDummy, m_view, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, false, txdata) &&
+ !CheckInputs(tx, stateDummy, m_view, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true, false, txdata)) {
+ // Only the witness is missing, so the transaction itself may be fine.
+ state.Invalid(ValidationInvalidReason::TX_WITNESS_MUTATED, false,
+ state.GetRejectCode(), state.GetRejectReason(), state.GetDebugMessage());
}
+ assert(IsTransactionReason(state.GetReason()));
+ return false; // state filled in by CheckInputs
}
+ return true;
+}
+
+bool MemPoolAccept::ConsensusScriptChecks(ATMPArgs& args, Workspace& ws, PrecomputedTransactionData& txdata)
+{
+ const CTransaction& tx = *ws.m_ptx;
+ const uint256& hash = ws.m_hash;
+
+ CValidationState &state = args.m_state;
+ const CChainParams& chainparams = args.m_chainparams;
+
+ // Check again against the current block tip's script verification
+ // flags to cache our script execution flags. This is, of course,
+ // useless if the next block has different script flags from the
+ // previous one, but because the cache tracks script flags for us it
+ // will auto-invalidate and we'll just have a few blocks of extra
+ // misses on soft-fork activation.
+ //
+ // This is also useful in case of bugs in the standard flags that cause
+ // transactions to pass as valid when they're actually invalid. For
+ // instance the STRICTENC flag was incorrectly allowing certain
+ // CHECKSIG NOT scripts to pass, even though they were invalid.
+ //
+ // There is a similar check in CreateNewBlock() to prevent creating
+ // invalid blocks (using TestBlockValidity), however allowing such
+ // transactions into the mempool can be exploited as a DoS attack.
+ unsigned int currentBlockScriptVerifyFlags = GetBlockScriptFlags(::ChainActive().Tip(), chainparams.GetConsensus());
+ if (!CheckInputsFromMempoolAndCache(tx, state, m_view, m_pool, currentBlockScriptVerifyFlags, true, txdata)) {
+ return error("%s: BUG! PLEASE REPORT THIS! CheckInputs failed against latest-block but not STANDARD flags %s, %s",
+ __func__, hash.ToString(), FormatStateMessage(state));
+ }
+
+ return true;
+}
+
+bool MemPoolAccept::Finalize(ATMPArgs& args, Workspace& ws)
+{
+ const CTransaction& tx = *ws.m_ptx;
+ const uint256& hash = ws.m_hash;
+ CValidationState &state = args.m_state;
+ const bool bypass_limits = args.m_bypass_limits;
+
+ CTxMemPool::setEntries& allConflicting = ws.m_all_conflicting;
+ CTxMemPool::setEntries& setAncestors = ws.m_ancestors;
+ const CAmount& nModifiedFees = ws.m_modified_fees;
+ const CAmount& nConflictingFees = ws.m_conflicting_fees;
+ const size_t& nConflictingSize = ws.m_conflicting_size;
+ const bool fReplacementTransaction = ws.m_replacement_transaction;
+ std::unique_ptr<CTxMemPoolEntry>& entry = ws.m_entry;
+
+ // Remove conflicting transactions from the mempool
+ for (CTxMemPool::txiter it : allConflicting)
+ {
+ LogPrint(BCLog::MEMPOOL, "replacing tx %s with %s for %s BTC additional fees, %d delta bytes\n",
+ it->GetTx().GetHash().ToString(),
+ hash.ToString(),
+ FormatMoney(nModifiedFees - nConflictingFees),
+ (int)entry->GetTxSize() - (int)nConflictingSize);
+ if (args.m_replaced_transactions)
+ args.m_replaced_transactions->push_back(it->GetSharedTx());
+ }
+ m_pool.RemoveStaged(allConflicting, false, MemPoolRemovalReason::REPLACED);
+
+ // This transaction should only count for fee estimation if:
+ // - it isn't a BIP 125 replacement transaction (may not be widely supported)
+ // - it's not being re-added during a reorg which bypasses typical mempool fee limits
+ // - the node is not behind
+ // - the transaction is not dependent on any other transactions in the mempool
+ bool validForFeeEstimation = !fReplacementTransaction && !bypass_limits && IsCurrentForFeeEstimation() && m_pool.HasNoInputsOf(tx);
+
+ // Store transaction in memory
+ m_pool.addUnchecked(*entry, setAncestors, validForFeeEstimation);
+
+ // trim mempool and check if tx was trimmed
+ if (!bypass_limits) {
+ LimitMempoolSize(m_pool, gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60);
+ if (!m_pool.exists(hash))
+ return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "mempool full");
+ }
+ return true;
+}
+
+bool MemPoolAccept::AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs& args)
+{
+ AssertLockHeld(cs_main);
+ LOCK(m_pool.cs); // mempool "read lock" (held through GetMainSignals().TransactionAddedToMempool())
+
+ Workspace workspace(ptx);
+
+ if (!PreChecks(args, workspace)) return false;
+
+ // Only compute the precomputed transaction data if we need to verify
+ // scripts (ie, other policy checks pass). We perform the inexpensive
+ // checks first and avoid hashing and signature verification unless those
+ // checks pass, to mitigate CPU exhaustion denial-of-service attacks.
+ PrecomputedTransactionData txdata(*ptx);
+
+ if (!PolicyScriptChecks(args, workspace, txdata)) return false;
+
+ if (!ConsensusScriptChecks(args, workspace, txdata)) return false;
+
+ // Tx was accepted, but not added
+ if (args.m_test_accept) return true;
+
+ if (!Finalize(args, workspace)) return false;
+
GetMainSignals().TransactionAddedToMempool(ptx);
return true;
}
+} // anon namespace
+
/** (try to) add transaction to memory pool with a specified acceptance time **/
static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx,
bool* pfMissingInputs, int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced,
bool bypass_limits, const CAmount nAbsurdFee, bool test_accept) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
std::vector<COutPoint> coins_to_uncache;
- bool res = AcceptToMemoryPoolWorker(chainparams, pool, state, tx, pfMissingInputs, nAcceptTime, plTxnReplaced, bypass_limits, nAbsurdFee, coins_to_uncache, test_accept);
+ MemPoolAccept::ATMPArgs args { chainparams, state, pfMissingInputs, nAcceptTime, plTxnReplaced, bypass_limits, nAbsurdFee, coins_to_uncache, test_accept };
+ bool res = MemPoolAccept(pool).AcceptSingleTransaction(tx, args);
if (!res) {
// Remove coins that were not present in the coins cache before calling ATMPW;
// this is to prevent memory DoS in case we receive a large number of
@@ -860,7 +1063,7 @@ static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPo
// (`CCoinsViewCache::cacheCoins`).
for (const COutPoint& hashTx : coins_to_uncache)
- pcoinsTip->Uncache(hashTx);
+ ::ChainstateActive().CoinsTip().Uncache(hashTx);
}
// After we've (potentially) uncached entries, ensure our coins cache is still within its size limits
CValidationState stateDummy;
@@ -1040,6 +1243,40 @@ CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams)
return nSubsidy;
}
+CoinsViews::CoinsViews(
+ std::string ldb_name,
+ size_t cache_size_bytes,
+ bool in_memory,
+ bool should_wipe) : m_dbview(
+ GetDataDir() / ldb_name, cache_size_bytes, in_memory, should_wipe),
+ m_catcherview(&m_dbview) {}
+
+void CoinsViews::InitCache()
+{
+ m_cacheview = MakeUnique<CCoinsViewCache>(&m_catcherview);
+}
+
+// NOTE: for now m_blockman is set to a global, but this will be changed
+// in a future commit.
+CChainState::CChainState() : m_blockman(g_blockman) {}
+
+
+void CChainState::InitCoinsDB(
+ size_t cache_size_bytes,
+ bool in_memory,
+ bool should_wipe,
+ std::string leveldb_name)
+{
+ m_coins_views = MakeUnique<CoinsViews>(
+ leveldb_name, cache_size_bytes, in_memory, should_wipe);
+}
+
+void CChainState::InitCoinsCache()
+{
+ assert(m_coins_views != nullptr);
+ m_coins_views->InitCache();
+}
+
// Note that though this is marked const, we may end up modifying `m_cached_finished_ibd`, which
// is a performance-related implementation detail. This function must be marked
// `const` so that `CValidationInterface` clients (which are given a `const CChainState*`)
@@ -1258,90 +1495,79 @@ void InitScriptExecutionCache() {
*
* Non-static (and re-declared) in src/test/txvalidationcache_tests.cpp
*/
-bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
- if (!tx.IsCoinBase())
- {
- if (pvChecks)
- pvChecks->reserve(tx.vin.size());
-
- // The first loop above does all the inexpensive checks.
- // Only if ALL inputs pass do we perform expensive ECDSA signature checks.
- // Helps prevent CPU exhaustion attacks.
-
- // Skip script verification when connecting blocks under the
- // assumevalid block. Assuming the assumevalid block is valid this
- // is safe because block merkle hashes are still computed and checked,
- // Of course, if an assumed valid block is invalid due to false scriptSigs
- // this optimization would allow an invalid chain to be accepted.
- if (fScriptChecks) {
- // First check if script executions have been cached with the same
- // flags. Note that this assumes that the inputs provided are
- // correct (ie that the transaction hash which is in tx's prevouts
- // properly commits to the scriptPubKey in the inputs view of that
- // transaction).
- uint256 hashCacheEntry;
- // We only use the first 19 bytes of nonce to avoid a second SHA
- // round - giving us 19 + 32 + 4 = 55 bytes (+ 8 + 1 = 64)
- static_assert(55 - sizeof(flags) - 32 >= 128/8, "Want at least 128 bits of nonce for script execution cache");
- CSHA256().Write(scriptExecutionCacheNonce.begin(), 55 - sizeof(flags) - 32).Write(tx.GetWitnessHash().begin(), 32).Write((unsigned char*)&flags, sizeof(flags)).Finalize(hashCacheEntry.begin());
- AssertLockHeld(cs_main); //TODO: Remove this requirement by making CuckooCache not require external locks
- if (scriptExecutionCache.contains(hashCacheEntry, !cacheFullScriptStore)) {
- return true;
- }
-
- for (unsigned int i = 0; i < tx.vin.size(); i++) {
- const COutPoint &prevout = tx.vin[i].prevout;
- const Coin& coin = inputs.AccessCoin(prevout);
- assert(!coin.IsSpent());
-
- // We very carefully only pass in things to CScriptCheck which
- // are clearly committed to by tx' witness hash. This provides
- // a sanity check that our caching is not introducing consensus
- // failures through additional data in, eg, the coins being
- // spent being checked as a part of CScriptCheck.
-
- // Verify signature
- CScriptCheck check(coin.out, tx, i, flags, cacheSigStore, &txdata);
- if (pvChecks) {
- pvChecks->push_back(CScriptCheck());
- check.swap(pvChecks->back());
- } else if (!check()) {
- if (flags & STANDARD_NOT_MANDATORY_VERIFY_FLAGS) {
- // Check whether the failure was caused by a
- // non-mandatory script verification check, such as
- // non-standard DER encodings or non-null dummy
- // arguments; if so, ensure we return NOT_STANDARD
- // instead of CONSENSUS to avoid downstream users
- // splitting the network between upgraded and
- // non-upgraded nodes by banning CONSENSUS-failing
- // data providers.
- CScriptCheck check2(coin.out, tx, i,
- flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheSigStore, &txdata);
- if (check2())
- return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError())));
- }
- // MANDATORY flag failures correspond to
- // ValidationInvalidReason::CONSENSUS. Because CONSENSUS
- // failures are the most serious case of validation
- // failures, we may need to consider using
- // RECENT_CONSENSUS_CHANGE for any script failure that
- // could be due to non-upgraded nodes which we may want to
- // support, to avoid splitting the network (but this
- // depends on the details of how net_processing handles
- // such errors).
- return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(check.GetScriptError())));
- }
- }
+ if (tx.IsCoinBase()) return true;
+
+ if (pvChecks) {
+ pvChecks->reserve(tx.vin.size());
+ }
+
+ // First check if script executions have been cached with the same
+ // flags. Note that this assumes that the inputs provided are
+ // correct (ie that the transaction hash which is in tx's prevouts
+ // properly commits to the scriptPubKey in the inputs view of that
+ // transaction).
+ uint256 hashCacheEntry;
+ // We only use the first 19 bytes of nonce to avoid a second SHA
+ // round - giving us 19 + 32 + 4 = 55 bytes (+ 8 + 1 = 64)
+ static_assert(55 - sizeof(flags) - 32 >= 128/8, "Want at least 128 bits of nonce for script execution cache");
+ CSHA256().Write(scriptExecutionCacheNonce.begin(), 55 - sizeof(flags) - 32).Write(tx.GetWitnessHash().begin(), 32).Write((unsigned char*)&flags, sizeof(flags)).Finalize(hashCacheEntry.begin());
+ AssertLockHeld(cs_main); //TODO: Remove this requirement by making CuckooCache not require external locks
+ if (scriptExecutionCache.contains(hashCacheEntry, !cacheFullScriptStore)) {
+ return true;
+ }
- if (cacheFullScriptStore && !pvChecks) {
- // We executed all of the provided scripts, and were told to
- // cache the result. Do so now.
- scriptExecutionCache.insert(hashCacheEntry);
+ for (unsigned int i = 0; i < tx.vin.size(); i++) {
+ const COutPoint &prevout = tx.vin[i].prevout;
+ const Coin& coin = inputs.AccessCoin(prevout);
+ assert(!coin.IsSpent());
+
+ // We very carefully only pass in things to CScriptCheck which
+ // are clearly committed to by tx' witness hash. This provides
+ // a sanity check that our caching is not introducing consensus
+ // failures through additional data in, eg, the coins being
+ // spent being checked as a part of CScriptCheck.
+
+ // Verify signature
+ CScriptCheck check(coin.out, tx, i, flags, cacheSigStore, &txdata);
+ if (pvChecks) {
+ pvChecks->push_back(CScriptCheck());
+ check.swap(pvChecks->back());
+ } else if (!check()) {
+ if (flags & STANDARD_NOT_MANDATORY_VERIFY_FLAGS) {
+ // Check whether the failure was caused by a
+ // non-mandatory script verification check, such as
+ // non-standard DER encodings or non-null dummy
+ // arguments; if so, ensure we return NOT_STANDARD
+ // instead of CONSENSUS to avoid downstream users
+ // splitting the network between upgraded and
+ // non-upgraded nodes by banning CONSENSUS-failing
+ // data providers.
+ CScriptCheck check2(coin.out, tx, i,
+ flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheSigStore, &txdata);
+ if (check2())
+ return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError())));
}
+ // MANDATORY flag failures correspond to
+ // ValidationInvalidReason::CONSENSUS. Because CONSENSUS
+ // failures are the most serious case of validation
+ // failures, we may need to consider using
+ // RECENT_CONSENSUS_CHANGE for any script failure that
+ // could be due to non-upgraded nodes which we may want to
+ // support, to avoid splitting the network (but this
+ // depends on the details of how net_processing handles
+ // such errors).
+ return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(check.GetScriptError())));
}
}
+ if (cacheFullScriptStore && !pvChecks) {
+ // We executed all of the provided scripts, and were told to
+ // cache the result. Do so now.
+ scriptExecutionCache.insert(hashCacheEntry);
+ }
+
return true;
}
@@ -1608,7 +1834,7 @@ static ThresholdConditionCache warningcache[VERSIONBITS_NUM_BITS] GUARDED_BY(cs_
// environment. See test/functional/p2p-segwit.py.
static bool IsScriptWitnessEnabled(const Consensus::Params& params)
{
- return params.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout != 0;
+ return params.SegwitHeight != std::numeric_limits<int>::max();
}
static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consensus::Params& consensusparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
@@ -1644,12 +1870,13 @@ static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consens
flags |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY;
}
- // Start enforcing BIP68 (sequence locks) and BIP112 (CHECKSEQUENCEVERIFY) using versionbits logic.
- if (VersionBitsState(pindex->pprev, consensusparams, Consensus::DEPLOYMENT_CSV, versionbitscache) == ThresholdState::ACTIVE) {
+ // Start enforcing BIP112 (CHECKSEQUENCEVERIFY)
+ if (pindex->nHeight >= consensusparams.CSVHeight) {
flags |= SCRIPT_VERIFY_CHECKSEQUENCEVERIFY;
}
- if (IsNullDummyEnabled(pindex->pprev, consensusparams)) {
+ // Start enforcing BIP147 NULLDUMMY (activated simultaneously with segwit)
+ if (IsWitnessEnabled(pindex->pprev, consensusparams)) {
flags |= SCRIPT_VERIFY_NULLDUMMY;
}
@@ -1728,6 +1955,11 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
pindexBestHeader->GetAncestor(pindex->nHeight) == pindex &&
pindexBestHeader->nChainWork >= nMinimumChainWork) {
// This block is a member of the assumed verified chain and an ancestor of the best header.
+ // Script verification is skipped when connecting blocks under the
+ // assumevalid block. Assuming the assumevalid block is valid this
+ // is safe because block merkle hashes are still computed and checked,
+ // Of course, if an assumed valid block is invalid due to false scriptSigs
+ // this optimization would allow an invalid chain to be accepted.
// The equivalent time check discourages hash power from extorting the network via DOS attack
// into accepting an invalid block through telling users they must manually set assumevalid.
// Requiring a software change or burying the invalid block, regardless of the setting, makes
@@ -1749,7 +1981,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
// If such overwrites are allowed, coinbases and transactions depending upon those
// can be duplicated to remove the ability to spend the first instance -- even after
// being sent to another address.
- // See BIP30 and http://r6.ca/blog/20120206T005236Z.html for more information.
+ // See BIP30, CVE-2012-1909, and http://r6.ca/blog/20120206T005236Z.html for more information.
// This logic is not necessary for memory pool transactions, as AcceptToMemoryPool
// already refuses previously-known transaction ids entirely.
// This rule was originally applied to all blocks with a timestamp after March 15, 2012, 0:00 UTC.
@@ -1834,9 +2066,9 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
}
}
- // Start enforcing BIP68 (sequence locks) and BIP112 (CHECKSEQUENCEVERIFY) using versionbits logic.
+ // Start enforcing BIP68 (sequence locks)
int nLockTimeFlags = 0;
- if (VersionBitsState(pindex->pprev, chainparams.GetConsensus(), Consensus::DEPLOYMENT_CSV, versionbitscache) == ThresholdState::ACTIVE) {
+ if (pindex->nHeight >= chainparams.GetConsensus().CSVHeight) {
nLockTimeFlags |= LOCKTIME_VERIFY_SEQUENCE;
}
@@ -1911,7 +2143,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
{
std::vector<CScriptCheck> vChecks;
bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */
- if (!CheckInputs(tx, state, view, fScriptChecks, flags, fCacheResults, fCacheResults, txdata[i], nScriptCheckThreads ? &vChecks : nullptr)) {
+ if (fScriptChecks && !CheckInputs(tx, state, view, flags, fCacheResults, fCacheResults, txdata[i], nScriptCheckThreads ? &vChecks : nullptr)) {
if (state.GetReason() == ValidationInvalidReason::TX_NOT_STANDARD) {
// CheckInputs may return NOT_STANDARD for extra flags we passed,
// but we can't return that, as it's not defined for a block, so
@@ -1981,6 +2213,7 @@ bool CChainState::FlushStateToDisk(
{
int64_t nMempoolUsage = mempool.DynamicMemoryUsage();
LOCK(cs_main);
+ assert(this->CanFlushToDisk());
static int64_t nLastWrite = 0;
static int64_t nLastFlush = 0;
std::set<int> setFilesToPrune;
@@ -2014,7 +2247,7 @@ bool CChainState::FlushStateToDisk(
nLastFlush = nNow;
}
int64_t nMempoolSizeMax = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
- int64_t cacheSize = pcoinsTip->DynamicMemoryUsage();
+ int64_t cacheSize = CoinsTip().DynamicMemoryUsage();
int64_t nTotalSpace = nCoinCacheUsage + std::max<int64_t>(nMempoolSizeMax - nMempoolUsage, 0);
// The cache is large and we're within 10% and 10 MiB of the limit, but we have time now (not in the middle of a block processing).
bool fCacheLarge = mode == FlushStateMode::PERIODIC && cacheSize > std::max((9 * nTotalSpace) / 10, nTotalSpace - MAX_BLOCK_COINSDB_USAGE * 1024 * 1024);
@@ -2058,17 +2291,17 @@ bool CChainState::FlushStateToDisk(
nLastWrite = nNow;
}
// Flush best chain related state. This can only be done if the blocks / block index write was also done.
- if (fDoFullFlush && !pcoinsTip->GetBestBlock().IsNull()) {
+ if (fDoFullFlush && !CoinsTip().GetBestBlock().IsNull()) {
// Typical Coin structures on disk are around 48 bytes in size.
// Pushing a new one to the database can cause it to be written
// twice (once in the log, and once in the tables). This is already
// an overestimation, as most will delete an existing entry or
// overwrite one. Still, use a conservative safety factor of 2.
- if (!CheckDiskSpace(GetDataDir(), 48 * 2 * 2 * pcoinsTip->GetCacheSize())) {
+ if (!CheckDiskSpace(GetDataDir(), 48 * 2 * 2 * CoinsTip().GetCacheSize())) {
return AbortNode(state, "Disk space is too low!", _("Error: Disk space is too low!").translated, CClientUIInterface::MSG_NOPREFIX);
}
// Flush the chainstate (which may refer to block index entries).
- if (!pcoinsTip->Flush())
+ if (!CoinsTip().Flush())
return AbortNode(state, "Failed to write to coin database");
nLastFlush = nNow;
full_flush_completed = true;
@@ -2120,7 +2353,9 @@ static void AppendWarning(std::string& res, const std::string& warn)
}
/** Check warning conditions and do some notifications on new chain tip set. */
-void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams& chainParams) {
+void static UpdateTip(const CBlockIndex* pindexNew, const CChainParams& chainParams)
+ EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
+{
// New best block
mempool.AddTransactionsUpdated(1);
@@ -2158,14 +2393,12 @@ void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams& chainPar
if (nUpgraded > 0)
AppendWarning(warningMessages, strprintf(_("%d of last 100 blocks have unexpected version").translated, nUpgraded));
}
- LogPrintf("%s: new best=%s height=%d version=0x%08x log2_work=%.8g tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo)", __func__, /* Continued */
+ LogPrintf("%s: new best=%s height=%d version=0x%08x log2_work=%.8g tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo)%s\n", __func__,
pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, pindexNew->nVersion,
log(pindexNew->nChainWork.getdouble())/log(2.0), (unsigned long)pindexNew->nChainTx,
FormatISO8601DateTime(pindexNew->GetBlockTime()),
- GuessVerificationProgress(chainParams.TxData(), pindexNew), pcoinsTip->DynamicMemoryUsage() * (1.0 / (1<<20)), pcoinsTip->GetCacheSize());
- if (!warningMessages.empty())
- LogPrintf(" warning='%s'", warningMessages); /* Continued */
- LogPrintf("\n");
+ GuessVerificationProgress(chainParams.TxData(), pindexNew), ::ChainstateActive().CoinsTip().DynamicMemoryUsage() * (1.0 / (1<<20)), ::ChainstateActive().CoinsTip().GetCacheSize(),
+ !warningMessages.empty() ? strprintf(" warning='%s'", warningMessages) : "");
}
@@ -2191,7 +2424,7 @@ bool CChainState::DisconnectTip(CValidationState& state, const CChainParams& cha
// Apply the block atomically to the chain state.
int64_t nStart = GetTimeMicros();
{
- CCoinsViewCache view(pcoinsTip.get());
+ CCoinsViewCache view(&CoinsTip());
assert(view.GetBestBlock() == pindexDelete->GetBlockHash());
if (DisconnectBlock(block, pindexDelete, view) != DISCONNECT_OK)
return error("DisconnectTip(): DisconnectBlock %s failed", pindexDelete->GetBlockHash().ToString());
@@ -2319,7 +2552,7 @@ bool CChainState::ConnectTip(CValidationState& state, const CChainParams& chainp
int64_t nTime3;
LogPrint(BCLog::BENCH, " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * MILLI, nTimeReadFromDisk * MICRO);
{
- CCoinsViewCache view(pcoinsTip.get());
+ CCoinsViewCache view(&CoinsTip());
bool rv = ConnectBlock(blockConnecting, state, pindexNew, view, chainparams);
GetMainSignals().BlockChecked(blockConnecting, state);
if (!rv) {
@@ -2428,6 +2661,8 @@ void CChainState::PruneBlockIndexCandidates() {
/**
* Try to make some progress towards making pindexMostWork the active block.
* pblock is either nullptr or a pointer to a CBlock corresponding to pindexMostWork.
+ *
+ * @returns true unless a system error occurred
*/
bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace)
{
@@ -2506,7 +2741,7 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar
// any disconnected transactions back to the mempool.
UpdateMempoolForReorg(disconnectpool, true);
}
- mempool.check(pcoinsTip.get());
+ mempool.check(&CoinsTip());
// Callbacks/notifications for a new best chain.
if (fInvalidFound)
@@ -2517,7 +2752,7 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar
return true;
}
-static void NotifyHeaderTip() LOCKS_EXCLUDED(cs_main) {
+static bool NotifyHeaderTip() LOCKS_EXCLUDED(cs_main) {
bool fNotify = false;
bool fInitialBlockDownload = false;
static CBlockIndex* pindexHeaderOld = nullptr;
@@ -2536,6 +2771,7 @@ static void NotifyHeaderTip() LOCKS_EXCLUDED(cs_main) {
if (fNotify) {
uiInterface.NotifyHeaderTip(fInitialBlockDownload, pindexHeader);
}
+ return fNotify;
}
static void LimitValidationInterfaceQueue() LOCKS_EXCLUDED(cs_main) {
@@ -2546,15 +2782,6 @@ static void LimitValidationInterfaceQueue() LOCKS_EXCLUDED(cs_main) {
}
}
-/**
- * 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
// far from a guarantee. Things in the P2P/RPC will often end up calling
@@ -2602,8 +2829,10 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams&
bool fInvalidFound = false;
std::shared_ptr<const CBlock> nullBlockPtr;
- if (!ActivateBestChainStep(state, chainparams, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : nullBlockPtr, fInvalidFound, connectTrace))
+ if (!ActivateBestChainStep(state, chainparams, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : nullBlockPtr, fInvalidFound, connectTrace)) {
+ // A system error occurred
return false;
+ }
blocks_connected = true;
if (fInvalidFound) {
@@ -3024,6 +3253,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-cb-multiple", "more than one coinbase");
// Check transactions
+ // Must check for duplicate inputs (see CVE-2018-17144)
for (const auto& tx : block.vtx)
if (!CheckTransaction(*tx, state, true))
return state.Invalid(state.GetReason(), false, state.GetRejectCode(), state.GetRejectReason(),
@@ -3045,14 +3275,8 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P
bool IsWitnessEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& params)
{
- LOCK(cs_main);
- return (VersionBitsState(pindexPrev, params, Consensus::DEPLOYMENT_SEGWIT, versionbitscache) == ThresholdState::ACTIVE);
-}
-
-bool IsNullDummyEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& params)
-{
- LOCK(cs_main);
- return (VersionBitsState(pindexPrev, params, Consensus::DEPLOYMENT_SEGWIT, versionbitscache) == ThresholdState::ACTIVE);
+ int height = pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1;
+ return (height >= params.SegwitHeight);
}
// Compute at which vout of the block's coinbase transaction the witness
@@ -3087,7 +3311,7 @@ std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBloc
std::vector<unsigned char> commitment;
int commitpos = GetWitnessCommitmentIndex(block);
std::vector<unsigned char> ret(32, 0x00);
- if (consensusParams.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout != 0) {
+ if (consensusParams.SegwitHeight != std::numeric_limits<int>::max()) {
if (commitpos == -1) {
uint256 witnessroot = BlockWitnessMerkleRoot(block, nullptr);
CHash256().Write(witnessroot.begin(), 32).Write(ret.data(), 32).Finalize(witnessroot.begin());
@@ -3185,9 +3409,9 @@ static bool ContextualCheckBlock(const CBlock& block, CValidationState& state, c
{
const int nHeight = pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1;
- // Start enforcing BIP113 (Median Time Past) using versionbits logic.
+ // Start enforcing BIP113 (Median Time Past).
int nLockTimeFlags = 0;
- if (VersionBitsState(pindexPrev, consensusParams, Consensus::DEPLOYMENT_CSV, versionbitscache) == ThresholdState::ACTIVE) {
+ if (nHeight >= consensusParams.CSVHeight) {
assert(pindexPrev != nullptr);
nLockTimeFlags |= LOCKTIME_MEDIAN_TIME_PAST;
}
@@ -3222,7 +3446,7 @@ static bool ContextualCheckBlock(const CBlock& block, CValidationState& state, c
// {0xaa, 0x21, 0xa9, 0xed}, and the following 32 bytes are SHA256^2(witness root, witness reserved value). In case there are
// multiple, the last one is used.
bool fHaveWitness = false;
- if (VersionBitsState(pindexPrev, consensusParams, Consensus::DEPLOYMENT_SEGWIT, versionbitscache) == ThresholdState::ACTIVE) {
+ if (nHeight >= consensusParams.SegwitHeight) {
int commitpos = GetWitnessCommitmentIndex(block);
if (commitpos != -1) {
bool malleated = false;
@@ -3362,7 +3586,11 @@ bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, CValidatio
}
}
}
- NotifyHeaderTip();
+ if (NotifyHeaderTip()) {
+ if (::ChainstateActive().IsInitialBlockDownload() && ppindex && *ppindex) {
+ LogPrintf("Synchronizing blockheaders, height: %d (~%.2f%%)\n", (*ppindex)->nHeight, 100.0/((*ppindex)->nHeight+(GetAdjustedTime() - (*ppindex)->GetBlockTime()) / Params().GetConsensus().nPowTargetSpacing) * (*ppindex)->nHeight);
+ }
+ }
return true;
}
@@ -3508,7 +3736,7 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams,
{
AssertLockHeld(cs_main);
assert(pindexPrev && pindexPrev == ::ChainActive().Tip());
- CCoinsViewCache viewNew(pcoinsTip.get());
+ CCoinsViewCache viewNew(&::ChainstateActive().CoinsTip());
uint256 block_hash(block.GetHash());
CBlockIndex indexDummy(block);
indexDummy.pprev = pindexPrev;
@@ -3861,12 +4089,14 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams) EXCLUSIVE_LOCKS_RE
bool LoadChainTip(const CChainParams& chainparams)
{
AssertLockHeld(cs_main);
- assert(!pcoinsTip->GetBestBlock().IsNull()); // Never called when the coins view is empty
+ const CCoinsViewCache& coins_cache = ::ChainstateActive().CoinsTip();
+ assert(!coins_cache.GetBestBlock().IsNull()); // Never called when the coins view is empty
- if (::ChainActive().Tip() && ::ChainActive().Tip()->GetBlockHash() == pcoinsTip->GetBestBlock()) return true;
+ if (::ChainActive().Tip() &&
+ ::ChainActive().Tip()->GetBlockHash() == coins_cache.GetBestBlock()) return true;
// Load pointer to end of best chain
- CBlockIndex* pindex = LookupBlockIndex(pcoinsTip->GetBestBlock());
+ CBlockIndex* pindex = LookupBlockIndex(coins_cache.GetBestBlock());
if (!pindex) {
return false;
}
@@ -3943,7 +4173,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
}
}
// check level 3: check for inconsistencies during memory-only disconnect of tip blocks
- if (nCheckLevel >= 3 && (coins.DynamicMemoryUsage() + pcoinsTip->DynamicMemoryUsage()) <= nCoinCacheUsage) {
+ if (nCheckLevel >= 3 && (coins.DynamicMemoryUsage() + ::ChainstateActive().CoinsTip().DynamicMemoryUsage()) <= nCoinCacheUsage) {
assert(coins.GetBestBlock() == pindex->GetBlockHash());
DisconnectResult res = ::ChainstateActive().DisconnectBlock(block, pindex, coins);
if (res == DISCONNECT_FAILED) {
diff --git a/src/validation.h b/src/validation.h
index d747fdbf27..06252b6bc8 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -19,6 +19,7 @@
#include <script/script_error.h>
#include <sync.h>
#include <txmempool.h> // For CTxMemPool::cs
+#include <txdb.h>
#include <versionbits.h>
#include <algorithm>
@@ -37,7 +38,6 @@ class CBlockIndex;
class CBlockTreeDB;
class CBlockUndo;
class CChainParams;
-class CCoinsViewDB;
class CInv;
class CConnman;
class CScriptCheck;
@@ -50,10 +50,6 @@ struct DisconnectedBlockTransactions;
struct PrecomputedTransactionData;
struct LockPoints;
-/** Default for -whitelistrelay. */
-static const bool DEFAULT_WHITELISTRELAY = true;
-/** Default for -whitelistforcerelay. */
-static const bool DEFAULT_WHITELISTFORCERELAY = false;
/** Default for -minrelaytxfee, minimum relay fee for transactions */
static const unsigned int DEFAULT_MIN_RELAY_TX_FEE = 1000;
/** Default for -limitancestorcount, max number of in-mempool ancestors */
@@ -215,7 +211,7 @@ static const uint64_t MIN_DISK_SPACE_FOR_BLOCK_FILES = 550 * 1024 * 1024;
* @param[in] pblock The block we want to process.
* @param[in] fForceProcessing Process this block even if unrequested; used for non-network block sources and whitelisted peers.
* @param[out] fNewBlock A boolean which is set to indicate if the block was first received via this call
- * @return True if state.IsValid()
+ * @returns If the block was processed, independently of block validity
*/
bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock> pblock, bool fForceProcessing, bool* fNewBlock) LOCKS_EXCLUDED(cs_main);
@@ -383,12 +379,10 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P
/** Check a block is completely valid from start to finish (only works on top of our current best block) */
bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, const CBlock& block, CBlockIndex* pindexPrev, bool fCheckPOW = true, bool fCheckMerkleRoot = true) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-/** Check whether witness commitments are required for block. */
+/** Check whether witness commitments are required for a block, and whether to enforce NULLDUMMY (BIP 147) rules.
+ * Note that transaction witness validation rules are always enforced when P2SH is enforced. */
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) LOCKS_EXCLUDED(cs_main);
@@ -506,6 +500,41 @@ public:
};
/**
+ * A convenience class for constructing the CCoinsView* hierarchy used
+ * to facilitate access to the UTXO set.
+ *
+ * This class consists of an arrangement of layered CCoinsView objects,
+ * preferring to store and retrieve coins in memory via `m_cacheview` but
+ * ultimately falling back on cache misses to the canonical store of UTXOs on
+ * disk, `m_dbview`.
+ */
+class CoinsViews {
+
+public:
+ //! The lowest level of the CoinsViews cache hierarchy sits in a leveldb database on disk.
+ //! All unspent coins reside in this store.
+ CCoinsViewDB m_dbview GUARDED_BY(cs_main);
+
+ //! This view wraps access to the leveldb instance and handles read errors gracefully.
+ CCoinsViewErrorCatcher m_catcherview GUARDED_BY(cs_main);
+
+ //! This is the top layer of the cache hierarchy - it keeps as many coins in memory as
+ //! can fit per the dbcache setting.
+ std::unique_ptr<CCoinsViewCache> m_cacheview GUARDED_BY(cs_main);
+
+ //! This constructor initializes CCoinsViewDB and CCoinsViewErrorCatcher instances, but it
+ //! *does not* create a CCoinsViewCache instance by default. This is done separately because the
+ //! presence of the cache has implications on whether or not we're allowed to flush the cache's
+ //! state to disk, which should not be done until the health of the database is verified.
+ //!
+ //! All arguments forwarded onto CCoinsViewDB.
+ CoinsViews(std::string ldb_name, size_t cache_size_bytes, bool in_memory, bool should_wipe);
+
+ //! Initialize the CCoinsViewCache member.
+ void InitCache() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
+};
+
+/**
* CChainState stores and provides an API to update our local knowledge of the
* current best chain.
*
@@ -553,12 +582,39 @@ private:
//! easily as opposed to referencing a global.
BlockManager& m_blockman;
+ //! Manages the UTXO set, which is a reflection of the contents of `m_chain`.
+ std::unique_ptr<CoinsViews> m_coins_views;
+
public:
- CChainState(BlockManager& blockman) : m_blockman(blockman) { }
+ CChainState(BlockManager& blockman) : m_blockman(blockman) {}
+ CChainState();
+
+ /**
+ * Initialize the CoinsViews UTXO set database management data structures. The in-memory
+ * cache is initialized separately.
+ *
+ * All parameters forwarded to CoinsViews.
+ */
+ void InitCoinsDB(
+ size_t cache_size_bytes,
+ bool in_memory,
+ bool should_wipe,
+ std::string leveldb_name = "chainstate");
+
+ //! Initialize the in-memory coins cache (to be done after the health of the on-disk database
+ //! is verified).
+ void InitCoinsCache() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
+
+ //! @returns whether or not the CoinsViews object has been fully initialized and we can
+ //! safely flush this object to disk.
+ bool CanFlushToDisk() EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
+ return m_coins_views && m_coins_views->m_cacheview;
+ }
//! The current chain of blockheaders we consult and build on.
//! @see CChain, CBlockIndex.
CChain m_chain;
+
/**
* The set of all CBlockIndex entries with BLOCK_VALID_TRANSACTIONS (for itself and all ancestors) and
* as good as our current tip or better. Entries may be failed, though, and pruning nodes may be
@@ -566,6 +622,29 @@ public:
*/
std::set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexCandidates;
+ //! @returns A reference to the in-memory cache of the UTXO set.
+ CCoinsViewCache& CoinsTip() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+ {
+ assert(m_coins_views->m_cacheview);
+ return *m_coins_views->m_cacheview.get();
+ }
+
+ //! @returns A reference to the on-disk UTXO set database.
+ CCoinsViewDB& CoinsDB() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+ {
+ return m_coins_views->m_dbview;
+ }
+
+ //! @returns A reference to a wrapped view of the in-memory UTXO set that
+ //! handles disk read errors gracefully.
+ CCoinsViewErrorCatcher& CoinsErrorCatcher() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+ {
+ return m_coins_views->m_catcherview;
+ }
+
+ //! Destructs all objects related to accessing the UTXO set.
+ void ResetCoinsViews() { m_coins_views.reset(); }
+
/**
* Update the on-disk chain state.
* The caches and indexes are flushed depending on the mode we're called with
@@ -574,6 +653,8 @@ public:
*
* If FlushStateMode::NONE is used, then FlushStateToDisk(...) won't do anything
* besides checking if we need to prune.
+ *
+ * @returns true unless a system error occurred
*/
bool FlushStateToDisk(
const CChainParams& chainparams,
@@ -588,7 +669,20 @@ public:
//! if we pruned.
void PruneAndFlush();
- bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock) LOCKS_EXCLUDED(cs_main);
+ /**
+ * 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.
+ *
+ * @returns true unless a system error occurred
+ */
+ bool ActivateBestChain(CValidationState& state,
+ const CChainParams& chainparams,
+ std::shared_ptr<const CBlock> pblock) LOCKS_EXCLUDED(cs_main);
bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@@ -597,7 +691,7 @@ public:
bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex,
CCoinsViewCache& view, const CChainParams& chainparams, bool fJustCheck = false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- // Block disconnection on our pcoinsTip:
+ // Apply the effects of a block disconnection on the UTXO set.
bool DisconnectTip(CValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions* disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, ::mempool.cs);
// Manual block validity manipulation:
@@ -659,11 +753,10 @@ CChain& ChainActive();
/** @returns the global block index map. */
BlockMap& BlockIndex();
-/** Global variable that points to the coins database (protected by cs_main) */
-extern std::unique_ptr<CCoinsViewDB> pcoinsdbview;
-
-/** Global variable that points to the active CCoinsView (protected by cs_main) */
-extern std::unique_ptr<CCoinsViewCache> pcoinsTip;
+// Most often ::ChainstateActive() should be used instead of this, but some code
+// may not be able to assume that this has been initialized yet and so must use it
+// directly, e.g. init.cpp.
+extern std::unique_ptr<CChainState> g_chainstate;
/** Global variable that points to the active block tree (protected by cs_main) */
extern std::unique_ptr<CBlockTreeDB> pblocktree;
diff --git a/src/versionbits.cpp b/src/versionbits.cpp
index 3f297c0ebb..2285579cd9 100644
--- a/src/versionbits.cpp
+++ b/src/versionbits.cpp
@@ -94,7 +94,6 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex*
return state;
}
-// return the numerical statistics of blocks signalling the specified BIP9 condition in this current period
BIP9Stats AbstractThresholdConditionChecker::GetStateStatisticsFor(const CBlockIndex* pindex, const Consensus::Params& params) const
{
BIP9Stats stats = {};
diff --git a/src/versionbits.h b/src/versionbits.h
index cdc947cd9e..d8dda7d95b 100644
--- a/src/versionbits.h
+++ b/src/versionbits.h
@@ -17,12 +17,17 @@ static const int32_t VERSIONBITS_TOP_MASK = 0xE0000000UL;
/** Total bits available for versionbits */
static const int32_t VERSIONBITS_NUM_BITS = 29;
+/** BIP 9 defines a finite-state-machine to deploy a softfork in multiple stages.
+ * State transitions happen during retarget period if conditions are met
+ * In case of reorg, transitions can go backward. Without transition, state is
+ * inherited between periods. All blocks of a period share the same state.
+ */
enum class ThresholdState {
- DEFINED,
- STARTED,
- LOCKED_IN,
- ACTIVE,
- FAILED,
+ DEFINED, // First state that each softfork starts out as. The genesis block is by definition in this state for each deployment.
+ STARTED, // For blocks past the starttime.
+ LOCKED_IN, // For one retarget period after the first retarget period with STARTED blocks of which at least threshold have the associated bit set in nVersion.
+ ACTIVE, // For all blocks after the LOCKED_IN retarget period (final state)
+ FAILED, // For all blocks once the first retarget period after the timeout time is hit, if LOCKED_IN wasn't already reached (final state)
};
// A map that gives the state for blocks whose height is a multiple of Period().
@@ -30,11 +35,17 @@ enum class ThresholdState {
// will either be nullptr or a block with (height + 1) % Period() == 0.
typedef std::map<const CBlockIndex*, ThresholdState> ThresholdConditionCache;
+/** Display status of an in-progress BIP9 softfork */
struct BIP9Stats {
+ /** Length of blocks of the BIP9 signalling period */
int period;
+ /** Number of blocks with the version bit set required to activate the softfork */
int threshold;
+ /** Number of blocks elapsed since the beginning of the current period */
int elapsed;
+ /** Number of blocks with the version bit set since the beginning of the current period */
int count;
+ /** False if there are not enough blocks left in this period to pass activation threshold */
bool possible;
};
@@ -50,12 +61,17 @@ protected:
virtual int Threshold(const Consensus::Params& params) const =0;
public:
+ /** Returns the numerical statistics of an in-progress BIP9 softfork in the current period */
BIP9Stats GetStateStatisticsFor(const CBlockIndex* pindex, const Consensus::Params& params) const;
- // Note that the functions below take a pindexPrev as input: they compute information for block B based on its parent.
+ /** Returns the state for pindex A based on parent pindexPrev B. Applies any state transition if conditions are present.
+ * Caches state from first block of period. */
ThresholdState GetStateFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const;
+ /** Returns the height since when the ThresholdState has started for pindex A based on parent pindexPrev B, all blocks of a period share the same */
int GetStateSinceHeightFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const;
};
+/** BIP 9 allows multiple softforks to be deployed in parallel. We cache per-period state for every one of them
+ * keyed by the bit position used to signal support. */
struct VersionBitsCache
{
ThresholdConditionCache caches[Consensus::MAX_VERSION_BITS_DEPLOYMENTS];
diff --git a/src/versionbitsinfo.cpp b/src/versionbitsinfo.cpp
index ecf3482927..82df92ac90 100644
--- a/src/versionbitsinfo.cpp
+++ b/src/versionbitsinfo.cpp
@@ -11,12 +11,4 @@ const struct VBDeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_B
/*.name =*/ "testdummy",
/*.gbt_force =*/ true,
},
- {
- /*.name =*/ "csv",
- /*.gbt_force =*/ true,
- },
- {
- /*.name =*/ "segwit",
- /*.gbt_force =*/ true,
- }
};
diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp
index cb94799d9e..e766deadb7 100644
--- a/src/wallet/init.cpp
+++ b/src/wallet/init.cpp
@@ -34,41 +34,41 @@ const WalletInitInterface& g_wallet_init_interface = WalletInit();
void WalletInit::AddWalletOptions() const
{
- 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("-avoidpartialspends", strprintf("Group outputs by address, selecting all or none, instead of selecting on a per-output basis. Privacy is improved as an address is only used once (unless someone sends to it after spending from it), but may result in slightly higher fees as suboptimal coin selection may result due to the added limitation (default: %u (always enabled for wallets with \"avoid_reuse\" enabled))", DEFAULT_AVOIDPARTIALSPENDS), 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("-addresstype", strprintf("What type of addresses to use (\"legacy\", \"p2sh-segwit\", or \"bech32\", default: \"%s\")", FormatOutputType(DEFAULT_ADDRESS_TYPE)), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
+ gArgs.AddArg("-avoidpartialspends", strprintf("Group outputs by address, selecting all or none, instead of selecting on a per-output basis. Privacy is improved as an address is only used once (unless someone sends to it after spending from it), but may result in slightly higher fees as suboptimal coin selection may result due to the added limitation (default: %u (always enabled for wallets with \"avoid_reuse\" enabled))", DEFAULT_AVOIDPARTIALSPENDS), ArgsManager::ALLOW_ANY, 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)", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
+ gArgs.AddArg("-disablewallet", "Do not load the wallet and disable wallet RPC calls", ArgsManager::ALLOW_ANY, 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)), false, OptionsCategory::WALLET);
+ CURRENCY_UNIT, FormatMoney(DEFAULT_DISCARD_FEE)), ArgsManager::ALLOW_ANY, 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);
+ CURRENCY_UNIT, FormatMoney(DEFAULT_FALLBACK_FEE)), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
+ gArgs.AddArg("-keypool=<n>", strprintf("Set key pool size to <n> (default: %u)", DEFAULT_KEYPOOL_SIZE), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
gArgs.AddArg("-maxtxfee=<amt>", strprintf("Maximum total fees (in %s) to use in a single wallet transaction; setting this too low may abort large transactions (default: %s)",
- CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MAXFEE)), false, OptionsCategory::DEBUG_TEST);
+ CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MAXFEE)), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
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);
+ CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MINFEE)), ArgsManager::ALLOW_ANY, 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", strprintf("Make the wallet broadcast transactions (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);
+ CURRENCY_UNIT, FormatMoney(CFeeRate{DEFAULT_PAY_TX_FEE}.GetFeePerK())), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
+ gArgs.AddArg("-rescan", "Rescan the block chain for missing wallet transactions on startup", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
+ gArgs.AddArg("-salvagewallet", "Attempt to recover private keys from a corrupt wallet on startup", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
+ gArgs.AddArg("-spendzeroconfchange", strprintf("Spend unconfirmed change when sending transactions (default: %u)", DEFAULT_SPEND_ZEROCONF_CHANGE), ArgsManager::ALLOW_ANY, 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), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
+ gArgs.AddArg("-upgradewallet", "Upgrade wallet to latest format on startup", ArgsManager::ALLOW_ANY, 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>.)", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::WALLET);
+ gArgs.AddArg("-walletbroadcast", strprintf("Make the wallet broadcast transactions (default: %u)", DEFAULT_WALLETBROADCAST), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
+ gArgs.AddArg("-walletdir=<dir>", "Specify directory to hold wallets (default: <datadir>/wallets if it exists, otherwise <datadir>)", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
#if HAVE_SYSTEM
- gArgs.AddArg("-walletnotify=<cmd>", "Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)", false, OptionsCategory::WALLET);
+ gArgs.AddArg("-walletnotify=<cmd>", "Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
#endif
- gArgs.AddArg("-walletrbf", strprintf("Send transactions with full-RBF opt-in enabled (RPC only, default: %u)", DEFAULT_WALLET_RBF), false, OptionsCategory::WALLET);
+ gArgs.AddArg("-walletrbf", strprintf("Send transactions with full-RBF opt-in enabled (RPC only, default: %u)", DEFAULT_WALLET_RBF), ArgsManager::ALLOW_ANY, 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. payment request information, 2 = drop tx meta data)", false, OptionsCategory::WALLET);
+ " (1 = keep tx meta data e.g. payment request information, 2 = drop tx meta data)", ArgsManager::ALLOW_ANY, 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);
+ gArgs.AddArg("-dblogsize=<n>", strprintf("Flush wallet database activity from memory to disk log every <n> megabytes (default: %u)", DEFAULT_WALLET_DBLOGSIZE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST);
+ gArgs.AddArg("-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %u)", DEFAULT_FLUSHWALLET), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST);
+ gArgs.AddArg("-privdb", strprintf("Sets the DB_PRIVATE flag in the wallet db environment (default: %u)", DEFAULT_WALLET_PRIVDB), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, 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), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST);
}
bool WalletInit::ParameterInteraction() const
diff --git a/src/wallet/load.h b/src/wallet/load.h
index 81f078fd10..5a62e29303 100644
--- a/src/wallet/load.h
+++ b/src/wallet/load.h
@@ -17,7 +17,7 @@ class Chain;
//! 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).
+//! being loaded (WalletInit::ParameterInteraction() forbids -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet).
bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files);
//! Load wallet databases.
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index a905cc0c55..f52e4318c8 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -384,8 +384,7 @@ UniValue importprunedfunds(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Something wrong with merkleblock");
}
- wtx.nIndex = txnIndex;
- wtx.hashBlock = merkleBlock.header.GetHash();
+ wtx.SetConf(CWalletTx::Status::CONFIRMED, merkleBlock.header.GetHash(), txnIndex);
auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
@@ -1098,9 +1097,10 @@ static UniValue ProcessImportDescriptor(ImportData& import_data, std::map<CKeyID
const std::string& descriptor = data["desc"].get_str();
FlatSigningProvider keys;
- auto parsed_desc = Parse(descriptor, keys, /* require_checksum = */ true);
+ std::string error;
+ auto parsed_desc = Parse(descriptor, keys, error, /* require_checksum = */ true);
if (!parsed_desc) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Descriptor is invalid");
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error);
}
have_solving_data = parsed_desc->IsSolvable();
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index bf72ef6b16..216205ed61 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -52,6 +52,23 @@ static inline bool GetAvoidReuseFlag(CWallet * const pwallet, const UniValue& pa
return avoid_reuse;
}
+
+/** Used by RPC commands that have an include_watchonly parameter.
+ * We default to true for watchonly wallets if include_watchonly isn't
+ * explicitly set.
+ */
+static bool ParseIncludeWatchonly(const UniValue& include_watchonly, const CWallet& pwallet)
+{
+ if (include_watchonly.isNull()) {
+ // if include_watchonly isn't explicitly set, then check if we have a watchonly wallet
+ return pwallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
+ }
+
+ // otherwise return whatever include_watchonly was set to
+ return include_watchonly.get_bool();
+}
+
+
/** Checks if a CKey is in the given CWallet compressed or otherwise*/
bool HaveKey(const CWallet& wallet, const CKey& key)
{
@@ -117,10 +134,10 @@ static void WalletTxToJSON(interfaces::Chain& chain, interfaces::Chain::Lock& lo
entry.pushKV("generated", true);
if (confirms > 0)
{
- entry.pushKV("blockhash", wtx.hashBlock.GetHex());
- entry.pushKV("blockindex", wtx.nIndex);
+ entry.pushKV("blockhash", wtx.m_confirm.hashBlock.GetHex());
+ entry.pushKV("blockindex", wtx.m_confirm.nIndex);
int64_t block_time;
- bool found_block = chain.findBlock(wtx.hashBlock, nullptr /* block */, &block_time);
+ bool found_block = chain.findBlock(wtx.m_confirm.hashBlock, nullptr /* block */, &block_time);
assert(found_block);
entry.pushKV("blocktime", block_time);
} else {
@@ -309,10 +326,6 @@ static CTransactionRef SendMoney(interfaces::Chain::Lock& locked_chain, CWallet
if (nValue > curBalance)
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
- if (pwallet->GetBroadcastTransactions() && !pwallet->chain().p2pEnabled()) {
- throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
- }
-
// Parse Bitcoin address
CScript scriptPubKey = GetScriptForDestination(address);
@@ -359,8 +372,8 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
" transaction, just kept in your wallet."},
{"subtractfeefromamount", RPCArg::Type::BOOL, /* default */ "false", "The fee will be deducted from the amount being sent.\n"
" The recipient will receive less bitcoins than you enter in the amount field."},
- {"replaceable", RPCArg::Type::BOOL, /* default */ "fallback to wallet's default", "Allow this transaction to be replaced by a transaction with higher fees via BIP 125"},
- {"conf_target", RPCArg::Type::NUM, /* default */ "fallback to wallet's default", "Confirmation target (in blocks)"},
+ {"replaceable", RPCArg::Type::BOOL, /* default */ "wallet default", "Allow this transaction to be replaced by a transaction with higher fees via BIP 125"},
+ {"conf_target", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks)"},
{"estimate_mode", RPCArg::Type::STR, /* default */ "UNSET", "The fee estimate mode, must be one of:\n"
" \"UNSET\"\n"
" \"ECONOMICAL\"\n"
@@ -714,7 +727,7 @@ static UniValue getbalance(const JSONRPCRequest& request)
{
{"dummy", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "Remains for backward compatibility. Must be excluded or set to \"*\"."},
{"minconf", RPCArg::Type::NUM, /* default */ "0", "Only include transactions confirmed at least this many times."},
- {"include_watchonly", RPCArg::Type::BOOL, /* default */ "false", "Also include balance in watch-only addresses (see 'importaddress')"},
+ {"include_watchonly", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Also include balance in watch-only addresses (see 'importaddress')"},
{"avoid_reuse", RPCArg::Type::BOOL, /* default */ "true", "(only available if avoid_reuse wallet flag is set) Do not include balance in dirty outputs; addresses are considered dirty if they have previously been used in a transaction."},
},
RPCResult{
@@ -747,10 +760,7 @@ static UniValue getbalance(const JSONRPCRequest& request)
min_depth = request.params[1].get_int();
}
- bool include_watchonly = false;
- if (!request.params[2].isNull() && request.params[2].get_bool()) {
- include_watchonly = true;
- }
+ bool include_watchonly = ParseIncludeWatchonly(request.params[2], *pwallet);
bool avoid_reuse = GetAvoidReuseFlag(pwallet, request.params[3]);
@@ -815,8 +825,8 @@ static UniValue sendmany(const JSONRPCRequest& request)
{"address", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Subtract fee from this address"},
},
},
- {"replaceable", RPCArg::Type::BOOL, /* default */ "fallback to wallet's default", "Allow this transaction to be replaced by a transaction with higher fees via BIP 125"},
- {"conf_target", RPCArg::Type::NUM, /* default */ "fallback to wallet's default", "Confirmation target (in blocks)"},
+ {"replaceable", RPCArg::Type::BOOL, /* default */ "wallet default", "Allow this transaction to be replaced by a transaction with higher fees via BIP 125"},
+ {"conf_target", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks)"},
{"estimate_mode", RPCArg::Type::STR, /* default */ "UNSET", "The fee estimate mode, must be one of:\n"
" \"UNSET\"\n"
" \"ECONOMICAL\"\n"
@@ -845,10 +855,6 @@ static UniValue sendmany(const JSONRPCRequest& request)
auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
- if (pwallet->GetBroadcastTransactions() && !pwallet->chain().p2pEnabled()) {
- throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
- }
-
if (!request.params[0].isNull() && !request.params[0].get_str().empty()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Dummy value must be set to \"\"");
}
@@ -1031,9 +1037,10 @@ static UniValue ListReceived(interfaces::Chain::Lock& locked_chain, CWallet * co
fIncludeEmpty = params[1].get_bool();
isminefilter filter = ISMINE_SPENDABLE;
- if(!params[2].isNull())
- if(params[2].get_bool())
- filter = filter | ISMINE_WATCH_ONLY;
+
+ if (ParseIncludeWatchonly(params[2], *pwallet)) {
+ filter |= ISMINE_WATCH_ONLY;
+ }
bool has_filtered_address = false;
CTxDestination filtered_address = CNoDestination();
@@ -1177,7 +1184,7 @@ static UniValue listreceivedbyaddress(const JSONRPCRequest& request)
{
{"minconf", RPCArg::Type::NUM, /* default */ "1", "The minimum number of confirmations before payments are included."},
{"include_empty", RPCArg::Type::BOOL, /* default */ "false", "Whether to include addresses that haven't received any payments."},
- {"include_watchonly", RPCArg::Type::BOOL, /* default */ "false", "Whether to include watch-only addresses (see 'importaddress')."},
+ {"include_watchonly", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Whether to include watch-only addresses (see 'importaddress')"},
{"address_filter", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "If present, only return information on this address."},
},
RPCResult{
@@ -1228,7 +1235,7 @@ static UniValue listreceivedbylabel(const JSONRPCRequest& request)
{
{"minconf", RPCArg::Type::NUM, /* default */ "1", "The minimum number of confirmations before payments are included."},
{"include_empty", RPCArg::Type::BOOL, /* default */ "false", "Whether to include labels that haven't received any payments."},
- {"include_watchonly", RPCArg::Type::BOOL, /* default */ "false", "Whether to include watch-only addresses (see 'importaddress')."},
+ {"include_watchonly", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Whether to include watch-only addresses (see 'importaddress')"},
},
RPCResult{
"[\n"
@@ -1369,7 +1376,7 @@ UniValue listtransactions(const JSONRPCRequest& request)
" with the specified label, or \"*\" to disable filtering and return all transactions."},
{"count", RPCArg::Type::NUM, /* default */ "10", "The number of transactions to return"},
{"skip", RPCArg::Type::NUM, /* default */ "0", "The number of transactions to skip"},
- {"include_watchonly", RPCArg::Type::BOOL, /* default */ "false", "Include transactions to watch-only addresses (see 'importaddress')"},
+ {"include_watchonly", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Include transactions to watch-only addresses (see 'importaddress')"},
},
RPCResult{
"[\n"
@@ -1432,9 +1439,10 @@ UniValue listtransactions(const JSONRPCRequest& request)
if (!request.params[2].isNull())
nFrom = request.params[2].get_int();
isminefilter filter = ISMINE_SPENDABLE;
- if(!request.params[3].isNull())
- if(request.params[3].get_bool())
- filter = filter | ISMINE_WATCH_ONLY;
+
+ if (ParseIncludeWatchonly(request.params[3], *pwallet)) {
+ filter |= ISMINE_WATCH_ONLY;
+ }
if (nCount < 0)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
@@ -1500,7 +1508,7 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
{
{"blockhash", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "If set, the block hash to list transactions since, otherwise list all transactions."},
{"target_confirmations", RPCArg::Type::NUM, /* default */ "1", "Return the nth block hash from the main chain. e.g. 1 would mean the best block hash. Note: this is not used as a filter, but only affects [lastblock] in the return value"},
- {"include_watchonly", RPCArg::Type::BOOL, /* default */ "false", "Include transactions to watch-only addresses (see 'importaddress')"},
+ {"include_watchonly", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Include transactions to watch-only addresses (see 'importaddress')"},
{"include_removed", RPCArg::Type::BOOL, /* default */ "true", "Show transactions that were removed due to a reorg in the \"removed\" array\n"
" (not guaranteed to work on pruned nodes)"},
},
@@ -1577,8 +1585,8 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
}
}
- if (!request.params[2].isNull() && request.params[2].get_bool()) {
- filter = filter | ISMINE_WATCH_ONLY;
+ if (ParseIncludeWatchonly(request.params[2], *pwallet)) {
+ filter |= ISMINE_WATCH_ONLY;
}
bool include_removed = (request.params[3].isNull() || request.params[3].get_bool());
@@ -1640,7 +1648,10 @@ static UniValue gettransaction(const JSONRPCRequest& request)
"\nGet detailed information about in-wallet transaction <txid>\n",
{
{"txid", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction id"},
- {"include_watchonly", RPCArg::Type::BOOL, /* default */ "false", "Whether to include watch-only addresses in balance calculation and details[]"},
+ {"include_watchonly", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false",
+ "Whether to include watch-only addresses in balance calculation and details[]"},
+ {"verbose", RPCArg::Type::BOOL, /* default */ "false",
+ "Whether to include a `decoded` field containing the decoded transaction (equivalent to RPC decoderawtransaction)"},
},
RPCResult{
"{\n"
@@ -1676,11 +1687,14 @@ static UniValue gettransaction(const JSONRPCRequest& request)
" ,...\n"
" ],\n"
" \"hex\" : \"data\" (string) Raw data for transaction\n"
+ " \"decoded\" : transaction (json object) Optional, the decoded transaction (only present when `verbose` is passed), equivalent to the\n"
+ " RPC decoderawtransaction method, or the RPC getrawtransaction method when `verbose` is passed.\n"
"}\n"
},
RPCExamples{
HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
+ HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\" true")
+ + HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\" false true")
+ HelpExampleRpc("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
},
}.Check(request);
@@ -1695,9 +1709,12 @@ static UniValue gettransaction(const JSONRPCRequest& request)
uint256 hash(ParseHashV(request.params[0], "txid"));
isminefilter filter = ISMINE_SPENDABLE;
- if(!request.params[1].isNull())
- if(request.params[1].get_bool())
- filter = filter | ISMINE_WATCH_ONLY;
+
+ if (ParseIncludeWatchonly(request.params[1], *pwallet)) {
+ filter |= ISMINE_WATCH_ONLY;
+ }
+
+ bool verbose = request.params[2].isNull() ? false : request.params[2].get_bool();
UniValue entry(UniValue::VOBJ);
auto it = pwallet->mapWallet.find(hash);
@@ -1724,6 +1741,12 @@ static UniValue gettransaction(const JSONRPCRequest& request)
std::string strHex = EncodeHexTx(*wtx.tx, pwallet->chain().rpcSerializationFlags());
entry.pushKV("hex", strHex);
+ if (verbose) {
+ UniValue decoded(UniValue::VOBJ);
+ TxToUniv(*wtx.tx, uint256(), decoded, false);
+ entry.pushKV("decoded", decoded);
+ }
+
return entry;
}
@@ -2676,11 +2699,12 @@ static UniValue createwallet(const JSONRPCRequest& request)
}
SecureString passphrase;
passphrase.reserve(100);
+ std::string warning;
if (!request.params[3].isNull()) {
passphrase = request.params[3].get_str().c_str();
if (passphrase.empty()) {
- // Empty string is invalid
- throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Cannot encrypt a wallet with a blank password");
+ // Empty string means unencrypted
+ warning = "Empty string given as passphrase, wallet will not be encrypted.";
}
}
@@ -2689,9 +2713,9 @@ static UniValue createwallet(const JSONRPCRequest& request)
}
std::string error;
- std::string warning;
+ std::string create_warning;
std::shared_ptr<CWallet> wallet;
- WalletCreationStatus status = CreateWallet(*g_rpc_interfaces->chain, passphrase, flags, request.params[0].get_str(), error, warning, wallet);
+ WalletCreationStatus status = CreateWallet(*g_rpc_interfaces->chain, passphrase, flags, request.params[0].get_str(), error, create_warning, wallet);
switch (status) {
case WalletCreationStatus::CREATION_FAILED:
throw JSONRPCError(RPC_WALLET_ERROR, error);
@@ -2702,6 +2726,12 @@ static UniValue createwallet(const JSONRPCRequest& request)
// no default case, so the compiler can warn about missing cases
}
+ if (warning.empty()) {
+ warning = create_warning;
+ } else if (!warning.empty() && !create_warning.empty()){
+ warning += "; " + create_warning;
+ }
+
UniValue obj(UniValue::VOBJ);
obj.pushKV("name", wallet->GetName());
obj.pushKV("warning", warning);
@@ -3016,8 +3046,7 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
}
}
- if (options.exists("includeWatching"))
- coinControl.fAllowWatchOnly = options["includeWatching"].get_bool();
+ coinControl.fAllowWatchOnly = ParseIncludeWatchonly(options["includeWatching"], *pwallet);
if (options.exists("lockUnspents"))
lockUnspents = options["lockUnspents"].get_bool();
@@ -3049,6 +3078,9 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
}
}
}
+ } else {
+ // if options is null and not a bool
+ coinControl.fAllowWatchOnly = ParseIncludeWatchonly(NullUniValue, *pwallet);
}
if (tx.vout.size() == 0)
@@ -3103,7 +3135,7 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
{"changeAddress", RPCArg::Type::STR, /* default */ "pool address", "The bitcoin address to receive the change"},
{"changePosition", RPCArg::Type::NUM, /* default */ "random", "The index of the change output"},
{"change_type", RPCArg::Type::STR, /* default */ "set by -changetype", "The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
- {"includeWatching", RPCArg::Type::BOOL, /* default */ "false", "Also select inputs which are watch only"},
+ {"includeWatching", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Also select inputs which are watch only"},
{"lockUnspents", RPCArg::Type::BOOL, /* default */ "false", "Lock selected unspent outputs"},
{"feeRate", RPCArg::Type::AMOUNT, /* default */ "not set: makes wallet determine the fee", "Set a specific fee rate in " + CURRENCY_UNIT + "/kB"},
{"subtractFeeFromOutputs", RPCArg::Type::ARR, /* default */ "empty array", "A json array of integers.\n"
@@ -3114,9 +3146,9 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
{"vout_index", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The zero-based output index, before a change output is added."},
},
},
- {"replaceable", RPCArg::Type::BOOL, /* default */ "fallback to wallet's default", "Marks this transaction as BIP125 replaceable.\n"
+ {"replaceable", RPCArg::Type::BOOL, /* default */ "wallet default", "Marks this transaction as BIP125 replaceable.\n"
" Allows this transaction to be replaced by a transaction with higher fees"},
- {"conf_target", RPCArg::Type::NUM, /* default */ "fallback to wallet's default", "Confirmation target (in blocks)"},
+ {"conf_target", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks)"},
{"estimate_mode", RPCArg::Type::STR, /* default */ "UNSET", "The fee estimate mode, must be one of:\n"
" \"UNSET\"\n"
" \"ECONOMICAL\"\n"
@@ -3251,7 +3283,10 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
}
pwallet->chain().findCoins(coins);
- return SignTransaction(mtx, request.params[1], pwallet, coins, false, request.params[2]);
+ // Parse the prevtxs array
+ ParsePrevouts(request.params[1], nullptr, coins);
+
+ return SignTransaction(mtx, pwallet, coins, request.params[2]);
}
static UniValue bumpfee(const JSONRPCRequest& request)
@@ -3279,7 +3314,7 @@ static UniValue bumpfee(const JSONRPCRequest& request)
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The txid to be bumped"},
{"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "",
{
- {"confTarget", RPCArg::Type::NUM, /* default */ "fallback to wallet's default", "Confirmation target (in blocks)"},
+ {"confTarget", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks)"},
{"totalFee", RPCArg::Type::NUM, /* default */ "fallback to 'confTarget'", "Total fee (NOT feerate) to pay, in satoshis. (DEPRECATED)\n"
" In rare cases, the actual fee paid might be slightly higher than the specified\n"
" totalFee if the tx change output has to be removed because it is too close to\n"
@@ -4048,7 +4083,7 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
{"changeAddress", RPCArg::Type::STR_HEX, /* default */ "pool address", "The bitcoin address to receive the change"},
{"changePosition", RPCArg::Type::NUM, /* default */ "random", "The index of the change output"},
{"change_type", RPCArg::Type::STR, /* default */ "set by -changetype", "The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
- {"includeWatching", RPCArg::Type::BOOL, /* default */ "false", "Also select inputs which are watch only"},
+ {"includeWatching", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Also select inputs which are watch only"},
{"lockUnspents", RPCArg::Type::BOOL, /* default */ "false", "Lock selected unspent outputs"},
{"feeRate", RPCArg::Type::AMOUNT, /* default */ "not set: makes wallet determine the fee", "Set a specific fee rate in " + CURRENCY_UNIT + "/kB"},
{"subtractFeeFromOutputs", RPCArg::Type::ARR, /* default */ "empty array", "A json array of integers.\n"
@@ -4059,7 +4094,7 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
{"vout_index", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The zero-based output index, before a change output is added."},
},
},
- {"replaceable", RPCArg::Type::BOOL, /* default */ "false", "Marks this transaction as BIP125 replaceable.\n"
+ {"replaceable", RPCArg::Type::BOOL, /* default */ "wallet default", "Marks this transaction as BIP125 replaceable.\n"
" Allows this transaction to be replaced by a transaction with higher fees"},
{"conf_target", RPCArg::Type::NUM, /* default */ "Fallback to wallet's confirmation target", "Confirmation target (in blocks)"},
{"estimate_mode", RPCArg::Type::STR, /* default */ "UNSET", "The fee estimate mode, must be one of:\n"
@@ -4094,7 +4129,13 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
CAmount fee;
int change_position;
- CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], request.params[3]["replaceable"]);
+ bool rbf = pwallet->m_signal_rbf;
+ const UniValue &replaceable_arg = request.params[3]["replaceable"];
+ if (!replaceable_arg.isNull()) {
+ RPCTypeCheckArgument(replaceable_arg, UniValue::VBOOL);
+ rbf = replaceable_arg.isTrue();
+ }
+ CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], rbf);
FundTransaction(pwallet, rawTx, fee, change_position, request.params[3]);
// Make a blank psbt
@@ -4151,7 +4192,7 @@ static const CRPCCommand commands[] =
{ "wallet", "getrawchangeaddress", &getrawchangeaddress, {"address_type"} },
{ "wallet", "getreceivedbyaddress", &getreceivedbyaddress, {"address","minconf"} },
{ "wallet", "getreceivedbylabel", &getreceivedbylabel, {"label","minconf"} },
- { "wallet", "gettransaction", &gettransaction, {"txid","include_watchonly"} },
+ { "wallet", "gettransaction", &gettransaction, {"txid","include_watchonly","verbose"} },
{ "wallet", "getunconfirmedbalance", &getunconfirmedbalance, {} },
{ "wallet", "getbalances", &getbalances, {} },
{ "wallet", "getwalletinfo", &getwalletinfo, {} },
diff --git a/src/wallet/test/init_tests.cpp b/src/wallet/test/init_tests.cpp
index 1816fca257..279542ffad 100644
--- a/src/wallet/test/init_tests.cpp
+++ b/src/wallet/test/init_tests.cpp
@@ -4,6 +4,7 @@
#include <boost/test/unit_test.hpp>
+#include <noui.h>
#include <test/setup_common.h>
#include <util/system.h>
#include <wallet/test/init_test_fixture.h>
@@ -33,21 +34,27 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_custom)
BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_does_not_exist)
{
SetWalletDir(m_walletdir_path_cases["nonexistent"]);
+ noui_suppress();
bool result = m_chain_client->verify();
+ noui_reconnect();
BOOST_CHECK(result == false);
}
BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_is_not_directory)
{
SetWalletDir(m_walletdir_path_cases["file"]);
+ noui_suppress();
bool result = m_chain_client->verify();
+ noui_reconnect();
BOOST_CHECK(result == false);
}
BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_is_not_relative)
{
SetWalletDir(m_walletdir_path_cases["relative"]);
+ noui_suppress();
bool result = m_chain_client->verify();
+ noui_reconnect();
BOOST_CHECK(result == false);
}
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 8af05dea45..fc3be2b6ab 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -249,8 +249,7 @@ BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
LockAssertion lock(::cs_main);
LOCK(wallet.cs_wallet);
- wtx.hashBlock = ::ChainActive().Tip()->GetBlockHash();
- wtx.nIndex = 0;
+ wtx.SetConf(CWalletTx::Status::CONFIRMED, ::ChainActive().Tip()->GetBlockHash(), 0);
// Call GetImmatureCredit() once before adding the key to the wallet to
// cache the current immature credit amount, which is 0.
@@ -281,14 +280,19 @@ static int64_t AddTx(CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64
}
CWalletTx wtx(&wallet, MakeTransactionRef(tx));
- if (block) {
- wtx.SetMerkleBranch(block->GetBlockHash(), 0);
- }
- {
- LOCK(cs_main);
+ LOCK(cs_main);
+ LOCK(wallet.cs_wallet);
+ // If transaction is already in map, to avoid inconsistencies, unconfirmation
+ // is needed before confirm again with different block.
+ std::map<uint256, CWalletTx>::iterator it = wallet.mapWallet.find(wtx.GetHash());
+ if (it != wallet.mapWallet.end()) {
+ wtx.setUnconfirmed();
wallet.AddToWallet(wtx);
}
- LOCK(wallet.cs_wallet);
+ if (block) {
+ wtx.SetConf(CWalletTx::Status::CONFIRMED, block->GetBlockHash(), 0);
+ }
+ wallet.AddToWallet(wtx);
return wallet.mapWallet.at(wtx.GetHash()).nTimeSmart;
}
@@ -382,7 +386,7 @@ public:
LOCK(wallet->cs_wallet);
auto it = wallet->mapWallet.find(tx->GetHash());
BOOST_CHECK(it != wallet->mapWallet.end());
- it->second.SetMerkleBranch(::ChainActive().Tip()->GetBlockHash(), 1);
+ it->second.SetConf(CWalletTx::Status::CONFIRMED, ::ChainActive().Tip()->GetBlockHash(), 1);
return it->second;
}
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index ef5f0a61e1..7cf09d554b 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -93,13 +93,14 @@ std::shared_ptr<CWallet> GetWallet(const std::string& name)
static Mutex g_wallet_release_mutex;
static std::condition_variable g_wallet_release_cv;
-static std::set<CWallet*> g_unloading_wallet_set;
+static std::set<std::string> g_unloading_wallet_set;
// Custom deleter for shared_ptr<CWallet>.
static void ReleaseWallet(CWallet* wallet)
{
// Unregister and delete the wallet right after BlockUntilSyncedToCurrentChain
// so that it's in sync with the current chainstate.
+ const std::string name = wallet->GetName();
wallet->WalletLogPrintf("Releasing wallet\n");
wallet->BlockUntilSyncedToCurrentChain();
wallet->Flush();
@@ -108,7 +109,7 @@ static void ReleaseWallet(CWallet* wallet)
// Wallet is now released, notify UnloadWallet, if any.
{
LOCK(g_wallet_release_mutex);
- if (g_unloading_wallet_set.erase(wallet) == 0) {
+ if (g_unloading_wallet_set.erase(name) == 0) {
// UnloadWallet was not called for this wallet, all done.
return;
}
@@ -119,21 +120,21 @@ static void ReleaseWallet(CWallet* wallet)
void UnloadWallet(std::shared_ptr<CWallet>&& wallet)
{
// Mark wallet for unloading.
- CWallet* pwallet = wallet.get();
+ const std::string name = wallet->GetName();
{
LOCK(g_wallet_release_mutex);
- auto it = g_unloading_wallet_set.insert(pwallet);
+ auto it = g_unloading_wallet_set.insert(name);
assert(it.second);
}
// The wallet can be in use so it's not possible to explicitly unload here.
// Notify the unload intent so that all remaining shared pointers are
// released.
- pwallet->NotifyUnload();
+ wallet->NotifyUnload();
// Time to ditch our shared_ptr and wait for ReleaseWallet call.
wallet.reset();
{
WAIT_LOCK(g_wallet_release_mutex, lock);
- while (g_unloading_wallet_set.count(pwallet) == 1) {
+ while (g_unloading_wallet_set.count(name) == 1) {
g_wallet_release_cv.wait(lock);
}
}
@@ -523,18 +524,9 @@ bool CWallet::LoadCScript(const CScript& redeemScript)
static bool ExtractPubKey(const CScript &dest, CPubKey& pubKeyOut)
{
- //TODO: Use Solver to extract this?
- CScript::const_iterator pc = dest.begin();
- opcodetype opcode;
- std::vector<unsigned char> vch;
- if (!dest.GetOp(pc, opcode, vch) || !CPubKey::ValidSize(vch))
- return false;
- pubKeyOut = CPubKey(vch);
- if (!pubKeyOut.IsFullyValid())
- return false;
- if (!dest.GetOp(pc, opcode, vch) || opcode != OP_CHECKSIG || dest.GetOp(pc, opcode, vch))
- return false;
- return true;
+ std::vector<std::vector<unsigned char>> solutions;
+ return Solver(dest, solutions) == TX_PUBKEY &&
+ (pubKeyOut = CPubKey(solutions[0])).IsFullyValid();
}
bool CWallet::AddWatchOnlyInMem(const CScript &dest)
@@ -1118,22 +1110,14 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
bool fUpdated = false;
if (!fInsertedNew)
{
- // Merge
- if (!wtxIn.hashUnset() && wtxIn.hashBlock != wtx.hashBlock)
- {
- wtx.hashBlock = wtxIn.hashBlock;
- fUpdated = true;
- }
- // If no longer abandoned, update
- if (wtxIn.hashBlock.IsNull() && wtx.isAbandoned())
- {
- wtx.hashBlock = wtxIn.hashBlock;
- fUpdated = true;
- }
- if (wtxIn.nIndex != -1 && (wtxIn.nIndex != wtx.nIndex))
- {
- wtx.nIndex = wtxIn.nIndex;
+ if (wtxIn.m_confirm.status != wtx.m_confirm.status) {
+ wtx.m_confirm.status = wtxIn.m_confirm.status;
+ wtx.m_confirm.nIndex = wtxIn.m_confirm.nIndex;
+ wtx.m_confirm.hashBlock = wtxIn.m_confirm.hashBlock;
fUpdated = true;
+ } else {
+ assert(wtx.m_confirm.nIndex == wtxIn.m_confirm.nIndex);
+ assert(wtx.m_confirm.hashBlock == wtxIn.m_confirm.hashBlock);
}
if (wtxIn.fFromMe && wtxIn.fFromMe != wtx.fFromMe)
{
@@ -1180,8 +1164,19 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
return true;
}
-void CWallet::LoadToWallet(const CWalletTx& wtxIn)
+void CWallet::LoadToWallet(CWalletTx& wtxIn)
{
+ // If wallet doesn't have a chain (e.g wallet-tool), lock can't be taken.
+ auto locked_chain = LockChain();
+ // If tx hasn't been reorged out of chain while wallet being shutdown
+ // change tx status to UNCONFIRMED and reset hashBlock/nIndex.
+ if (!wtxIn.m_confirm.hashBlock.IsNull()) {
+ if (locked_chain && !locked_chain->getBlockHeight(wtxIn.m_confirm.hashBlock)) {
+ wtxIn.setUnconfirmed();
+ wtxIn.m_confirm.hashBlock = uint256();
+ wtxIn.m_confirm.nIndex = 0;
+ }
+ }
uint256 hash = wtxIn.GetHash();
const auto& ins = mapWallet.emplace(hash, wtxIn);
CWalletTx& wtx = ins.first->second;
@@ -1194,14 +1189,14 @@ void CWallet::LoadToWallet(const CWalletTx& wtxIn)
auto it = mapWallet.find(txin.prevout.hash);
if (it != mapWallet.end()) {
CWalletTx& prevtx = it->second;
- if (prevtx.nIndex == -1 && !prevtx.hashUnset()) {
- MarkConflicted(prevtx.hashBlock, wtx.GetHash());
+ if (prevtx.isConflicted()) {
+ MarkConflicted(prevtx.m_confirm.hashBlock, wtx.GetHash());
}
}
}
}
-bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, const uint256& block_hash, int posInBlock, bool fUpdate)
+bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, CWalletTx::Status status, const uint256& block_hash, int posInBlock, bool fUpdate)
{
const CTransaction& tx = *ptx;
{
@@ -1248,9 +1243,9 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, const uint256
CWalletTx wtx(this, ptx);
- // Get merkle branch if transaction was found in a block
- if (!block_hash.IsNull())
- wtx.SetMerkleBranch(block_hash, posInBlock);
+ // Block disconnection override an abandoned tx as unconfirmed
+ // which means user may have to call abandontransaction again
+ wtx.SetConf(status, block_hash, posInBlock);
return AddToWallet(wtx, false);
}
@@ -1310,7 +1305,7 @@ bool CWallet::AbandonTransaction(interfaces::Chain::Lock& locked_chain, const ui
if (currentconfirm == 0 && !wtx.isAbandoned()) {
// If the orig tx was not in block/mempool, none of its spends can be in mempool
assert(!wtx.InMempool());
- wtx.nIndex = -1;
+ wtx.m_confirm.nIndex = 0;
wtx.setAbandoned();
wtx.MarkDirty();
batch.WriteTx(wtx);
@@ -1364,8 +1359,9 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx)
if (conflictconfirms < currentconfirm) {
// Block is 'more conflicted' than current confirm; update.
// Mark transaction as conflicted with this block.
- wtx.nIndex = -1;
- wtx.hashBlock = hashBlock;
+ wtx.m_confirm.nIndex = 0;
+ wtx.m_confirm.hashBlock = hashBlock;
+ wtx.setConflicted();
wtx.MarkDirty();
batch.WriteTx(wtx);
// Iterate over all its outputs, and mark transactions in the wallet that spend them conflicted too
@@ -1383,8 +1379,9 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx)
}
}
-void CWallet::SyncTransaction(const CTransactionRef& ptx, const uint256& block_hash, int posInBlock, bool update_tx) {
- if (!AddToWalletIfInvolvingMe(ptx, block_hash, posInBlock, update_tx))
+void CWallet::SyncTransaction(const CTransactionRef& ptx, CWalletTx::Status status, const uint256& block_hash, int posInBlock, bool update_tx)
+{
+ if (!AddToWalletIfInvolvingMe(ptx, status, block_hash, posInBlock, update_tx))
return; // Not one of ours
// If a transaction changes 'conflicted' state, that changes the balance
@@ -1396,7 +1393,7 @@ void CWallet::SyncTransaction(const CTransactionRef& ptx, const uint256& block_h
void CWallet::TransactionAddedToMempool(const CTransactionRef& ptx) {
auto locked_chain = chain().lock();
LOCK(cs_wallet);
- SyncTransaction(ptx, {} /* block hash */, 0 /* position in block */);
+ SyncTransaction(ptx, CWalletTx::Status::UNCONFIRMED, {} /* block hash */, 0 /* position in block */);
auto it = mapWallet.find(ptx->GetHash());
if (it != mapWallet.end()) {
@@ -1416,22 +1413,14 @@ void CWallet::BlockConnected(const CBlock& block, const std::vector<CTransaction
const uint256& block_hash = block.GetHash();
auto locked_chain = chain().lock();
LOCK(cs_wallet);
- // TODO: Temporarily ensure that mempool removals are notified before
- // connected transactions. This shouldn't matter, but the abandoned
- // state of transactions in our wallet is currently cleared when we
- // receive another notification and there is a race condition where
- // notification of a connected conflict might cause an outside process
- // to abandon a transaction and then have it inadvertently cleared by
- // the notification that the conflicted transaction was evicted.
- for (const CTransactionRef& ptx : vtxConflicted) {
- SyncTransaction(ptx, {} /* block hash */, 0 /* position in block */);
- TransactionRemovedFromMempool(ptx);
- }
for (size_t i = 0; i < block.vtx.size(); i++) {
- SyncTransaction(block.vtx[i], block_hash, i);
+ SyncTransaction(block.vtx[i], CWalletTx::Status::CONFIRMED, block_hash, i);
TransactionRemovedFromMempool(block.vtx[i]);
}
+ for (const CTransactionRef& ptx : vtxConflicted) {
+ TransactionRemovedFromMempool(ptx);
+ }
m_last_block_processed = block_hash;
}
@@ -1440,8 +1429,12 @@ void CWallet::BlockDisconnected(const CBlock& block) {
auto locked_chain = chain().lock();
LOCK(cs_wallet);
+ // At block disconnection, this will change an abandoned transaction to
+ // be unconfirmed, whether or not the transaction is added back to the mempool.
+ // User may have to call abandontransaction again. It may be addressed in the
+ // future with a stickier abandoned state or even removing abandontransaction call.
for (const CTransactionRef& ptx : block.vtx) {
- SyncTransaction(ptx, {} /* block hash */, 0 /* position in block */);
+ SyncTransaction(ptx, CWalletTx::Status::UNCONFIRMED, {} /* block hash */, 0 /* position in block */);
}
}
@@ -2078,7 +2071,7 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
break;
}
for (size_t posInBlock = 0; posInBlock < block.vtx.size(); ++posInBlock) {
- SyncTransaction(block.vtx[posInBlock], block_hash, posInBlock, fUpdate);
+ SyncTransaction(block.vtx[posInBlock], CWalletTx::Status::CONFIRMED, block_hash, posInBlock, fUpdate);
}
// scan succeeded, record block as most recent successfully scanned
result.last_scanned_block = block_hash;
@@ -2134,8 +2127,7 @@ void CWallet::ReacceptWalletTransactions(interfaces::Chain::Lock& locked_chain)
std::map<int64_t, CWalletTx*> mapSorted;
// Sort pending wallet transactions based on their initial wallet insertion order
- for (std::pair<const uint256, CWalletTx>& item : mapWallet)
- {
+ for (std::pair<const uint256, CWalletTx>& item : mapWallet) {
const uint256& wtxid = item.first;
CWalletTx& wtx = item.second;
assert(wtx.GetHash() == wtxid);
@@ -2150,32 +2142,37 @@ void CWallet::ReacceptWalletTransactions(interfaces::Chain::Lock& locked_chain)
// Try to add wallet transactions to memory pool
for (const std::pair<const int64_t, CWalletTx*>& item : mapSorted) {
CWalletTx& wtx = *(item.second);
- CValidationState state;
- wtx.AcceptToMemoryPool(locked_chain, state);
+ std::string unused_err_string;
+ wtx.SubmitMemoryPoolAndRelay(unused_err_string, false, locked_chain);
}
}
-bool CWalletTx::RelayWalletTransaction(interfaces::Chain::Lock& locked_chain)
+bool CWalletTx::SubmitMemoryPoolAndRelay(std::string& err_string, bool relay, interfaces::Chain::Lock& locked_chain)
{
// Can't relay if wallet is not broadcasting
if (!pwallet->GetBroadcastTransactions()) return false;
- // Don't relay coinbase transactions outside blocks
- if (IsCoinBase()) return false;
// Don't relay abandoned transactions
if (isAbandoned()) return false;
- // Don't relay conflicted or already confirmed transactions
+ // Don't try to submit coinbase transactions. These would fail anyway but would
+ // cause log spam.
+ if (IsCoinBase()) return false;
+ // Don't try to submit conflicted or confirmed transactions.
if (GetDepthInMainChain(locked_chain) != 0) return false;
- // Don't relay transactions that aren't accepted to the mempool
- CValidationState unused_state;
- if (!InMempool() && !AcceptToMemoryPool(locked_chain, unused_state)) return false;
- // Don't try to relay if the node is not connected to the p2p network
- if (!pwallet->chain().p2pEnabled()) return false;
- // Try to relay the transaction
- pwallet->WalletLogPrintf("Relaying wtx %s\n", GetHash().ToString());
- pwallet->chain().relayTransaction(GetHash());
-
- return true;
+ // Submit transaction to mempool for relay
+ pwallet->WalletLogPrintf("Submitting wtx %s to mempool for relay\n", GetHash().ToString());
+ // We must set fInMempool here - while it will be re-set to true by the
+ // entered-mempool callback, if we did not there would be a race where a
+ // user could call sendmoney in a loop and hit spurious out of funds errors
+ // because we think that this newly generated transaction's change is
+ // unavailable as we're not yet aware that it is in the mempool.
+ //
+ // Irrespective of the failure reason, un-marking fInMempool
+ // out-of-order is incorrect - it should be unmarked when
+ // TransactionRemovedFromMempool fires.
+ bool ret = pwallet->chain().broadcastTransaction(tx, err_string, pwallet->m_default_max_tx_fee, relay);
+ fInMempool |= ret;
+ return ret;
}
std::set<uint256> CWalletTx::GetConflicts() const
@@ -2366,7 +2363,7 @@ void CWallet::ResendWalletTransactions()
if (m_best_block_time < nLastResend) return;
nLastResend = GetTime();
- int relayed_tx_count = 0;
+ int submitted_tx_count = 0;
{ // locked_chain and cs_wallet scope
auto locked_chain = chain().lock();
@@ -2375,15 +2372,17 @@ void CWallet::ResendWalletTransactions()
// Relay transactions
for (std::pair<const uint256, CWalletTx>& item : mapWallet) {
CWalletTx& wtx = item.second;
- // only rebroadcast unconfirmed txes older than 5 minutes before the
- // last block was found
+ // Attempt to rebroadcast all txes more than 5 minutes older than
+ // the last block. SubmitMemoryPoolAndRelay() will not rebroadcast
+ // any confirmed or conflicting txs.
if (wtx.nTimeReceived > m_best_block_time - 5 * 60) continue;
- if (wtx.RelayWalletTransaction(*locked_chain)) ++relayed_tx_count;
+ std::string unused_err_string;
+ if (wtx.SubmitMemoryPoolAndRelay(unused_err_string, true, *locked_chain)) ++submitted_tx_count;
}
} // locked_chain and cs_wallet
- if (relayed_tx_count > 0) {
- WalletLogPrintf("%s: rebroadcast %u unconfirmed transactions\n", __func__, relayed_tx_count);
+ if (submitted_tx_count > 0) {
+ WalletLogPrintf("%s: resubmit %u unconfirmed transactions\n", __func__, submitted_tx_count);
}
}
@@ -3322,12 +3321,10 @@ bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve
if (fBroadcastTransactions)
{
- // Broadcast
- if (!wtx.AcceptToMemoryPool(*locked_chain, state)) {
- WalletLogPrintf("CommitTransaction(): Transaction cannot be broadcast immediately, %s\n", FormatStateMessage(state));
+ std::string err_string;
+ if (!wtx.SubmitMemoryPoolAndRelay(err_string, true, *locked_chain)) {
+ WalletLogPrintf("CommitTransaction(): Transaction cannot be broadcast immediately, %s\n", err_string);
// TODO: if we expect the failure to be long term or permanent, instead delete wtx from the wallet and return failure.
- } else {
- wtx.RelayWalletTransaction(*locked_chain);
}
}
}
@@ -3336,6 +3333,11 @@ bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve
DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
{
+ // Even if we don't use this lock in this function, we want to preserve
+ // lock order in LoadToWallet if query of chain state is needed to know
+ // tx status. If lock can't be taken (e.g wallet-tool), tx confirmation
+ // status may be not reliable.
+ auto locked_chain = LockChain();
LOCK(cs_wallet);
fFirstRunRet = false;
@@ -4046,7 +4048,7 @@ void CWallet::GetKeyBirthTimes(interfaces::Chain::Lock& locked_chain, std::map<C
for (const auto& entry : mapWallet) {
// iterate over all wallet transactions...
const CWalletTx &wtx = entry.second;
- if (Optional<int> height = locked_chain.getBlockHeight(wtx.hashBlock)) {
+ if (Optional<int> height = locked_chain.getBlockHeight(wtx.m_confirm.hashBlock)) {
// ... which are already in a block
for (const CTxOut &txout : wtx.tx->vout) {
// iterate over all their outputs
@@ -4089,9 +4091,9 @@ void CWallet::GetKeyBirthTimes(interfaces::Chain::Lock& locked_chain, std::map<C
unsigned int CWallet::ComputeTimeSmart(const CWalletTx& wtx) const
{
unsigned int nTimeSmart = wtx.nTimeReceived;
- if (!wtx.hashUnset()) {
+ if (!wtx.isUnconfirmed() && !wtx.isAbandoned()) {
int64_t blocktime;
- if (chain().findBlock(wtx.hashBlock, nullptr /* block */, &blocktime)) {
+ if (chain().findBlock(wtx.m_confirm.hashBlock, nullptr /* block */, &blocktime)) {
int64_t latestNow = wtx.nTimeReceived;
int64_t latestEntry = 0;
@@ -4119,7 +4121,7 @@ unsigned int CWallet::ComputeTimeSmart(const CWalletTx& wtx) const
nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow));
} else {
- WalletLogPrintf("%s: found %s in block %s not in index\n", __func__, wtx.GetHash().ToString(), wtx.hashBlock.ToString());
+ WalletLogPrintf("%s: found %s in block %s not in index\n", __func__, wtx.GetHash().ToString(), wtx.m_confirm.hashBlock.ToString());
}
}
return nTimeSmart;
@@ -4237,6 +4239,11 @@ bool CWallet::Verify(interfaces::Chain& chain, const WalletLocation& location, b
// Recover readable keypairs:
CWallet dummyWallet(&chain, WalletLocation(), WalletDatabase::CreateDummy());
std::string backup_filename;
+ // Even if we don't use this lock in this function, we want to preserve
+ // lock order in LoadToWallet if query of chain state is needed to know
+ // tx status. If lock can't be taken, tx confirmation status may be not
+ // reliable.
+ auto locked_chain = dummyWallet.LockChain();
if (!WalletBatch::Recover(wallet_path, (void *)&dummyWallet, WalletBatch::RecoverKeysOnlyFilter, backup_filename)) {
return false;
}
@@ -4247,7 +4254,7 @@ bool CWallet::Verify(interfaces::Chain& chain, const WalletLocation& location, b
std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain, const WalletLocation& location, uint64_t wallet_creation_flags)
{
- const std::string& walletFile = WalletDataFilePath(location.GetPath()).string();
+ const std::string walletFile = WalletDataFilePath(location.GetPath()).string();
// needed to restore wallet transaction meta data after -zapwallettxes
std::vector<CWalletTx> vWtx;
@@ -4391,23 +4398,23 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
}
if (!gArgs.GetArg("-addresstype", "").empty() && !ParseOutputType(gArgs.GetArg("-addresstype", ""), walletInstance->m_default_address_type)) {
- chain.initError(strprintf("Unknown address type '%s'", gArgs.GetArg("-addresstype", "")));
+ chain.initError(strprintf(_("Unknown address type '%s'").translated, gArgs.GetArg("-addresstype", "")));
return nullptr;
}
if (!gArgs.GetArg("-changetype", "").empty() && !ParseOutputType(gArgs.GetArg("-changetype", ""), walletInstance->m_default_change_type)) {
- chain.initError(strprintf("Unknown change type '%s'", gArgs.GetArg("-changetype", "")));
+ chain.initError(strprintf(_("Unknown change type '%s'").translated, gArgs.GetArg("-changetype", "")));
return nullptr;
}
if (gArgs.IsArgSet("-mintxfee")) {
CAmount n = 0;
if (!ParseMoney(gArgs.GetArg("-mintxfee", ""), n) || 0 == n) {
- chain.initError(AmountErrMsg("mintxfee", gArgs.GetArg("-mintxfee", "")));
+ chain.initError(AmountErrMsg("mintxfee", gArgs.GetArg("-mintxfee", "")).translated);
return nullptr;
}
if (n > HIGH_TX_FEE_PER_KB) {
- chain.initWarning(AmountHighWarn("-mintxfee") + " " +
+ chain.initWarning(AmountHighWarn("-mintxfee").translated + " " +
_("This is the minimum transaction fee you pay on every transaction.").translated);
}
walletInstance->m_min_fee = CFeeRate(n);
@@ -4421,7 +4428,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
return nullptr;
}
if (nFeePerK > HIGH_TX_FEE_PER_KB) {
- chain.initWarning(AmountHighWarn("-fallbackfee") + " " +
+ chain.initWarning(AmountHighWarn("-fallbackfee").translated + " " +
_("This is the transaction fee you may pay when fee estimates are not available.").translated);
}
walletInstance->m_fallback_fee = CFeeRate(nFeePerK);
@@ -4434,7 +4441,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
return nullptr;
}
if (nFeePerK > HIGH_TX_FEE_PER_KB) {
- chain.initWarning(AmountHighWarn("-discardfee") + " " +
+ chain.initWarning(AmountHighWarn("-discardfee").translated + " " +
_("This is the transaction fee you may discard if change is smaller than dust at this level").translated);
}
walletInstance->m_discard_rate = CFeeRate(nFeePerK);
@@ -4442,11 +4449,11 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
if (gArgs.IsArgSet("-paytxfee")) {
CAmount nFeePerK = 0;
if (!ParseMoney(gArgs.GetArg("-paytxfee", ""), nFeePerK)) {
- chain.initError(AmountErrMsg("paytxfee", gArgs.GetArg("-paytxfee", "")));
+ chain.initError(AmountErrMsg("paytxfee", gArgs.GetArg("-paytxfee", "")).translated);
return nullptr;
}
if (nFeePerK > HIGH_TX_FEE_PER_KB) {
- chain.initWarning(AmountHighWarn("-paytxfee") + " " +
+ chain.initWarning(AmountHighWarn("-paytxfee").translated + " " +
_("This is the transaction fee you will pay if you send a transaction.").translated);
}
walletInstance->m_pay_tx_fee = CFeeRate(nFeePerK, 1000);
@@ -4461,7 +4468,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
{
CAmount nMaxFee = 0;
if (!ParseMoney(gArgs.GetArg("-maxtxfee", ""), nMaxFee)) {
- chain.initError(AmountErrMsg("maxtxfee", gArgs.GetArg("-maxtxfee", "")));
+ chain.initError(AmountErrMsg("maxtxfee", gArgs.GetArg("-maxtxfee", "")).translated);
return nullptr;
}
if (nMaxFee > HIGH_MAX_TX_FEE) {
@@ -4475,9 +4482,10 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
walletInstance->m_default_max_tx_fee = nMaxFee;
}
- if (chain.relayMinFee().GetFeePerK() > HIGH_TX_FEE_PER_KB)
- chain.initWarning(AmountHighWarn("-minrelaytxfee") + " " +
+ if (chain.relayMinFee().GetFeePerK() > HIGH_TX_FEE_PER_KB) {
+ chain.initWarning(AmountHighWarn("-minrelaytxfee").translated + " " +
_("The wallet will avoid paying less than the minimum relay fee.").translated);
+ }
walletInstance->m_confirm_target = gArgs.GetArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET);
walletInstance->m_spend_zero_conf_change = gArgs.GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE);
@@ -4630,21 +4638,23 @@ CKeyPool::CKeyPool(const CPubKey& vchPubKeyIn, bool internalIn)
m_pre_split = false;
}
-void CWalletTx::SetMerkleBranch(const uint256& block_hash, int posInBlock)
+void CWalletTx::SetConf(Status status, const uint256& block_hash, int posInBlock)
{
+ // Update tx status
+ m_confirm.status = status;
+
// Update the tx's hashBlock
- hashBlock = block_hash;
+ m_confirm.hashBlock = block_hash;
// set the position of the transaction in the block
- nIndex = posInBlock;
+ m_confirm.nIndex = posInBlock;
}
int CWalletTx::GetDepthInMainChain(interfaces::Chain::Lock& locked_chain) const
{
- if (hashUnset())
- return 0;
+ if (isUnconfirmed() || isAbandoned()) return 0;
- return locked_chain.getBlockDepth(hashBlock) * (nIndex == -1 ? -1 : 1);
+ return locked_chain.getBlockDepth(m_confirm.hashBlock) * (isConflicted() ? -1 : 1);
}
int CWalletTx::GetBlocksToMaturity(interfaces::Chain::Lock& locked_chain) const
@@ -4662,18 +4672,6 @@ bool CWalletTx::IsImmatureCoinBase(interfaces::Chain::Lock& locked_chain) const
return GetBlocksToMaturity(locked_chain) > 0;
}
-bool CWalletTx::AcceptToMemoryPool(interfaces::Chain::Lock& locked_chain, CValidationState& state)
-{
- // We must set fInMempool here - while it will be re-set to true by the
- // entered-mempool callback, if we did not there would be a race where a
- // user could call sendmoney in a loop and hit spurious out of funds errors
- // because we think that this newly generated transaction's change is
- // unavailable as we're not yet aware that it is in the mempool.
- bool ret = locked_chain.submitToMemoryPool(tx, pwallet->m_default_max_tx_fee, state);
- fInMempool |= ret;
- return ret;
-}
-
void CWallet::LearnRelatedScripts(const CPubKey& key, OutputType type)
{
if (key.IsCompressed() && (type == OutputType::P2SH_SEGWIT || type == OutputType::BECH32)) {
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 86c2cf851c..3428e8e001 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -396,7 +396,9 @@ class CWalletTx
private:
const CWallet* pwallet;
- /** Constant used in hashBlock to indicate tx has been abandoned */
+ /** Constant used in hashBlock to indicate tx has been abandoned, only used at
+ * serialization/deserialization to avoid ambiguity with conflicted.
+ */
static const uint256 ABANDON_HASH;
public:
@@ -444,7 +446,7 @@ public:
* on this bitcoin node, and set to 0 for transactions that were created
* externally and came in through the network or sendrawtransaction RPC.
*/
- char fFromMe;
+ bool fFromMe;
int64_t nOrderPos; //!< position in ordered transaction list
std::multimap<int64_t, CWalletTx*>::const_iterator m_it_wtxOrdered;
@@ -457,9 +459,7 @@ public:
mutable CAmount nChangeCached;
CWalletTx(const CWallet* pwalletIn, CTransactionRef arg)
- : tx(std::move(arg)),
- hashBlock(uint256()),
- nIndex(-1)
+ : tx(std::move(arg))
{
Init(pwalletIn);
}
@@ -477,16 +477,37 @@ public:
fInMempool = false;
nChangeCached = 0;
nOrderPos = -1;
+ m_confirm = Confirmation{};
}
CTransactionRef tx;
- uint256 hashBlock;
- /* An nIndex == -1 means that hashBlock (in nonzero) refers to the earliest
- * block in the chain we know this or any in-wallet dependency conflicts
- * with. Older clients interpret nIndex == -1 as unconfirmed for backward
- * compatibility.
+
+ /* New transactions start as UNCONFIRMED. At BlockConnected,
+ * they will transition to CONFIRMED. In case of reorg, at BlockDisconnected,
+ * they roll back to UNCONFIRMED. If we detect a conflicting transaction at
+ * block connection, we update conflicted tx and its dependencies as CONFLICTED.
+ * If tx isn't confirmed and outside of mempool, the user may switch it to ABANDONED
+ * by using the abandontransaction call. This last status may be override by a CONFLICTED
+ * or CONFIRMED transition.
+ */
+ enum Status {
+ UNCONFIRMED,
+ CONFIRMED,
+ CONFLICTED,
+ ABANDONED
+ };
+
+ /* Confirmation includes tx status and a pair of {block hash/tx index in block} at which tx has been confirmed.
+ * This pair is both 0 if tx hasn't confirmed yet. Meaning of these fields changes with CONFLICTED state
+ * where they instead point to block hash and index of the deepest conflicting tx.
*/
- int nIndex;
+ struct Confirmation {
+ Status status = UNCONFIRMED;
+ uint256 hashBlock = uint256();
+ int nIndex = 0;
+ };
+
+ Confirmation m_confirm;
template<typename Stream>
void Serialize(Stream& s) const
@@ -501,8 +522,10 @@ public:
std::vector<char> dummy_vector1; //!< Used to be vMerkleBranch
std::vector<char> dummy_vector2; //!< Used to be vtxPrev
- char dummy_char = false; //!< Used to be fSpent
- s << tx << hashBlock << dummy_vector1 << nIndex << dummy_vector2 << mapValueCopy << vOrderForm << fTimeReceivedIsTxTime << nTimeReceived << fFromMe << dummy_char;
+ bool dummy_bool = false; //!< Used to be fSpent
+ uint256 serializedHash = isAbandoned() ? ABANDON_HASH : m_confirm.hashBlock;
+ int serializedIndex = isAbandoned() || isConflicted() ? -1 : m_confirm.nIndex;
+ s << tx << serializedHash << dummy_vector1 << serializedIndex << dummy_vector2 << mapValueCopy << vOrderForm << fTimeReceivedIsTxTime << nTimeReceived << fFromMe << dummy_bool;
}
template<typename Stream>
@@ -512,8 +535,26 @@ public:
std::vector<uint256> dummy_vector1; //!< Used to be vMerkleBranch
std::vector<CMerkleTx> dummy_vector2; //!< Used to be vtxPrev
- char dummy_char; //! Used to be fSpent
- s >> tx >> hashBlock >> dummy_vector1 >> nIndex >> dummy_vector2 >> mapValue >> vOrderForm >> fTimeReceivedIsTxTime >> nTimeReceived >> fFromMe >> dummy_char;
+ bool dummy_bool; //! Used to be fSpent
+ int serializedIndex;
+ s >> tx >> m_confirm.hashBlock >> dummy_vector1 >> serializedIndex >> dummy_vector2 >> mapValue >> vOrderForm >> fTimeReceivedIsTxTime >> nTimeReceived >> fFromMe >> dummy_bool;
+
+ /* At serialization/deserialization, an nIndex == -1 means that hashBlock refers to
+ * the earliest block in the chain we know this or any in-wallet ancestor conflicts
+ * with. If nIndex == -1 and hashBlock is ABANDON_HASH, it means transaction is abandoned.
+ * In same context, an nIndex >= 0 refers to a confirmed transaction (if hashBlock set) or
+ * unconfirmed one. Older clients interpret nIndex == -1 as unconfirmed for backward
+ * compatibility (pre-commit 9ac63d6).
+ */
+ if (serializedIndex == -1 && m_confirm.hashBlock == ABANDON_HASH) {
+ m_confirm.hashBlock = uint256();
+ setAbandoned();
+ } else if (serializedIndex == -1) {
+ setConflicted();
+ } else if (!m_confirm.hashBlock.IsNull()) {
+ m_confirm.nIndex = serializedIndex;
+ setConfirmed();
+ }
ReadOrderPos(nOrderPos, mapValue);
nTimeSmart = mapValue.count("timesmart") ? (unsigned int)atoi64(mapValue["timesmart"]) : 0;
@@ -579,11 +620,8 @@ public:
int64_t GetTxTime() const;
- // Pass this transaction to the node to relay to its peers
- bool RelayWalletTransaction(interfaces::Chain::Lock& locked_chain);
-
- /** Pass this transaction to the mempool. Fails if absolute fee exceeds absurd fee. */
- bool AcceptToMemoryPool(interfaces::Chain::Lock& locked_chain, CValidationState& state);
+ // Pass this transaction to node for mempool insertion and relay to peers if flag set to true
+ bool SubmitMemoryPoolAndRelay(std::string& err_string, bool relay, interfaces::Chain::Lock& locked_chain);
// TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
// annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The annotation
@@ -593,7 +631,7 @@ public:
// in place.
std::set<uint256> GetConflicts() const NO_THREAD_SAFETY_ANALYSIS;
- void SetMerkleBranch(const uint256& block_hash, int posInBlock);
+ void SetConf(Status status, const uint256& block_hash, int posInBlock);
/**
* Return depth of transaction in blockchain:
@@ -610,10 +648,18 @@ public:
* >0 : is a coinbase transaction which matures in this many blocks
*/
int GetBlocksToMaturity(interfaces::Chain::Lock& locked_chain) const;
- bool hashUnset() const { return (hashBlock.IsNull() || hashBlock == ABANDON_HASH); }
- bool isAbandoned() const { return (hashBlock == ABANDON_HASH); }
- void setAbandoned() { hashBlock = ABANDON_HASH; }
-
+ bool isAbandoned() const { return m_confirm.status == CWalletTx::ABANDONED; }
+ void setAbandoned()
+ {
+ m_confirm.status = CWalletTx::ABANDONED;
+ m_confirm.hashBlock = uint256();
+ m_confirm.nIndex = 0;
+ }
+ bool isConflicted() const { return m_confirm.status == CWalletTx::CONFLICTED; }
+ void setConflicted() { m_confirm.status = CWalletTx::CONFLICTED; }
+ bool isUnconfirmed() const { return m_confirm.status == CWalletTx::UNCONFIRMED; }
+ void setUnconfirmed() { m_confirm.status = CWalletTx::UNCONFIRMED; }
+ void setConfirmed() { m_confirm.status = CWalletTx::CONFIRMED; }
const uint256& GetHash() const { return tx->GetHash(); }
bool IsCoinBase() const { return tx->IsCoinBase(); }
bool IsImmatureCoinBase(interfaces::Chain::Lock& locked_chain) const;
@@ -663,28 +709,6 @@ public:
}
};
-/** Private key that was serialized by an old wallet (only used for deserialization) */
-struct OldKey {
- CPrivKey vchPrivKey;
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
- // no longer used by the wallet, thus dropped after deserialization:
- int64_t nTimeCreated;
- int64_t nTimeExpires;
- std::string strComment;
-
- int nVersion = s.GetVersion();
- if (!(s.GetType() & SER_GETHASH))
- READWRITE(nVersion);
- READWRITE(vchPrivKey);
- READWRITE(nTimeCreated);
- READWRITE(nTimeExpires);
- READWRITE(LIMITED_STRING(strComment, 65536));
- }
-};
-
struct CoinSelectionParams
{
bool use_bnb = true;
@@ -775,7 +799,7 @@ private:
* Abandoned state should probably be more carefully tracked via different
* posInBlock signals or by checking mempool presence when necessary.
*/
- bool AddToWalletIfInvolvingMe(const CTransactionRef& tx, const uint256& block_hash, int posInBlock, bool fUpdate) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool AddToWalletIfInvolvingMe(const CTransactionRef& tx, CWalletTx::Status status, const uint256& block_hash, int posInBlock, bool fUpdate) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/* Mark a transaction (and its in-wallet descendants) as conflicting with a particular block. */
void MarkConflicted(const uint256& hashBlock, const uint256& hashTx);
@@ -787,7 +811,7 @@ private:
/* Used by TransactionAddedToMemorypool/BlockConnected/Disconnected/ScanForWalletTransactions.
* Should be called with non-zero block_hash and posInBlock if this is for a transaction that is included in a block. */
- void SyncTransaction(const CTransactionRef& tx, const uint256& block_hash, int posInBlock = 0, bool update_tx = true) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ void SyncTransaction(const CTransactionRef& tx, CWalletTx::Status status, const uint256& block_hash, int posInBlock = 0, bool update_tx = true) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/* the HD chain data model (external chain counters) */
CHDChain hdChain;
@@ -922,6 +946,9 @@ public:
bool IsLocked() const;
bool Lock();
+ /** Interface to assert chain access and if successful lock it */
+ std::unique_ptr<interfaces::Chain::Lock> LockChain() { return m_chain ? m_chain->lock() : nullptr; }
+
std::map<uint256, CWalletTx> mapWallet GUARDED_BY(cs_wallet);
typedef std::multimap<int64_t, CWalletTx*> TxItems;
@@ -1067,7 +1094,7 @@ public:
void MarkDirty();
bool AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose=true);
- void LoadToWallet(const CWalletTx& wtxIn) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ void LoadToWallet(CWalletTx& wtxIn) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void TransactionAddedToMempool(const CTransactionRef& tx) override;
void BlockConnected(const CBlock& block, const std::vector<CTransactionRef>& vtxConflicted) override;
void BlockDisconnected(const CBlock& block) override;
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index 9f467c0f5a..635997afc9 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -115,7 +115,6 @@ bool WalletBatch::WriteCryptedKey(const CPubKey& vchPubKey,
return false;
}
EraseIC(std::make_pair(DBKeys::KEY, vchPubKey));
- EraseIC(std::make_pair(DBKeys::OLD_KEY, vchPubKey));
return true;
}
@@ -256,7 +255,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
ssValue >> fYes;
if (fYes == '1')
pwallet->LoadWatchOnly(script);
- } else if (strType == DBKeys::KEY || strType == DBKeys::OLD_KEY) {
+ } else if (strType == DBKeys::KEY) {
CPubKey vchPubKey;
ssKey >> vchPubKey;
if (!vchPubKey.IsValid())
@@ -268,14 +267,8 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
CPrivKey pkey;
uint256 hash;
- if (strType == DBKeys::KEY) {
- wss.nKeys++;
- ssValue >> pkey;
- } else {
- OldKey wkey;
- ssValue >> wkey;
- pkey = wkey.vchPrivKey;
- }
+ wss.nKeys++;
+ ssValue >> pkey;
// Old wallets store keys as DBKeys::KEY [pubkey] => [privkey]
// ... which was slow for wallets with lots of keys, because the public key is re-derived from the private key
@@ -407,6 +400,9 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
strErr = "Error reading wallet database: Unknown non-tolerable wallet flags found";
return false;
}
+ } else if (strType == DBKeys::OLD_KEY) {
+ strErr = "Found unsupported 'wkey' record, try loading with version 0.18";
+ return false;
} else if (strType != DBKeys::BESTBLOCK && strType != DBKeys::BESTBLOCK_NOMERKLE &&
strType != DBKeys::MINVERSION && strType != DBKeys::ACENTRY &&
strType != DBKeys::VERSION && strType != DBKeys::SETTINGS) {
@@ -428,7 +424,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
bool WalletBatch::IsKeyType(const std::string& strType)
{
- return (strType == DBKeys::KEY || strType == DBKeys::OLD_KEY ||
+ return (strType == DBKeys::KEY ||
strType == DBKeys::MASTER_KEY || strType == DBKeys::CRYPTED_KEY);
}
diff --git a/src/walletinitinterface.h b/src/walletinitinterface.h
index 22aca65990..2e1fdf4f3a 100644
--- a/src/walletinitinterface.h
+++ b/src/walletinitinterface.h
@@ -5,10 +5,6 @@
#ifndef BITCOIN_WALLETINITINTERFACE_H
#define BITCOIN_WALLETINITINTERFACE_H
-#include <string>
-
-class CScheduler;
-class CRPCTable;
struct InitInterfaces;
class WalletInitInterface {
diff --git a/test/functional/combine_logs.py b/test/functional/combine_logs.py
index 45ecaabe14..367d0f6916 100755
--- a/test/functional/combine_logs.py
+++ b/test/functional/combine_logs.py
@@ -8,6 +8,7 @@ If no argument is provided, the most recent test directory will be used."""
import argparse
from collections import defaultdict, namedtuple
+import glob
import heapq
import itertools
import os
@@ -76,9 +77,17 @@ def read_logs(tmp_dir):
Delegates to generator function get_log_events() to provide individual log events
for each of the input log files."""
+ # Find out what the folder is called that holds the debug.log file
+ chain = glob.glob("{}/node0/*/debug.log".format(tmp_dir))
+ if chain:
+ chain = chain[0] # pick the first one if more than one chain was found (should never happen)
+ chain = re.search(r'node0/(.+?)/debug\.log$', chain).group(1) # extract the chain name
+ else:
+ chain = 'regtest' # fallback to regtest (should only happen when none exists)
+
files = [("test", "%s/test_framework.log" % tmp_dir)]
for i in itertools.count():
- logfile = "{}/node{}/regtest/debug.log".format(tmp_dir, i)
+ logfile = "{}/node{}/{}/debug.log".format(tmp_dir, i, chain)
if not os.path.isfile(logfile):
break
files.append(("node%d" % i, logfile))
@@ -164,25 +173,26 @@ def get_log_events(source, logfile):
def print_logs_plain(log_events, colors):
- """Renders the iterator of log events into text."""
- for event in log_events:
- lines = event.event.splitlines()
- print("{0} {1: <5} {2} {3}".format(colors[event.source.rstrip()], event.source, lines[0], colors["reset"]))
- if len(lines) > 1:
- for line in lines[1:]:
- print("{0}{1}{2}".format(colors[event.source.rstrip()], line, colors["reset"]))
+ """Renders the iterator of log events into text."""
+ for event in log_events:
+ lines = event.event.splitlines()
+ print("{0} {1: <5} {2} {3}".format(colors[event.source.rstrip()], event.source, lines[0], colors["reset"]))
+ if len(lines) > 1:
+ for line in lines[1:]:
+ print("{0}{1}{2}".format(colors[event.source.rstrip()], line, colors["reset"]))
def print_logs_html(log_events):
- """Renders the iterator of log events into html."""
- try:
- import jinja2
- except ImportError:
- print("jinja2 not found. Try `pip install jinja2`")
- sys.exit(1)
- print(jinja2.Environment(loader=jinja2.FileSystemLoader('./'))
+ """Renders the iterator of log events into html."""
+ try:
+ import jinja2
+ except ImportError:
+ print("jinja2 not found. Try `pip install jinja2`")
+ sys.exit(1)
+ print(jinja2.Environment(loader=jinja2.FileSystemLoader('./'))
.get_template('combined_log_template.html')
.render(title="Combined Logs from testcase", log_events=[event._asdict() for event in log_events]))
+
if __name__ == '__main__':
main()
diff --git a/test/functional/data/blockheader_testnet3.hex b/test/functional/data/blockheader_testnet3.hex
new file mode 100644
index 0000000000..882133aa2b
--- /dev/null
+++ b/test/functional/data/blockheader_testnet3.hex
@@ -0,0 +1,548 @@
+fork:0000002043497fd7f826957108f4a30fd9cec3aeba79972084e90ead01ea3309000000000943e54375082c03172552ae841bab31ebf2463484574f6ce6fe9c3723e3defb719a485dffff001db8b63209
+fork:00000020da2809ab72cf2502ecb29137dbe63e51fb82fb5babe6c9530dd86dea000000005dfcdc47012c19a2708b53e820c71b529f616a45529d48bad484948c84685d572d9c485dffff001dd3530a08
+0100000043497fd7f826957108f4a30fd9cec3aeba79972084e90ead01ea330900000000bac8b0fa927c0ac8234287e33c5f74d38d354820e24756ad709d7038fc5f31f020e7494dffff001d03e4b672
+0100000006128e87be8b1b4dea47a7247d5528d2702c96826c7a648497e773b800000000e241352e3bec0a95a6217e10c3abb54adfa05abb12c126695595580fb92e222032e7494dffff001d00d23534
+0100000020782a005255b657696ea057d5b98f34defcf75196f64f6eeac8026c0000000041ba5afc532aae03151b8aa87b65e1594f97504a768e010c98c0add79216247186e7494dffff001d058dc2b6
+0100000010befdc16d281e40ecec65b7c9976ddc8fd9bc9752da5827276e898b000000004c976d5776dda2da30d96ee810cd97d23ba852414990d64c4c720f977e651f2daae7494dffff001d02a97640
+01000000dde5b648f594fdd2ec1c4083762dd13b197bb1381e74b1fff90a5d8b00000000b3c6c6c1118c3b6abaa17c5aa74ee279089ad34dc3cec3640522737541cb016818e8494dffff001d02da84c0
+01000000a1213bd4754a6606444b97b5e8c46e9b7832773ff434bd5f87ac45bc00000000d1e7026986a9cd247b5b85a3f30ecbabb6d61840d0abb81f905c411d5fc145e831e8494dffff001d004138f9
+010000007b0a09f26fdde2c432167d8349681c7801d0128f4dfae4dc5e68336600000000c1d71f59ce4419c793eb829380a41dc1ad48c19fcb0083b8f67094d5cae263ad81e8494dffff001d004ddad5
+01000000a62bc0c08afc1d12e6c6a7eb4a464c848190ac0e44123d5fa63a9ee2000000000214335cde9edeb6aa0195f68c08e5e46b07043e24aeff51fd9a3ff992ce6976a0e8494dffff001d02f33927
+01000000f9e2142a93185496f7b21314d8b6fa736d0a30fa3a6d339ab3a1ba9c0000000061974472615d348df6de106dbaaa08cf4dec65e39cefc62af6097b967b9bea52fde8494dffff001d00ca48a2
+010000001e93aa99c8ff9749037d74a2207f299502fa81d56a4ea2ad5330ff50000000002ec2266c3249ce2e079059e0aec01a2d8d8306a468ad3f18f06051f2c3b1645435e9494dffff001d008918cf
+010000002e9afd58b91f15c3ec9eb0f01ed9d503134da1918b6bb416a9920e700000000029fb495afdb58f3a26d1c90fafec93aed840e2fa37ad6173ba1e7fadb7121ee57de9494dffff001d02e7f318
+0100000027e0ca29a9802c0a2390ecfa90a9bd814fecc54446510e155652dead000000007e8d5344557575c8f018cc62a32e8e0bd80638643b4ec34945ec4662fcab138142ea494dffff001d04acbc3c
+01000000001f3ada9b561378e324e80ee68facd5d232f72f773b86328393054700000000eaf3be35e3f0ace8b6abdeb5509d72999eae2329657238b53fa437e319c8e96b99ea494dffff001d027801a8
+01000000781bc7847e15c3b936a6a6a178e38fa29ee6e4916a8a62e10795c69200000000d44c3443fa8bd88bf32b94b9257f09ce6fb6ec0d5420504d631568f8685200dfa1ea494dffff001d01f781d0
+01000000133991a938b505ee8f6f347f313c3372d82a9d8b42b08b0dd0fc086400000000a0ef58c239e0197a65aa248c2cf52c437d8c8ea30d1b835e630a87c941f7d4e9adea494dffff001d030ef2e0
+0100000028d34cdb13e555032e4bec55fcce3d0fef8212803fb1bab851e1259400000000542c71544b9f28bd5a6fec95ecd509ae49d0b04f8718c685d0751f71d38285d0c3ea494dffff001d056b3115
+010000006b00cf1ce31b33fe1e2c4648a0834dedd972ffb2a2f341f75ad7cbc400000000adebf7afcbf176f765aec16b74d92896f55c3d65e14dd1a8becee0871000291751eb494dffff001d006f85e8
+0100000043a78ddf30a2d28a42cc66f90d13cb8211ee0fca9dbf8a4cce8c19fe000000004edbd2b89cb6d6fd69b575a62bd4e3103b1e0ce19e31bccf9a093ad8ccd753cf7deb494dffff001d0591a0b3
+01000000489ac81592595a4004e14331cb096ffef12b1daf709f6378e9c3558d00000000c757bebd6f2c2c071a3cf739a4cf98b27441809790a5cf40652b46df8a98a473b0eb494dffff001d011aedb6
+01000000a9c570a45d959023551f9a694ace9c12206174f21383f30949ca3b9b00000000eaf93dbbfb3551a1ff8b6bd5ba4cea7508e790c23cd07b9d9e791936a79d5fd4b3eb494dffff001d0385a7dd
+01000000d35d5fa860dc70c8bdaf12f18e16d8b4cc29d141c28d59cc317fe5ed00000000507dae091a9657b6c073863ca71ba6989a2cf4417fb81e940668568a35d34a7119ec494dffff001d00effec3
+0100000073379e3ff3dffd006e0090e52ac571a9a309490a23e64d15f8af291a0000000051f1c5b2b7c8f980e7715b4d3ce0180f99c44a16fc9c00ede2f5984b8d7cc22d16ed494dffff001d0082467f
+01000000869845a3343adddfa5b1f534b507d9b67c3685b0f1d89d526cdbd34200000000823623e8c6fe2c449065d2c0ae57aeb4bfd8e9687126a6c99d1ce916e2fca63f4ded494dffff001d034f940c
+010000005da49f64cf0025ab1111651d94748b00bdb00b780744b88b42f962c200000000fee9e254a5a74c858297e89ebcd2305ad2707a8acc131ad07f6abc0d8e38def969ee494dffff001d05c512e2
+010000006a65bc120bf3e6dfadc3b9543e48f8876cb826aed0d8f809bc34bb220000000090f489f48c88442aa7d9250f743b386558ba1fa2e7e240e5d32195d56cf1c34ffff0494dffff001d014394ce
+01000000e3f21ff9cc51ef282bf6bebc90e6f96968a36a704452192724c839bd00000000b6d553c98016b66fcb4856ffedc13a2de720288d4c2e8fed86206981259791a23af1494dffff001d017f7044
+01000000e16daff1b16a81a3058d982e79550c9c9ba84a207a8b84ae092eb4b300000000f2f34dd423f99930aee95815b2885906f9cdeaba04a9bb076c1f359c2031732059f1494dffff001d01b322bb
+0100000059ebd22dc26158414c60868355e78ab4b6891345fd97602f6c106d9c00000000e6c5bea3888e891bc5f9f8fcd166d332071d3b434e933763fb20db50e47dad3f5ef1494dffff001d0067a0e5
+0100000015ddbe82f202b27febceb00547dc19653604ef434f080848f22e3b0900000000e26e8971a53396413f0f39b88a697f593993999c8d07fa2dde608111fb2ccbd3daf1494dffff001d04362f37
+01000000a76595e37692f85d5de0438da8e75b5f611fd7b7071816b9ace8ba2b000000004cfb6d8faca8e8e77a71359d2cf0d12d2e52f266591f5fb807aa737c90869d2a81f2494dffff001d040e87a6
+01000000a774311853f32f32d87081529bb0506d5e4e90f7e455bb640081215f00000000228b387354daa9e5d38201811fe746591ab08a66bb3c4fc796a45535acb8c61baaf2494dffff001d01ebfd80
+01000000d0efaa4f6924659f1e0221f910e99f6fae76b36b759b212852d1343a000000005e40d3b65982e929433ee02037a60f05b62e70e6d51f608974fb1f2926169398bff2494dffff001d05c41c3a
+010000004c600547d8ed4b9dc946bb455f64917131dd98974bf2dfda05afc3a800000000e5b01ac4a611211847b0777b9ba9e396b0b7348ac401041e4fb6168ed091691ccef2494dffff001d037a60b7
+01000000f571553e5aa32b8374bd5a0f3c58834a46e05727b64dbe62a942376600000000a91cac1e92c7c597af565594565f5b54e658fd2e022e4fc35eb92aa165d9aa4fd3f2494dffff001d02687811
+01000000d8273e218df68e333782ab3b79929a8609404bd85bc225b46debba3700000000e91f9128f70a0502d338221316a0a3a1f4181821f9d2220c3fce7b5cf8e305d514f3494dffff001d01f22226
+01000000ad615025da247d14a4f092f21c4fddc6d1a84e0b4b0929f9ccf81c180000000069cabd55fc74596566ed1548b6b4ac23943d33e817028d8bc8695a7bba82256e6df3494dffff001d00e066e2
+01000000e7da808f2e62c3ec1b443e2979c972f5f69f32146cb4385bcb6fdca2000000002f4092fa4879ecc1a39471c41f8fae20f10ad2204bcda34e79ac37cbbda973757df3494dffff001d0316a8a6
+01000000928b86c36f27d22c7d2baf27f31b50bbec7754d33d12aa37342723db00000000ece3fdbc63b66327abb251165ac0e19b1a02fb79295c9b6eb8b38b68c1c59107dff4494dffff001d05bcddf0
+0100000088c96b45d3e252cdc38780843e3679d74e858a1d218f9e3e0866335e00000000432cba2143d62f6349faf4f7956f7354733518c22a309284d96236a9d8c9616e0ef5494dffff001d026ebb97
+01000000885d76fd42926155ff9ebb1d3c41a517d7beb70564b98c3608719f740000000011a3bb257acf328a5bf1258bbb653b88a40b2d2a66db695a1ff1aa9d31d5186590f5494dffff001d02bcabdb
+0100000093f416076c9b2eb0d147e8ce3bac03aa0784e44a6de6fc6f61183a2800000000be46ca90dd31022ee15f74c221208eadf1840e208a6adcaf127a60cc61d959c4d2f6494dffff001d02d68fd2
+01000000f9da861df4a86158e751c062e0a82d3c90e9038330f6447df4aee3d000000000972534907cdd1bc56f21adde0850dd17d988711a5c36ff81ec84610b4f762e9ae3f6494dffff001d00e96635
+01000000303a04a9f551e93a8ca88ceaba1c1d28892323564988ab7f99a9c0d70000000087a11f1aa5769968461b251ca9229f59acad999284e29976331cc13da46432364ff7494dffff001d012c54d2
+010000006b82062aabb19e4cc89addeded5c65ec28684cb54d30d463719f094800000000a98137fab92249e632091c38e3fe4c7fd8bba11bc956fe83c41357e217929e1e8ff7494dffff001d01edcd15
+01000000f7332e78d21adde6e52aa20362c71c1fd7bc745b3edff6e72497067500000000d521571198d3d2c4124b8bd9ba7842716b3bdd93236a92ca6af7c0a7adf9633c2df8494dffff001d031b1c13
+01000000c9ebcb8ea6f7e6a611117d0268639625cee28155c88708b4c09d4aea000000002d4c50f85979f0d2491d015206d867b273b353c253a173abf18dfbc5d96d088e50fa494dffff001d0397f4fc
+01000000562c889f95c49db05f6a33277aae9bfe68e92f00a5d6f67ac8c366a100000000b7b0896c4a53b6aab15282cf53e1119676d89be0eda11064cadc40dc8ec194dc66fa494dffff001d01f0369e
+01000000d41cef71d625aa5380f6cdc6452c67951e7e4f5b27b7904b4c2da413000000001c1e1096ab473aba614651fb98f47a375c03ec470daccff39a2d2d65bfd881b47bfa494dffff001d02454a15
+0100000095359a35957b89dd268576d562f49db7939baeda6de4855426ab5d9c000000006f68039da08fc314bcc71631be6f4b2ef5e0a2f9491fc078b11fad3dd49ac287a0fb494dffff001d02fbc213
+010000007d505f65addb5a3b50eb33cc5cd3bcafe03ee597c4027166aad2d2630000000090d221539ba3dabdcf0eacfa9d63f272c59dcc07e5e193271a24f4c11caf2c24d9fb494dffff001d05542cd6
+0100000013d5bb77b9235002acc75014e0c061c79de1752d3edc6859b4c0df7a000000000d17f332abe46c092877537ae764aa99e9b25d6bdd94f4007eac43f4861cf4675cfc494dffff001d02f62def
+01000000612ec5ae40cb2a58e18d3124fd70664a4fcb9329f7d268d4bcaefacb00000000f7bc31a9984831440cfae529df94ba1680fa15b4490454454920ea8947af4a3765fc494dffff001d01c6c1af
+01000000f13ce1836e92f0d12e4892eb229cbf6d50b9b2080af16b01f00b25de000000003f8f64092fddae84dad92736fb7d350e25bda6118bb1c660b06a4a5730fd352815fd494dffff001d055e65b0
+01000000ee24ae636bd70cadac603bf8cc631369bafbdfa8ac8effbc7dcb0fa5000000006faf8082545aef971aa4f92bd411947929c4949362300cd1302ec8bf74091fbe35fd494dffff001d0139a790
+010000006350a3cd2181f0eb75fa8c4022634ad85b6b9e1f9b7346a2eef5517900000000a09d738f407e2dcc888e8ecbde93458d8599720e0359a380d503ee56c688ef72f9fd494dffff001d02661caa
+01000000a29d92b014057dbff2775280b8bae8b3877fa9a345f4972034c0711b00000000447bae5734b6c67dc36ae088869c18740a836d7a9feff0b349efdd7373a21358fdfd494dffff001d01b13c06
+01000000dcbf4a9455bf33482c2e17640cb89711468c0def1ba9f6e26e5a4aed00000000b555247649a364adb88b07afac3aa2f9ddf5c379154cb17d22f69fd74c84a2e24afe494dffff001d03144aac
+010000000429b2aa02abcb8275705dc6482230dcb7c8678388fe7d75022c1ed800000000d5efd425dce6deb5750306ce8a0e5d045e0a607ee0b530866833e956dbeea0575ffe494dffff001d026ba2bb
+01000000871d8a30b3b39373d71fb9c5c7d8d7a3c005ad688d9c8ca974cd330e000000005ec14d2b9adfccae05f78556b12fe2ca03cddd8bd67896fa5c6b6fb090e90177a3fe494dffff001d0147b26b
+0100000019fd8a4e03a52e43322506693b696af00b828361e2a0968898708e43000000003853a270a7d9e6ad80fbf911146428c7ed63b2a1154df748f5a980fe20e9e8e2b3fe494dffff001d04538437
+01000000d250d67bd2679caba305e5e13698c7880098a1a4383c2f464fe9006d00000000d793e7c2c9416eae3d8d81046985db5f3d1df7922e869fda6ec5b22181ee46f725ff494dffff001d02391748
+010000009d83b8dbaae3dee980529b6dbd307df153a22b5b0fc35ea694a1faad000000003cfd458875824fadce673ca8c3125fd12c36b7230035b5f056a12d4e0d6b7e6b7eff494dffff001d0448653d
+01000000a7d1ff0415c1d1be8a11c7dc20557e001d2fd96c0f396f52969e330d00000000ca20885f915b1d3c5e614cf76067fcf8d9894204db63664d512806ceab58ea27d2ff494dffff001d0327cc00
+01000000d98a10f08f4a7aa698ba8564a85bf821b747c7a44f656fb2e3b9335f00000000dad4bbd7663a69c07ff9579434972bb203d0d78d07858ddc7ca32513101c964e3d004a4dffff001d04df7568
+0100000031965a378dbb0d024df444b3ebf894f370d28df8abdd54565b598b9e000000007ad93f6eed4656fa7c0a606c08b067be35dfa5392eda5d77f133dabdfd08739794004a4dffff001d04facfab
+0100000004cc9e4dad8b5d2166fb175e7bfe13ad56018dc81255d335336b8b38000000002d011ec2248c2899ca72a01673535807e2cd0fbcd4465ebafe2a002f3f6c9ec1a3004a4dffff001d04d4b0a1
+010000002093e7853950d588f1a36601643a97b7e187f6b7b274ae43d82410a6000000008ea10bf2d0d535c64d9db748aec8723566b270e0becacdb07438273654e59feea8004a4dffff001d04d4db49
+01000000eff17d4b5360e16151019597f732fc8f6e3b19ecf0c88e2ab1a2bd3a000000005d9b9c1d04856052c8f01497c9ce05d01c4c80c1c4296b15549ace3e04e2cc24e2004a4dffff001d02358897
+01000000bf5ea56c32a49508f0987cf0b18513d285edbe987b0dad25ceef3f1b00000000ba933ef648e2544227c7db41de45f0cf39aa612478c126e5ce5393668e230f1213014a4dffff001d044c6575
+0100000091ff63e6ab577bdc347fc87265fc316d53303bf28d0294a24791143300000000a97e4a6f79bb595fe0239f31549d5b06e13e7d5062e3707ebab0f38f0df5d1832e014a4dffff001d0083c449
+01000000bc749fb377c9a937702bd4ff35d376b43f5bc56726029a93a38e8232000000002bad8475b38ccd82eb98377132453c72706ae9626de3f913e8da2cca6795481166014a4dffff001d0594c61b
+01000000a02349924ea393906e7fe3ffc2b9d152fdf55bcc1bace5251517168100000000448e52c8902abd28f82f3ff5eec8c97ab16b71b61674713cbc4f9a896f733b5ba5014a4dffff001d05c09af9
+0100000011f5cea7297afda99c9a141c4d438ff7708f451b571b4aa57b4b69f00000000087550789e53e3c66c4ce24d145f615c95f593d7557fa3ae2eee2a1f985549f9bfc014a4dffff001d00e035ee
+010000004ba4c615f54d1557c5f691cb614cecb9edbad0011ebbd3b94fd458ee00000000c9782c5584bbe7c3a51f048dbf1e428e0b0e092607ea6514c401f13a45afd2b6cd024a4dffff001d049ecd2a
+01000000718886f10113146ac974a449c8b5bb205e0307f42d1b81990cf1991e00000000b88f0b4d80b631a24a15d7c640ecc15ee82188bb16d692be42467e21b3971c1421044a4dffff001d03e07f40
+0100000081fbffbf5c27e4e908c14bc8f303a4d91bb71ae05445e839c531ec81000000002aefeed1a7378f70e1f434bde89af706e1777993d25d9a95f5b4f4a0d83bcda8d8044a4dffff001d02cda9e0
+010000006d7f371b437cc054548511f0f9ec50b6e04bb4f3a4915a099c3bad36000000005b72bacbca2f6bb848d4753733f6427849b1aa4805b244e2751c38ff8d3e339c41054a4dffff001d0018ffae
+01000000b82c2d751368b79004a8e15e5b9f1bc7f620d5de2d6105d83d8ec2f6000000000cdfbd9bd57b78356abb0d07551b0c5c20d5258091604be05674d4d19455df2fb8054a4dffff001d003c4d86
+010000006efef1bc9d35256bbe500ee9c92230e0f987296e373e20ee7f3da4710000000030a2d3961a799e229a81337790254ebaefa055bb85ec7f03e5c8e9c3250eb35ad7054a4dffff001d0514a76d
+01000000e789ed685960b594652b9b83a28ded0995725d6ad82b2f358058cbe80000000035b73fb2084e2e8faa28334b17c12668004b7cc8965831aaba65b38b7c54ba97ff064a4dffff001d055247e5
+010000000606ede205a1002e0995f8d009de821d488e22fb37167bcc9120afbe00000000f6f0d5c7f8204a7142f2e37a7cb406e0a57fd7c9784c12111c55a1cde5418ed11a074a4dffff001d01a5f8d1
+010000006da3e0f7fa132425557519508e4e97cbec301a33e25d4b65ac054a0d00000000d657340c13f9f8accfc3af62529a25d3d9e656caf2993fd3166f73850f397c228e074a4dffff001d0309686d
+01000000cb9c32345bb264b3b2a6e96c8843f65c9598e4948737c569c0a00073000000000894a15ed694c745d87d2135f90eb9dcf8b454937b48c82132d52405e0d4c4689f074a4dffff001d02d5845a
+0100000095349f59ed0c0ce5ab0de43b9d55fbc7b8afac7eb6dacebeaaf290f100000000df9bf22064b03cf08e658257b5d19662fdbe3d7acd27894cef0d3f9ff5a657bbbc074a4dffff001d00a13d79
+010000000be1a15c8ae486da44f0585514eea60780091e52e35f838cd1572bf50000000077333896bed3021acee751bf0c073d95bbdc99125f4298c052db8998bf7c3270f9084a4dffff001d008f86bf
+01000000c0b7b630de7b1bd92c4be9d32e19faeadd68f60316ec97db96eaec0f000000005e3c98a964d941f4abd129531d0ee81cd5e7dc098179c3188ba36ccd1e5f9fff7e094a4dffff001d03d8a118
+01000000ae3c51439dc8f2bdd807e1d88c25a5a0b1a3005bcbb50bbb4e48493f00000000ac719d460d514cc4489597ac78e995276865ac07d7606a313c12b16e769b9294f50a4a4dffff001d04735f73
+01000000ce12350c698e84085e7b62c039249c63cb6a6cc9404776c9cc7fba8800000000ca128b2e74fafb345ad249d61c538f75a2a230bbf2266d470d47986555894deb370b4a4dffff001d001c0a17
+010000003dcca8252a636d4c0b8bc4ed9285b749434aaebc29965fc691a635ea00000000baebe5ae323c2bc5c316f6a8f1947b4666c7707c9ae127a03e6429fea7624ae53b0b4a4dffff001d00b4a44c
+01000000f6cd7f4cec06d5c6aea54e64b45f049640680e4cb8249cf18587b314000000004a1b2b51da86ee82eadce5d3b852aa8f9b3e63106d877e129c5cf450b47f5c02480c4a4dffff001d02552721
+01000000c64e139bfae4adcd96860bbef3969b84851dc4d4fd8d06f16cd03698000000005e4b195fd24b314f69f7bb5b0139861b0f07a1286b8f6f42dbb6c82524bfdb93890c4a4dffff001d033b3297
+01000000a3e811a098612235feb5e1acb407cd98132a9d5f4dd99aa8d8b576ea0000000025a13ad0cce0eeae69ddb545bda3b490230e68dbf0687af3ddfa132caebadd7cb30c4a4dffff001d04a39aa9
+01000000ab5ab86fe14c9c765d8cdfd67f9bd5d41505f9f9f67e4da1851f099700000000beb6740250c060fa7b472f4daea18186d47e266dfafc88f088d7efe3ca5a2ac1060d4a4dffff001d0511eae4
+010000006ee9f67721369cabaf6e3e9045b2efb70bc1344256dda634a92eec5200000000d5b0833f82460e5ea635a31c60314eec20bb317f2cdaf354023ed4225e31f264380e4a4dffff001d03a74f10
+01000000b3ef61423747695daed4acef8980b5ef4c8feaaa908b3fdc6fbecdab000000004fd416b35e12b775e2899ba509a06822ed8b6627311364f5195d32ca6a314dbb3d0e4a4dffff001d0255ab96
+010000001c75c30c7bb6391ec7d94f6d52bd2aeb0b8f5224907b0106791ccdd70000000016f86bf97a3dda131f108ab4ab456d5ed3cc5e67eec631d8fa044b95c19f3449410e4a4dffff001d048e1c98
+01000000a8bb0604235d5310973b23a5c797442a1ae8c07b96d8cd33c191b45300000000322948a4806acfeca2b32248d0e183c8eb09d5e5ef48adf33777307635414cc05b0e4a4dffff001d00edcc80
+010000004d37f2af0c42371bb77b52cfe7f539e550e126631dbae6056e54dec80000000014238140ff083fe67f0d5c0627857c4330c4c96187964f42b680ec460ad0ccd6620e4a4dffff001d048185d3
+0100000087e774423a88647a4567d3a15e7099b7f271d8d846066af9776ef4ca000000000d90cd436a707875c28222178146cb93f6b048dc4e7555cf37b96757e3b90a5b740e4a4dffff001d030ccab4
+010000002f3be6a1d59b7786d8601330a47f030fcdf2354275fbafa8f4c129490000000098a23359c17ca2678e2039c8ff9081b18c4913749c9a081ac3f62958f09fa472e10e4a4dffff001d00c7fe6b
+0100000050ac3cacdb94018a26258b82299da1307ccf3e2bf62a8f4acc19e02c00000000e09f513a024d3e13473d7a65f79073b36a90cc228613672d2a47812368ff42d1df0f4a4dffff001d007f726d
+010000000e044393202d6b239c902d3f634e3dfdfa31ae439d339238ab1688e30000000055b5d3d496e196d624a471b818ba0b1778417ae335a544033536654fdda3eef6e80f4a4dffff001d03ed4240
+010000006396e6ba5ef4924d42f4f3114ec7507b81bce51e9e2eb51ca9c53c420000000049af9208af7b7a06d65ce1cfcab6ad9123a8dd7538fb0aca332c63429ff48d59ee0f4a4dffff001d056528d3
+01000000497b15826573b28c3e83a5d0c5ed30cf48b97dd6bd797849144ea2400000000018b50db063333a3261b9b41e887b4aa5b69becdc9967550507c120e22a76496710104a4dffff001d0408eda5
+01000000f5ddc74872eb899f5113e002f642e7b507f871d99a9900edba304aa0000000008f6546b850a14744afc3fe55f76f3959f40799bf4dcefe01ae4dba5903cf2fb553104a4dffff001d018f658e
+01000000918a758ed54c9f495edb24ef3fe0f4432ede25853c324fc0f33a458f000000007a8e49b22114f17b5933fc7a8005421ff8370b8c48fa04c24323e91bd02d701492104a4dffff001d053e6e0f
+0100000035893f7cdeb0e9af7d9fbda1584ef6d5219dfbb141b07b31257a1658000000008b9b3abfbe24d0e375deebb5f41e74949203c00772a678ba69c1126156c5489bfa104a4dffff001d012a69fb
+01000000b6f8c48e94ca346b12373281acbaa08fa54d1cfdcd9c01e020cdda9f00000000be5b4753c6062e3eaed75f5412e43d6dce8d242c5816b436689f795f90536f28cf114a4dffff001d01b6887f
+010000006006db00d70ce04a9940c203dc865b3c5d070f8c2d1295498ccd6c32000000008bc41e410a44b764dacb38c1138a3ff2c038a188a063509c6fed4aacaae72ee67b124a4dffff001d009531e5
+010000005e0dc170558d7b2872ddc85f481531dd823dffa66cc620c065adef7700000000e82e91ade6f25c8c6f4c053aa62d94926324ced07ba2f3aae072b13a2c5dc5f89e124a4dffff001d05ae2281
+0100000056320cab20bc1daf4fe3cf0115f2436523e44c40ebcf8c18e6b5822f00000000ce415eab9cba354ae042c22ac9f06c1a69d7a5dba67136fabef93d82f374dd3e01134a4dffff001d05a0d736
+01000000ccb063ab7d74a4030fd155615f046f95c8068078557568ce6b8092fc00000000ff33b27214141ef3d183b1d2499666c8635a57943ef5f515f4e60515f9ea0064c1134a4dffff001d021fa95a
+010000007f6d7a61bd46dd27be404b8c883b812c2899095462591dcd75a96f1e0000000091777c00b7168a888d7a7db4b5f78758129e79ef909f92a84110b9f33f9c4c5505144a4dffff001d03526c4d
+010000006be8cfe3e176d34d1a46f68b7d20a01ad3f9e2aa6f7540ef6a32573c00000000a870da2f87071c1366a22e77c829a6b85d745ab2279e0333872518d58b8dc0181a144a4dffff001d042c5db7
+010000009cb87223258aba43742de401d0157ee2b4057da95b23e1665880725700000000d54d368cd4243da3793fe3ba2af1570dd44a905d77ecd1a5cbd07cb8f72ad80f30144a4dffff001d048f280c
+0100000052b771a3a85c26bc796ae0841ae894c6ad4527b062c94812d98b9c5800000000eeed0f4d975db8f66788f809ecf8c351d19ff5805276ef31983bc5682548342d52144a4dffff001d01aa3be1
+010000007ca07eb5637ef7696d7bf985b9114de19317a9abdfbe4ee79d8bcb1a00000000d7172956946d5547bd98c6a7040d353c3cb6285fbad096a1780f3b7503539adf3b154a4dffff001d0333fec4
+01000000326f947390bf03abdda16f673d26326d4b159e0b7f732a67286ba8e40000000085701296d47b03f388fd85431c2a9fc817afc9b24870a9a7da850d3a43a8154b71154a4dffff001d0164b7d2
+01000000a45af68df42f6f23ecc47e1cd0f41d47c1e5a26e8343951f9881e51400000000b7c1565d19d406d8c56448571d7e7da2ac559bf9b43887e16e4f416276ccb99b8e164a4dffff001d02ec606d
+010000007eaec4cfdb152a8ecf8ec155b7343e2cc04750be00b96c61a477e57c00000000096a9263a5008a48e1c2b527b922a81cfc269fd401ce429976c587a951ff00798b174a4dffff001d015bd123
+01000000db36ec19328691975cb8c6666866be64b5be79ae42bee9fce3b5db5800000000b5e73d7d102476db3ca2379bdd891b311140b49626ef42937356ccdb8fed589103184a4dffff001d032d07eb
+01000000d8f8a6686ecdeac529caa3ab9ecfa84a5fb62b06849ee09b8331d89e0000000038b408676c2a78fc63ddc1807804d17e8ad9433387cc3cc0edd68c07e4a714b610184a4dffff001d0437e51c
+01000000c54675276e0401706aa93db6494dd7d1058b19424f23c8d7c01076da000000001c4375c8056b0ded0fa3d7fc1b5511eaf53216aed72ea95e1b5d19eccbe855f91a184a4dffff001d0336a226
+01000000bca72b7ccb44f1f0dd803f2c321143c9dda7f5a2a6ed87c76aac918a000000004266985f02f11bdffa559a233f5600c95c04bd70340e75673cadaf3ef6ac72b448194a4dffff001d035c84d8
+01000000769d6d6e4672a620669baa56dd39d066523e461762ad3610fb2055b400000000c50652340352ad79b799b870e3fa2c80804d0fc54063b413e0e2d6dc66ca3f9a55194a4dffff001d022510a4
+01000000e846583e9bd64108b3b89ad3883bec7731ddf1688a4cc8f79530fed800000000d2954cb816c87a9572bf822138dc84b5f6847fb502cce3d6073f9ffe40588571a1194a4dffff001d045d675b
+010000001d72012c553d72f1f75863310ac0450e53a9e9026b9bf9556ca024ee000000001b7142acd57304290a2ade0e2c96d4fbd3ec924a02a5a0cd30c04f0e96265423ef194a4dffff001d041600b6
+01000000d2c5dfbfa04c7b67457c58f55a8d190dc5f8ec5ab94af969dfb748ba0000000069492041bb66f32c9bd69b74e7ba9bff6d4122e931eacf9c89b45eca2c35eb25211a4a4dffff001d03bca431
+01000000880be932720bbf22f1b14da0e6d16c2773f83699935d390e8621533f000000000f5d2500bce42137fe905225ed9a7380eceb7445c89011bfcc740cf2e9985a034e1a4a4dffff001d03d30924
+0100000000b0b174d61c08a92313345717ca7776a75cb67b77662c04ea7d3e2b00000000c8ac0a2fb1c01e0e0a5339d296eb072b2b9f9cb1d410a1fdd69a2c797094dda56c1a4a4dffff001d05225e37
+0100000089535760639df16a512f9caed73be0edf8c9b5466fcea14336f4a1bc000000001527b6224d45722c8ee351976c69c8fca59c11d3daef7abf1d189aab0e959f7ba71b4a4dffff001d0551b67c
+01000000a61d5d887f8fd4c86f7111c2c5a4d0d593665b527cdf84dac7a0d57d0000000069b7df87a13603be78ccb048370aa1d2da0969f3b1822791d24aa921f8e268ffc51b4a4dffff001d055313f2
+010000007f678f2bbdae181d396123431faacd0b956633c30a55a9595ae6657f0000000085daad94e57797b9340c299e483531dfcd0f3c6996da98ffb2ab31bbe34e346a001c4a4dffff001d03ef37a0
+0100000054112b758ce49f1fd22d613250599ffe92c48202b6a477b9289f3d7900000000006302548e973a0d5764711fe84e1900dccedad0de9f054fbaaed3735b70ed62391c4a4dffff001d057c8c98
+01000000bb142886ff32916975d060c649c9119aca0b47e3f169acd3b7f1b9dd00000000ff166532d3f30299c5a82856e3411957dbe35fe7e17c4f58b92b2ef12c399dd7d41c4a4dffff001d04a2a121
+0100000029a936f51d08ad1f1353890300131fb7c04e20606eb48197dc863eb200000000c7bffe64778d6b4815226c6aab915985d8937fb0d3aaaa983bb513c69305caade61c4a4dffff001d0514cd03
+010000007d16758418920cd6d81283aa30108f20f37dc7114076e23025bb881800000000bda8c051f6e99590cbea0919b7a4189e4d3620ff3f46caa4b797fd52e204ff1fcc1d4a4dffff001d047ea807
+0100000087a6243ae1dc858cf91caf8a1f92dc473bdb14203c573b9d9bc134cb000000004caa084ebef276e6c454dff401271b39e55da21a8cd5a3afdf2d0e0f94b94a2dda1d4a4dffff001d039d9a6d
+010000002d0a1a0b18f1f74ee797beef1bd4766a050a3480b5d7457303b5c54e00000000955ec547d5ff2bcbb3c9f108828e431a674576e1de0b8da794bfa3a70b794281ef1d4a4dffff001d0019c619
+0100000060500ea2003736b74596d4a507f5cf001daa55e7c93b53c77d32a32e00000000c545bbc6ae68433be1fbbf0ebf59f22751af853d0a6fd6c944458aa73ec7a014f71d4a4dffff001d03d8b5ea
+01000000cc3d1b428029cae46634a9b96857475b2bc59619b8408e615f65b7b9000000003563548d04e24c89e7706a3f4ad681bebb3017133e87f7434d824db4e51f479b891e4a4dffff001d038a2031
+0100000073da9ccb3fdbdb4a9e3723a4bd5270c70ef3e78f448fad7e77a8eda0000000001f0a3749af61eeb59aff1499892c6641d1a464a26c156608d02cb74c264786ccfe1e4a4dffff001d0284512b
+01000000ef5a98df2a193c1f8a5c271dc4d45de465b7122dfeefe96fedad105100000000cf330295467623ec1378dc6fa312103ad8a210b3e1351f2f4b6a57ac43fcd472071f4a4dffff001d050ac986
+0100000048256cc5b9ec6e7a12c378c93c1dd2ada859c9a9997adca75166c935000000008e3430573cfde2f3e1eece8aefe661dd841bcb665d35832415bab4f7526785229a1f4a4dffff001d02ae4b52
+01000000eb5d7c4b706d8891ddae3ba5bb57fcf509689fbc196f3ef73837f27b00000000226e90ccf96f41e04f011e69a86e18e96c09df6fbba19416132247f1d2a6e4073b204a4dffff001d0488c17c
+01000000e5214de98aa5cdf1766c5129649895816f49ec82e93c4bbda787897f000000000abd68f73823585582e65529a8739e90bd943fc4e214ac00a20ca0369b70d45d7d204a4dffff001d041786ab
+01000000fd4fc04cdc29aaa117b16b2420aadb9bb92fd19ef2a7aec3c40f71250000000078e5266df52051011f39eb29939c8782564563c20b3856f7aaafa6dc52921359e8204a4dffff001d0205fe6e
+010000007252c67173d343874ecfab4d5f57ab5936f2d87f173047c99c20e73500000000d2b61f338da6ac531884c623db2804c0d7eeb84263b501524cbab6d5edfdf56f00214a4dffff001d0027a318
+010000005a7746eb6d1d19cdb24466e0a87a23b6ba8c2e461ba8928edb84253b00000000f1b03cf0680b9ef33fd311f6bbc6db3f1c164f9341f48a02df1905cec4ce241b2e214a4dffff001d03246ebd
+01000000e93f1fd6ddca6d8fdc3ef50fe0f31769200f8fde592a0d5d6f8e1d290000000078966e9f0a2d4452ab2418249fa6fb1a325a04f039d015899141a82aa5a6c05c92224a4dffff001d01d8361e
+01000000dd5f3ae3d2c2876ffbfe0956b914fe72750b160b667b5bef5aaee61400000000434d2b0f298874c3f6d8467c07dea6883a650de00d48298cd6fb48e8322e1058b4234a4dffff001d0161f3ff
+01000000f814899e7f50c4494806f75523c9d8ea6c0198d13f1f14431fb541ff000000005b1015187325285e42c022e0c8388c0bd00a7efb0b28cd0828a5e9575bc040011d244a4dffff001d0579aa5d
+010000000f72c6372c87d91f2df95b0c9e91cafab596f29270cb01cc67186040000000001fe6898ac074a680fe7458ff87a03956db73a880d2ace6539efcc43002bd97ed87244a4dffff001d0434b3b7
+010000007cb39114d2372ca1d5dbcc3a1137cf9314a666349dd79b268be1f15100000000b17cb4572964d7c6d671e7cc67b04b9fb1b68b31e52e6b4f956f3a0b72ccc4ccc5244a4dffff001d057875e3
+01000000f8d8634fa1aab0666f63fbebfd61e0ffc1dcc647e218414c528e17dd0000000073a2c54d536c19f0d09156efbad18ca6f96b1e9f3bc8490342958f24ed8fc32d28254a4dffff001d0130c923
+01000000943aae5118b0abf4ff55e050b234c21d223871e815e9fae6fdaf693c000000006fd85c0213cfe9863573596a4d5f1509ac41a91b572e6c1bdafe46d9249a5fa4e9254a4dffff001d05878bcb
+010000006c6040618ce7a449cc26ad0578a7c897b4464ee32260014fc5ce6bf20000000096567fa4ac682f9bec7e646452d3bd69088000b19bf7a90eaccc197b632fa79bf3254a4dffff001d02a22cd0
+01000000c067deb4fa218c0f26a247766a969af8a475e5c88c004c300c1c69b100000000ec5a827a707edd70451f070665bf6a9e6f4dd8f815b0265296790f24024142b181264a4dffff001d00e76cbc
+010000005cba08b87cefedceed1e60297564a3eb9e9e2bf942bf63f74fdd7f3300000000e3082dfad468d5c0c8e2f8857a999f898081c8cf59e48857997152445a57218095264a4dffff001d05a30a7d
+010000001dcc225203fab8d972215ad2311203570fe49707799e6871908e37d900000000cea1cec6febbeb980af51f052dc20065b95b2d65520205616a95284480a4219abf264a4dffff001d01b05d9b
+01000000644fa81c1f8c64f08712b41b616a24d3e8af833a4f370c188068133a0000000069f2096bbede7015fee2fb307f7d7dd084641b7f4af5c3074dc7b2b6df03277c80274a4dffff001d01dcf233
+01000000ba750a8cc870173cd7f17dff4c23c228282d9aebc2bbd5ebd9449c7b00000000b28b51c3a1a322c8e29c2b6808ded7981dc085cd7fb529184eff6ce556e09ccefc274a4dffff001d02c039a8
+01000000e0f40d912882e77044fee84e325fdfffbb3aa0fb1fe6ba864d5be65d000000006141e05ebe4e62fc76d0c9f1e61a4d17e6509209309f6fdbaab476fa227f1f4fc9284a4dffff001d0306d223
+0100000003e8e5f89f6d5ee55c6e8ee0d4c1e88d8e4a3a5f05f0c9ec32d580e5000000004e2b02e05fea22c5067327060d3c00482569021252423d372cad30746408d0fcea284a4dffff001d0410f81a
+01000000f389178af7b0ea88a83ae251392f7eb336771c8d7ae666d1219f2455000000005f98a8017e8458e6081be384b89f4ed68a6aaee5ac41cc0ad2331929e657deb27b294a4dffff001d04c90ab8
+01000000d8cec9c7b6dd3093ad29b64c234258bce36693f87a4167d6234342cb00000000d5cfb9095ceb210b374686dfc11fb8d8c7932c30b4a3916c7fa4fa7b760a0831122a4a4dffff001d01708f58
+01000000d850850454d9b392ab01e886bacb717a5615f8f70b6a4ef9bf788df50000000075f11e157a482aa640ea8dc7e038ffcbff0e9aa758ff092222fabc325b9c1a56152a4a4dffff001d021f806d
+010000003cf1b7fbfa5978d5ce1cae5a5f454d41f840cebb72ad3d600f551901000000004503bb32aa7568d6abb1df7a05b80be6ffad47e5a55886488736b5c344a41d431f2a4a4dffff001d048df744
+010000002d7f6a8f2dec6f914a7a63052facc546567fbb02742dddb150427ab10000000031ea5d7ad7f128ec4daa2855b7944503da503818a75f049dd6980e636aaf59af242a4a4dffff001d017a6543
+010000008976cdb2c5a16c0929b45f29784ad52cf3db3035f112e562ed42447700000000bee2958fb6624ca2382e4a6a1bd7aedc2c58edcb7266a106c76d4504fd39eb89682a4a4dffff001d04d4a620
+01000000d6d7f91896014f1f4419628791a5bc39263704165d3e14e23895f57b000000001ae0ce43e200fa010ca331ee891ce9ee93d468c602703a23ca2eeb693c1e05e1862a4a4dffff001d025a8bf1
+010000002be250378c6001da52d435aaa1240522ccb14a94880ff0e0d1eee82a0000000046297804a9aabf1d08096b034a84364055573c45d09be862c33ae30beb3b5ab5ae2a4a4dffff001d01a34339
+01000000064ac21081e5a5c6509f634ffae17551bd322e46d3396ac49b8d68b300000000d15854d1e5ba349daf72089f470b24557a2be25105b7831a3f18a62fb8bab677342c4a4dffff001d050c273a
+01000000ad1cbb656f4799fd8e9de9acb70a47d589ef5311dadade94c494ff60000000007546bbac9ae1c8980da6e8c154b368eb4df305b6f3f27ff38f195a13c9ee0484922c4a4dffff001d059b5c46
+010000009f0d835251ab6812b04939a5889a36d50f4d7486ef98a7f61e45f062000000008dab4ee487e33f872140c04372bbdb6c573b7e9e4ec31cb5d8dff36da17bd8f20d2d4a4dffff001d016572b5
+01000000d1105edb3d0105e862f3bc95a034c0d0815a79505b4b68fdcce9083e0000000061573706774bc7a579a7968281e10612b4551195e16c8051381cdd3a6f93f479292d4a4dffff001d039dfead
+01000000d1e7c872bf92ed9d9007926fbe72d976079ae35efd6f81ba4101a98600000000b4bbecee818dd986e5ab82f36dbd5ccc29ab134614e304c0a397e14082fe7bb73e2d4a4dffff001d00b8c45f
+010000009287b073f80a1a91caa1f664b8c9c578837f878e6ff04108db28d9c8000000007dc81aba2560e72756db13b1e427c9fb4bdec85486c65da8aca5bf968a5bf51e412d4a4dffff001d03a98277
+01000000a3f00f008ca15686450bda91266f8d01a99f345c7846e9e5bfcc2628000000005ac244c2a763cbc311a245df0d6f98a29e187165048a9da449d29edddf6b1923d42d4a4dffff001d02df0172
+0100000012ced3b143532ef3999faae421b6bd79c8ad62a4dc8db862cb05bbf70000000060e6e42fad3fdc3d353a22f1699b5ead453eb11343a0e603ffd8c77cdc773be2892e4a4dffff001d04945d60
+01000000f0c39624b690456af00d742f323762471854e1db5c96099adad5e53d00000000a2367f2d2214ef900583269eb812ed05d4a8aba11d40081eef999ef16af1d5ebd72e4a4dffff001d02d98796
+01000000294a2e12b2716816a62d761c624248fcc10ae22cc1a80290432c85cb00000000fc4f1ed498c5f31fe90b10389f12566a3350a5080db1dba1f01f8834e5813ca9e42e4a4dffff001d054ad0b6
+01000000774ed03a366b9909cb163ca0178cc6b42847461c4a672a537fb7aee600000000225666176205fe41ad268ba6f5d15633a5381c18fbb6cd2f83700ef928c58fd7362f4a4dffff001d04928537
+0100000083900a5d5ac952d05976df43ee7e280faf6105038a4afa75c4d8a2a50000000091cd49f33c9f5b51fc61790d25539d2e896e87c8c195cf305b499bdf42e029a0d82f4a4dffff001d03994aeb
+0100000010336b8cdbaaa1bd6ba9b1e41e85e17bec5b68bc8ff043e5731ed09200000000fc5eecfa90d46aeeda36bb1a2f2da61e4f9be81253033ae55625d00acb13ef35f92f4a4dffff001d049e0a22
+01000000ec5b52a76675e3b1de2e75ad45d6498684a0e81092c5ee36e7fbf60b00000000474d71b72f905a8084842ce4202c2ab9795f3abcf51aa7c458349d21eb6e310498304a4dffff001d015d80bc
+010000007af7e899b49494f77c351fe434981ad8b6352ad62527ed9d6d23a8a700000000bcc43ee02af281701574077d6916c07d6bd15cb96c623f6f304de260f616bd57ec304a4dffff001d03a0956e
+01000000f22be93799f6fd1527bfa224601261300d38571dddedd804c0c7b94e00000000ca1cb89732eb51c0ace08a564445a2ee762a2cc819209886cd0a09993951544ffb314a4dffff001d03b10c74
+0100000099146a7924d99cd7a2c1e17ce7206712a8349399b34ec98eb706d3b3000000002c4fb29a89bfe568586dd52c4db39c3daed014bce2d94f66d79dadb82bd8300024324a4dffff001d04edf452
+01000000e5139dd4cf7511d9eddccb69d2c7aa0917cbe49eb660fe9293667aa80000000068e91b23282bef6f06f22e479587d03f6e1a2e4891b5d644541f1e10275e52e142324a4dffff001d03ef54c9
+0100000052a5c0e3a6cc34383a58d939053af3261acf266ba6cfd3165435bb2f00000000a32d9b10b7b75323db98d486828a5ea4f9e7a2609b76e496f6d86e0ab13a31587c324a4dffff001d0031025a
+010000002ed6742b063239cad1841ce6bcf676a59c66e65eb4a0ccf68ac4eceb00000000efbe4df65aa1137ba3efae5236178fe6646d6eb96a177d5394bba48066a87448a7324a4dffff001d053cfc69
+010000004d4a3f638bb32808c3667937b3a0847eb780a3b3fe70875027adac3e000000000ecc7b73d8cd5d377d74d836bb6e3473478554a923154286ddaf6985948fd9d3f9324a4dffff001d05bead3d
+010000007b86d803ae4a477ebf754711cab10b1b7799dcd50fdd6f25cec45e1c0000000033c06971fe80386570f8daafda6e4ab7e72a18624e211481e7b96633c625a52b3a334a4dffff001d01146b3f
+0100000034231ba96cab51515ffaa9831930a46d1a8df9bb83d343690edd9ca000000000848736034ba62c9f4f3410b11d2a5ec921592f40b2ad6b1d442ecb3188049e27f5334a4dffff001d00a7bc36
+010000009427d53219ae3be5d968cd3dbe9f52232b0b32f662bbf74b4c2cab8f00000000755e1e0417036010faf0520d2524c806dda1e3fce2ea99dd4e70a42efe44f64011344a4dffff001d02c03350
+01000000db9740f3109d4450584d3223ec605be1f62457d8af2c22671b93b260000000009e93a056a6515e7916fc049578708d188c2146d3c12638acac92e0b72e076edd72344a4dffff001d037e783f
+01000000de444daddd51d48d32e2119e59629110ecb69dda27ba85d7e85d40e00000000069b02fd420c2e86a575941a89e46d174a4e0fbc1379dbd6f6d88f3ee58613e5087344a4dffff001d001d016a
+01000000a173420cdd0ccf0695a0a341d4f8cd0dc7cd8fd3aa3d01b68066561400000000cf0fb8b5f6fe33f698450ec147ef896ad953e677b4033e50400ec15454d3067fde344a4dffff001d03475525
+01000000343f2b87a053dfebef86bf7108c93e449c6e289e71a75489fe6125130000000088d412c44abb44236383afd182862dde877813143d21d92a3cc373d4bd4017455c354a4dffff001d02b22dbe
+010000004375b71a891e69ba37f9c0cc54373be6796f43ba90b16b12564414a400000000e3abf5981a1bd6457ec0cdcab76cc2a176dc0d7e16f6d3781aebc684f13cc4fd81354a4dffff001d00caa9dc
+01000000f7b2f9d9a6f44012728b6e09c48750838780f1e1cac9cbaa09e011420000000014bdd0dbddd8e6c917324a49df6459de897031f514b77999c8476d287e47f23b84354a4dffff001d026719b9
+01000000a688cd51d1d4a0cf1b438ae4347bd2e0b07fef348ddeb6a4168f71e000000000e7da7a46f5efaf4fc835468b21620987dbdf8b5f66daa0ffefcca5b0cae2e533a8354a4dffff001d0017ef75
+0100000010f5b842b85241ab32f795ad605ee899389c64c77126503e8eba7f4d00000000df686a7f31c2c1de6a608553b26d6336434719fa45428eb3df59bbef75ce9e7ecf354a4dffff001d02e94089
+01000000ee47f9bf0d5d6d59bafc6ade45ae6da0702b2ae98b792f7a6b95d13f000000009c13741588ce9e729f1b29b82823a97da91a3c6bb89d03c9b33c6cda9d71f0aedc354a4dffff001d00ab6a3b
+01000000a0396d99b1042613554a8fe19f38a485daa2eb5b65f5347f3375a838000000008985dce081d487c7eb8f8b871738869922dd73120c75242333c45895bb91e64638364a4dffff001d04d09fc0
+010000000787ce301e6110773cfffc92ae93a3e017ce136ac2fbe2004bfd06ad0000000069ed55457af8174da637b03acdfcaaaa36cf822ec6e72145526cbfc03a6a83f546364a4dffff001d02fc547b
+01000000ae76485a78a7a225541cf8025578b9085d39b45d7c2f68669f05eb7c000000005ebf1334d253dd7a7ee381625bc200973e7af65a8a022de034d6955e37823c628b364a4dffff001d06e25145
+0100000057ccd09f8dfb8ff31d4ec9e743ce56ed5c5c501046d709f49c356f08000000003f818cdbb02b1a468aa23e9795f7518ca5a92276559fc40bca459a79e010bee2f7364a4dffff001d04a53656
+0100000042284cac669ab917936f5b96469048d55214c074eee3c7aec95a07f200000000334a4de0559a4db974ee451fe484a0376a44470a1495797992b9a6a0ec7b24cea5374a4dffff001d03a8bda4
+010000005d820c5c53908a13f372a860b57050b9c60ac3360a7bc865fec2029200000000d29036af962f1d725133c89721cc7dea6d3cffda3a529c82e39397027aad7efbce374a4dffff001d02146600
+010000003c3b2d4120567d650494757e86595b7e9c70223af627fa1857ae9cd000000000a4f56464ac27919b3cf8c9ae4390e679cbb2f878dd7098057431c0d92171fb29d7374a4dffff001d01dfdf26
+010000007e74fa0f82027865fdf1aa434e494c2a60e6a9e1b70d2f7c4fc21ae5000000008a360dec0defc12a33c3d959a7ed168aff8e34547db15b516e1b8cca754c0de543384a4dffff001d0568ea46
+010000004960b70d04579667192055ae5e019eae175dd65050e58be04d10b7f5000000002eb73ccd1a64ddd448405955586fe5c95bc54928eb34679f74bfb4c9da8e1b92aa384a4dffff001d00cfcabe
+010000008954c37766bb7293e105c476cf2b320cf9932b9543b1398823757c0400000000f85f4ed98adf68f14127aea968a338f2109597b484a808dfc8638112ad0623428f394a4dffff001d036b2da4
+0100000052d3ed4b6951d3d5db9100bf46b7dbfb177917539341431898a0aa5700000000d226fea91b99c5a31a034d340f647b722e50950c96a876eb96569efaeaf3b227a13a4a4dffff001d0051797f
+010000002d54616f93e70461538548ac861c860a8bb9077c3452be5b4c00d4c5000000003f88cda74f26fa4ceb0a97161e771a8b1f6caff1069f7b53ab49386916f3820b213b4a4dffff001d024510e3
+01000000e226b7f57fe7d32fc6166057172a4c2931bd4bc3b619a036df08c95a000000005b7df01117c335ca3e8a802376e171e537f090dbe2e3bd1fc291e45e219a8717323b4a4dffff001d05d6def5
+010000005412ebec87fb8c61287f1d2fa1c52617ded826adec22368b84c42bb5000000007992420cd0f0f24840c8d92a4a865e6c906e473c23f1cbe3c183de70c2b2bf42453b4a4dffff001d02a246ad
+01000000ba24ca40c17418d7bab5a3cac3c0bf00b9d3a0c09ec5771d0b14015c00000000a30e3f1429bf2a8ed14ca6ec9f3396b8593b8bb6ac1e4d35bd435f05058094ea983b4a4dffff001d02c75930
+010000006e823e852756b7106a0ba02b1b51045d87582001c006a2e8807cd612000000003c11b146d43fd62ec36b733942a52ba0c352c95a3f078808a38d080898cb8330dd3b4a4dffff001d053f9e6a
+010000003f03adc125b43a2c8cf9d47ac43dc81fddbcf12ffccc11ebb226520300000000b17c3beacfe667f4014c9da9057fdb6252978fa76dc4dc2d9ddef0562d293d39333c4a4dffff001d0467b375
+010000002bdffedf5912eb0e14baefa637c22cd523c8eb4147bb111db143ce9c00000000e24fd45c98a5b07ef9b236be48aaa1085b1bdf993a1c0db1dc04766895b8a2fb813c4a4dffff001d01c464fc
+0100000011c1b397772463dcb8430f661d548b3c55c61d415918681d6c4d4180000000008c32f04b4b70f43c849a8f424c3a82352cc0bc6bb25b2c2ec4a039964ea563c4893c4a4dffff001d05292e68
+01000000a0e705a21be059d1f8780cdcea04e7008b7497cfe8faa0fb59a666600000000059f060dbd892c1499758c7ce9bf8779d8e4dc298484ceeeca6467c17769fad5ccf3c4a4dffff001d013a65ce
+01000000492c5c9b8543822db5ad8694cce758fa2019bd5b6f12db4160bee95b00000000f640c60ea438dc020048599869836f5323ef47477ee17caddf076ed428898f71da3c4a4dffff001d0065ac66
+0100000095e3a7f3bdbcfd7a40901eefd95edb7d6d60562f6a427f7986d0861e0000000059ac3c37adfa89b9a907ef9d485c57262e9283e1eb96069c2de04369ef1b3c76e23c4a4dffff001d046e85da
+010000000a3f0effcb7af394aacaaa6e2383ec478034ae4ec4d488e47153d21000000000e25e353605728130ebf943b1f468937fc489589975c13765fd677e5050b487df2f3d4a4dffff001d00fe3805
+01000000a1f0d96d647241f42d4a3e02ea933690a4cc33542ed89de511c914c600000000153aef1fb8837f74a82054a0d9df9c566ea9d50df292ff62288082f311dd4212333d4a4dffff001d03d34187
+01000000f78b3189170c0de482943849707aa16247e5d62772953eb54bcc4d9a000000002183968b34446981f960895ed3713dd60fede3a9eeba1d40389123c6c409d3ba6a3e4a4dffff001d03d38a89
+0100000059835cec3351179502bdf8f4b0a0542d3ad336e22549c2a78f1af97d00000000f7f8e1a8f0b4bc0337d67fa4a2fceac7e83adda354d6fa3ba18dcf7e6fa2f2b2c43e4a4dffff001d01558b25
+01000000c6a2e4d395a1899b4be7d915c8dc660efc1298a34b66263044f5a5bf00000000e64b9c2a409a4a1310fb4b4f59559f79b15dc85e686eefc07b0b0a6b7fb6ee5bf03e4a4dffff001d05113e7f
+0100000067e62d9c29833a10aa99f00514ec678c06a234e05f5a1de4412988ce00000000e4a12fde56c03eea1acb5eb1b57d35ab1da6e55d544e8212c47ce277416b1b3e0e3f4a4dffff001d00652740
+01000000d468ac8c936b469fdbc0510d88d031ad3d22c3858abb8f89184f0b84000000000fa35a88c8fd0bf5897b921ac75fac304760f6d60ed2355cca82e689ebc0cd53513f4a4dffff001d0405baa5
+01000000d69dcd4cfb5a9ecda166293b3c0bd72e21a804b3a565c8ec4783f8e900000000b3ec99d78bf68d284104779999ff447ab7bc73a783e9fab814c6741e71849cf0d63f4a4dffff001d0355fe58
+01000000a4ee15d19e3a355d221af4e4d6b69a84510a60857ead0af7e3b086c1000000001c25b8c364fd6f8417d45b9501384908824493931b544baabf2299e25277305540404a4dffff001d052500d7
+01000000deea32ce4f7ba1bee123958ad36ad37df8d53372f518a88761d14ff600000000aaeef85b297622ecf311dc5d29669fd4e3863454c6feea361ece4f547ba52e6a96404a4dffff001d03a82644
+01000000b8751c32196c89a35575c5ea97c6395e6dde705aa771287c867b909200000000cad183a8636b222a8ab775d34658b1de92ed57818e26203ee43ffbd4d8bef95b28414a4dffff001d0587f431
+0100000035a68fc6ca182ccb6663ee8b7e44556b950a8351bf2c64cd75dc2d0400000000d59d2a49b1883c6f7ac68a9d2649dc0dde3f0205e19d8fdaf8065381f9ba61cccf414a4dffff001d0592553d
+010000002e5b592b34f37b1ae9fe17aa093d2d9c815aaafc5ce3dfb8b91e33d200000000a2990b3a24b766c99702c1d157925089bcd0d377cb70cacd30be33a3c90bd067e0414a4dffff001d03c217d4
+01000000773bd09b330364f704d0b3fa46d8431c5572a100faf117958fc15a3f00000000e739176d62b588566afa47f5fbbc0ec01aa3f058c036ca0ee8f3cd13e4223e01ed414a4dffff001d056b45a8
+01000000e9aa44db469453b2c657b1e4f3e29aba0532b1ba7ce4a8bd8dd00c4a00000000fa5f2cec2dd5e7122182c8e4b2f10a1cff77ce7972ceb53308b827aed06751fbf1424a4dffff001d00523f0e
+01000000337df8fe8603beee390360abeed7e956a280fda2dd5aa238e110de090000000071f85d3e358a18a3a3465a1bc93f17e7725549a9507d1b218dfe6d1dc349641e9d434a4dffff001d0042c372
+0100000048bb0f9a7dc5c4fc15e8a4c5ce0adefc49c10db0a582e0476c1498ea00000000640a330a8e9f920e7684276e987d7f5a682838ef8510fbfdc9de08474ed3e74ac5434a4dffff001d01a51ad1
+0100000098349ad38c19d6e481627c6523d9d4f52c031525b8660ed55c215679000000008c06f072a655daa64b9910b1894ca250077f930333c0281c843a1198a6a0924629444a4dffff001d02e40f45
+010000003ad363a33fab1606f7a32896d093b06b84a7d6e9784047cf8b9bc953000000003093ae1fa781fb09af9fe147aa05ed26e90f6e7312a448e934afcb4554d74c11b2444a4dffff001d0029912b
+010000006b8abc2fe01dc3a118f4d18c4f0b36b640fc85987db55437099aad3600000000f2e0926ac96d0c8010e095b34c5089f549f2fc8fa626e20b2f0b68dd46a72c2c06454a4dffff001d00149193
+01000000665559872e38dd76cd839459a94b0f6efeaded839d785b2c43c0be1900000000c305ed4fdbd3a11585c8805863472f53be7fb65b7b4404a688c070e8197bf40edc454a4dffff001d01b3084a
+010000008fc81523766707ea9cdc7926f5771527eb6a5e6810f86a836ec81d5900000000408efc695c947acb1c0239a9b011d77e55b702fe9c2f6082571ca3bd9ad46bccfb454a4dffff001d05bb7361
+01000000efcd6d1c267d0b764044e85d112b6a32cabb8698bd13b4e947c178ed00000000fc53d40de77f595e1419b205c2dbda50be0a5595c45e001f8dfeaa8d3f19d90f1e464a4dffff001d01fe7935
+01000000339acd071c0a09f678896b302b2c79833152e048f4536994737db5f5000000003c651e6ebe94d51b8d1d1542f3b43f0c26298e4e5c8fb3d275e4e13062800f6c9b464a4dffff001d05eb40cd
+01000000ce3f890a2580af6e15d0305dbf50d1fdce03678b626ef2c6f7be3c7a0000000092b0e20be269413c7f519a5e7fc316074aa24e930b8a3edb8a618077567aa832ee464a4dffff001d03713885
+0100000006f4241b0703e90950c5ec74c3e033c39e7b0c7b9b877d5515a6ef9600000000eceece737cbc2bfaba21cd1cd8515d614c2a39ea26d9b41b7daa77a17dd5ea5121474a4dffff001d0326e2f6
+01000000e65e080e4f1df42a905b85fd5d0f0b2d70f3e987e0fbba97cb41111900000000812ce3ea678b31aafecb99db5b0ca6718f9fb3474e0baed35aa6aa7290f5a40972474a4dffff001d043e313c
+01000000ef823ae34b33e5aaba6945f46e6c2bea1df86769d877ac0b8ddb0811000000001962162ca36701e79b5ee44367d46a8ad8421b1f80aad651a02022a7b9e1e0a3a2484a4dffff001d0259a126
+01000000c65b1537a27f39bbe1a2884f77807c958faa56f3819aa5e4d49472bf00000000ba78186dcdbed13712e6c29812a54fa620fca9ccf297b8219f7ca736e36e46b7df484a4dffff001d036752dc
+01000000d48b9919f537d161b9ba4404e9ba71bda419efe7173ec3569243364a00000000501790f1566c93f0edd817937da1e287147de7bd62629b65b395935017fdc80758494a4dffff001d023a2b4b
+0100000083aa1f8934efdf864a78a3594d5c16f7fc8303fa38c01cff7c0c94ec000000005f8a10b8f3b9a4f159bb071ed05f349849e16774cfd79990af242f0ff6a60ea5314a4a4dffff001d04ad807d
+01000000f699462ec6c1179023d1504bd5b9510cad887286984b783c943a33610000000074895c719bedafb41d7e5833189d876220fc5cdca55c6ad4b94a971a8e96a259374a4a4dffff001d002f84a0
+0100000077393e4b34076f0a6b6aa9350c799c7b32756a200eb1a7619e1b036f000000004c6b88dde02bd09335ea8bfb6db1e0094a58bdcb59d5ab78303034ff972b8538484a4a4dffff001d013ccb67
+010000004828e19e639deeb00d55f748421f46dc94eebd75e376104740535adf0000000014da74d7e2da39b2b2d676b957e3edc3619f4999922f3fa66c95b64be8fd92a34b4a4a4dffff001d0260001e
+0100000092f7a9432462b827705bf9b31224972d61cc9fa8d478939e015ce3540000000076cf4d344b2c5db45b55ea38207cc477342c74b993c401b753378509761729637c4a4a4dffff001d03396962
+01000000b87a944ec8bbd6a73f5a6054efcea696124dcfacb88876995bcff95d000000002dc8a455f4bd8723a5d58a675c5a5e833ee411490a443f6d47f08a6fbf7594a2024b4a4dffff001d03699294
+01000000bc2c1291105e0c6209906934c0f1fe57c4ad13dd74e43830b293c6ee00000000070ef89563a7d8b1c7232b9e391813476ce93c9160051ed55bd396c6747618ee364b4a4dffff001d03bbea80
+0100000062db800da374d1b83b1b460566da0a35d277bdec5a6ef743132b2499000000004af49eb22a467e87048f4625dc9021249c16561d372366a21e8c20cad2c65aaea44b4a4dffff001d0142e34c
+01000000e09e577320f539aef8178a3ec09dc306b301acf52e135839fb3fc55300000000734d92340bc4c287b0ef2b88001bac1f41bf5c6d3c725e0fc39e7c593d6ebd4f9f4d4a4dffff001d00bd8009
+01000000389998eeb4bc9b68abc63e8ed99218d00b857dcc5591966d46af4eb500000000752857853f92f5ca863f3254bf18ec5e3c62e1223c3f7968894048d40413daf3e64d4a4dffff001d00cc8cd4
+010000005faf1cf2ae1e1231a1b5aab191614ffeeec0ca86d86e105233c681230000000026dc493ba668f17fde5156a19662ca1a5bfd93b2376edf51ef9e3d623e4ef4a0f64d4a4dffff001d0183b4ab
+0100000005bf42fd63d8189e37f471cd3e8a411bf81b4ff8e65532897b755caf00000000e53a837e9e3c05a4b635531c91c39c3c45565dc75e413228dc93d0c764327f86154e4a4dffff001d036f8a98
+01000000dd081cffd3a2812f5c770b8e79aea5e63d67b972ebb5d9dfe8cea3a9000000000803c135f2c72cbd06b77cc2a2a13a3c063028bf4b188b4760bafa112e71e579f74e4a4dffff001d05fe4d4d
+01000000a9496d62f029c4b765548666b71a84adeeb9dd5674148b708b7aa70e00000000ad1924dbb0711c96b12ad1376816374008b98ffc43677b0990bca7973432f814334f4a4dffff001d0190dbb1
+01000000ffcc01d81e891bb248fda71e6338614dbea2d46b8e5f71460e6ae2aa000000005510b8c188460a26868d9bb3fc9bca7fca4d9b20f573e182466398f793d5ea8e44504a4dffff001d004f08f5
+010000004300ed388b39e36268421a7b15d337598edb40c8c290166b4376f2490000000054d4f73e569ddc7e67130ea0a43a352064bf3ded26f80877536b0e7277237a7857504a4dffff001d0268c158
+01000000d7e1a7d5fb8d460e90f62666eb9891f9fab06cc2c220899498cb01ec000000006a6a5be5a31aa8a7ced02f8ece18f44261e96ee9176b772fe654f1764cf6a4fd89504a4dffff001d02c96f9d
+0100000049599a3ddafecc620c9ad61cc4520a0c70fae3d34dec595669587d260000000018073767fa7a1ef0c4a570f373492a822b5fad5fe3092dde239bcd8081fd3e8c8d504a4dffff001d000e0681
+01000000eed0b252aae103849e6805f28147abd17f294fe55c5c2d966d8850a500000000e15a0907e27914f317962823d82981df6f2dbd256738860027c740ac3f842bcba6504a4dffff001d05ebd48f
+0100000031b485a9bc2b16f2ae5108ec786708f3f14447551e7f0f0a8575c75a00000000f6dc88e1c894cf88e930d30adcab0380af8fe00c3ebce9969c0e43242ca422b372514a4dffff001d0103697c
+01000000ed18faa5febd8be98c42fe2a1a5dc9e8ddb6a618d0a05d924c522b4500000000811cb2e938cc88f159a82f1e160d80f4c6b429ef51c63ac9f2724810ff7694880b524a4dffff001d0097c6fa
+010000008738d8fc729ed970f5223d77d8e776d5e0569ef2385a33596dcbc77e000000000bff98f2293caadd5f7fa74a7c5b3e2399268c171e5adb490d8e1dde1d4926d7a0524a4dffff001d02132278
+010000007ff2786f280df6f5039bfcdc1d2d18eab08e4120a5f67c769b8b464a0000000077b5007a1d08cd2a6abe2cb56a279ee4167092775aabf4245a55723455f7297059534a4dffff001d03c30540
+0100000002ddd5983c40e70e36643ebbe8567a8c115dd7a21262b8c61d693acd00000000504305d5fc375c8303ca009ce2bb783798b303208c8744fce95d0656375b682ab0544a4dffff001d0483c2bf
+01000000bc5ffef87baf263ab01ed870a064c46731fdb99beb941bb6ab73fb05000000001679c9d394f4c959d3fa9e054a4e89af624f9206a777368a00d1cb6b175ac5f7ce544a4dffff001d02c1d1b7
+01000000a0fa21e1683774f057767d4545610a63a4c0ffdb774a6577224116cb0000000025db6d92e12ad8376e7e37c12b541faf358404c4b5617b9b287a43c2db4a067b6f554a4dffff001d04cfae4f
+010000003abf3b8f61bdfc588bb0440b0f61cc8894cee1291298996712b3e3c8000000009160b7843ed42d0b5ba734634abb2dbb231e68bc37a1d508b2f27ad88d040e3e5a564a4dffff001d00028074
+01000000f23f3229508a7b80a755b69ab0dabb405f5eb566419eb60e2cc1ce780000000015819a3494e7b49042529989b007e8f89a6f69f6d22acd6f0132c270923a192eb9564a4dffff001d05ecd17d
+010000005fc1bed214932ceb26a0a81a4dcc6fe290e5b7fbcdbc490f89c310e900000000c6fe4434b767859a8b403df5f195e223e622058ea98abcb0517bec7c5a734fd4c4564a4dffff001d030425fe
+01000000390b6d99e31e1f64e6c16a94e7fed310d80bcccf9c7f9e82fd8bb1120000000012d6f8906065837ccd176ea73171077e73a29257d7e0309377e710ebb5629009fe564a4dffff001d0391c3f4
+01000000005ca35550b9d7bb3026a8a9e5c512e0c4714f11e3066f99be7323100000000045ab4575f33ef6bfc4b1c9fce77ea21cb9b606acf2b9a0331b8f9f931169fbbc64584a4dffff001d03f6a4c3
+010000007f0ab1ef14e058aa9823bc23ecaefb694cabcc7de8ca93966dc1cdd300000000272ef40d0354ece93dda9c8370dbaf29c6a2894fecd84a063c5d3de0ba90a088de584a4dffff001d049fcb9e
+01000000f6707f93948703990287c88f79c47c2171dbfa2890d69c742ca6dca5000000001f75bdfb31993e02ff9de178dd3f1d765fd7bfb4dc87280c40f21270d7384df6fc584a4dffff001d00b0d294
+01000000096d0d56aa45bfdc9561a804aebf045d86dbe4538ba8abfc483800a9000000007a22463cae54d2b07238b58272c2f47c7bbc3bfc92a15d836f014d34160dc3c10f594a4dffff001d03465905
+01000000edc840e0c2f736161507190b975b87e70ee7842a0449b1bc785975070000000073998e2bd1d6f08ceccde03077d9d86adda70cd56234563c8d37e275767765de74594a4dffff001d03c9895c
+01000000fa8f5340eb43544d2562339f1854add2ca2336be9ef3f1659cd2b3c300000000048d698b6d1dbdaecb6b5fa3cdf3c0b06346e43c2aa592606736404a17c7080af85a4a4dffff001d02cc3a19
+01000000683e618822053eeeb0d0a85cb955b3eff25df82e41beb94adae2e81600000000ef84f5c1c80ce84101f205ed007bef67688a709a1638b20bdec8e2d5404820f6cd5b4a4dffff001d0004d110
+010000001a4ac80ae8fba6ec44d500b823515af90fc24eae8aed36d88f73b39100000000d20b7be0fb4d88ad841c2d111b49b33e7d3b786ab431f3b32e6a75d2cb8299bfcf5c4a4dffff001d02b5e1c6
+010000005d8227de291d61223a71e9c11f27298dfda9b82538dc67ecfbbae9b800000000f75f109d50bb4674cee3023c0780403b4998067852cdb56881e69c4412c445b2485d4a4dffff001d00bd7afd
+01000000140bf311410ecb5324301a67d5303bf817dc0401cb674e1c9d2b05e600000000cfd1b67825492351322e5e64da5db259d9eb1fa2b309a9917c921685232c9435775d4a4dffff001d03dbbfd3
+01000000a51f6b0e1785a625b71505e4c9ffe60acbae34d5a8e463376b0dd37100000000124c2dfbc87a28fbf04646cef5ecc103f568b8d28ab19661327995a7389d7325c05d4a4dffff001d003facb6
+0100000017b0e14d03cd3cfd82a7e8d54fdf338b7679ffc731ee9eece8d10b3500000000c96f851c0cf08646a15dfc49640c994343795b9609e87350e83f7c69fed804eecb5d4a4dffff001d0180f4c6
+010000007efda66c68e131be2f78ac7331d1d50f0203205a1532e96bebf3c3a10000000075eebc78e8a107eeb04c122618d963c038a00c956a67fe0c4933351c92f68def235e4a4dffff001d03a98d25
+01000000528b90ace50435d962ef0e1457a28b1c140e887060f6b277b37211de00000000739c079a15781e97e60a15e05212ca04c97d6c7509737d5daacdcc61982672e8355e4a4dffff001d053ea865
+010000005533106eb5b75af3ca4e3fb1196fa4c0d4e22413617c385e4ae0984d00000000a6b546b2b262c57c6691555d6f2720383c481ad64f7525c0c266669014632fa2ab5e4a4dffff001d01af78b0
+01000000c6d164638ea706719ccf9a75c7b1cba7a4ab982815490ddc3b2567fb00000000ba99c12105363f09230c6331157d8cd67336745aa9e0ecc46494634e26f875439e604a4dffff001d012bdbc1
+01000000364f362c384a75bde3177d475a8c85340d4fd4ab0ed0b30f17c3317400000000df6d00f0aa27587fab36da531d0c21a27e8b9ba3e7be77a780dc8277fdadad078e614a4dffff001d04ab300f
+010000006fe6f7e2e0d1df2b6d71c090c4fa296a2d7d175c9b7853851d0fb03500000000610e56ccf020380fa1918b77eae3eab93231c4bb8d741efdae7c5256d7f01604d9614a4dffff001d032e0df9
+010000009f469645bedab4134a07b9bf6108960de1a2ddac68d59eef58b493a400000000faff99059c41eaf7d7ebdd38efd9b0e5a3ceb2ecdbf2df4b266f4bab573363371a624a4dffff001d024ad9f7
+01000000d4767418fc518c1dad34cddcdbaf06e38a73f680556295a88bae600100000000be0a584aa45ca81a0ec8e4943b91a7e3cd4b33fa67da59db3a15979326eb5e0973634a4dffff001d05e4339e
+010000000bcd63dc71092a95a3cfa24deb7a99c097b5f74ee21389f4cf2cec0600000000c590cf15019323e3eef49355d341374d4eb747c85118d2af7ccf749bd6322d81c6634a4dffff001d0453fbbc
+01000000e244d491df952155b701e23db092c318752154d0c94b7f1918c139b800000000a7f1371e40c1c7479a0182184e7858b9fc654b553014683443fba375927f9d4f36654a4dffff001d03e0298c
+0100000028836ee71421916bfbb216200b5943426946b65d28ead2f6f6b639f3000000000494ca74bd3ac3a44c3e8d2bad9337945f89e45eb63fee5b9860d46a7d4c9d893b654a4dffff001d00ef98f7
+0100000017116f1db86d264512d1e919a74da904290965abc0492169532572230000000060081ea832d5c3e9f675736cf23993c36e8eecdde049a44fcf955ea24e7e9366c0654a4dffff001d034412fb
+01000000e79cb282d213491706610e7fc8b02ea62a98e437829b076db087e081000000006fd1b2d70cc93f628dc0f1ec50b4fa820007225cf827e3f94aa581c30f5ae51528684a4dffff001d02c8f506
+010000009539a568fa254f13b10401a64dbf10d2709111822efb587824955f8a00000000a4cc887854ee5ae17aee01ff839bc7cf869aaea30fef9c8782eecf239da4e83f30684a4dffff001d03f4d7dc
+01000000b021d8608d5c1517ed201830c7ab24f957ae2d71794ff149c7d11af300000000e58177c0a9f4858d209aa9008b6fae86b6b436c84c906e5faae402e0b14093cb39684a4dffff001d01e43108
+01000000b9d79c9f24321d231a894313febb7c5c264796a1bf60815a0cbcc96a00000000951b66c72f193f3b8374fcf39baa8183795f647fa69a5b2e21ee6505ca3ffe2ba2684a4dffff001d016977c7
+0100000083cbbadd846fd2fca57ab49a7f4b5ad6b1b64889b046ecc00424fe15000000002e26a89403d2d4d2c0cd13b959f9b86c6b6baee4ad0d99e6d04ce21995039981ad684a4dffff001d05ba9aa4
+01000000e435195dc29f89337100e0c2e80944137d2ca37e8e00256fad8b3bf200000000a644a58880e151d73b166f2ee7264025292968dfc2ac6b19516b9fea051a001b3d694a4dffff001d00a59466
+01000000fc27d9c3e7632342b188f423df64d41d8b8ca4c75b7d8a47fadfb13400000000f092bd85e17e84ee69919ffaa3a1936fa24ced5b3d0d0ca073398a256c5293dd4c694a4dffff001d004914dc
+010000001dc0cbac620aa5469e889e5709b5ffd3dde92cce25715e084d2265d000000000bb549c5ad4bbfcde450dd1998da568ba168ce940479f92d795235e95ffaff538c4694a4dffff001d01cf90a7
+01000000403be502a01dc5d45288d1f305acdc9e15cdad638ad918392738320300000000cb513c7878b4d2c2376dd1a8d91aafcbfce7fb3ebe6bf419ad69e7408d8e620cf2694a4dffff001d0000764d
+01000000919b3148b047d8639e8b46bcc4845d91654c03c6249ea4c443e9b99d0000000061a3e0d8305112ea97d9a2c29b258bd047cf7169c70b4136ba66feffee680f03b56a4a4dffff001d03c843b3
+01000000f90755f869f150d7c4a7191ffb3f74cc1752d0e1c8fa6ea51ed182cc00000000cbdf8c487a3679669f416aa49e20d8665e75ad2895d1256589b996a7f57fdfc6f76a4a4dffff001d003cdf61
+0100000047b42b4f634f26c6344841cba855c2cd7ad377cb35a8774e5e7f58d000000000cf16ae92ffab695258610ee3db7e1681447afcfe60c28ab3ae30e3ecc06de718036c4a4dffff001d036703df
+010000009c57f3083374e7552e56d053b0f07fe93a47f6a30430fd36eb40b5250000000017eec598976bfbc55ab4c5d48e00162f31b6aca31bc42ab7084f1f139049b2c60d6c4a4dffff001d03b92cbe
+0100000087de9b5ae134e6f3c94361941a079bc41b84ccd02a3416e43369ebeb00000000a0fed8fa4afb32368c57427cc0d9b44869fe8faee44f71910e9408636b82f3b2166c4a4dffff001d02379365
+010000000d544d94180289b766a4cf81e3ce5299f938c7ca3f6956424558ea700000000016164d5a284a0b308928c567c1db87c566e87d1f4810489c8256edb290b21512776c4a4dffff001d003abad2
+01000000555e499668283fc21cbbf2d5d911334ef50c6a58264fe4a9f1c7406b0000000042bf33d98dc089614e3ce83d39736949489ae64bdd1fc477f451c2b6b523c8b88e6c4a4dffff001d0210c9fa
+01000000fba43e3e3dda545e1f21d1743123d5b717b0ca9c3b6bab06ee978fc6000000007b3e06d1951308c10625b2c7eeb400f1d9de694922b34bc48bc31cc57945ed609f6c4a4dffff001d0256b7c5
+0100000046b8f387b47ed742c035e8619551c1ed480d111d4e8d8fb231265fb400000000ebe3b715280fa9cb3a6de1d7ed2b27c7f769fa8db53956a443bf9fb8051c96502c6d4a4dffff001d0587e243
+010000009987e9a48351803b25ab0eb6b84ef62d92e33a73b2133687e126c09d000000001460afd3c98403c75ab5858f765e6bdc9a91a9a7377d64f262b0c4d4764fea93936d4a4dffff001d04816e32
+01000000f38c31b10930176f539d4c5c54f22a4e65bdf568ca89ca65518207d300000000ca13d344cf8d51261573a71698c8fe10b7e5d9f7134ae4e60682ff793b1b0278cd6d4a4dffff001d028a436a
+01000000f0cc611660dbbddda4cc05700f1ceaa209e1d9e166117e5a4c2748670000000014f341b19c7417fc5b4231ae70027556174a78af7e554174541c36e2e1af9265e96d4a4dffff001d00d47799
+010000003f732830a72d4d1043e43e627abb770e9586c38b530c03331655c0b400000000a28bc5d7b507b81bd9208b9e304460a400dab435a53afd843782a9f9e7ef0b076e6e4a4dffff001d006eba5c
+010000001c219fb9bf6eab86c72b3d3bc789b446d5f214222d91c752c4cfdbdb00000000a62967c97b01f47d3a252fd612e772eba117edc1f6033fafae4fa66eabcefcc7096f4a4dffff001d019c3e0f
+0100000006de9b309fdeb6361b60029f1c7d6de6c99ff2819609a8c236d9feb300000000cbac2ac965c6b2ea471a630cc99ffa835915fd914c107b2db2dae5050b2ea9f36b6f4a4dffff001d05df3994
+010000002b59084f6b07d1210181294223741d72117914a497e7a07d0c730d4000000000b3ff096b0e79008a55e1816779ba4811e3ff6f3a064b6cf84bb4b1dcc433890cbf6f4a4dffff001d04090899
+010000007001e49d2d872afdb4cc1836b2d91ea483fc7aca9abed6e2e78a8438000000007444e881fd0c51e363543a35b27effae3cff02aa828a8de3c37bdc2db2b090bcc46f4a4dffff001d016b52c7
+01000000d5c3785332c02c192281491fb6b1af615bee7e2a2fcbbc124ba72e8700000000787f53156727d75d5b5a8d88aa93400ca4d53b9d90ac33da81545750405cedd3dc6f4a4dffff001d034694bb
+010000009309d876716d2ead60108db4f93782659182759f0b351cd92ba9644900000000fd05849b870af4a38ba121721850a8cd344ca95c3aef88c1ccf25288776264d0b6704a4dffff001d00d1727e
+01000000e9206fbdca986700b3747c62ab8169e60181ffdd3f5ca43923ffe8b900000000e08afbe6f2f42beeb5891d78d6774548971dcc9b3b7d3407821e3df29fa1aa0fc1704a4dffff001d045d66ba
+01000000bc31e83685c5e76b127e81f12e8867ab8342c02ca51f137a6abed1b3000000001e6e383300d86f0ab1c84d68a38e3a64f94eda07cb5d7b6b20072cd5a610cdaa42714a4dffff001d00cd948d
+01000000048a5ab82d24f3dd8bcf472d9081d4aaf4f16a16113821e0ec9d244000000000c1f04bbd361fc5125d1721c032aa88337bfdd16267b16498a8f80f00e2d3ab23cd724a4dffff001d02989202
+01000000e891684a9cdf9608002bb1861338f42e0a42ab96d9656f9f40edd3280000000055248a96fb577bd5c2c929f6b0c5757284ebdd13d09200fd4c342604ea3e5ccb7e734a4dffff001d031094b0
+01000000904984b520e8c777654412655520279fad1f546529d4b255136e42430000000035c50c2c01355804c412bece60540459c86d4a93f336396a664e88acd642347600744a4dffff001d017ae930
+01000000a31367e28327fdcc4b9c28664e5aceff0c6439f86378f8d88a42017200000000c26f017904f09d6c5cdd1b33194d0505a594a6b37ce0a7635ba56ad35e63183f14744a4dffff001d023e46b9
+01000000e78b13a567fa3b7f4329ea4a3700acba52539ea92128bd929e05853000000000980fb406fb9cd2562abbcbd3d2a9698da8aeb255af37c1237a552dbdddabc17d39744a4dffff001d057ca591
+0100000085f94fc42a95c9958bc3e963daf2c1550d97ca945988180a9fa276d100000000931a84625efbff3f1fa30974dc3b58d89a3a6c8c71b673e6a9b84e659fd657d3dc744a4dffff001d05acf2dd
+01000000877bc8f841c9fc5f943d51655b089a4d7f545cf233f7b3289d6b113600000000d9df6431534132d6fa686d7496a4825304f8aca55654260c56d4edeaa4ee7bbae2744a4dffff001d05c15bd3
+010000006730e2f111da5208e30dc182cc59c9318675812c24b596e3e5398a4d0000000092c49fe9e2c4008f430cb72949871a174abca604b3e4731d51dc02337cda177ea2754a4dffff001d059577de
+01000000a924ed313100b3de054fcb9e9748a12692e0f66d375cc05e01f163760000000039cbb3487e69c00c58056f6cbb3d5a3c136effa76d0d9f0df9fa8963ac0a197bd0754a4dffff001d050295f7
+010000009d977cb94b7caab58fbe8bfb6339a7bc09dc50a8aed9dec7a624e25000000000a1275135d09b38392ea718c862397f389bd358fc242242072db18a35a211ca93fb754a4dffff001d025f7654
+01000000f03542a950904c6e1ff7e8ddcd6c53a9438000df6d3846300afe005a00000000ba9ebb9774f0bacb321c7960e0e13443d58270ddc5493313d96c7784c47f1ae4ff754a4dffff001d042989ea
+0100000092f5763855630332ff4790e27b1a8a37975899d435190bb50f8448d800000000c0e15d72865802279f4f5cd13fc86749ce27aac9fd4ba5a8b57c973a82d04a017d764a4dffff001d03bc04b5
+01000000b2dd87c5ae6f6368f5c00991d7d900c14441b315e0fe3cce598cf0a8000000003e0462da7bb4bbbf44948d8cb4af46d7fb0017f598329f2139f52c76a433ff819a764a4dffff001d01486ffc
+010000009369370b1d364678e42fbb797a4ee0121fbc3916e5cffbfc5a8e1a620000000031774efc0e33444c762baf022787fbede880fdf466dfd754e3e457f4afe839b32a774a4dffff001d043d36f6
+01000000514da16d5dd2a4f82d4e52941092e8508c4cb7339e4895eb255cd87d00000000b6b3d4911dd13b8bb3d06b75eefd834250ee263cec3e94d9b4c93f600e1de90f5f774a4dffff001d01e3c40d
+01000000f3a31ef7b1009ab512e3f3f03860dec8a09c3e7ec246e75163708bd80000000035f2a99aa12f8fa65fa0ec244ebc42cb7161e80bfcbfb1bf3bd89c3585368fe504784a4dffff001d01275ec4
+010000003526d4c92a0e4a1ef78043d2832b94108ae0d3dd21f7738e2033f3c700000000a5ce8a4c43d3b59b6fdc38416eff39c2d1068817022c79a4426b071b85a30cfdb9784a4dffff001d0052c6fc
+01000000875794831d6f2c236759cca424704c18b8d89ef9f39347aa4b4fe46800000000dd4ed8001a3ef95027770fea474b5e63fefb27ec2f6b8648865f15e69308ff8734794a4dffff001d004298dc
+01000000fb0922edf49601327afef737beb3d683f98b19d128f1ae64725b5abb00000000bf3d088f283f962487807e618960923be192a236beef35f4392d7ecfeb6832ed38794a4dffff001d049e4ba7
+01000000046dac6511ee9b24b34cc63cd704497d6383d1794cc058b28badd5d300000000e885af2ab2be11c610d75db7eb1bd6eaea463efae811fec483ffa41aa7e83f8b687b4a4dffff001d04f872ed
+01000000a658975dbe7c08a66b40f3a2e2f0ef98a255d60a79ba975b89a06204000000001e7d0335d1b38ddfa9aa6303ef27c0c6929af97154ab2062f3934a8a29b78a58137c4a4dffff001d04ecec44
+010000008c36b480e7358039d56678918d633d9e92a8f3bca09cfff8286e3fa60000000013ac254600fca551cfb3665dc3beaf8114aec034861d36308d5e6b213f7c01c5a57c4a4dffff001d0061a279
+01000000169009ccd47ab6783ed3657ecb5c962b3b3d3d0edad231df0c9f193700000000623d61fe4724621efeddafcda266ef8edb29c8f2ae051f4c6cf6eb2edd214e5ca87c4a4dffff001d045f755f
+010000000b89ccf6b22dc7ff1668cd3de5d6a812f1e5ef46b512cc9e352f3f3b000000007c644560629caa21e1a3fb2bc113deb42f78541283833c4a3abbe6a6fb6d877dd17c4a4dffff001d0588650d
+010000003f9e8ff2fbb3e04c36c9683dd534a2d4fee5607825e10a2b4885e8e90000000034dbb7fb145e4c0ef8b98d4a03758ad3e9fde5016b147095494046dade14e834df7c4a4dffff001d0320c105
+010000004ee7497463e160c70196b1412a2ccbd2cc41062a773c4a3202b7b047000000007cd53c97e3e7d4f9f8e172a6168aee8539dcf2f4aa98caefe37be637000aaff9fc7c4a4dffff001d04b19e89
+01000000999ad8365c277b20b05558240e3c383ebea0d478faa53122dfa506b50000000088b03731e11466e46268a2473feb0bfb996a77f1d39d1ad9a23d27aa158ef8719b7d4a4dffff001d041a251c
+01000000864569f3105e048d65a9151047f0ca03a5c3311c39dfd84c35bc279b00000000b0c3d72e0b5e89d3cdd7b5d44396e6a4ef92f4a2e7e198a6ee583768d86c839fd47d4a4dffff001d0348f715
+01000000783b15ae83eabb19aac502a6fbae8c6b4b2d8dff30a1c3cb2af0b318000000005e2072b31fecdf48b38a60a1613a73f8c370cb4a857c74844ae17ae30adc9677ca7e4a4dffff001d05a98d10
+010000009b974d3c625f7fcd4e18d7b8e5d53ec6db57b0987f7ceef46e9175c20000000005c8b8ec09654909eb66cb5d6f22c7ba56afafbd3e6486fe8885c7f0ef53b77bd27e4a4dffff001d03cabe9d
+01000000d04963c151d6298a97a6ad4c762ccbacec3dabe179b3c39ab96eafed0000000004aa8cff9a7ef9a2029c8f3d45561888c3c50d9d7e9610de7c7976f159bd0d55ff7e4a4dffff001d02e2bf3e
+0100000032b6290c64c98539ce9570e69dec2e354bce3909fb62badb2c6dbbdc0000000027cb02841e5e22c93f1e2b61c1cb40553e97205e0956a3b01777c137d3264cdb107f4a4dffff001d03e0d16d
+01000000b96ea7181758239d2d12e3c43cb81c47110526fe397595956ec24e1800000000e89deade7ce10d96cd17b1371d1bd50a16e04c997331c3a49baa0ba0cf84e6c5de7f4a4dffff001d02ea02c6
+01000000c80ecafc4bca833c5f2b956f4acd585becd120bc9712517eca873bd5000000003c4a3ed10ee0ff614113e34850ec14018c7286ba1868f4eb541ba0c68a0db05def7f4a4dffff001d05c8b8b2
+01000000c451a1fcaa893e25e72b921e74bbbe82e4489aaeff82806bae8e2ff3000000004d8c8758514bb308bd043ba7c6ab04555831f523ef439b44b6c2d26755a110b86e804a4dffff001d01868381
+01000000b8d4747b5c3a2d6c8e50e389fc938ef7b8c04eb4104c17a2e2694f6000000000aed381b06d2dfe28d0ce14b4f43f5c167dcfbc80d44e37054a014a828a2c8c7236814a4dffff001d05517119
+01000000d0a1893dfc1470c05078598f83a28af1e44df83621ef6a34319f1f79000000008c68d3c55f59e4264a26c2cd1a6a3ed4d45c98eefd14bbfb9a26cf55ba30611d2b824a4dffff001d038d2dc8
+01000000d892825bf696f9c10eead1e3e97dfd043618e0123b72dd058988f92a000000007b921b39ee758310c934e9fc074942513e85716327fa08526e089895530fe6bbca824a4dffff001d0276d82a
+0100000062f08100a53cfee15d6960d2915fecda72ac40a116600d176bd6eb5a000000007d4eefd21df4c8472009c501a9c023613b9b67c27231f130cfa72d97978ae996d2824a4dffff001d04f2f222
+01000000c5b9489065fa7e1ac4facc51a5a0ccc2111911609f43386ebe7ca1d200000000a0db3bbb22a2a8441d84dbe335c24959ea3d3d6e91bf67e66bbcb0d7e0a9c4836a834a4dffff001d04181366
+010000007c967c39b155d44a57d37c46bcb47506c0b00a7987d9debe642c4c1a000000009969fab4c50985190c20867d5eb2622f8994a72c05fb9c91ab057be79a80526d94834a4dffff001d0391a66f
+0100000015276dffb7a0360993a5a83751944fc88a2d3f13f8e9363e5f3cdce300000000cdaef86e7120eb9f7e0e9c1b3009b4581d1133e5e2371f0da2a0b7d314465f8dd8834a4dffff001d04184062
+01000000d9e737d6b012027382d48297bfa52d08eac8ff7aac62810a3bc6798900000000be2b0f66f65fd65f4d4e387b96041ee0aeadeb736b467f8b64e12663a7f8b92971844a4dffff001d016cbc40
+010000002059d85f0b361f764b21d2254602420fbd84fff571daed5304862a42000000000c0bdb3ed18e03a8b3b92ee43926dfcab9a2bc048c98afa86c1f24e1f7f1ab7e23894a4dffff001d03dc41a4
+01000000c34c5ce16980a5ee4c66a17ca4d8619600a15f7e0dff5edec12209ae00000000e7be4fb74031df2ddaab02750360d6b806cfb54cfb9519ee517c20fd9c636b2332894a4dffff001d05c8f18a
+0100000071f7cdf7526fe514f6a17de62bfd240d2eb1fd5f8e406da3833394490000000025156dd8c9381fef3564ec230644a84f35524ee664c3643ccec644088b95d86f7c8a4a4dffff001d054f7dff
+010000005b09986f4fe70f0f864a258266fc73e908e170c29b3cfcac9ca9fbe800000000e0f8760379c70b7d7904b5a50c59bc16afc4529114d9b9ac1de43e698debefdba18b4a4dffff001d04457234
+0100000067391d93a161d82b8f968b7041a48507ace6bb30d4dfcc789e0b6f7b00000000a035032fecaa07e192fa2c97f683fc201a50f90c7ce4abcc33ee37aa528503e60b8c4a4dffff001d02bc415c
+01000000ddb642656ce71cdf2052b12984eb8c297eed7803ebf000dc63a262ce00000000b9bb4103d0d93289e9f9448ed7e63a8ac751ceaa41d1cc9627d2bca7d7c28a16598c4a4dffff001d02242de7
+010000005705c3df459247a82aaa9c3d836ee2661cfe00c826faa3f2a8462222000000002204f4022b778f21a952719904fcddbefad0afb48be8305e89ddaac4ec8b133c8f8c4a4dffff001d01ef8ecc
+01000000560f99e148e5e82dc838535b9cbb5a6116eae26b586531fc2e2a8315000000007760f7a3f07d88e8c7361e1223ea4a9e4fc5f9d8c421037578ac06d921f11095528f4a4dffff001d0392d460
+01000000cb6dafa616960c0d1b87c67ce99dd7238f3891d280444d7a15099f710000000090adecefcdd85452dce9b830a3ceb9240da10806a62404f8b7048c2d85ea0f7d818f4a4dffff001d0331ed48
+010000008f7e1c2ed57b82e56977639379c6cc7eaccdfde5f181fa381b0495610000000023130ef1dba152270b2153aefbb5d4e29c22be3e79b2041ca20620c1b381e099ce8f4a4dffff001d05e4da4e
+0100000005b132a4f74a8799a57a4202d0eeb09612cc08d295401f007c4530dd0000000073340035d03933e01bff3c47f14a5b0a8ceae33be12a8dd521315628ebf42eb3f98f4a4dffff001d003a7958
+010000004e6790a025117e8aa81fd453c7c6c236da838f3dacb169abe51897d500000000091aa6ac0aa796201482553ebe24961fdb79bcb2c1b0902f2ecd2c9e7c705d830e934a4dffff001d001badf1
+01000000ef72b16c3d1e58804b715c8ca9d02f2158524171a8a4742d0a07974900000000ecb21277a56d483af3a7ea1615a8a6d0566bf87ef230146ed4a8cc8fbae417b2fc934a4dffff001d0370ca3b
+010000004fca0bc6408f652b0d9f79713e880890605c993fe954bb73808f2cba00000000a70a8a17b106c669acb346ac08ca99344b307121658cb8cab8d00eba9adf7c9138944a4dffff001d0328fe63
+01000000077ee2849664864a66985199aa49b030f66c79f8022116c979e234b30000000087be9e1e4f0bda5f90e3c1db350737e4f390064d8aec43d6e00ea92a27c2957ee4944a4dffff001d03af3fb8
+010000008b0d4a2ecef90647f9d1a923020adf2bc138626877f0f855fde5000a00000000ecaa67add6b1f10ff91e5df491b59ab1594c357b39ba9f1f06022b078844375e99944a4dffff001d0037c556
+010000000bafbf706c6cdcd91f5fd082255862927a4d3a0457f7d7fcc6ff3e7600000000087028b6299590346eafb168c13132979b89b4912750975d9bac131a7d4c68c3e2964a4dffff001d0525ab4d
+0100000025d15b892b9a091f18e865d579f536c655f18a4994e960b06227a643000000005a2a640ca55a1660daf5363b670daa00560628bc3c39a6dc88d7d59cf2dcb669d0974a4dffff001d0226a908
+010000006843e8fc9750cbf5174a0a778ae5a6a63186683d1296a8514359770d00000000c5eb73e7834883d87b8c0ceef4ba9b850fe503039eee9b28bad767afdf0d0416d7984a4dffff001d05d4fe47
+01000000e3756d4e486befea24a302f095aa87c984f583a66687fae68d92ed1c00000000549848fd7d128b20aae864145aa351ba58eee3feffd7a8089c7adf4445b8de874b994a4dffff001d0496907a
+01000000cb8461389ac8693f9f277f037cec93d37eb8b6a6558cbe10bdacd1c3000000008aa6ad7fa12e8ce311c1c659166e587c880e3a6fcdae871eed1bb350d713f25113994a4dffff001d04744479
+010000005573c2792f3790bb99c05da5d71c9a24bce92ffeec84093243a1aeae000000005945dda5801905c0a507e521331b06a9263279c4761dfcdaef431c0a76698688b4994a4dffff001d0546400c
+01000000afbc89759982446b305b9f48cdb547f3c95760014fe738ec3e32dc8e00000000fea6f2fb7688cb32351b15857d3f250e581af5b20d9379070b5542a00c42ca70699a4a4dffff001d00ac7c0f
+01000000e17f97a9d791f0724271f652f057075f27d96aeb365838d8ab17191500000000a91c00538ff6917dce0bf745e5a040479862ef7134de60320297eb029f7bc07f469a4a4dffff001d03b1cae3
+0100000050e436836fe519456bd7faabec5d522aa80bca6a53af3f9d57457c4f00000000c66962dac9d02aab13c8501636c123f672ab771c8e088e1b24275b105d222c7e569a4a4dffff001d02c1415f
+01000000dd654b9a8a51371c852e448ca417a3fb05413a70672783ecb1346f640000000044fae19fa72a7abcbf78d966a5097415b961a316322184bce818191677a3f129fc9a4a4dffff001d01c6aeea
+010000004386a29fd8c55a0c47b8e97c6d04e1267549de89abb6db75a577d6ee00000000fa993f2f31763c2969af7e8c181a32cb663e07bd210ec14e29fff768621d4b58459a4a4dffff001d03c6039f
+01000000c5cb299e1b345b77d730c527034828c846f23bd3630ff74355c2d9ed0000000056ad317661356b368d8589f343000fb68353f92172d8c7f68a9c9792cc94ca5c779a4a4dffff001d0485feb1
+0100000028fd73f5c4415839b41e887ecd606661462ad914da28c2140afdedd100000000af9d78520fff7f6212f88c7c98521e2c9a8d2a23740ed2068e1b2a55e0c7aba71d9b4a4dffff001d03a07cff
+010000000cb32bab66fb7c93a66f841608c74f61dc36d662cb01f30a8f01efd400000000f8bef1ce0194b9aee865355e8e5b0881092bb9fe8e3842c4f7110f7306caec26629b4a4dffff001d004d55fa
+01000000c2a8b9e3677de7a6f7286c8a458cd5f09409c254209d9d8c445e0d24000000006bf3416c661bd39443f16a48fb560e97558e2cdab18da1e598086cd40a2ac6ca379b4a4dffff001d02c8a75b
+01000000653dea66e42fd4d1019adec51e1f7493b06f458f960a6b08501d8534000000009ec37ca4395220c81233283dfbe020c1982b43c3cec6b82ff425173ef558a51a5c9b4a4dffff001d05681cfd
+01000000443c685dd9cd61dbb1d4c1964e8eef7a1e6c6688b38775c2707b2f2e00000000cfb0c5cc863b3809bb9c642a497ab8eb5a2b0e6cb448fb1a12b08c3d0f3b1e13989c4a4dffff001d04a008e2
+0100000059ded6607bd4ccfeb4348c41b04a13343602879158b8d9aa1f3acc6b00000000af43000d9c2194875f48dbe377b839bfa2157758b941b5020e58b55e2ed04fb5de9b4a4dffff001d05540f10
+01000000d04014b56c58d45f823589d9d36bb755c50ee96359ff44449d71cfc4000000009d6e5c4cadaa256c698491f2c3086ceafc5f73eda5e54d4ebd50ac8ed90eefe0c79c4a4dffff001d015a6219
+0100000098fbe05200b867e000286338ee405f70ce4362f54769db2bc9d919db0000000078b787d3d8da61dce5a9fe4d4a9232e804021e439906e6fd52cf8f041f948b3d919d4a4dffff001d05774a61
+01000000c5f311edc79caeab3404ee358871e5f886712e56005f4c67141dadd7000000005545fe6585b08eceabf29cb4ff68ba07020fb36520301d4d5e7f30dd1489aa6e849d4a4dffff001d0505dfe1
+01000000f24700786a174a784f2d99ad24d4c0165e2f64acdf0893cd4839590a0000000022c09454ffd3a434342bed475ee4e9f7a34b99d26b65f2ab92bcf7418e77f732e69d4a4dffff001d04872d33
+010000009849d7f7915299b00804b04d89a946033b4bac82b431a04781ee1c6a000000006801e653da56cf733fbab3ebb4d3f248ae5066f00b0b879a178ce3522c7e1572cb9e4a4dffff001d01e83192
+010000002cc41fc32ae1712dbf174b8d810498ea52c9908a96100cbca447858e00000000a9eefb8c49a77795e353cef1a18ddf9f75556b599049cc5c326bbedbccc3cc5aac9e4a4dffff001d0559b918
+0100000029bd58b2759e2398dafa0ed714764e7ea87a61e9966b4777837ad0520000000073173503f0ea326597487757ead4a2afaded9c36545e6f94578d3dc6e5d58fbdef9f4a4dffff001d01bb0d51
+010000008d7f3d0a93bb78055f440276924456111c24af5ed639ca3b3a2226d600000000fce8d0f71b20ad1d4797818e77da4739eabd906dafb8baa38bed17cc3f8d723906a04a4dffff001d04416634
+01000000e26e977c4cabd28830a8e1f87983e91ecbac81030dbbd7cea6efd4c40000000029045a4c17ed6ba6bfc40396d8b9d2640a705fd487c3c0271eafa3c503d0f54b9ba04a4dffff001d02477ab9
+01000000f3249f94c4a61be9fe426f496ddc9670874206c5034c68fb03a77edb000000008f1c9da6092af60d73b296ebbc796caf1b8a095f7ed33078bae45cbd2925029961a14a4dffff001d0253c63b
+010000006f6cd48debac250a7b42738755888a7571eb9935bae9e94d7cc28b1100000000d42144297dd9954a62226db7fa2de24cba50b4c963ae6226ae5fa652946dd0a7e5a24a4dffff001d003cb819
+010000009ff0cf741383d94f79a6efeff2a317aaf6375ac3d252db55362280e00000000029e2f50ac5d203e020e7b59855380dda3e918c80627117c6249f987b28ac807bd6a24a4dffff001d030ba92c
+01000000f61d5499f92cf040f17a16d02b348dcb3dd041b224625bc99acd35a400000000400de7fdd4b27e6d8d358cbe3b329417d75352a9aaff9af70c1f685cf842560675a34a4dffff001d0271c0e5
+010000000d1e2a2e2670286100f862dbae397fef5d8dede11bbcb013c453e8ec0000000043db438150141c0240a7f3f03cca32f4fd0ebbd6bae6daf898a6c7aac99b8a85c9a34a4dffff001d010d4882
+01000000abcc3483c5e1adeec60646454065ae3a116dc60aea65be6d0e66bc7c000000006c0e3cda9dd0367de3a4e1d356c0ad23cd88f26cdff41b412b0ca1de1d9c738cbda34a4dffff001d00b44c37
+01000000e0d79f654270f553820b56d0332b5ec18a4cb5d969c5d67c8adf9b2a00000000f2cf8413e5df690ebfdc8441586639e39ee7d8571d88e07853117ed693bc0b8d35a44a4dffff001d017fc225
+01000000ee4c6ec5ebb31e14991634916e68379e6f08617bd3e2b0816c0605fc00000000203f6062b808d073f0471cc4bad3fe9cc51cb9c732ab53e46a4127fbf0b960fbbfa34a4dffff001d03592dc6
+010000008000bccda2bdbf0a6293178dd1cdce1a368824e9c99587798b7fc1c600000000d29fc4754ff52a23715d3147c6a0ae31ddf06fa70465f4d09d41b4aa08da707bc8a34a4dffff001d05e36843
+01000000386cd6cb2551941a87716c2c618e1e56e9fe7acb49b0a71f414047eb000000007bd7a97b969168050de5f20cd3d0ea212b138f4b43e3c27755f46aa08f98f1ce06a44a4dffff001d019dfd3f
+0100000053402e2e9f85db9774e0193414436cfbbc70b658c4e54913c67bcad300000000fbcf82e5b9b5c1aff879c25c172d4db531905915b205187bc3ae1425603e7c764da54a4dffff001d0154db1a
+01000000ded3e38a879dcbfde2c9ff5690db8377d0c596e20b8be94dafe611df000000009d8b84155b519e0c1cd97837329fc3505f76944245c1bd686617779dc700c15850a54a4dffff001d00ea1441
+0100000011a98d2176e7952434447a06a9ce9f726651737d3a3e006373183b2400000000a50e90ca36c2cab93fd8acec6eae0755d7d2e710688a26fc16557bb5c038d12c0ca54a4dffff001d044b4279
+010000008cffb590fc8e390cc07b0fc7d7e1f83033a7f09c20ef114f6efd73fc0000000098f65f5151c0fdce2398474d0987d7a531aab1c2659675c07658b07cd954f80b29a64a4dffff001d03e15e65
+010000000a7213bcde17cdc86191ebe8580e3a9e893e3b9a189e2787394c5cac00000000230d4c63ebf11994b50ef9c5f1b75b2b8ce6950f63fad01665fec3ed5ff2bcd243a64a4dffff001d02706123
+010000002c29a0a93ef692c20f7a5d0a917a69be60ec5bd73788f9ae92a2e7f90000000048634496267a248713364570b8c4cc6ad5b1a3f32fb74cc1f11fd22c3b293e88b5a64a4dffff001d010845af
+01000000fd50cc2daafbf969480d2675a4cdc41f397d1d815a2aeaf863236bbc0000000086d723685d916a3b3aec61efe9b88a845d96fb903e78760e0759aa2089de4e2e66a74a4dffff001d0389da36
+01000000e897be2520f0c6faad2d0c4be209e0d4cb2edfacb7579a6aa939a10a00000000ae3f02363ed042360b679fece5726b2499e74f27f1bc596c3beaf701463be4465aa84a4dffff001d04ea7f5c
+01000000a4f61773f2105205cff43c8ccb188a0ebe56f0811834cc0772a5a1e8000000003b776a5a9f039715342ed278feea0e87f1454ffdd086ec1e3663cc738965e9d536a94a4dffff001d04479ccf
+01000000cd182da4f3e6641784b8035ac96d74b182939696dda02368f01889b7000000002fa23a7882a373528b5403d34eeb13a26fbf25d668e7019e7a0403ccb5ded71387a84a4dffff001d033151e9
+01000000913cb00e5db68a01cd2c2c469773d0946e3e1589cd72c56349d0405c00000000e04420559afddb03dcf51c67a8317370080e517e24ea38dbbb5bd529680c1d2c3aa94a4dffff001d01ae2ae9
+01000000f87f4f7f7f99202d77747619865391ed07d87afb9c7c9a48b7f319ab00000000b4e21e67bcdbd54bee76d62b5cfdb6b5cdfc5fd1c6ffe9fd6b1e2f39677aeacd2ea94a4dffff001d03275902
+01000000c7a1220a895e2a83917ab688040dea8a000bbd5f4858e8bda00f3bd100000000fcddd0541ddd056f19fcfac0e637cc8fab7503352757707daf37da8e859434f397a94a4dffff001d02d171f6
+01000000c5711e518c4a53e9a6d684759ecd07d69b5d859e1edffcf4bcd3896800000000e4ce2882f8c5b8e467a080c647ca97b8c11070b7ad10a1eaeef13e1b49d0028cdaaa4a4dffff001d03ac5ea8
+010000000a1bf9a1db633b5c817be9ecef65ea99ed980a58d070cfab68b70d5200000000c9fca844eac0515369a587359fce6a2cec83ebe565dbf46b92928ca831a1abd54ab24a4dffff001d0370f65f
+0100000081f9fcc96f7824b23225d5526b65d3d5826bfa8b65c083028e7b287700000000163cdfb6b5d8589c539ef411a96eb96ee49dcf03ef4538d8f752762893db209515b34a4dffff001d0215c77e
+01000000a563fe8e23fd9acdbd8ce0db616cb72c1b05fc8610c4e89fcd32b21f0000000074fccc385cc517580daf8966fcbae398b81ce1f077bdc5732c529d6c3502e16183b34a4dffff001d033652b3
+01000000de5ca94254b771527679b5dc4862a7eee044409cc33ab2960cbe3920000000005f20491b7a3093fe2b342e816a5b8d409916c2f11a6cb6743dc528e8c7ac51b7ccb44a4dffff001d00d437a6
+01000000f8d57b05a64cb39a5adff645ba434bb707aa118322851cbce137df2800000000d760a34f6e7b07b3718a8355b71b014f07fadb917707e4bb635cc1277cb8ca7f5ab54a4dffff001d00d8ddeb
+01000000a54d55adebdea113e46d6911a15712f7545709577a5ef6684a37974800000000849ecac186963e52f8b855e51f70e9cfc90cd0f7054c23f1bcbc1675206a00bb4db54a4dffff001d01cec36c
+01000000f2f0b39c9e0f8ae81ceaf09ad25ee9166e8f3d33addaca1ca1366c5100000000060e3cb266839d6c8155ebef78939c33b659156bb2cbca1cc95b50e58ad5100b2eb74a4dffff001d02e282eb
+010000004a016d8b02a6e3c33705715c29754230218f04485693994b29d78a2c000000007286500c919f491aa5a0721558e4b8a64230ec2d1d955c45ac871da0e74c898d3eb74a4dffff001d036714f3
+010000002d24b64aca8efd3a0a948e813c5303891d1971e6295be97c1923e05c00000000eca7286e07ea252409b51cb2124e3fcd6adb8e6fdcf87046de18666e36037511e6b74a4dffff001d037e372e
+01000000dc8280b9a38ed7829f3ae49bf8f01642c0b16b90642b2e911c2cb69000000000a10c2661ab1b02ab9f3c5bf928db087c27c06ee7537b04ee66ea4785fb625c6205b84a4dffff001d053fb75f
+01000000280fd86185912d306966b6b7bd90a765e659bad17564825f0c997f2d000000006637b339af8cdf579a250267ce7ce41f19de798c7ee6ff7823312076e0950962c7b84a4dffff001d00c1fc82
+01000000993b4428fc00d1eef46e5fb166b32f32ea75e3e49105a04322845d2800000000fbdd76c6562c86af0e63ea9a6c46a91b8d92c4e7df480977ec0d53b7f3c25b9812b94a4dffff001d0099e992
+01000000c3b9d6eb714b91e367d7a44145f498cdde7f579b68c66bc1b9af7886000000005b0df3f16d5bb399b09989beaf8e0665c695ef49b7aa8473ba8690f0b029ce0094b94a4dffff001d036efa36
+01000000a702519acf76d7519ce42d6c0bfff12644be3694621a942a55e4b36900000000f9357358ee5931b907a8c3df01115891a740ce6f4df224eda4586eaf450d8b9f3bb94a4dffff001d02d9d39c
+01000000931d5230c5a2b7d43d322cebf9026ca68fe1a70afe0d8c8abed78d320000000005634a2446d5fb785d1f1582e6f35418bc6fbfb6abb9507213b010e58dac1c3ce0b94a4dffff001d02e9d793
+010000009c7bf22857a46add066ce2267450ddd5a6f280943e961b1db4d3be0a000000005eef5906ff42a8baf4a8a2d8119f88112d802484361ea778d22e44300f6ab20c55bb4a4dffff001d0179f4e9
+01000000e2a926d9239573962e534a47311e7875482832ecc6306bfaf1137c0500000000fe6ae11390e7bca224e9937759c658c9aadc8971e8ba384caf2a58de311826e7abbb4a4dffff001d01d2102e
+01000000dd3fb5ca9283091e24a2dec99871d9c033f39b1009f5ac592ae2cf190000000086a971609dbe58f45f99ae1a769c07ed6b0575ffe811844b8f6e04edac722bdae9bc4a4dffff001d037251ae
+01000000a7f31bcf8d5a86bda0ddaadc3c8b77ec71af748584b3a5eda6b88c4600000000adc6c0ba4cebd8e2b0154e9f876f1e6ea54e293e6505a5200a4a75d394521a7882bd4a4dffff001d02defce7
+010000003846b6ba73428b23c1aea96cb3a4cf8b72ea4f40268ed68fd16ad9bc0000000050a4b5b4d3fb6c72d198506c83d6e2792262134ac07813baf355921ffee7c6579abd4a4dffff001d04687d8c
+01000000df7e6b1a5947867dda62ed7a79f75d939d4190f3a575b447c31308f9000000002523ea33f4e43dc2dbdb14116d5d087aa3544a7f886a69693caa154ae0abcfdf05be4a4dffff001d030a6d3c
+01000000799f7e9b6d0ef1bcc5d4e6b82936272e3492df8a920b934dc9e1973000000000d0038b40a4b3c20cb7a6b431d6c2edcd235c59dfaca4698dc0f4a057d89eded6ebbd4a4dffff001d01e9924b
+010000005dc85295d33956fc24736fe54dfaa12ca98cae2ba02ef7eb9655a12a00000000b7bae6ad3a263adfc01e5e76acfa7dbdc0e60962230af6fc59003d59b437b28d90be4a4dffff001d032ce3c6
+01000000a3b52bc4a8ade1fb7764462099a412272fa13b3d9de508c356a3d7910000000001eef5a3b0e24e946235bdbdaf78601185042a5974a6f9aa7d8deab62b6bc49c18bf4a4dffff001d05a9f6dc
+010000000e816fbf3db7c990098f50722e36029dda668e9aa386c23a201e9d8e0000000027d9b4ef4291c5332a05c4a2918b5526b96a117c0bb2bc97cd18f5a6eea15caca7bf4a4dffff001d023f105f
+01000000514062fccf6e88a8ad9d0ff0fa2ca4d2716c2b5fcc577f52e4557fab00000000ca12c4c5fa9b8106d21f12f4bb24e1954ee95686b849c643e0504328653198fe1cc04a4dffff001d022e817e
+01000000b8631eb7fbe8abdc32f81b54c6f86c96cdb5d7c270cbc8e8026890e80000000032510d84003ef4d4b634cb4bc914de16f8e9b72b6f1ea20dd9eb847f11351641f7bf4a4dffff001d011d83b5
+01000000bf3ce738c0692a474dadc784640fe4e2f06beef2fffed0bfee65cff100000000a463353790f4669516f33c405164df2ee08f7738bc91ded3ca2a025e18143ac967c04a4dffff001d01fcc651
+01000000fd4bb907a2089b41433dde0fcfa70db3ac3a6f075be657f0e15144d1000000004bc439544765cbaef9a184ce7fb26bd62cf1ab32267eed5fb3bc7050a8f0c427b4c04a4dffff001d05d07be8
+01000000d0d3565bbad3a5fd165a7fbd978fb2ff2974dac1fb4ef21ee0c6282200000000e4e9ef647dfd719c086990944594c3892d0deffc18fc90e092d61a3e586e4cf8c6c14a4dffff001d0188a8b7
+01000000d2d5324fd546db69e8c42e6f6c5f2f686cf29a9fae985224d450ca7b00000000b3c4d587370073598ce1a226be2f70e0ccbe446e80f88f0446dc5b179782186b78c34a4dffff001d027bf975
+0100000074b16276f3e902ba74a5a938a86ad7249db7c620b06023385b515109000000001b94a65422bddf5384bb8da9d866fc7a7e5c5abfbd5867efdc8eada3b78b8e3babc34a4dffff001d02d402ae
+0100000057f6b87254267089a1d84eeb06f20c3b181177372b88cb9152f356dd00000000c70258c4f96177b1cdd9d6fd5ad0e9a65abb44e7fc3dbe507f45e2984ab830d9c9c34a4dffff001d03cfee0e
+0100000078eea9a9282653e1e213c716004ee3c5b7d323c37bdec7a64565831200000000ac37dfa5948ecb63e944a61eb0e83cbf7dff61cd61cf66d21f447d9a97bcbec123c44a4dffff001d05c7a466
+010000006c0d72985fd57581d55754a3dc631a89e6e4e1edbad1696ec271d6c200000000c1d26e829e285126d03a1943bc46d72c7e68f7fe1855393079dcc6406328ebc53ac44a4dffff001d01ec1ff0
+0100000060f628c70ffabdde8c265ff6aa1b49e0f42d250a0645ac0fa2963ffc0000000074f7d49e03dd5ccae2109db1e4ff11507238642d86fc771aaa999ae48b06f3d26fc44a4dffff001d044b6b47
+01000000f2c8d522c94e93a8ff5d947c057e4125a4deb167e5eabed5dfb56736000000005127f86da79df2980cedb16ab9a4b167e6daeadcda0d645f83deb48a3dcc065eb8c44a4dffff001d0561c0ee
+01000000e6c388d72c717914e507f11c58a139137fd483dbd746e9c5f2a1eb0f00000000afa8dd6f0e319d4ddedfa1425f6c03e461dfa005d42d3f355a04cae42626c9b121c64a4dffff001d059f05b2
+010000001b354bd6d10f5a24879851bb2aff42548cb66cebda04e7103bc2521900000000622c689b318941bef087640fddab2c5ef6dd6024f133ac28c1ccfaf30e380bfa4dc64a4dffff001d00484506
+010000003e226e00e126f0a4a35b1c6c09509e0d999122428262a5303e563c6000000000d7432b21fd48689d7e79f9b6f7f624aefe8b72503fca13b814d355618b9023cf23c74a4dffff001d0247f874
+01000000fbb3060018eb1f40d734cc64744134d96536c4a331e00d4c05e2cf9b0000000061b34d561ddfd18ea3381d48a8bbe4627727c6ead00a84030e7f76eeb788148a71c74a4dffff001d012e783d
+01000000fff008ec46c2d35cf469eded16dfadc77ca6e9a9680cea0b0611661b000000001d1e1087d20d41da1f0b4a5423aeb281ecd48cfc434feb45a4c6c31b6b61398124c84a4dffff001d05928767
+0100000093ebaafdaac804feef3ec352e40cc78be60e7ef1edb167891ccd99cc000000006bf225e961b1f1b8e042c45966c2118f20871551e0996eaf51bb628cd9d49b7a45c84a4dffff001d00381daf
+01000000e85fb976eb4817418c0ed2987dba1674c0fd757f70b470f83d01a2d300000000fd6717e5d49bf303d41d861fc50503aa8575eb52243f426de308869a8cd57c57f5c74a4dffff001d033a590f
+010000009cf5f976b9ae634b4c867bf78cb602bee8150ea1838d0ef3d06ce94700000000fe6ce75c462d7d09aa8d917fa90a49bd6a4c41f02457ff40ca47ded089021042b0c84a4dffff001d03a5f6aa
+010000002eff16b6669f88de6f40810c57349804014f734c692a44ee822b7f1200000000c095ce4ab7e0e02110b75a33012da97d75e26b83de5ae0bd392fc3b8191b77e8e8c84a4dffff001d032c2bf6
+0100000001aa2c494f2d4b7ecf367734c734b931d3592b1572dcff557186ece700000000a10e60b120c06cd1be1d2153abb7901e2d9f1a3f43de9e6642ad269be1cae22e44c94a4dffff001d01f2005b
+010000003d04e058642d22428704ad4337f372bb8574cae072c2a4b469b2e1dd00000000bff07e723c3859fe85bb849d860b21a325b1baf1494a3c1e73435dd7992bf8e53dc94a4dffff001d00262757
+01000000272ecd270665dc39e924838516da62f8588270f1e37812aabdb1d48c000000001725d4769aaca3cf86c5b0dd199bf93f2d16dbb13fbd4029633bdd1085283fdd13c94a4dffff001d02f79a8a
+01000000705f1bbf68b4976a9ade4ff01ae933030cfe4cd43e8092c9604442a2000000000cb7f30461a3563992f7b02095263fdceb13984123a28ea68d0989601655e84ab8c94a4dffff001d0017e0d9
+0100000082d6e6bf4ed6c5b989705920effd072fe6b7119ea5b8c8fa6d0bf5c70000000071959f90386cdc263655d36033817a6f69cbc2d99fc56cc81f893b9787907a4fcfc94a4dffff001d000f9093
+01000000374e3a88a3a3eef84b4212e90e863754ac2a6999747c22667c0b29e0000000003a0e3693a8455ac94beb574efd98297948c5b5f69eaf0b813e6d5e57426e39e279ca4a4dffff001d042b079c
+0100000003f06b3017023d1cbddd4cf169c4e74879754683f0d7670f1d1197f70000000098b12eacd55084fe2807a33dc2c89e6fc25f382aba3c6b4c7ee2f74a6999a6bbfbca4a4dffff001d03ba1eb8
+010000009c0730eadadb66fa486e40dbddd14b635ec7939f788fdfd999e043f2000000006b0a86109aea3d5e4b7dc320da600dff19d81dce7b77dab9596204c20c6125b06eca4a4dffff001d01c88a1c
+01000000410f77945e68dba04d76fc931b26ccb571159c9bad438df5f8fc4d190000000004d902fedee34fce75de583a54e1fe7ef26a7946b48b93b9b0834dbf19a0b47f3dcb4a4dffff001d016618bc
+0100000025ec91d09a451f18667262004e0cebb2ad66262407ed0a8f4ddbff580000000092f8e1be93272aae6b36766d721df3d80444582edec0143174c5fafad4cef50c79cd4a4dffff001d031ee0cf
+01000000d8d0cea1249f726362ee78259d97d98efa17ab0efeb1b9d0a0cf0d880000000035c3dc39e544b855457fb0711697a24123fc3ba6c439123714b193b9d1d9ec7514cd4a4dffff001d0526c906
+010000003e824b7a8cb7bea1dddcaee7bbc4720e8c5d2f53c9c28dbaea4662a90000000061ef7a1ca4f6f61cc594d040490d3cb2f11054639a0a964c67cebc6dbc079e36cacc4a4dffff001d03af40bf
+01000000da9201093da940fb6badc420406540a979cd5c06fc2f47db3215130b0000000069577de9d33e6351fb0ac1be26158f94c217d2522c35e9f813e47bf9811fad72dacd4a4dffff001d02f70faa
+010000003dca51f6b97f7c05a44f329c1c9f4d7cf03eeb3b16d94bd56e34b50600000000092122ea597797ea0de7841945f09c221578800d0c44de4961cad7ecfb93c47323ce4a4dffff001d03bd9635
+01000000a560b2ead8a3f50df84385c1d5a51654c22a321268e077dfcb9ef90c0000000091db9cfec3e6c44a790d6295a2effcb8dc94f9f1c0fc4f6d867e90e140c1e9f325cf4a4dffff001d03a86875
+0100000056e6a9a6b93f9ad3ad847ee845ee9c366dc9376489516c31d892ed5e00000000bf2f3b6867855fe1058092fec7c5ea89c15cf8a70d0c565c26362d10129221af47cf4a4dffff001d027a9b69
+01000000d123e40889dc9dd8039943b5b1a0de53026e9ce92cfc69d5f8482fbc0000000022b49f0319707490d002d5b2c04adfc7a6433c2931ed04545e9c33f37a12b5bb924bbe4fffff001d00b7fffa
+0100000065aa216d118366b22580747fb4d876d80ae3be145e318563984e0b040000000000c2b33efaf934623c6bea64570558d6551524e2f45d022b0fe935387279e6a37b51be4fffff001d0511e593
+01000000eced0b25fadf8818fabbf65b9c16f39874336d7faca51b74698ad55500000000ee49643ded3202eb46b451c55a652c6cf38d82da93de97a2f2bc0e8a28c1be7ba951be4fffff001d01f82bc0
+01000000bc9c266783225bfa273dbba2342f54809dcc2b263e1e6bf3269bda300000000001aa244355f8827bdc7eaaa0defe1a928816cf7d919890781c1fe3ee58d985684d53be4fffff001d01a443c5
+010000001b7467dcebcf2baa4814161dd6c7ab966ba082d670a69eaacc1785910000000050098a241f8df957a6bbea8ecc39473091636a4e55d673ae723edb6a3f50b9835853be4fffff001d013788f6
+01000000f5023ad6b3432b6b2d5afe707cfbfa0a30e94000799c8430a97da151000000007a53e7dbfb43a28ee9d5956be8da4190e5be6f16da4a5636f6139742754ec0cedc54be4fffff001d04474930
+010000002077ea8e53ba9a132d83b91e40fb1f4c724217b8197c4533a5bee9e90000000096a376627bd7b42278fb33609713b83665f195d100316b9e1651638cd4d7d47cfd84bf4fffff001d027c40c5
+010000001936a4ef861f194c15f171e843866f33ec3b3cf09394bc323753beb8000000002e3288941ec156d0fdc90d314d85a71a418d272704e12f2a99576fe3201e2d1b3887bf4fffff001d027aefaf
+010000006e5c2e805629d476b92e0a8d74c3ee0dea0c24e9f35ab4afc6ac77b400000000d26cbed9a7c1e51fe657c6b3cdf202926e0b6e371ebf41ccdcbc0909470d56256e88bf4fffff001d05a2db35
+01000000e5f26b5abf56353520e1f9baa35e9cdeb57f257f59ff6a4ee42c3795000000007becb215d446a70b351fdb14659d105b3c258802fa4314b536c4e5249adaab2a9c88bf4fffff001d04e36b51
+010000003cf3550994f31a27c040b1340c7ae9ccd36d554a1bfa797cf46504c00000000005f56011e8d4bf55bca3dd8c9799e0d83304e43ae8b66ee58c688bb9d7f7fefeb088bf4fffff001d0314c7c0
+01000000ad48a4d4922e3834375f8c50e8cb1842688829c8952204f66b89a7e9000000004579dc3ff13abb920007843862e9091b0e142564cd0a75f5fc3272982b816d4ee988bf4fffff001d00becf18
+0100000098eff668afac04d3d8531d9bc31aafcd48f7ba68b080b40641dfddc200000000a91184d97bec523016c15dfc3473e7407278b70638f4fd6c9dc1ba210454f7fb1189bf4fffff001d042a1349
+01000000ed2b2b04575642de5fc665cb8e30f3e39dea66299592405204bf6b5d00000000f3af0d684b2c16f307b6ddf9a02b58b9079e858dd2d093aa29288856b83b82183789bf4fffff001d002cfc38
+01000000a398a8e978de6f1e7b9b84523d45e5f2021feb29f6d39890a2f48afd00000000f4a78ab4703ca95ed478e78e526f1a8198c3cbfb8a9ee6455338d517aa18173f3e89bf4fffff001d008b40b4
+0100000036bef7eedabe3b05e1e049efcf66823190285d64d11ccfd2eccb5e3200000000243f2ed308bd48cd3dd25d4cc649dea0d0518d6e12612e03c8a0888aa284bf2d6189bf4fffff001d0498f47c
+010000008d84b4fadc45563b60c0f50797ffaf71046f287482ab9fe629f64e4e000000003ae5c2515afb4117d032de958e0ba535e484575b80ff6d6e2e0a23e6dc95e0ea7889bf4fffff001d0518a2da
+0100000020ddc91a15f8c9ec5161bf1709ad7f37c33ecce9809b353dfd3df2a400000000f2b6d372af2c7164d36dc1c369090f4f9179b0870676ab6ec2641b0922b1fd179289bf4fffff001d04bbff73
+010000009701e67638ec4e8b47c547ac3125ac6c1892a98d77a580ab88f069d5000000007c740055656afd61883f9c1c102fec710315e6e4d58904accd184cba85c9f958cd8bbf4fffff001d03f8f01b
+01000000fa9f56e3e4fe1352905b3a1f12567caa3f9f4251f0bda94fb07f1684000000009ff7fbae47f6afb47a1723662d196155b5f4bc1c62e882a93f54ba2d9a10913b508cbf4fffff001d02d1c97b
+0100000002ae1c66166259ef43d290c6a60cf8eb5783e6c91293a3566946673a00000000846d597237ea76576bc49381badb59bd509cfda3095c2c1ce5c75788b96aa144718cbf4fffff001d0139c1f5
+010000001ed0a06499985c4cfb05371eec681819d8e193b66011f57b66792565000000008980b714aeb09568fd34128f4c7ff84d3e09223dcb2b290c5948816cc636d69fa58cbf4fffff001d0122c5f4
+010000008c045f9e0e200a8eea9c0957b12e045e927dc768a8a5fc2d8fd8d9ad000000009925a3f617c025b95f6329247379b7ef7bd081e52838676671c7360eec07255a098dbf4fffff001d0099c40f
+01000000a6adbd81b12cff9948911af583db14484c4c0d30e91b1af2e7fd4d4f00000000a70e4b2057854c71170f8468b799f03d0344905213872216f4210f7bfcf76980198dbf4fffff001d002b7c3a
+0100000059e1e5ae6727e02c2acf95bc5f24d8274e42495c790125ba52a581170000000043412a5a9f1749688460eee46b62851641fd9b17f9ed5131b3f50e4f160eadc45cb3bf4fffff001d03a966ba
+01000000c704e90a0c4cb992d6973d636a7094ef2790a60231db345a926b6cb6000000006f98c89001a2e89ea35aaf00b85d965af5598b61bc48d9d9847609331995383effb4bf4fffff001d02ee5af9
+010000005468467a64f255415f3ad770f0d5393275f2813fd0e1e915a2b8c28b000000000566f29ed6b8cdf85a4b197dcbb0135dfb7b671f83ca67970bffea903309ab7445b5bf4fffff001d032ac068
+0100000021ad75b801d48a30fa5b4e6b1e43106a294a5e2763307a992fbd5282000000002c20851e58dc1a2ceb7c7771822ed41c7d0994731d01829628c3e938f4155a276eb5bf4fffff001d01a1420f
+0100000002954bf32d68d54adac42cb5190efc06f18ead35208dc14a8face6f1000000009002c5c2dfd0259960507e69399c688f0d74bbcd52c810cd2d2507a829133f4941bdbf4fffff001d01724be9
+01000000add5df18f427437ace8b40064b3806583217a3f724672cf72cf8c18300000000bc652800c84513b2de38e0e97be92a582a628e1e49a9fe8d49aea5623251eda54abdbf4fffff001d045ec8ea
+010000003b0c953fed585c1ac952df84e4c3953ba551da457c700035f6f3fc0c00000000831c01723b0fa955a6fbd51d9ddc5c77964814868f96f52ea3eaf3b66098e6df9dbdbf4fffff001d00cb77db
+01000000f6667e8c4557c28f88b86a6892d03509a5afc9a89088a5bb8638eafa00000000ff61d87fbc86dac7f80962702153a2a6d9a84dac13c265106b883ed7ef68fc91cebdbf4fffff001d00e07f63
+010000002cb99b76cd981a422d02d61684f76bacea92669298e552e412ce41df000000005f2a9977b14cf34078500f956be0cc154291ce5a0e34c00a3dbbae97bed5e930f5bdbf4fffff001d008fd760
diff --git a/test/functional/data/invalid_txs.py b/test/functional/data/invalid_txs.py
index 454eb583f7..9dc06422c4 100644
--- a/test/functional/data/invalid_txs.py
+++ b/test/functional/data/invalid_txs.py
@@ -24,7 +24,24 @@ import abc
from test_framework.messages import CTransaction, CTxIn, CTxOut, COutPoint
from test_framework import script as sc
from test_framework.blocktools import create_tx_with_script, MAX_BLOCK_SIGOPS
-
+from test_framework.script import (
+ CScript,
+ OP_CAT,
+ OP_SUBSTR,
+ OP_LEFT,
+ OP_RIGHT,
+ OP_INVERT,
+ OP_AND,
+ OP_OR,
+ OP_XOR,
+ OP_2MUL,
+ OP_2DIV,
+ OP_MUL,
+ OP_DIV,
+ OP_MOD,
+ OP_LSHIFT,
+ OP_RSHIFT
+)
basic_p2sh = sc.CScript([sc.OP_HASH160, sc.hash160(sc.CScript([sc.OP_0])), sc.OP_EQUAL])
@@ -178,7 +195,44 @@ class TooManySigops(BadTxTemplate):
script_pub_key=lotsa_checksigs,
amount=1)
+def getDisabledOpcodeTemplate(opcode):
+ """ Creates disabled opcode tx template class"""
+ def get_tx(self):
+ tx = CTransaction()
+ vin = self.valid_txin
+ vin.scriptSig = CScript([opcode])
+ tx.vin.append(vin)
+ tx.vout.append(CTxOut(1, basic_p2sh))
+ tx.calc_sha256()
+ return tx
+
+ return type('DisabledOpcode_' + str(opcode), (BadTxTemplate,), {
+ 'reject_reason': "disabled opcode",
+ 'expect_disconnect': True,
+ 'get_tx': get_tx,
+ 'valid_in_block' : True
+ })
+
+# Disabled opcode tx templates (CVE-2010-5137)
+DisabledOpcodeTemplates = [getDisabledOpcodeTemplate(opcode) for opcode in [
+ OP_CAT,
+ OP_SUBSTR,
+ OP_LEFT,
+ OP_RIGHT,
+ OP_INVERT,
+ OP_AND,
+ OP_OR,
+ OP_XOR,
+ OP_2MUL,
+ OP_2DIV,
+ OP_MUL,
+ OP_DIV,
+ OP_MOD,
+ OP_LSHIFT,
+ OP_RSHIFT]]
+
def iter_all_templates():
"""Iterate through all bad transaction template types."""
return BadTxTemplate.__subclasses__()
+
diff --git a/test/functional/data/wallets/high_minversion/.walletlock b/test/functional/data/wallets/high_minversion/.walletlock
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/functional/data/wallets/high_minversion/.walletlock
diff --git a/test/functional/data/wallets/high_minversion/GENERATE.md b/test/functional/data/wallets/high_minversion/GENERATE.md
new file mode 100644
index 0000000000..e55c4557ca
--- /dev/null
+++ b/test/functional/data/wallets/high_minversion/GENERATE.md
@@ -0,0 +1,8 @@
+The wallet has been created by starting Bitcoin Core with the options
+`-regtest -datadir=/tmp -nowallet -walletdir=$(pwd)/test/functional/data/wallets/`.
+
+In the source code, `WalletFeature::FEATURE_LATEST` has been modified to be large, so that the minversion is too high
+for a current build of the wallet.
+
+The wallet has then been created with the RPC `createwallet high_minversion true true`, so that a blank wallet with
+private keys disabled is created.
diff --git a/test/functional/data/wallets/high_minversion/db.log b/test/functional/data/wallets/high_minversion/db.log
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/functional/data/wallets/high_minversion/db.log
diff --git a/test/functional/data/wallets/high_minversion/wallet.dat b/test/functional/data/wallets/high_minversion/wallet.dat
new file mode 100644
index 0000000000..99ab809263
--- /dev/null
+++ b/test/functional/data/wallets/high_minversion/wallet.dat
Binary files differ
diff --git a/test/functional/feature_assumevalid.py b/test/functional/feature_assumevalid.py
index b7814bf33e..1b434c4485 100755
--- a/test/functional/feature_assumevalid.py
+++ b/test/functional/feature_assumevalid.py
@@ -40,7 +40,7 @@ from test_framework.messages import (
CTxIn,
CTxOut,
msg_block,
- msg_headers
+ msg_headers,
)
from test_framework.mininode import P2PInterface
from test_framework.script import (CScript, OP_TRUE)
@@ -72,7 +72,7 @@ class AssumeValidTest(BitcoinTestFramework):
break
try:
p2p_conn.send_message(msg_block(self.blocks[i]))
- except IOError as e:
+ except IOError:
assert not p2p_conn.is_connected
break
@@ -180,7 +180,7 @@ class AssumeValidTest(BitcoinTestFramework):
for i in range(2202):
p2p1.send_message(msg_block(self.blocks[i]))
# Syncing 2200 blocks can take a while on slow systems. Give it plenty of time to sync.
- p2p1.sync_with_ping(200)
+ p2p1.sync_with_ping(960)
assert_equal(self.nodes[1].getblock(self.nodes[1].getbestblockhash())['height'], 2202)
# Send blocks to node2. Block 102 will be rejected.
diff --git a/test/functional/feature_bip68_sequence.py b/test/functional/feature_bip68_sequence.py
index f0bf09e172..fe6f9eade1 100755
--- a/test/functional/feature_bip68_sequence.py
+++ b/test/functional/feature_bip68_sequence.py
@@ -14,8 +14,8 @@ from test_framework.util import (
assert_equal,
assert_greater_than,
assert_raises_rpc_error,
- get_bip9_status,
satoshi_round,
+ softfork_active,
)
SEQUENCE_LOCKTIME_DISABLE_FLAG = (1<<31)
@@ -52,7 +52,7 @@ class BIP68Test(BitcoinTestFramework):
self.log.info("Running test sequence-lock-unconfirmed-inputs")
self.test_sequence_lock_unconfirmed_inputs()
- self.log.info("Running test BIP68 not consensus before versionbits activation")
+ self.log.info("Running test BIP68 not consensus before activation")
self.test_bip68_not_consensus()
self.log.info("Activating BIP68 (and 112/113)")
@@ -336,12 +336,12 @@ class BIP68Test(BitcoinTestFramework):
self.nodes[0].invalidateblock(self.nodes[0].getblockhash(cur_height+1))
self.nodes[0].generate(10)
- # Make sure that BIP68 isn't being used to validate blocks, prior to
- # versionbits activation. If more blocks are mined prior to this test
+ # Make sure that BIP68 isn't being used to validate blocks prior to
+ # activation height. If more blocks are mined prior to this test
# being run, then it's possible the test has activated the soft fork, and
# this test should be moved to run earlier, or deleted.
def test_bip68_not_consensus(self):
- assert get_bip9_status(self.nodes[0], 'csv')['status'] != 'active'
+ assert not softfork_active(self.nodes[0], 'csv')
txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 2)
tx1 = FromHex(CTransaction(), self.nodes[0].getrawtransaction(txid))
@@ -391,9 +391,9 @@ class BIP68Test(BitcoinTestFramework):
height = self.nodes[0].getblockcount()
assert_greater_than(min_activation_height - height, 2)
self.nodes[0].generate(min_activation_height - height - 2)
- assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], "locked_in")
+ assert not softfork_active(self.nodes[0], 'csv')
self.nodes[0].generate(1)
- assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], "active")
+ assert softfork_active(self.nodes[0], 'csv')
self.sync_blocks()
# Use self.nodes[1] to test that version 2 transactions are standard.
diff --git a/test/functional/feature_block.py b/test/functional/feature_block.py
index fdb608d457..c74270febc 100755
--- a/test/functional/feature_block.py
+++ b/test/functional/feature_block.py
@@ -74,6 +74,10 @@ class CBrokenBlock(CBlock):
def normal_serialize(self):
return super().serialize()
+
+DUPLICATE_COINBASE_SCRIPT_SIG = b'\x01\x78' # Valid for block at height 120
+
+
class FullBlockTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
@@ -96,6 +100,13 @@ class FullBlockTest(BitcoinTestFramework):
self.spendable_outputs = []
# Create a new block
+ b_dup_cb = self.next_block('dup_cb')
+ b_dup_cb.vtx[0].vin[0].scriptSig = DUPLICATE_COINBASE_SCRIPT_SIG
+ b_dup_cb.vtx[0].rehash()
+ duplicate_tx = b_dup_cb.vtx[0]
+ b_dup_cb = self.update_block('dup_cb', [])
+ self.send_blocks([b_dup_cb])
+
b0 = self.next_block(0)
self.save_spendable_output()
self.send_blocks([b0])
@@ -758,7 +769,7 @@ class FullBlockTest(BitcoinTestFramework):
# Test a few invalid tx types
#
- # -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17)
+ # -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 ()
# \-> ??? (17)
#
@@ -784,35 +795,59 @@ class FullBlockTest(BitcoinTestFramework):
# reset to good chain
self.move_tip(57)
- b60 = self.next_block(60, spend=out[17])
+ b60 = self.next_block(60)
self.send_blocks([b60], True)
self.save_spendable_output()
- # Test BIP30
+ # Test BIP30 (reject duplicate)
#
- # -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17)
- # \-> b61 (18)
+ # -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 ()
+ # \-> b61 ()
#
# Blocks are not allowed to contain a transaction whose id matches that of an earlier,
# not-fully-spent transaction in the same chain. To test, make identical coinbases;
- # the second one should be rejected.
+ # the second one should be rejected. See also CVE-2012-1909.
#
self.log.info("Reject a block with a transaction with a duplicate hash of a previous transaction (BIP30)")
self.move_tip(60)
- b61 = self.next_block(61, spend=out[18])
- b61.vtx[0].vin[0].scriptSig = b60.vtx[0].vin[0].scriptSig # Equalize the coinbases
+ b61 = self.next_block(61)
+ b61.vtx[0].vin[0].scriptSig = DUPLICATE_COINBASE_SCRIPT_SIG
b61.vtx[0].rehash()
b61 = self.update_block(61, [])
- assert_equal(b60.vtx[0].serialize(), b61.vtx[0].serialize())
+ assert_equal(duplicate_tx.serialize(), b61.vtx[0].serialize())
self.send_blocks([b61], success=False, reject_reason='bad-txns-BIP30', reconnect=True)
+ # Test BIP30 (allow duplicate if spent)
+ #
+ # -> b57 (16) -> b60 ()
+ # \-> b_spend_dup_cb (b_dup_cb) -> b_dup_2 ()
+ #
+ self.move_tip(57)
+ b_spend_dup_cb = self.next_block('spend_dup_cb')
+ tx = CTransaction()
+ tx.vin.append(CTxIn(COutPoint(duplicate_tx.sha256, 0)))
+ tx.vout.append(CTxOut(0, CScript([OP_TRUE])))
+ self.sign_tx(tx, duplicate_tx)
+ tx.rehash()
+ b_spend_dup_cb = self.update_block('spend_dup_cb', [tx])
+
+ b_dup_2 = self.next_block('dup_2')
+ b_dup_2.vtx[0].vin[0].scriptSig = DUPLICATE_COINBASE_SCRIPT_SIG
+ b_dup_2.vtx[0].rehash()
+ b_dup_2 = self.update_block('dup_2', [])
+ assert_equal(duplicate_tx.serialize(), b_dup_2.vtx[0].serialize())
+ assert_equal(self.nodes[0].gettxout(txid=duplicate_tx.hash, n=0)['confirmations'], 119)
+ self.send_blocks([b_spend_dup_cb, b_dup_2], success=True)
+ # The duplicate has less confirmations
+ assert_equal(self.nodes[0].gettxout(txid=duplicate_tx.hash, n=0)['confirmations'], 1)
+
# Test tx.isFinal is properly rejected (not an exhaustive tx.isFinal test, that should be in data-driven transaction tests)
#
- # -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17)
- # \-> b62 (18)
+ # -> b_spend_dup_cb (b_dup_cb) -> b_dup_2 ()
+ # \-> b62 (18)
#
self.log.info("Reject a block with a transaction with a nonfinal locktime")
- self.move_tip(60)
+ self.move_tip('dup_2')
b62 = self.next_block(62)
tx = CTransaction()
tx.nLockTime = 0xffffffff # this locktime is non-final
@@ -825,11 +860,11 @@ class FullBlockTest(BitcoinTestFramework):
# Test a non-final coinbase is also rejected
#
- # -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17)
- # \-> b63 (-)
+ # -> b_spend_dup_cb (b_dup_cb) -> b_dup_2 ()
+ # \-> b63 (-)
#
self.log.info("Reject a block with a coinbase transaction with a nonfinal locktime")
- self.move_tip(60)
+ self.move_tip('dup_2')
b63 = self.next_block(63)
b63.vtx[0].nLockTime = 0xffffffff
b63.vtx[0].vin[0].nSequence = 0xDEADBEEF
@@ -845,14 +880,14 @@ class FullBlockTest(BitcoinTestFramework):
# What matters is that the receiving node should not reject the bloated block, and then reject the canonical
# block on the basis that it's the same as an already-rejected block (which would be a consensus failure.)
#
- # -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18)
- # \
- # b64a (18)
+ # -> b_spend_dup_cb (b_dup_cb) -> b_dup_2 () -> b64 (18)
+ # \
+ # b64a (18)
# b64a is a bloated block (non-canonical varint)
# b64 is a good block (same as b64 but w/ canonical varint)
#
self.log.info("Accept a valid block even if a bloated version of the block has previously been sent")
- self.move_tip(60)
+ self.move_tip('dup_2')
regular_block = self.next_block("64a", spend=out[18])
# make it a "broken_block," with non-canonical serialization
@@ -878,7 +913,7 @@ class FullBlockTest(BitcoinTestFramework):
node.disconnect_p2ps()
self.reconnect_p2p()
- self.move_tip(60)
+ self.move_tip('dup_2')
b64 = CBlock(b64a)
b64.vtx = copy.deepcopy(b64a.vtx)
assert_equal(b64.hash, b64a.hash)
@@ -890,7 +925,7 @@ class FullBlockTest(BitcoinTestFramework):
# Spend an output created in the block itself
#
- # -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19)
+ # -> b_dup_2 () -> b64 (18) -> b65 (19)
#
self.log.info("Accept a block with a transaction spending an output created in the same block")
self.move_tip(64)
@@ -903,8 +938,8 @@ class FullBlockTest(BitcoinTestFramework):
# Attempt to spend an output created later in the same block
#
- # -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19)
- # \-> b66 (20)
+ # -> b64 (18) -> b65 (19)
+ # \-> b66 (20)
self.log.info("Reject a block with a transaction spending an output created later in the same block")
self.move_tip(65)
b66 = self.next_block(66)
@@ -915,8 +950,8 @@ class FullBlockTest(BitcoinTestFramework):
# Attempt to double-spend a transaction created in a block
#
- # -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19)
- # \-> b67 (20)
+ # -> b64 (18) -> b65 (19)
+ # \-> b67 (20)
#
#
self.log.info("Reject a block with a transaction double spending a transaction created in the same block")
@@ -930,8 +965,8 @@ class FullBlockTest(BitcoinTestFramework):
# More tests of block subsidy
#
- # -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20)
- # \-> b68 (20)
+ # -> b64 (18) -> b65 (19) -> b69 (20)
+ # \-> b68 (20)
#
# b68 - coinbase with an extra 10 satoshis,
# creates a tx that has 9 satoshis from out[20] go to fees
@@ -957,8 +992,8 @@ class FullBlockTest(BitcoinTestFramework):
# Test spending the outpoint of a non-existent transaction
#
- # -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20)
- # \-> b70 (21)
+ # -> b65 (19) -> b69 (20)
+ # \-> b70 (21)
#
self.log.info("Reject a block containing a transaction spending from a non-existent input")
self.move_tip(69)
@@ -973,8 +1008,8 @@ class FullBlockTest(BitcoinTestFramework):
# Test accepting an invalid block which has the same hash as a valid one (via merkle tree tricks)
#
- # -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20) -> b72 (21)
- # \-> b71 (21)
+ # -> b65 (19) -> b69 (20) -> b72 (21)
+ # \-> b71 (21)
#
# b72 is a good block.
# b71 is a copy of 72, but re-adds one of its transactions. However, it has the same hash as b72.
@@ -1002,8 +1037,8 @@ class FullBlockTest(BitcoinTestFramework):
# Test some invalid scripts and MAX_BLOCK_SIGOPS
#
- # -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20) -> b72 (21)
- # \-> b** (22)
+ # -> b69 (20) -> b72 (21)
+ # \-> b** (22)
#
# b73 - tx with excessive sigops that are placed after an excessively large script element.
@@ -1226,7 +1261,7 @@ class FullBlockTest(BitcoinTestFramework):
self.save_spendable_output()
spend = self.get_spendable_output()
- self.send_blocks(blocks, True, timeout=480)
+ self.send_blocks(blocks, True, timeout=960)
chain1_tip = i
# now create alt chain of same length
@@ -1238,14 +1273,14 @@ class FullBlockTest(BitcoinTestFramework):
# extend alt chain to trigger re-org
block = self.next_block("alt" + str(chain1_tip + 1), version=4)
- self.send_blocks([block], True, timeout=480)
+ self.send_blocks([block], True, timeout=960)
# ... and re-org back to the first chain
self.move_tip(chain1_tip)
block = self.next_block(chain1_tip + 1, version=4)
self.send_blocks([block], False, force_send=True)
block = self.next_block(chain1_tip + 2, version=4)
- self.send_blocks([block], True, timeout=480)
+ self.send_blocks([block], True, timeout=960)
self.log.info("Reject a block with an invalid block header version")
b_v1 = self.next_block('b_v1', version=1)
diff --git a/test/functional/feature_blocksdir.py b/test/functional/feature_blocksdir.py
index 3a4889bbe9..6f01f97ea2 100755
--- a/test/functional/feature_blocksdir.py
+++ b/test/functional/feature_blocksdir.py
@@ -18,10 +18,10 @@ class BlocksdirTest(BitcoinTestFramework):
def run_test(self):
self.stop_node(0)
- assert os.path.isdir(os.path.join(self.nodes[0].datadir, "regtest", "blocks"))
+ assert os.path.isdir(os.path.join(self.nodes[0].datadir, self.chain, "blocks"))
assert not os.path.isdir(os.path.join(self.nodes[0].datadir, "blocks"))
shutil.rmtree(self.nodes[0].datadir)
- initialize_datadir(self.options.tmpdir, 0)
+ initialize_datadir(self.options.tmpdir, 0, self.chain)
self.log.info("Starting with nonexistent blocksdir ...")
blocksdir_path = os.path.join(self.options.tmpdir, 'blocksdir')
self.nodes[0].assert_start_raises_init_error(["-blocksdir=" + blocksdir_path], 'Error: Specified blocks directory "{}" does not exist.'.format(blocksdir_path))
@@ -30,8 +30,8 @@ class BlocksdirTest(BitcoinTestFramework):
self.start_node(0, ["-blocksdir=" + blocksdir_path])
self.log.info("mining blocks..")
self.nodes[0].generatetoaddress(10, self.nodes[0].get_deterministic_priv_key().address)
- assert os.path.isfile(os.path.join(blocksdir_path, "regtest", "blocks", "blk00000.dat"))
- assert os.path.isdir(os.path.join(self.nodes[0].datadir, "regtest", "blocks", "index"))
+ assert os.path.isfile(os.path.join(blocksdir_path, self.chain, "blocks", "blk00000.dat"))
+ assert os.path.isdir(os.path.join(self.nodes[0].datadir, self.chain, "blocks", "index"))
if __name__ == '__main__':
diff --git a/test/functional/feature_cltv.py b/test/functional/feature_cltv.py
index af34f9f0db..e00219ca4a 100755
--- a/test/functional/feature_cltv.py
+++ b/test/functional/feature_cltv.py
@@ -69,14 +69,11 @@ class BIP65Test(BitcoinTestFramework):
self.skip_if_no_wallet()
def test_cltv_info(self, *, is_active):
- assert_equal(
- next(s for s in self.nodes[0].getblockchaininfo()['softforks'] if s['id'] == 'bip65'),
+ assert_equal(self.nodes[0].getblockchaininfo()['softforks']['bip65'],
{
- "id": "bip65",
- "version": 4,
- "reject": {
- "status": is_active
- }
+ "active": is_active,
+ "height": CLTV_HEIGHT,
+ "type": "buried",
},
)
@@ -104,9 +101,9 @@ class BIP65Test(BitcoinTestFramework):
block.hashMerkleRoot = block.calc_merkle_root()
block.solve()
- self.test_cltv_info(is_active=False)
+ self.test_cltv_info(is_active=False) # Not active as of current tip and next block does not need to obey rules
self.nodes[0].p2p.send_and_ping(msg_block(block))
- self.test_cltv_info(is_active=False) # Not active as of current tip, but next block must obey rules
+ self.test_cltv_info(is_active=True) # Not active as of current tip, but next block must obey rules
assert_equal(self.nodes[0].getbestblockhash(), block.hash)
self.log.info("Test that blocks must now be at least version 4")
@@ -155,7 +152,7 @@ class BIP65Test(BitcoinTestFramework):
block.hashMerkleRoot = block.calc_merkle_root()
block.solve()
- self.test_cltv_info(is_active=False) # Not active as of current tip, but next block must obey rules
+ self.test_cltv_info(is_active=True) # Not active as of current tip, but next block must obey rules
self.nodes[0].p2p.send_and_ping(msg_block(block))
self.test_cltv_info(is_active=True) # Active as of current tip
assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
diff --git a/test/functional/feature_config_args.py b/test/functional/feature_config_args.py
index aae262344d..b997c76025 100755
--- a/test/functional/feature_config_args.py
+++ b/test/functional/feature_config_args.py
@@ -35,9 +35,10 @@ class ConfArgsTest(BitcoinTestFramework):
conf.write('-dash=1\n')
self.nodes[0].assert_start_raises_init_error(expected_msg='Error: Error reading configuration file: parse error on line 1: -dash=1, options in configuration file must be specified without leading -')
- with open(inc_conf_file_path, 'w', encoding='utf8') as conf:
- conf.write("wallet=foo\n")
- self.nodes[0].assert_start_raises_init_error(expected_msg='Error: Config setting for -wallet only applied on regtest network when in [regtest] section.')
+ if self.is_wallet_compiled():
+ with open(inc_conf_file_path, 'w', encoding='utf8') as conf:
+ conf.write("wallet=foo\n")
+ self.nodes[0].assert_start_raises_init_error(expected_msg='Error: Config setting for -wallet only applied on regtest network when in [regtest] section.')
with open(inc_conf_file_path, 'w', encoding='utf-8') as conf:
conf.write('regtest=0\n') # mainnet
@@ -108,17 +109,15 @@ class ConfArgsTest(BitcoinTestFramework):
f.write("datadir=" + new_data_dir + "\n")
f.write(conf_file_contents)
- # Temporarily disabled, because this test would access the user's home dir (~/.bitcoin)
- #self.nodes[0].assert_start_raises_init_error(['-conf=' + conf_file], 'Error reading configuration file: specified data directory "' + new_data_dir + '" does not exist.')
+ self.nodes[0].assert_start_raises_init_error(['-conf=' + conf_file], 'Error: Error reading configuration file: specified data directory "' + new_data_dir + '" does not exist.')
# Create the directory and ensure the config file now works
os.mkdir(new_data_dir)
- # Temporarily disabled, because this test would access the user's home dir (~/.bitcoin)
- #self.start_node(0, ['-conf='+conf_file, '-wallet=w1'])
- #self.stop_node(0)
- #assert os.path.exists(os.path.join(new_data_dir, 'regtest', 'blocks'))
- #if self.is_wallet_compiled():
- #assert os.path.exists(os.path.join(new_data_dir, 'regtest', 'wallets', 'w1'))
+ self.start_node(0, ['-conf='+conf_file, '-wallet=w1'])
+ self.stop_node(0)
+ assert os.path.exists(os.path.join(new_data_dir, 'regtest', 'blocks'))
+ if self.is_wallet_compiled():
+ assert os.path.exists(os.path.join(new_data_dir, 'regtest', 'wallets', 'w1'))
# Ensure command line argument overrides datadir in conf
os.mkdir(new_data_dir_2)
diff --git a/test/functional/feature_csv_activation.py b/test/functional/feature_csv_activation.py
index 887e9dafa3..6bd321992a 100755
--- a/test/functional/feature_csv_activation.py
+++ b/test/functional/feature_csv_activation.py
@@ -2,23 +2,17 @@
# Copyright (c) 2015-2019 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 activation of the first version bits soft fork.
+"""Test CSV soft fork activation.
This soft fork will activate the following BIPS:
BIP 68 - nSequence relative lock times
BIP 112 - CHECKSEQUENCEVERIFY
BIP 113 - MedianTimePast semantics for nLockTime
-regtest lock-in with 108/144 block signalling
-activation after a further 144 blocks
-
mine 82 blocks whose coinbases will be used to generate inputs for our tests
-mine 61 blocks to transition from DEFINED to STARTED
-mine 144 blocks only 100 of which are signaling readiness in order to fail to change state this period
-mine 144 blocks with 108 signaling and verify STARTED->LOCKED_IN
-mine 140 blocks and seed block chain with the 82 inputs will use for our tests at height 572
-mine 3 blocks and verify still at LOCKED_IN and test that enforcement has not triggered
-mine 1 block and test that enforcement has triggered (which triggers ACTIVE)
+mine 345 blocks and seed block chain with the 82 inputs will use for our tests at height 427
+mine 2 blocks and verify soft fork not yet activated
+mine 1 block and test that soft fork is activated (rules enforced for next block)
Test BIP 113 is enforced
Mine 4 blocks so next height is 580 and test BIP 68 is enforced for time and height
Mine 1 block so next height is 581 and test BIP 68 now passes time but not height
@@ -58,11 +52,12 @@ from test_framework.script import (
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
- get_bip9_status,
hex_str_to_bytes,
+ softfork_active,
)
BASE_RELATIVE_LOCKTIME = 10
+CSV_ACTIVATION_HEIGHT = 432
SEQ_DISABLE_FLAG = 1 << 31
SEQ_RANDOM_HIGH_BIT = 1 << 25
SEQ_TYPE_FLAG = 1 << 22
@@ -148,20 +143,19 @@ class BIP68_112_113Test(BitcoinTestFramework):
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
- def generate_blocks(self, number, version, test_blocks=None):
- if test_blocks is None:
- test_blocks = []
+ def generate_blocks(self, number):
+ test_blocks = []
for i in range(number):
- block = self.create_test_block([], version)
+ block = self.create_test_block([])
test_blocks.append(block)
self.last_block_time += 600
self.tip = block.sha256
self.tipheight += 1
return test_blocks
- def create_test_block(self, txs, version=536870912):
+ def create_test_block(self, txs):
block = create_block(self.tip, create_coinbase(self.tipheight + 1), self.last_block_time + 600)
- block.nVersion = version
+ block.nVersion = 4
block.vtx.extend(txs)
block.hashMerkleRoot = block.calc_merkle_root()
block.rehash()
@@ -187,45 +181,14 @@ class BIP68_112_113Test(BitcoinTestFramework):
self.tip = int(self.nodes[0].getbestblockhash(), 16)
self.nodeaddress = self.nodes[0].getnewaddress()
- self.log.info("Test that the csv softfork is DEFINED")
- assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'defined')
- test_blocks = self.generate_blocks(61, 4)
- self.send_blocks(test_blocks)
-
- self.log.info("Advance from DEFINED to STARTED, height = 143")
- assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'started')
-
- self.log.info("Fail to achieve LOCKED_IN")
- # 100 out of 144 signal bit 0. Use a variety of bits to simulate multiple parallel softforks
-
- test_blocks = self.generate_blocks(50, 536870913) # 0x20000001 (signalling ready)
- test_blocks = self.generate_blocks(20, 4, test_blocks) # 0x00000004 (signalling not)
- test_blocks = self.generate_blocks(50, 536871169, test_blocks) # 0x20000101 (signalling ready)
- test_blocks = self.generate_blocks(24, 536936448, test_blocks) # 0x20010000 (signalling not)
- self.send_blocks(test_blocks)
-
- self.log.info("Failed to advance past STARTED, height = 287")
- assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'started')
-
- self.log.info("Generate blocks to achieve LOCK-IN")
- # 108 out of 144 signal bit 0 to achieve lock-in
- # using a variety of bits to simulate multiple parallel softforks
- test_blocks = self.generate_blocks(58, 536870913) # 0x20000001 (signalling ready)
- test_blocks = self.generate_blocks(26, 4, test_blocks) # 0x00000004 (signalling not)
- test_blocks = self.generate_blocks(50, 536871169, test_blocks) # 0x20000101 (signalling ready)
- test_blocks = self.generate_blocks(10, 536936448, test_blocks) # 0x20010000 (signalling not)
- self.send_blocks(test_blocks)
-
- self.log.info("Advanced from STARTED to LOCKED_IN, height = 431")
- assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'locked_in')
-
- # Generate 140 more version 4 blocks
- test_blocks = self.generate_blocks(140, 4)
+ # Activation height is hardcoded
+ test_blocks = self.generate_blocks(345)
self.send_blocks(test_blocks)
+ assert not softfork_active(self.nodes[0], 'csv')
- # Inputs at height = 572
+ # Inputs at height = 431
#
- # Put inputs for all tests in the chain at height 572 (tip now = 571) (time increases by 600s per block)
+ # Put inputs for all tests in the chain at height 431 (tip now = 430) (time increases by 600s per block)
# Note we reuse inputs for v1 and v2 txs so must test these separately
# 16 normal inputs
bip68inputs = []
@@ -255,7 +218,7 @@ class BIP68_112_113Test(BitcoinTestFramework):
bip113input = send_generic_input_tx(self.nodes[0], self.coinbase_blocks, self.nodeaddress)
self.nodes[0].setmocktime(self.last_block_time + 600)
- inputblockhash = self.nodes[0].generate(1)[0] # 1 block generated for inputs to be in chain at height 572
+ inputblockhash = self.nodes[0].generate(1)[0] # 1 block generated for inputs to be in chain at height 431
self.nodes[0].setmocktime(0)
self.tip = int(inputblockhash, 16)
self.tipheight += 1
@@ -263,11 +226,12 @@ class BIP68_112_113Test(BitcoinTestFramework):
assert_equal(len(self.nodes[0].getblock(inputblockhash, True)["tx"]), 82 + 1)
# 2 more version 4 blocks
- test_blocks = self.generate_blocks(2, 4)
+ test_blocks = self.generate_blocks(2)
self.send_blocks(test_blocks)
- self.log.info("Not yet advanced to ACTIVE, height = 574 (will activate for block 576, not 575)")
- assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'locked_in')
+ assert_equal(self.tipheight, CSV_ACTIVATION_HEIGHT - 2)
+ self.log.info("Height = {}, CSV not yet active (will activate for block {}, not {})".format(self.tipheight, CSV_ACTIVATION_HEIGHT, CSV_ACTIVATION_HEIGHT - 1))
+ assert not softfork_active(self.nodes[0], 'csv')
# Test both version 1 and version 2 transactions for all tests
# BIP113 test transaction will be modified before each use to put in appropriate block time
@@ -340,10 +304,11 @@ class BIP68_112_113Test(BitcoinTestFramework):
self.send_blocks([self.create_test_block(success_txs)])
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
- # 1 more version 4 block to get us to height 575 so the fork should now be active for the next block
- test_blocks = self.generate_blocks(1, 4)
+ # 1 more version 4 block to get us to height 432 so the fork should now be active for the next block
+ assert not softfork_active(self.nodes[0], 'csv')
+ test_blocks = self.generate_blocks(1)
self.send_blocks(test_blocks)
- assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'active')
+ assert softfork_active(self.nodes[0], 'csv')
self.log.info("Post-Soft Fork Tests.")
@@ -364,8 +329,8 @@ class BIP68_112_113Test(BitcoinTestFramework):
self.send_blocks([self.create_test_block([bip113tx])])
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
- # Next block height = 580 after 4 blocks of random version
- test_blocks = self.generate_blocks(4, 1234)
+ # Next block height = 437 after 4 blocks of random version
+ test_blocks = self.generate_blocks(4)
self.send_blocks(test_blocks)
self.log.info("BIP 68 tests")
@@ -392,8 +357,8 @@ class BIP68_112_113Test(BitcoinTestFramework):
for tx in bip68heighttxs:
self.send_blocks([self.create_test_block([tx])], success=False)
- # Advance one block to 581
- test_blocks = self.generate_blocks(1, 1234)
+ # Advance one block to 438
+ test_blocks = self.generate_blocks(1)
self.send_blocks(test_blocks)
# Height txs should fail and time txs should now pass 9 * 600 > 10 * 512
@@ -403,8 +368,8 @@ class BIP68_112_113Test(BitcoinTestFramework):
for tx in bip68heighttxs:
self.send_blocks([self.create_test_block([tx])], success=False)
- # Advance one block to 582
- test_blocks = self.generate_blocks(1, 1234)
+ # Advance one block to 439
+ test_blocks = self.generate_blocks(1)
self.send_blocks(test_blocks)
# All BIP 68 txs should pass
diff --git a/test/functional/feature_dbcrash.py b/test/functional/feature_dbcrash.py
index f227a06ebe..6bd6bb5b8c 100755
--- a/test/functional/feature_dbcrash.py
+++ b/test/functional/feature_dbcrash.py
@@ -30,17 +30,27 @@ import http.client
import random
import time
-from test_framework.messages import COIN, COutPoint, CTransaction, CTxIn, CTxOut, ToHex
+from test_framework.messages import (
+ COIN,
+ COutPoint,
+ CTransaction,
+ CTxIn,
+ CTxOut,
+ ToHex,
+)
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, create_confirmed_utxos, hex_str_to_bytes
+from test_framework.util import (
+ assert_equal,
+ create_confirmed_utxos,
+ hex_str_to_bytes,
+)
class ChainstateWriteCrashTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 4
self.setup_clean_chain = False
- # Need a bit of extra time for the nodes to start up for this test
- self.rpc_timeout = 90
+ self.rpc_timeout = 480
# Set -maxmempool=0 to turn off mempool memory sharing with dbcache
# Set -rpcservertimeout=900 to reduce socket disconnects in this
@@ -48,13 +58,14 @@ class ChainstateWriteCrashTest(BitcoinTestFramework):
self.base_args = ["-limitdescendantsize=0", "-maxmempool=0", "-rpcservertimeout=900", "-dbbatchsize=200000"]
# Set different crash ratios and cache sizes. Note that not all of
- # -dbcache goes to pcoinsTip.
+ # -dbcache goes to the in-memory coins cache.
self.node0_args = ["-dbcrashratio=8", "-dbcache=4"] + self.base_args
self.node1_args = ["-dbcrashratio=16", "-dbcache=8"] + self.base_args
self.node2_args = ["-dbcrashratio=24", "-dbcache=16"] + self.base_args
# Node3 is a normal node with default args, except will mine full blocks
- self.node3_args = ["-blockmaxweight=4000000"]
+ # and non-standard txs (e.g. txs with "dust" outputs)
+ self.node3_args = ["-blockmaxweight=4000000", "-acceptnonstdtxn"]
self.extra_args = [self.node0_args, self.node1_args, self.node2_args, self.node3_args]
def skip_test_if_missing_module(self):
diff --git a/test/functional/feature_dersig.py b/test/functional/feature_dersig.py
index 067e3be1f4..1bd9586364 100755
--- a/test/functional/feature_dersig.py
+++ b/test/functional/feature_dersig.py
@@ -52,14 +52,11 @@ class BIP66Test(BitcoinTestFramework):
self.skip_if_no_wallet()
def test_dersig_info(self, *, is_active):
- assert_equal(
- next(s for s in self.nodes[0].getblockchaininfo()['softforks'] if s['id'] == 'bip66'),
+ assert_equal(self.nodes[0].getblockchaininfo()['softforks']['bip66'],
{
- "id": "bip66",
- "version": 3,
- "reject": {
- "status": is_active
- }
+ "active": is_active,
+ "height": DERSIG_HEIGHT,
+ "type": "buried",
},
)
@@ -88,9 +85,9 @@ class BIP66Test(BitcoinTestFramework):
block.rehash()
block.solve()
- self.test_dersig_info(is_active=False)
+ self.test_dersig_info(is_active=False) # Not active as of current tip and next block does not need to obey rules
self.nodes[0].p2p.send_and_ping(msg_block(block))
- self.test_dersig_info(is_active=False) # Not active as of current tip, but next block must obey rules
+ self.test_dersig_info(is_active=True) # Not active as of current tip, but next block must obey rules
assert_equal(self.nodes[0].getbestblockhash(), block.hash)
self.log.info("Test that blocks must now be at least version 3")
@@ -144,7 +141,7 @@ class BIP66Test(BitcoinTestFramework):
block.rehash()
block.solve()
- self.test_dersig_info(is_active=False) # Not active as of current tip, but next block must obey rules
+ self.test_dersig_info(is_active=True) # Not active as of current tip, but next block must obey rules
self.nodes[0].p2p.send_and_ping(msg_block(block))
self.test_dersig_info(is_active=True) # Active as of current tip
assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
diff --git a/test/functional/feature_fee_estimation.py b/test/functional/feature_fee_estimation.py
index a4b9f213a1..d2d41b1206 100755
--- a/test/functional/feature_fee_estimation.py
+++ b/test/functional/feature_fee_estimation.py
@@ -28,6 +28,7 @@ P2SH_2 = CScript([OP_HASH160, hash160(REDEEM_SCRIPT_2), OP_EQUAL])
# Associated ScriptSig's to spend satisfy P2SH_1 and P2SH_2
SCRIPT_SIG = [CScript([OP_TRUE, REDEEM_SCRIPT_1]), CScript([OP_TRUE, REDEEM_SCRIPT_2])]
+
def small_txpuzzle_randfee(from_node, conflist, unconflist, amount, min_fee, fee_increment):
"""Create and send a transaction with a random fee.
@@ -69,6 +70,7 @@ def small_txpuzzle_randfee(from_node, conflist, unconflist, amount, min_fee, fee
return (ToHex(tx), fee)
+
def split_inputs(from_node, txins, txouts, initial_split=False):
"""Generate a lot of inputs so we can generate a ton of transactions.
@@ -97,6 +99,7 @@ def split_inputs(from_node, txins, txouts, initial_split=False):
txouts.append({"txid": txid, "vout": 0, "amount": half_change})
txouts.append({"txid": txid, "vout": 1, "amount": rem_change})
+
def check_estimates(node, fees_seen):
"""Call estimatesmartfee and verify that the estimates meet certain invariants."""
@@ -120,9 +123,17 @@ def check_estimates(node, fees_seen):
else:
assert_greater_than_or_equal(i + 1, e["blocks"])
+
class EstimateFeeTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 3
+ # mine non-standard txs (e.g. txs with "dust" outputs)
+ # Force fSendTrickle to true (via whitelist)
+ self.extra_args = [
+ ["-acceptnonstdtxn", "-whitelist=127.0.0.1"],
+ ["-acceptnonstdtxn", "-whitelist=127.0.0.1", "-blockmaxweight=68000"],
+ ["-acceptnonstdtxn", "-whitelist=127.0.0.1", "-blockmaxweight=32000"],
+ ]
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
@@ -133,9 +144,7 @@ class EstimateFeeTest(BitcoinTestFramework):
But first we need to use one node to create a lot of outputs
which we will use to generate our transactions.
"""
- self.add_nodes(3, extra_args=[["-maxorphantx=1000", "-whitelist=127.0.0.1"],
- ["-blockmaxweight=68000", "-maxorphantx=1000"],
- ["-blockmaxweight=32000", "-maxorphantx=1000"]])
+ self.add_nodes(3, extra_args=self.extra_args)
# Use node0 to mine blocks for input splitting
# Node1 mines small blocks but that are bigger than the expected transaction rate.
# NOTE: the CreateNewBlock code starts counting block weight at 4,000 weight,
@@ -160,9 +169,9 @@ class EstimateFeeTest(BitcoinTestFramework):
self.memutxo, Decimal("0.005"), min_fee, min_fee)
tx_kbytes = (len(txhex) // 2) / 1000.0
self.fees_per_kb.append(float(fee) / tx_kbytes)
- self.sync_mempools(self.nodes[0:3], wait=.1)
+ self.sync_mempools(wait=.1)
mined = mining_node.getblock(mining_node.generate(1)[0], True)["tx"]
- self.sync_blocks(self.nodes[0:3], wait=.1)
+ self.sync_blocks(wait=.1)
# update which txouts are confirmed
newmem = []
for utx in self.memutxo:
@@ -184,22 +193,22 @@ class EstimateFeeTest(BitcoinTestFramework):
split_inputs(self.nodes[0], self.nodes[0].listunspent(0), self.txouts, True)
# Mine
- while (len(self.nodes[0].getrawmempool()) > 0):
+ while len(self.nodes[0].getrawmempool()) > 0:
self.nodes[0].generate(1)
# Repeatedly split those 2 outputs, doubling twice for each rep
# Use txouts to monitor the available utxo, since these won't be tracked in wallet
reps = 0
- while (reps < 5):
+ while reps < 5:
# Double txouts to txouts2
- while (len(self.txouts) > 0):
+ while len(self.txouts) > 0:
split_inputs(self.nodes[0], self.txouts, self.txouts2)
- while (len(self.nodes[0].getrawmempool()) > 0):
+ while len(self.nodes[0].getrawmempool()) > 0:
self.nodes[0].generate(1)
# Double txouts2 to txouts
- while (len(self.txouts2) > 0):
+ while len(self.txouts2) > 0:
split_inputs(self.nodes[0], self.txouts2, self.txouts)
- while (len(self.nodes[0].getrawmempool()) > 0):
+ while len(self.nodes[0].getrawmempool()) > 0:
self.nodes[0].generate(1)
reps += 1
self.log.info("Finished splitting")
@@ -239,5 +248,6 @@ class EstimateFeeTest(BitcoinTestFramework):
self.log.info("Final estimates after emptying mempools")
check_estimates(self.nodes[1], self.fees_per_kb)
+
if __name__ == '__main__':
EstimateFeeTest().main()
diff --git a/test/functional/feature_logging.py b/test/functional/feature_logging.py
index 8bb7e02695..e6ff21ee9c 100755
--- a/test/functional/feature_logging.py
+++ b/test/functional/feature_logging.py
@@ -36,7 +36,7 @@ class LoggingTest(BitcoinTestFramework):
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+$"
+ exp_stderr = r"Error: Could not open debug log file \S+$"
self.nodes[0].assert_start_raises_init_error(["-debuglogfile=%s" % (invalidname)], exp_stderr, match=ErrorMatch.FULL_REGEX)
assert not os.path.isfile(os.path.join(invdir, "foo.log"))
diff --git a/test/functional/feature_nulldummy.py b/test/functional/feature_nulldummy.py
index 60a703c48f..250dee1528 100755
--- a/test/functional/feature_nulldummy.py
+++ b/test/functional/feature_nulldummy.py
@@ -41,7 +41,7 @@ class NULLDUMMYTest(BitcoinTestFramework):
self.setup_clean_chain = True
# This script tests NULLDUMMY activation, which is part of the 'segwit' deployment, so we go through
# normal segwit activation here (and don't use the default always-on behaviour).
- self.extra_args = [['-whitelist=127.0.0.1', '-vbparams=segwit:0:999999999999', '-addresstype=legacy']]
+ self.extra_args = [['-whitelist=127.0.0.1', '-segwitheight=432', '-addresstype=legacy']]
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
diff --git a/test/functional/feature_pruning.py b/test/functional/feature_pruning.py
index 66c395d7a2..51523f13e7 100755
--- a/test/functional/feature_pruning.py
+++ b/test/functional/feature_pruning.py
@@ -92,6 +92,7 @@ class PruneTest(BitcoinTestFramework):
["-maxreceivebuffer=20000"],
["-prune=550"],
]
+ self.rpc_timeout = 120
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
@@ -204,6 +205,7 @@ class PruneTest(BitcoinTestFramework):
self.log.info("Mine 220 more large blocks so we have requisite history")
mine_large_blocks(self.nodes[0], 220)
+ self.sync_blocks(self.nodes[0:3], timeout=120)
usage = calc_usage(self.prunedir)
self.log.info("Usage should be below target: %d" % usage)
diff --git a/test/functional/feature_segwit.py b/test/functional/feature_segwit.py
index a71d4071d5..b9db618575 100755
--- a/test/functional/feature_segwit.py
+++ b/test/functional/feature_segwit.py
@@ -55,20 +55,20 @@ class SegWitTest(BitcoinTestFramework):
[
"-acceptnonstdtxn=1",
"-rpcserialversion=0",
- "-vbparams=segwit:0:999999999999",
+ "-segwitheight=432",
"-addresstype=legacy",
],
[
"-acceptnonstdtxn=1",
"-blockversion=4",
"-rpcserialversion=1",
- "-vbparams=segwit:0:999999999999",
+ "-segwitheight=432",
"-addresstype=legacy",
],
[
"-acceptnonstdtxn=1",
"-blockversion=536870915",
- "-vbparams=segwit:0:999999999999",
+ "-segwitheight=432",
"-addresstype=legacy",
],
]
@@ -226,6 +226,16 @@ class SegWitTest(BitcoinTestFramework):
assert tx.wit.is_null() # This should not be a segwit input
assert txid1 in self.nodes[0].getrawmempool()
+ tx1_hex = self.nodes[0].gettransaction(txid1)['hex']
+ tx1 = FromHex(CTransaction(), tx1_hex)
+
+ # Check that wtxid is properly reported in mempool entry (txid1)
+ assert_equal(int(self.nodes[0].getmempoolentry(txid1)["wtxid"], 16), tx1.calc_sha256(True))
+
+ # Check that weight and vsize are properly reported in mempool entry (txid1)
+ assert_equal(self.nodes[0].getmempoolentry(txid1)["vsize"], (self.nodes[0].getmempoolentry(txid1)["weight"] + 3) // 4)
+ assert_equal(self.nodes[0].getmempoolentry(txid1)["weight"], len(tx1.serialize_without_witness())*3 + len(tx1.serialize_with_witness()))
+
# Now create tx2, which will spend from txid1.
tx = CTransaction()
tx.vin.append(CTxIn(COutPoint(int(txid1, 16), 0), b''))
@@ -235,6 +245,13 @@ class SegWitTest(BitcoinTestFramework):
tx = FromHex(CTransaction(), tx2_hex)
assert not tx.wit.is_null()
+ # Check that wtxid is properly reported in mempool entry (txid2)
+ assert_equal(int(self.nodes[0].getmempoolentry(txid2)["wtxid"], 16), tx.calc_sha256(True))
+
+ # Check that weight and vsize are properly reported in mempool entry (txid2)
+ assert_equal(self.nodes[0].getmempoolentry(txid2)["vsize"], (self.nodes[0].getmempoolentry(txid2)["weight"] + 3) // 4)
+ assert_equal(self.nodes[0].getmempoolentry(txid2)["weight"], len(tx.serialize_without_witness())*3 + len(tx.serialize_with_witness()))
+
# Now create tx3, which will spend from txid2
tx = CTransaction()
tx.vin.append(CTxIn(COutPoint(int(txid2, 16), 0), b""))
@@ -251,9 +268,13 @@ class SegWitTest(BitcoinTestFramework):
assert txid2 in template_txids
assert txid3 in template_txids
- # Check that wtxid is properly reported in mempool entry
+ # Check that wtxid is properly reported in mempool entry (txid3)
assert_equal(int(self.nodes[0].getmempoolentry(txid3)["wtxid"], 16), tx.calc_sha256(True))
+ # Check that weight and vsize are properly reported in mempool entry (txid3)
+ assert_equal(self.nodes[0].getmempoolentry(txid3)["vsize"], (self.nodes[0].getmempoolentry(txid3)["weight"] + 3) // 4)
+ assert_equal(self.nodes[0].getmempoolentry(txid3)["weight"], len(tx.serialize_without_witness())*3 + len(tx.serialize_with_witness()))
+
# Mine a block to clear the gbt cache again.
self.nodes[0].generate(1)
diff --git a/test/functional/feature_uacomment.py b/test/functional/feature_uacomment.py
index fb4ad21359..85c250173f 100755
--- a/test/functional/feature_uacomment.py
+++ b/test/functional/feature_uacomment.py
@@ -27,12 +27,12 @@ class UacommentTest(BitcoinTestFramework):
self.log.info("test -uacomment max length")
self.stop_node(0)
- expected = "Error: Total length of network version string \([0-9]+\) exceeds maximum length \(256\). Reduce the number or size of uacomments."
+ expected = r"Error: Total length of network version string \([0-9]+\) exceeds maximum length \(256\). Reduce the number or size of uacomments."
self.nodes[0].assert_start_raises_init_error(["-uacomment=" + 'a' * 256], expected, match=ErrorMatch.FULL_REGEX)
self.log.info("test -uacomment unsafe characters")
for unsafe_char in ['/', ':', '(', ')', '₿', '🏃']:
- expected = "Error: User Agent comment \(" + re.escape(unsafe_char) + "\) contains unsafe characters."
+ expected = r"Error: User Agent comment \(" + re.escape(unsafe_char) + r"\) contains unsafe characters."
self.nodes[0].assert_start_raises_init_error(["-uacomment=" + unsafe_char], expected, match=ErrorMatch.FULL_REGEX)
diff --git a/test/functional/interface_bitcoin_cli.py b/test/functional/interface_bitcoin_cli.py
index 7bb7044cc0..0a378c5ef5 100755
--- a/test/functional/interface_bitcoin_cli.py
+++ b/test/functional/interface_bitcoin_cli.py
@@ -29,7 +29,7 @@ class TestBitcoinCli(BitcoinTestFramework):
rpc_response = self.nodes[0].getblockchaininfo()
assert_equal(cli_response, rpc_response)
- user, password = get_auth_cookie(self.nodes[0].datadir)
+ user, password = get_auth_cookie(self.nodes[0].datadir, self.chain)
self.log.info("Test -stdinrpcpass option")
assert_equal(0, self.nodes[0].cli('-rpcuser=%s' % user, '-stdinrpcpass', input=password).getblockcount())
diff --git a/test/functional/interface_zmq.py b/test/functional/interface_zmq.py
index 8e58c85c15..5aea10fbce 100755
--- a/test/functional/interface_zmq.py
+++ b/test/functional/interface_zmq.py
@@ -7,14 +7,13 @@ import struct
from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.messages import CTransaction
-from test_framework.util import (
- assert_equal,
- hash256,
-)
+from test_framework.messages import CTransaction, hash256
+from test_framework.util import assert_equal, connect_nodes
from io import BytesIO
+from time import sleep
-ADDRESS = "tcp://127.0.0.1:28332"
+def hash256_reversed(byte_str):
+ return hash256(byte_str)[::-1]
class ZMQSubscriber:
def __init__(self, socket, topic):
@@ -43,67 +42,65 @@ class ZMQTest (BitcoinTestFramework):
self.skip_if_no_py3_zmq()
self.skip_if_no_bitcoind_zmq()
- def setup_nodes(self):
+ def run_test(self):
import zmq
+ self.ctx = zmq.Context()
+ try:
+ self.test_basic()
+ self.test_reorg()
+ finally:
+ # Destroy the ZMQ context.
+ self.log.debug("Destroying ZMQ context")
+ self.ctx.destroy(linger=None)
- # Initialize ZMQ context and socket.
+ def test_basic(self):
# All messages are received in the same socket which means
# that this test fails if the publishing order changes.
# Note that the publishing order is not defined in the documentation and
# is subject to change.
- self.zmq_context = zmq.Context()
- socket = self.zmq_context.socket(zmq.SUB)
+ import zmq
+ address = 'tcp://127.0.0.1:28332'
+ socket = self.ctx.socket(zmq.SUB)
socket.set(zmq.RCVTIMEO, 60000)
- socket.connect(ADDRESS)
# Subscribe to all available topics.
- self.hashblock = ZMQSubscriber(socket, b"hashblock")
- self.hashtx = ZMQSubscriber(socket, b"hashtx")
- self.rawblock = ZMQSubscriber(socket, b"rawblock")
- self.rawtx = ZMQSubscriber(socket, b"rawtx")
-
- self.extra_args = [
- ["-zmqpub%s=%s" % (sub.topic.decode(), ADDRESS) for sub in [self.hashblock, self.hashtx, self.rawblock, self.rawtx]],
- [],
- ]
- self.add_nodes(self.num_nodes, self.extra_args)
- self.start_nodes()
- self.import_deterministic_coinbase_privkeys()
+ hashblock = ZMQSubscriber(socket, b"hashblock")
+ hashtx = ZMQSubscriber(socket, b"hashtx")
+ rawblock = ZMQSubscriber(socket, b"rawblock")
+ rawtx = ZMQSubscriber(socket, b"rawtx")
- def run_test(self):
- try:
- self._zmq_test()
- finally:
- # Destroy the ZMQ context.
- self.log.debug("Destroying ZMQ context")
- self.zmq_context.destroy(linger=None)
+ self.restart_node(0, ["-zmqpub%s=%s" % (sub.topic.decode(), address) for sub in [hashblock, hashtx, rawblock, rawtx]])
+ connect_nodes(self.nodes[0], 1)
+ socket.connect(address)
+ # Relax so that the subscriber is ready before publishing zmq messages
+ sleep(0.2)
- def _zmq_test(self):
num_blocks = 5
self.log.info("Generate %(n)d blocks (and %(n)d coinbase txes)" % {"n": num_blocks})
genhashes = self.nodes[0].generatetoaddress(num_blocks, ADDRESS_BCRT1_UNSPENDABLE)
+
self.sync_all()
for x in range(num_blocks):
# Should receive the coinbase txid.
- txid = self.hashtx.receive()
+ txid = hashtx.receive()
# Should receive the coinbase raw transaction.
- hex = self.rawtx.receive()
+ hex = rawtx.receive()
tx = CTransaction()
tx.deserialize(BytesIO(hex))
tx.calc_sha256()
assert_equal(tx.hash, txid.hex())
# Should receive the generated block hash.
- hash = self.hashblock.receive().hex()
+ hash = hashblock.receive().hex()
assert_equal(genhashes[x], hash)
# The block should only have the coinbase txid.
assert_equal([txid.hex()], self.nodes[1].getblock(hash)["tx"])
# Should receive the generated raw block.
- block = self.rawblock.receive()
- assert_equal(genhashes[x], hash256(block[:80]).hex())
+ block = rawblock.receive()
+ assert_equal(genhashes[x], hash256_reversed(block[:80]).hex())
if self.is_wallet_compiled():
self.log.info("Wait for tx from second node")
@@ -111,23 +108,49 @@ class ZMQTest (BitcoinTestFramework):
self.sync_all()
# Should receive the broadcasted txid.
- txid = self.hashtx.receive()
+ txid = hashtx.receive()
assert_equal(payment_txid, txid.hex())
# Should receive the broadcasted raw transaction.
- hex = self.rawtx.receive()
- assert_equal(payment_txid, hash256(hex).hex())
+ hex = rawtx.receive()
+ assert_equal(payment_txid, hash256_reversed(hex).hex())
self.log.info("Test the getzmqnotifications RPC")
assert_equal(self.nodes[0].getzmqnotifications(), [
- {"type": "pubhashblock", "address": ADDRESS, "hwm": 1000},
- {"type": "pubhashtx", "address": ADDRESS, "hwm": 1000},
- {"type": "pubrawblock", "address": ADDRESS, "hwm": 1000},
- {"type": "pubrawtx", "address": ADDRESS, "hwm": 1000},
+ {"type": "pubhashblock", "address": address, "hwm": 1000},
+ {"type": "pubhashtx", "address": address, "hwm": 1000},
+ {"type": "pubrawblock", "address": address, "hwm": 1000},
+ {"type": "pubrawtx", "address": address, "hwm": 1000},
])
assert_equal(self.nodes[1].getzmqnotifications(), [])
+ def test_reorg(self):
+ import zmq
+ address = 'tcp://127.0.0.1:28333'
+ socket = self.ctx.socket(zmq.SUB)
+ socket.set(zmq.RCVTIMEO, 60000)
+ hashblock = ZMQSubscriber(socket, b'hashblock')
+
+ # Should only notify the tip if a reorg occurs
+ self.restart_node(0, ['-zmqpub%s=%s' % (hashblock.topic.decode(), address)])
+ socket.connect(address)
+ # Relax so that the subscriber is ready before publishing zmq messages
+ sleep(0.2)
+
+ # Generate 1 block in nodes[0] and receive all notifications
+ self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE)
+ assert_equal(self.nodes[0].getbestblockhash(), hashblock.receive().hex())
+
+ # Generate 2 blocks in nodes[1]
+ self.nodes[1].generatetoaddress(2, ADDRESS_BCRT1_UNSPENDABLE)
+
+ # nodes[0] will reorg chain after connecting back nodes[1]
+ connect_nodes(self.nodes[0], 1)
+
+ # Should receive nodes[1] tip
+ assert_equal(self.nodes[1].getbestblockhash(), hashblock.receive().hex())
+
if __name__ == '__main__':
ZMQTest().main()
diff --git a/test/functional/mempool_accept.py b/test/functional/mempool_accept.py
index 209a222004..e19e249f21 100755
--- a/test/functional/mempool_accept.py
+++ b/test/functional/mempool_accept.py
@@ -211,6 +211,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
rawtxs=[tx.serialize().hex()],
)
+ # The following two validations prevent overflow of the output amounts (see CVE-2010-5139).
self.log.info('A transaction with too large output value')
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
tx.vout[0].nValue = 21000000 * COIN + 1
diff --git a/test/functional/mempool_package_onemore.py b/test/functional/mempool_package_onemore.py
index 30f851fb8e..0739d7e29b 100755
--- a/test/functional/mempool_package_onemore.py
+++ b/test/functional/mempool_package_onemore.py
@@ -33,7 +33,7 @@ class MempoolPackagesTest(BitcoinTestFramework):
outputs = {}
for i in range(num_outputs):
outputs[node.getnewaddress()] = send_value
- rawtx = node.createrawtransaction(inputs, outputs)
+ rawtx = node.createrawtransaction(inputs, outputs, 0, True)
signedtx = node.signrawtransactionwithwallet(rawtx)
txid = node.sendrawtransaction(signedtx['hex'])
fulltx = node.getrawtransaction(txid, 1)
@@ -75,10 +75,16 @@ class MempoolPackagesTest(BitcoinTestFramework):
# ...especially if its > 40k weight
assert_raises_rpc_error(-26, "too-long-mempool-chain, too many descendants", self.chain_transaction, self.nodes[0], [chain[0][0]], [1], chain[0][1], fee, 350)
# But not if it chains directly off the first transaction
- self.chain_transaction(self.nodes[0], [chain[0][0]], [1], chain[0][1], fee, 1)
+ (replacable_txid, replacable_orig_value) = self.chain_transaction(self.nodes[0], [chain[0][0]], [1], chain[0][1], fee, 1)
# and the second chain should work just fine
self.chain_transaction(self.nodes[0], [second_chain], [0], second_chain_value, fee, 1)
+ # Make sure we can RBF the chain which used our carve-out rule
+ second_tx_outputs = {self.nodes[0].getrawtransaction(replacable_txid, True)["vout"][0]['scriptPubKey']['addresses'][0]: replacable_orig_value - (Decimal(1) / Decimal(100))}
+ second_tx = self.nodes[0].createrawtransaction([{'txid': chain[0][0], 'vout': 1}], second_tx_outputs)
+ signed_second_tx = self.nodes[0].signrawtransactionwithwallet(second_tx)
+ self.nodes[0].sendrawtransaction(signed_second_tx['hex'])
+
# Finally, check that we added two transactions
assert_equal(len(self.nodes[0].getrawmempool(True)), MAX_ANCESTORS + 3)
diff --git a/test/functional/p2p_blocksonly.py b/test/functional/p2p_blocksonly.py
index 12cb06a407..3258a38e3c 100755
--- a/test/functional/p2p_blocksonly.py
+++ b/test/functional/p2p_blocksonly.py
@@ -19,7 +19,7 @@ class P2PBlocksOnly(BitcoinTestFramework):
def run_test(self):
self.nodes[0].add_p2p_connection(P2PInterface())
- self.log.info('Check that txs from p2p are rejected')
+ self.log.info('Check that txs from p2p are rejected and result in disconnect')
prevtx = self.nodes[0].getblock(self.nodes[0].getblockhash(1), 2)['tx'][0]
rawtx = self.nodes[0].createrawtransaction(
inputs=[{
@@ -42,13 +42,17 @@ class P2PBlocksOnly(BitcoinTestFramework):
assert_equal(self.nodes[0].getnetworkinfo()['localrelay'], False)
with self.nodes[0].assert_debug_log(['transaction sent in violation of protocol peer=0']):
self.nodes[0].p2p.send_message(msg_tx(FromHex(CTransaction(), sigtx)))
- self.nodes[0].p2p.sync_with_ping()
+ self.nodes[0].p2p.wait_for_disconnect()
assert_equal(self.nodes[0].getmempoolinfo()['size'], 0)
+ # Remove the disconnected peer and add a new one.
+ del self.nodes[0].p2ps[0]
+ self.nodes[0].add_p2p_connection(P2PInterface())
+
self.log.info('Check that txs from rpc are not rejected and relayed to other peers')
assert_equal(self.nodes[0].getpeerinfo()[0]['relaytxes'], True)
txid = self.nodes[0].testmempoolaccept([sigtx])[0]['txid']
- with self.nodes[0].assert_debug_log(['received getdata for: tx {} peer=0'.format(txid)]):
+ with self.nodes[0].assert_debug_log(['received getdata for: tx {} peer=1'.format(txid)]):
self.nodes[0].sendrawtransaction(sigtx)
self.nodes[0].p2p.wait_for_tx(txid)
assert_equal(self.nodes[0].getmempoolinfo()['size'], 1)
diff --git a/test/functional/p2p_compactblocks.py b/test/functional/p2p_compactblocks.py
index eb3336bd3b..7905cf5018 100755
--- a/test/functional/p2p_compactblocks.py
+++ b/test/functional/p2p_compactblocks.py
@@ -14,7 +14,7 @@ from test_framework.messages import BlockTransactions, BlockTransactionsRequest,
from test_framework.mininode import mininode_lock, P2PInterface
from test_framework.script import CScript, OP_TRUE, OP_DROP
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, get_bip9_status, wait_until
+from test_framework.util import assert_equal, wait_until, softfork_active
# TestP2PConn: A peer we use to send messages to bitcoind, and store responses.
class TestP2PConn(P2PInterface):
@@ -803,7 +803,7 @@ class CompactBlocksTest(BitcoinTestFramework):
# We will need UTXOs to construct transactions in later tests.
self.make_utxos()
- assert_equal(get_bip9_status(self.nodes[0], "segwit")["status"], 'active')
+ assert softfork_active(self.nodes[0], "segwit")
self.log.info("Testing SENDCMPCT p2p message... ")
self.test_sendcmpct(self.segwit_node, old_node=self.old_node)
diff --git a/test/functional/p2p_dos_header_tree.py b/test/functional/p2p_dos_header_tree.py
new file mode 100755
index 0000000000..7d386c47f6
--- /dev/null
+++ b/test/functional/p2p_dos_header_tree.py
@@ -0,0 +1,90 @@
+#!/usr/bin/env python3
+# Copyright (c) 2019 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 that we reject low difficulty headers to prevent our block tree from filling up with useless bloat"""
+
+from test_framework.messages import (
+ CBlockHeader,
+ FromHex,
+)
+from test_framework.mininode import (
+ P2PInterface,
+ msg_headers,
+)
+from test_framework.test_framework import BitcoinTestFramework
+
+import os
+
+
+class RejectLowDifficultyHeadersTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.chain = 'testnet3' # Use testnet chain because it has an early checkpoint
+ self.num_nodes = 2
+
+ def add_options(self, parser):
+ parser.add_argument(
+ '--datafile',
+ default='data/blockheader_testnet3.hex',
+ help='Test data file (default: %(default)s)',
+ )
+
+ def run_test(self):
+ self.log.info("Read headers data")
+ self.headers_file_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), self.options.datafile)
+ with open(self.headers_file_path, encoding='utf-8') as headers_data:
+ h_lines = [l.strip() for l in headers_data.readlines()]
+
+ # The headers data is taken from testnet3 for early blocks from genesis until the first checkpoint. There are
+ # two headers with valid POW at height 1 and 2, forking off from genesis. They are indicated by the FORK_PREFIX.
+ FORK_PREFIX = 'fork:'
+ self.headers = [l for l in h_lines if not l.startswith(FORK_PREFIX)]
+ self.headers_fork = [l[len(FORK_PREFIX):] for l in h_lines if l.startswith(FORK_PREFIX)]
+
+ self.headers = [FromHex(CBlockHeader(), h) for h in self.headers]
+ self.headers_fork = [FromHex(CBlockHeader(), h) for h in self.headers_fork]
+
+ self.log.info("Feed all non-fork headers, including and up to the first checkpoint")
+ self.nodes[0].add_p2p_connection(P2PInterface())
+ self.nodes[0].p2p.send_message(msg_headers(self.headers))
+ self.nodes[0].p2p.sync_with_ping()
+ assert {
+ 'height': 546,
+ 'hash': '000000002a936ca763904c3c35fce2f3556c559c0214345d31b1bcebf76acb70',
+ 'branchlen': 546,
+ 'status': 'headers-only',
+ } in self.nodes[0].getchaintips()
+
+ self.log.info("Feed all fork headers (fails due to checkpoint)")
+ with self.nodes[0].assert_debug_log(['bad-fork-prior-to-checkpoint (code 67)']):
+ self.nodes[0].p2p.send_message(msg_headers(self.headers_fork))
+ self.nodes[0].p2p.wait_for_disconnect()
+
+ self.log.info("Feed all fork headers (succeeds without checkpoint)")
+ # On node 0 it succeeds because checkpoints are disabled
+ self.restart_node(0, extra_args=['-nocheckpoints'])
+ self.nodes[0].add_p2p_connection(P2PInterface())
+ self.nodes[0].p2p.send_message(msg_headers(self.headers_fork))
+ self.nodes[0].p2p.sync_with_ping()
+ assert {
+ "height": 2,
+ "hash": "00000000b0494bd6c3d5ff79c497cfce40831871cbf39b1bc28bd1dac817dc39",
+ "branchlen": 2,
+ "status": "headers-only",
+ } in self.nodes[0].getchaintips()
+
+ # On node 1 it succeeds because no checkpoint has been reached yet by a chain tip
+ self.nodes[1].add_p2p_connection(P2PInterface())
+ self.nodes[1].p2p.send_message(msg_headers(self.headers_fork))
+ self.nodes[1].p2p.sync_with_ping()
+ assert {
+ "height": 2,
+ "hash": "00000000b0494bd6c3d5ff79c497cfce40831871cbf39b1bc28bd1dac817dc39",
+ "branchlen": 2,
+ "status": "headers-only",
+ } in self.nodes[1].getchaintips()
+
+
+if __name__ == '__main__':
+ RejectLowDifficultyHeadersTest().main()
diff --git a/test/functional/p2p_invalid_block.py b/test/functional/p2p_invalid_block.py
index 1e0b876593..905534b862 100755
--- a/test/functional/p2p_invalid_block.py
+++ b/test/functional/p2p_invalid_block.py
@@ -53,10 +53,11 @@ class InvalidBlockRequestTest(BitcoinTestFramework):
block_time = best_block["time"] + 1
# Use merkle-root malleability to generate an invalid block with
- # same blockheader.
+ # same blockheader (CVE-2012-2459).
# Manufacture a block with 3 transactions (coinbase, spend of prior
# coinbase, spend of that spend). Duplicate the 3rd transaction to
# leave merkle root and blockheader unchanged but invalidate the block.
+ # For more information on merkle-root malleability see src/consensus/merkle.cpp.
self.log.info("Test merkle root malleability.")
block2 = create_block(tip, create_coinbase(height), block_time)
@@ -81,15 +82,16 @@ class InvalidBlockRequestTest(BitcoinTestFramework):
node.p2p.send_blocks_and_test([block2], node, success=False, reject_reason='bad-txns-duplicate')
- # Check transactions for duplicate inputs
+ # Check transactions for duplicate inputs (CVE-2018-17144)
self.log.info("Test duplicate input block.")
- block2_orig.vtx[2].vin.append(block2_orig.vtx[2].vin[0])
- block2_orig.vtx[2].rehash()
- block2_orig.hashMerkleRoot = block2_orig.calc_merkle_root()
- block2_orig.rehash()
- block2_orig.solve()
- node.p2p.send_blocks_and_test([block2_orig], node, success=False, reject_reason='bad-txns-inputs-duplicate')
+ block2_dup = copy.deepcopy(block2_orig)
+ block2_dup.vtx[2].vin.append(block2_dup.vtx[2].vin[0])
+ block2_dup.vtx[2].rehash()
+ block2_dup.hashMerkleRoot = block2_dup.calc_merkle_root()
+ block2_dup.rehash()
+ block2_dup.solve()
+ node.p2p.send_blocks_and_test([block2_dup], node, success=False, reject_reason='bad-txns-inputs-duplicate')
self.log.info("Test very broken block.")
@@ -105,5 +107,31 @@ class InvalidBlockRequestTest(BitcoinTestFramework):
node.p2p.send_blocks_and_test([block3], node, success=False, reject_reason='bad-cb-amount')
+ # Complete testing of CVE-2012-2459 by sending the original block.
+ # It should be accepted even though it has the same hash as the mutated one.
+
+ self.log.info("Test accepting original block after rejecting its mutated version.")
+ node.p2p.send_blocks_and_test([block2_orig], node, success=True, timeout=5)
+
+ # Update tip info
+ height += 1
+ block_time += 1
+ tip = int(block2_orig.hash, 16)
+
+ # Complete testing of CVE-2018-17144, by checking for the inflation bug.
+ # Create a block that spends the output of a tx in a previous block.
+ block4 = create_block(tip, create_coinbase(height), block_time)
+ tx3 = create_tx_with_script(tx2, 0, script_sig=b'\x51', amount=50 * COIN)
+
+ # Duplicates input
+ tx3.vin.append(tx3.vin[0])
+ tx3.rehash()
+ block4.vtx.append(tx3)
+ block4.hashMerkleRoot = block4.calc_merkle_root()
+ block4.rehash()
+ block4.solve()
+ self.log.info("Test inflation by duplicating input")
+ node.p2p.send_blocks_and_test([block4], node, success=False, reject_reason='bad-txns-inputs-duplicate')
+
if __name__ == '__main__':
InvalidBlockRequestTest().main()
diff --git a/test/functional/p2p_invalid_messages.py b/test/functional/p2p_invalid_messages.py
index d5c68e622b..58c72f89d8 100755
--- a/test/functional/p2p_invalid_messages.py
+++ b/test/functional/p2p_invalid_messages.py
@@ -85,7 +85,7 @@ class InvalidMessagesTest(BitcoinTestFramework):
# Peer 1, despite serving up a bunch of nonsense, should still be connected.
self.log.info("Waiting for node to drop junk messages.")
- node.p2p.sync_with_ping(timeout=120)
+ node.p2p.sync_with_ping(timeout=320)
assert node.p2p.is_connected
#
diff --git a/test/functional/p2p_permissions.py b/test/functional/p2p_permissions.py
new file mode 100755
index 0000000000..40b28d7533
--- /dev/null
+++ b/test/functional/p2p_permissions.py
@@ -0,0 +1,103 @@
+#!/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 p2p permission message.
+
+Test that permissions are correctly calculated and applied
+"""
+
+from test_framework.test_node import ErrorMatch
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+ connect_nodes,
+ p2p_port,
+)
+
+class P2PPermissionsTests(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 2
+ self.setup_clean_chain = True
+ self.extra_args = [[],[]]
+
+ def run_test(self):
+ self.checkpermission(
+ # default permissions (no specific permissions)
+ ["-whitelist=127.0.0.1"],
+ ["relay", "noban", "mempool"],
+ True)
+
+ self.checkpermission(
+ # relay permission removed (no specific permissions)
+ ["-whitelist=127.0.0.1", "-whitelistrelay=0"],
+ ["noban", "mempool"],
+ True)
+
+ self.checkpermission(
+ # forcerelay and relay permission added
+ # Legacy parameter interaction which set whitelistrelay to true
+ # if whitelistforcerelay is true
+ ["-whitelist=127.0.0.1", "-whitelistforcerelay"],
+ ["forcerelay", "relay", "noban", "mempool"],
+ True)
+
+ # Let's make sure permissions are merged correctly
+ # For this, we need to use whitebind instead of bind
+ # by modifying the configuration file.
+ ip_port = "127.0.0.1:{}".format(p2p_port(1))
+ self.replaceinconfig(1, "bind=127.0.0.1", "whitebind=bloomfilter,forcerelay@" + ip_port)
+ self.checkpermission(
+ ["-whitelist=noban@127.0.0.1" ],
+ # Check parameter interaction forcerelay should activate relay
+ ["noban", "bloomfilter", "forcerelay", "relay" ],
+ False)
+ self.replaceinconfig(1, "whitebind=bloomfilter,forcerelay@" + ip_port, "bind=127.0.0.1")
+
+ self.checkpermission(
+ # legacy whitelistrelay should be ignored
+ ["-whitelist=noban,mempool@127.0.0.1", "-whitelistrelay"],
+ ["noban", "mempool"],
+ False)
+
+ self.checkpermission(
+ # legacy whitelistforcerelay should be ignored
+ ["-whitelist=noban,mempool@127.0.0.1", "-whitelistforcerelay"],
+ ["noban", "mempool"],
+ False)
+
+ self.checkpermission(
+ # missing mempool permission to be considered legacy whitelisted
+ ["-whitelist=noban@127.0.0.1"],
+ ["noban"],
+ False)
+
+ self.checkpermission(
+ # all permission added
+ ["-whitelist=all@127.0.0.1"],
+ ["forcerelay", "noban", "mempool", "bloomfilter", "relay"],
+ False)
+
+ self.stop_node(1)
+ self.nodes[1].assert_start_raises_init_error(["-whitelist=oopsie@127.0.0.1"], "Invalid P2P permission", match=ErrorMatch.PARTIAL_REGEX)
+ self.nodes[1].assert_start_raises_init_error(["-whitelist=noban@127.0.0.1:230"], "Invalid netmask specified in", match=ErrorMatch.PARTIAL_REGEX)
+ self.nodes[1].assert_start_raises_init_error(["-whitebind=noban@127.0.0.1/10"], "Cannot resolve -whitebind address", match=ErrorMatch.PARTIAL_REGEX)
+
+ def checkpermission(self, args, expectedPermissions, whitelisted):
+ self.restart_node(1, args)
+ connect_nodes(self.nodes[0], 1)
+ peerinfo = self.nodes[1].getpeerinfo()[0]
+ assert_equal(peerinfo['whitelisted'], whitelisted)
+ assert_equal(len(expectedPermissions), len(peerinfo['permissions']))
+ for p in expectedPermissions:
+ if not p in peerinfo['permissions']:
+ raise AssertionError("Expected permissions %r is not granted." % p)
+
+ def replaceinconfig(self, nodeid, old, new):
+ with open(self.nodes[nodeid].bitcoinconf, encoding="utf8") as f:
+ newText=f.read().replace(old, new)
+ with open(self.nodes[nodeid].bitcoinconf, 'w', encoding="utf8") as f:
+ f.write(newText)
+
+if __name__ == '__main__':
+ P2PPermissionsTests().main()
diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py
index dca71aec43..98f6b1d71d 100755
--- a/test/functional/p2p_segwit.py
+++ b/test/functional/p2p_segwit.py
@@ -76,7 +76,7 @@ from test_framework.util import (
assert_equal,
connect_nodes,
disconnect_nodes,
- get_bip9_status,
+ softfork_active,
hex_str_to_bytes,
assert_raises_rpc_error,
)
@@ -88,6 +88,8 @@ VB_TOP_BITS = 0x20000000
MAX_SIGOP_COST = 80000
+SEGWIT_HEIGHT = 120
+
class UTXO():
"""Used to keep track of anyone-can-spend outputs that we can use in the tests."""
def __init__(self, sha256, n, value):
@@ -185,9 +187,9 @@ class SegWitTest(BitcoinTestFramework):
self.num_nodes = 3
# This test tests SegWit both pre and post-activation, so use the normal BIP9 activation.
self.extra_args = [
- ["-whitelist=127.0.0.1", "-acceptnonstdtxn=1", "-vbparams=segwit:0:999999999999"],
- ["-whitelist=127.0.0.1", "-acceptnonstdtxn=0", "-vbparams=segwit:0:999999999999"],
- ["-whitelist=127.0.0.1", "-acceptnonstdtxn=1", "-vbparams=segwit:0:0"],
+ ["-whitelist=127.0.0.1", "-acceptnonstdtxn=1", "-segwitheight={}".format(SEGWIT_HEIGHT)],
+ ["-whitelist=127.0.0.1", "-acceptnonstdtxn=0", "-segwitheight={}".format(SEGWIT_HEIGHT)],
+ ["-whitelist=127.0.0.1", "-acceptnonstdtxn=1", "-segwitheight=-1"]
]
def skip_test_if_missing_module(self):
@@ -231,26 +233,18 @@ class SegWitTest(BitcoinTestFramework):
# Keep a place to store utxo's that can be used in later tests
self.utxo = []
- # Segwit status 'defined'
- self.segwit_status = 'defined'
+ self.log.info("Starting tests before segwit activation")
+ self.segwit_active = False
self.test_non_witness_transaction()
- self.test_unnecessary_witness_before_segwit_activation()
self.test_v0_outputs_arent_spendable()
self.test_block_relay()
- self.advance_to_segwit_started()
-
- # Segwit status 'started'
-
self.test_getblocktemplate_before_lockin()
- self.advance_to_segwit_lockin()
-
- # Segwit status 'locked_in'
-
self.test_unnecessary_witness_before_segwit_activation()
self.test_witness_tx_relay_before_segwit_activation()
- self.test_block_relay()
self.test_standardness_v0()
+
+ self.log.info("Advancing to segwit activation")
self.advance_to_segwit_active()
# Segwit status 'active'
@@ -282,15 +276,15 @@ class SegWitTest(BitcoinTestFramework):
def subtest(func): # noqa: N805
"""Wraps the subtests for logging and state assertions."""
def func_wrapper(self, *args, **kwargs):
- self.log.info("Subtest: {} (Segwit status = {})".format(func.__name__, self.segwit_status))
+ self.log.info("Subtest: {} (Segwit active = {})".format(func.__name__, self.segwit_active))
# Assert segwit status is as expected
- assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], self.segwit_status)
+ assert_equal(softfork_active(self.nodes[0], 'segwit'), self.segwit_active)
func(self, *args, **kwargs)
# Each subtest should leave some utxos for the next subtest
assert self.utxo
self.sync_blocks()
# Assert segwit status is as expected at end of subtest
- assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], self.segwit_status)
+ assert_equal(softfork_active(self.nodes[0], 'segwit'), self.segwit_active)
return func_wrapper
@@ -392,7 +386,7 @@ class SegWitTest(BitcoinTestFramework):
# Check that we can getdata for witness blocks or regular blocks,
# and the right thing happens.
- if self.segwit_status != 'active':
+ if not self.segwit_active:
# Before activation, we should be able to request old blocks with
# or without witness, and they should be the same.
chain_height = self.nodes[0].getblockcount()
@@ -536,32 +530,18 @@ class SegWitTest(BitcoinTestFramework):
self.utxo.append(UTXO(txid, 2, value))
@subtest
- def advance_to_segwit_started(self):
- """Mine enough blocks for segwit's vb state to be 'started'."""
- height = self.nodes[0].getblockcount()
- # Will need to rewrite the tests here if we are past the first period
- assert height < VB_PERIOD - 1
- # Advance to end of period, status should now be 'started'
- self.nodes[0].generate(VB_PERIOD - height - 1)
- assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], 'started')
- self.segwit_status = 'started'
-
- @subtest
def test_getblocktemplate_before_lockin(self):
txid = int(self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1), 16)
for node in [self.nodes[0], self.nodes[2]]:
gbt_results = node.getblocktemplate({"rules": ["segwit"]})
- block_version = gbt_results['version']
if node == self.nodes[2]:
# If this is a non-segwit node, we should not get a witness
- # commitment, nor a version bit signalling segwit.
- assert_equal(block_version & (1 << VB_WITNESS_BIT), 0)
+ # commitment.
assert 'default_witness_commitment' not in gbt_results
else:
- # For segwit-aware nodes, check the version bit and the witness
- # commitment are correct.
- assert block_version & (1 << VB_WITNESS_BIT) != 0
+ # For segwit-aware nodes, check the witness
+ # commitment is correct.
assert 'default_witness_commitment' in gbt_results
witness_commitment = gbt_results['default_witness_commitment']
@@ -571,18 +551,9 @@ class SegWitTest(BitcoinTestFramework):
script = get_witness_script(witness_root, 0)
assert_equal(witness_commitment, script.hex())
- @subtest
- def advance_to_segwit_lockin(self):
- """Mine enough blocks to lock in segwit, but don't activate."""
- height = self.nodes[0].getblockcount()
- # Advance to end of period, and verify lock-in happens at the end
- self.nodes[0].generate(VB_PERIOD - 1)
- height = self.nodes[0].getblockcount()
- assert (height % VB_PERIOD) == VB_PERIOD - 2
- assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], 'started')
+ # Clear out the mempool
self.nodes[0].generate(1)
- assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], 'locked_in')
- self.segwit_status = 'locked_in'
+ self.sync_blocks()
@subtest
def test_witness_tx_relay_before_segwit_activation(self):
@@ -686,7 +657,7 @@ class SegWitTest(BitcoinTestFramework):
tx3.wit.vtxinwit.append(CTxInWitness())
tx3.wit.vtxinwit[0].scriptWitness.stack = [witness_program]
tx3.rehash()
- if self.segwit_status != 'active':
+ if not self.segwit_active:
# Just check mempool acceptance, but don't add the transaction to the mempool, since witness is disallowed
# in blocks and the tx is impossible to mine right now.
assert_equal(self.nodes[0].testmempoolaccept([tx3.serialize_with_witness().hex()]), [{'txid': tx3.hash, 'allowed': True}])
@@ -707,12 +678,13 @@ class SegWitTest(BitcoinTestFramework):
@subtest
def advance_to_segwit_active(self):
"""Mine enough blocks to activate segwit."""
+ assert not softfork_active(self.nodes[0], 'segwit')
height = self.nodes[0].getblockcount()
- self.nodes[0].generate(VB_PERIOD - (height % VB_PERIOD) - 2)
- assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], 'locked_in')
+ self.nodes[0].generate(SEGWIT_HEIGHT - height - 2)
+ assert not softfork_active(self.nodes[0], 'segwit')
self.nodes[0].generate(1)
- assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], 'active')
- self.segwit_status = 'active'
+ assert softfork_active(self.nodes[0], 'segwit')
+ self.segwit_active = True
@subtest
def test_p2sh_witness(self):
@@ -1924,13 +1896,13 @@ class SegWitTest(BitcoinTestFramework):
# Restart with the new binary
self.stop_node(2)
- self.start_node(2, extra_args=["-vbparams=segwit:0:999999999999"])
+ self.start_node(2, extra_args=["-segwitheight={}".format(SEGWIT_HEIGHT)])
connect_nodes(self.nodes[0], 2)
self.sync_blocks()
# Make sure that this peer thinks segwit has activated.
- assert get_bip9_status(self.nodes[2], 'segwit')['status'] == "active"
+ assert softfork_active(self.nodes[2], 'segwit')
# Make sure this peer's blocks match those of node0.
height = self.nodes[2].getblockcount()
diff --git a/test/functional/p2p_tx_download.py b/test/functional/p2p_tx_download.py
new file mode 100755
index 0000000000..19d78ff303
--- /dev/null
+++ b/test/functional/p2p_tx_download.py
@@ -0,0 +1,175 @@
+#!/usr/bin/env python3
+# Copyright (c) 2019 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 transaction download behavior
+"""
+
+from test_framework.messages import (
+ CInv,
+ CTransaction,
+ FromHex,
+ MSG_TX,
+ MSG_TYPE_MASK,
+ msg_inv,
+ msg_notfound,
+)
+from test_framework.mininode import (
+ P2PInterface,
+ mininode_lock,
+)
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+ wait_until,
+)
+from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE
+
+import time
+
+
+class TestP2PConn(P2PInterface):
+ def __init__(self):
+ super().__init__()
+ self.tx_getdata_count = 0
+
+ def on_getdata(self, message):
+ for i in message.inv:
+ if i.type & MSG_TYPE_MASK == MSG_TX:
+ self.tx_getdata_count += 1
+
+
+# Constants from net_processing
+GETDATA_TX_INTERVAL = 60 # seconds
+MAX_GETDATA_RANDOM_DELAY = 2 # seconds
+INBOUND_PEER_TX_DELAY = 2 # seconds
+MAX_GETDATA_IN_FLIGHT = 100
+TX_EXPIRY_INTERVAL = GETDATA_TX_INTERVAL * 10
+
+# Python test constants
+NUM_INBOUND = 10
+MAX_GETDATA_INBOUND_WAIT = GETDATA_TX_INTERVAL + MAX_GETDATA_RANDOM_DELAY + INBOUND_PEER_TX_DELAY
+
+
+class TxDownloadTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = False
+ self.num_nodes = 2
+
+ def test_tx_requests(self):
+ self.log.info("Test that we request transactions from all our peers, eventually")
+
+ txid = 0xdeadbeef
+
+ self.log.info("Announce the txid from each incoming peer to node 0")
+ msg = msg_inv([CInv(t=1, h=txid)])
+ for p in self.nodes[0].p2ps:
+ p.send_message(msg)
+ p.sync_with_ping()
+
+ outstanding_peer_index = [i for i in range(len(self.nodes[0].p2ps))]
+
+ def getdata_found(peer_index):
+ p = self.nodes[0].p2ps[peer_index]
+ with mininode_lock:
+ return p.last_message.get("getdata") and p.last_message["getdata"].inv[-1].hash == txid
+
+ node_0_mocktime = int(time.time())
+ while outstanding_peer_index:
+ node_0_mocktime += MAX_GETDATA_INBOUND_WAIT
+ self.nodes[0].setmocktime(node_0_mocktime)
+ wait_until(lambda: any(getdata_found(i) for i in outstanding_peer_index))
+ for i in outstanding_peer_index:
+ if getdata_found(i):
+ outstanding_peer_index.remove(i)
+
+ self.nodes[0].setmocktime(0)
+ self.log.info("All outstanding peers received a getdata")
+
+ def test_inv_block(self):
+ self.log.info("Generate a transaction on node 0")
+ tx = self.nodes[0].createrawtransaction(
+ inputs=[{ # coinbase
+ "txid": self.nodes[0].getblock(self.nodes[0].getblockhash(1))['tx'][0],
+ "vout": 0
+ }],
+ outputs={ADDRESS_BCRT1_UNSPENDABLE: 50 - 0.00025},
+ )
+ tx = self.nodes[0].signrawtransactionwithkey(
+ hexstring=tx,
+ privkeys=[self.nodes[0].get_deterministic_priv_key().key],
+ )['hex']
+ ctx = FromHex(CTransaction(), tx)
+ txid = int(ctx.rehash(), 16)
+
+ self.log.info(
+ "Announce the transaction to all nodes from all {} incoming peers, but never send it".format(NUM_INBOUND))
+ msg = msg_inv([CInv(t=1, h=txid)])
+ for p in self.peers:
+ p.send_message(msg)
+ p.sync_with_ping()
+
+ self.log.info("Put the tx in node 0's mempool")
+ self.nodes[0].sendrawtransaction(tx)
+
+ # Since node 1 is connected outbound to an honest peer (node 0), it
+ # should get the tx within a timeout. (Assuming that node 0
+ # announced the tx within the timeout)
+ # The timeout is the sum of
+ # * the worst case until the tx is first requested from an inbound
+ # peer, plus
+ # * the first time it is re-requested from the outbound peer, plus
+ # * 2 seconds to avoid races
+ timeout = 2 + (MAX_GETDATA_RANDOM_DELAY + INBOUND_PEER_TX_DELAY) + (
+ GETDATA_TX_INTERVAL + MAX_GETDATA_RANDOM_DELAY)
+ self.log.info("Tx should be received at node 1 after {} seconds".format(timeout))
+ self.sync_mempools(timeout=timeout)
+
+ def test_in_flight_max(self):
+ self.log.info("Test that we don't request more than {} transactions from any peer, every {} minutes".format(
+ MAX_GETDATA_IN_FLIGHT, TX_EXPIRY_INTERVAL / 60))
+ txids = [i for i in range(MAX_GETDATA_IN_FLIGHT + 2)]
+
+ p = self.nodes[0].p2ps[0]
+
+ with mininode_lock:
+ p.tx_getdata_count = 0
+
+ p.send_message(msg_inv([CInv(t=1, h=i) for i in txids]))
+ wait_until(lambda: p.tx_getdata_count >= MAX_GETDATA_IN_FLIGHT, lock=mininode_lock)
+ with mininode_lock:
+ assert_equal(p.tx_getdata_count, MAX_GETDATA_IN_FLIGHT)
+
+ self.log.info("Now check that if we send a NOTFOUND for a transaction, we'll get one more request")
+ p.send_message(msg_notfound(vec=[CInv(t=1, h=txids[0])]))
+ wait_until(lambda: p.tx_getdata_count >= MAX_GETDATA_IN_FLIGHT + 1, timeout=10, lock=mininode_lock)
+ with mininode_lock:
+ assert_equal(p.tx_getdata_count, MAX_GETDATA_IN_FLIGHT + 1)
+
+ WAIT_TIME = TX_EXPIRY_INTERVAL // 2 + TX_EXPIRY_INTERVAL
+ self.log.info("if we wait about {} minutes, we should eventually get more requests".format(WAIT_TIME / 60))
+ self.nodes[0].setmocktime(int(time.time() + WAIT_TIME))
+ wait_until(lambda: p.tx_getdata_count == MAX_GETDATA_IN_FLIGHT + 2)
+ self.nodes[0].setmocktime(0)
+
+ def run_test(self):
+ # Setup the p2p connections
+ self.peers = []
+ for node in self.nodes:
+ for i in range(NUM_INBOUND):
+ self.peers.append(node.add_p2p_connection(TestP2PConn()))
+
+ self.log.info("Nodes are setup with {} incoming connections each".format(NUM_INBOUND))
+
+ # Test the in-flight max first, because we want no transactions in
+ # flight ahead of this test.
+ self.test_in_flight_max()
+
+ self.test_inv_block()
+
+ self.test_tx_requests()
+
+
+if __name__ == '__main__':
+ TxDownloadTest().main()
diff --git a/test/functional/rpc_bind.py b/test/functional/rpc_bind.py
index acc7cd811c..8979251a26 100755
--- a/test/functional/rpc_bind.py
+++ b/test/functional/rpc_bind.py
@@ -55,7 +55,7 @@ class RPCBindTest(BitcoinTestFramework):
self.nodes[0].rpchost = None
self.start_nodes([node_args])
# connect to node through non-loopback interface
- node = get_rpc_proxy(rpc_url(self.nodes[0].datadir, 0, "%s:%d" % (rpchost, rpcport)), 0, coveragedir=self.options.coveragedir)
+ node = get_rpc_proxy(rpc_url(self.nodes[0].datadir, 0, self.chain, "%s:%d" % (rpchost, rpcport)), 0, coveragedir=self.options.coveragedir)
node.getnetworkinfo()
self.stop_nodes()
diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py
index facb05b54c..266a0d6cd2 100755
--- a/test/functional/rpc_blockchain.py
+++ b/test/functional/rpc_blockchain.py
@@ -78,7 +78,6 @@ class BlockchainTest(BitcoinTestFramework):
keys = [
'bestblockhash',
- 'bip9_softforks',
'blocks',
'chain',
'chainwork',
@@ -124,6 +123,31 @@ class BlockchainTest(BitcoinTestFramework):
assert_equal(res['prune_target_size'], 576716800)
assert_greater_than(res['size_on_disk'], 0)
+ assert_equal(res['softforks'], {
+ 'bip34': {'type': 'buried', 'active': False, 'height': 500},
+ 'bip66': {'type': 'buried', 'active': False, 'height': 1251},
+ 'bip65': {'type': 'buried', 'active': False, 'height': 1351},
+ 'csv': {'type': 'buried', 'active': False, 'height': 432},
+ 'segwit': {'type': 'buried', 'active': True, 'height': 0},
+ 'testdummy': {
+ 'type': 'bip9',
+ 'bip9': {
+ 'status': 'started',
+ 'bit': 28,
+ 'startTime': 0,
+ 'timeout': 0x7fffffffffffffff, # testdummy does not have a timeout so is set to the max int64 value
+ 'since': 144,
+ 'statistics': {
+ 'period': 144,
+ 'threshold': 108,
+ 'elapsed': 57,
+ 'count': 57,
+ 'possible': True,
+ },
+ },
+ 'active': False}
+ })
+
def _test_getchaintxstats(self):
self.log.info("Test getchaintxstats")
@@ -162,6 +186,7 @@ class BlockchainTest(BitcoinTestFramework):
assert_equal(chaintxstats['time'], b200['time'])
assert_equal(chaintxstats['txcount'], 201)
assert_equal(chaintxstats['window_final_block_hash'], b200_hash)
+ assert_equal(chaintxstats['window_final_block_height'], 200)
assert_equal(chaintxstats['window_block_count'], 199)
assert_equal(chaintxstats['window_tx_count'], 199)
assert_equal(chaintxstats['window_interval'], time_diff)
@@ -171,6 +196,7 @@ class BlockchainTest(BitcoinTestFramework):
assert_equal(chaintxstats['time'], b1['time'])
assert_equal(chaintxstats['txcount'], 2)
assert_equal(chaintxstats['window_final_block_hash'], b1_hash)
+ assert_equal(chaintxstats['window_final_block_height'], 1)
assert_equal(chaintxstats['window_block_count'], 0)
assert 'window_tx_count' not in chaintxstats
assert 'window_interval' not in chaintxstats
diff --git a/test/functional/rpc_createmultisig.py b/test/functional/rpc_createmultisig.py
index 62f3843756..056e193d55 100755
--- a/test/functional/rpc_createmultisig.py
+++ b/test/functional/rpc_createmultisig.py
@@ -134,6 +134,27 @@ class RpcCreateMultiSigTest(BitcoinTestFramework):
assert_raises_rpc_error(-8, "Missing redeemScript/witnessScript", node2.signrawtransactionwithkey, rawtx, self.priv[0:self.nsigs-1], [prevtx_err])
+ # if witnessScript specified, all ok
+ prevtx_err["witnessScript"] = prevtxs[0]["redeemScript"]
+ node2.signrawtransactionwithkey(rawtx, self.priv[0:self.nsigs-1], [prevtx_err])
+
+ # both specified, also ok
+ prevtx_err["redeemScript"] = prevtxs[0]["redeemScript"]
+ node2.signrawtransactionwithkey(rawtx, self.priv[0:self.nsigs-1], [prevtx_err])
+
+ # redeemScript mismatch to witnessScript
+ prevtx_err["redeemScript"] = "6a" # OP_RETURN
+ assert_raises_rpc_error(-8, "redeemScript does not correspond to witnessScript", node2.signrawtransactionwithkey, rawtx, self.priv[0:self.nsigs-1], [prevtx_err])
+
+ # redeemScript does not match scriptPubKey
+ del prevtx_err["witnessScript"]
+ assert_raises_rpc_error(-8, "redeemScript/witnessScript does not match scriptPubKey", node2.signrawtransactionwithkey, rawtx, self.priv[0:self.nsigs-1], [prevtx_err])
+
+ # witnessScript does not match scriptPubKey
+ prevtx_err["witnessScript"] = prevtx_err["redeemScript"]
+ del prevtx_err["redeemScript"]
+ assert_raises_rpc_error(-8, "redeemScript/witnessScript does not match scriptPubKey", node2.signrawtransactionwithkey, rawtx, self.priv[0:self.nsigs-1], [prevtx_err])
+
rawtx2 = node2.signrawtransactionwithkey(rawtx, self.priv[0:self.nsigs - 1], prevtxs)
rawtx3 = node2.signrawtransactionwithkey(rawtx2["hex"], [self.priv[-1]], prevtxs)
diff --git a/test/functional/rpc_deriveaddresses.py b/test/functional/rpc_deriveaddresses.py
index 1984694692..42128d5767 100755
--- a/test/functional/rpc_deriveaddresses.py
+++ b/test/functional/rpc_deriveaddresses.py
@@ -13,14 +13,14 @@ class DeriveaddressesTest(BitcoinTestFramework):
self.supports_cli = 1
def run_test(self):
- assert_raises_rpc_error(-5, "Invalid descriptor", self.nodes[0].deriveaddresses, "a")
+ assert_raises_rpc_error(-5, "Missing checksum", self.nodes[0].deriveaddresses, "a")
descriptor = "wpkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/0)#t6wfjs64"
address = "bcrt1qjqmxmkpmxt80xz4y3746zgt0q3u3ferr34acd5"
assert_equal(self.nodes[0].deriveaddresses(descriptor), [address])
descriptor = descriptor[:-9]
- assert_raises_rpc_error(-5, "Invalid descriptor", self.nodes[0].deriveaddresses, descriptor)
+ assert_raises_rpc_error(-5, "Missing checksum", self.nodes[0].deriveaddresses, descriptor)
descriptor_pubkey = "wpkh(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/1/0)#s9ga3alw"
address = "bcrt1qjqmxmkpmxt80xz4y3746zgt0q3u3ferr34acd5"
diff --git a/test/functional/rpc_fundrawtransaction.py b/test/functional/rpc_fundrawtransaction.py
index cdf636e200..b621081752 100755
--- a/test/functional/rpc_fundrawtransaction.py
+++ b/test/functional/rpc_fundrawtransaction.py
@@ -41,11 +41,11 @@ class RawTransactionsTest(BitcoinTestFramework):
connect_nodes_bi(self.nodes, 0, 3)
def run_test(self):
- min_relay_tx_fee = self.nodes[0].getnetworkinfo()['relayfee']
+ self.min_relay_tx_fee = self.nodes[0].getnetworkinfo()['relayfee']
# This test is not meant to test fee estimation and we'd like
# to be sure all txs are sent at a consistent desired feerate
for node in self.nodes:
- node.settxfee(min_relay_tx_fee)
+ node.settxfee(self.min_relay_tx_fee)
# if the fee's positive delta is higher than this value tests will fail,
# neg. delta always fail the tests.
@@ -53,13 +53,43 @@ class RawTransactionsTest(BitcoinTestFramework):
# than a minimum sized signature.
# = 2 bytes * minRelayTxFeePerByte
- feeTolerance = 2 * min_relay_tx_fee/1000
+ self.fee_tolerance = 2 * self.min_relay_tx_fee / 1000
self.nodes[2].generate(1)
self.sync_all()
self.nodes[0].generate(121)
self.sync_all()
+ self.test_change_position()
+ self.test_simple()
+ self.test_simple_two_coins()
+ self.test_simple_two_outputs()
+ self.test_change()
+ self.test_no_change()
+ self.test_invalid_option()
+ self.test_invalid_change_address()
+ self.test_valid_change_address()
+ self.test_change_type()
+ self.test_coin_selection()
+ self.test_two_vin()
+ self.test_two_vin_two_vout()
+ self.test_invalid_input()
+ self.test_fee_p2pkh()
+ self.test_fee_p2pkh_multi_out()
+ self.test_fee_p2sh()
+ self.test_fee_4of5()
+ self.test_spend_2of2()
+ self.test_locked_wallet()
+ self.test_many_inputs_fee()
+ self.test_many_inputs_send()
+ self.test_op_return()
+ self.test_watchonly()
+ self.test_all_watched_funds()
+ self.test_option_feerate()
+ self.test_address_reuse()
+ self.test_option_subtract_fee_from_outputs()
+
+ def test_change_position(self):
# ensure that setting changePosition in fundraw with an exact match is handled properly
rawmatch = self.nodes[2].createrawtransaction([], {self.nodes[2].getnewaddress():50})
rawmatch = self.nodes[2].fundrawtransaction(rawmatch, {"changePosition":1, "subtractFeeFromOutputs":[0]})
@@ -67,15 +97,15 @@ class RawTransactionsTest(BitcoinTestFramework):
watchonly_address = self.nodes[0].getnewaddress()
watchonly_pubkey = self.nodes[0].getaddressinfo(watchonly_address)["pubkey"]
- watchonly_amount = Decimal(200)
+ self.watchonly_amount = Decimal(200)
self.nodes[3].importpubkey(watchonly_pubkey, "", True)
- watchonly_txid = self.nodes[0].sendtoaddress(watchonly_address, watchonly_amount)
+ self.watchonly_txid = self.nodes[0].sendtoaddress(watchonly_address, self.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.watchonly_vout = find_vout_for_address(self.nodes[0], self.watchonly_txid, watchonly_address)
+ self.nodes[0].lockunspent(False, [{"txid": self.watchonly_txid, "vout": self.watchonly_vout}])
- self.nodes[0].sendtoaddress(self.nodes[3].getnewaddress(), watchonly_amount / 10)
+ self.nodes[0].sendtoaddress(self.nodes[3].getnewaddress(), self.watchonly_amount / 10)
self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 1.5)
self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 1.0)
@@ -84,6 +114,7 @@ class RawTransactionsTest(BitcoinTestFramework):
self.nodes[0].generate(1)
self.sync_all()
+ def test_simple(self):
###############
# simple test #
###############
@@ -92,10 +123,10 @@ class RawTransactionsTest(BitcoinTestFramework):
rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
dec_tx = self.nodes[2].decoderawtransaction(rawtx)
rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
- fee = rawtxfund['fee']
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
assert len(dec_tx['vin']) > 0 #test that we have enough inputs
+ def test_simple_two_coins(self):
##############################
# simple test with two coins #
##############################
@@ -105,25 +136,11 @@ class RawTransactionsTest(BitcoinTestFramework):
dec_tx = self.nodes[2].decoderawtransaction(rawtx)
rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
- fee = rawtxfund['fee']
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
assert len(dec_tx['vin']) > 0 #test if we have enough inputs
-
- ##############################
- # simple test with two coins #
- ##############################
- inputs = [ ]
- outputs = { self.nodes[0].getnewaddress() : 2.6 }
- rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
- dec_tx = self.nodes[2].decoderawtransaction(rawtx)
-
- rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
- fee = rawtxfund['fee']
- dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
- assert len(dec_tx['vin']) > 0
assert_equal(dec_tx['vin'][0]['scriptSig']['hex'], '')
-
+ def test_simple_two_outputs(self):
################################
# simple test with two outputs #
################################
@@ -133,7 +150,6 @@ class RawTransactionsTest(BitcoinTestFramework):
dec_tx = self.nodes[2].decoderawtransaction(rawtx)
rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
- fee = rawtxfund['fee']
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
totalOut = 0
for out in dec_tx['vout']:
@@ -142,7 +158,7 @@ class RawTransactionsTest(BitcoinTestFramework):
assert len(dec_tx['vin']) > 0
assert_equal(dec_tx['vin'][0]['scriptSig']['hex'], '')
-
+ def test_change(self):
#########################################################################
# test a fundrawtransaction with a VIN greater than the required amount #
#########################################################################
@@ -156,6 +172,7 @@ class RawTransactionsTest(BitcoinTestFramework):
rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
fee = rawtxfund['fee']
+ self.test_no_change_fee = fee # Use the same fee for the next tx
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
totalOut = 0
for out in dec_tx['vout']:
@@ -163,14 +180,14 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_equal(fee + totalOut, utx['amount']) #compare vin total and totalout+fee
-
+ def test_no_change(self):
#####################################################################
# test a fundrawtransaction with which will not get a change output #
#####################################################################
utx = get_unspent(self.nodes[2].listunspent(), 5)
inputs = [ {'txid' : utx['txid'], 'vout' : utx['vout']}]
- outputs = { self.nodes[0].getnewaddress() : Decimal(5.0) - fee - feeTolerance }
+ outputs = {self.nodes[0].getnewaddress(): Decimal(5.0) - self.test_no_change_fee - self.fee_tolerance}
rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
dec_tx = self.nodes[2].decoderawtransaction(rawtx)
assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])
@@ -185,7 +202,7 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_equal(rawtxfund['changepos'], -1)
assert_equal(fee + totalOut, utx['amount']) #compare vin total and totalout+fee
-
+ def test_invalid_option(self):
####################################################
# test a fundrawtransaction with an invalid option #
####################################################
@@ -202,6 +219,7 @@ class RawTransactionsTest(BitcoinTestFramework):
# reserveChangeKey was deprecated and is now removed
assert_raises_rpc_error(-3, "Unexpected key reserveChangeKey", lambda: self.nodes[2].fundrawtransaction(hexstring=rawtx, options={'reserveChangeKey': True}))
+ def test_invalid_change_address(self):
############################################################
# test a fundrawtransaction with an invalid change address #
############################################################
@@ -215,6 +233,7 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_raises_rpc_error(-5, "changeAddress must be a valid bitcoin address", self.nodes[2].fundrawtransaction, rawtx, {'changeAddress':'foobar'})
+ def test_valid_change_address(self):
############################################################
# test a fundrawtransaction with a provided change address #
############################################################
@@ -233,6 +252,7 @@ class RawTransactionsTest(BitcoinTestFramework):
out = dec_tx['vout'][0]
assert_equal(change, out['scriptPubKey']['addresses'][0])
+ def test_change_type(self):
#########################################################
# test a fundrawtransaction with a provided change type #
#########################################################
@@ -247,6 +267,7 @@ class RawTransactionsTest(BitcoinTestFramework):
dec_tx = self.nodes[2].decoderawtransaction(rawtx['hex'])
assert_equal('witness_v0_keyhash', dec_tx['vout'][rawtx['changepos']]['scriptPubKey']['type'])
+ def test_coin_selection(self):
#########################################################################
# test a fundrawtransaction with a VIN smaller than the required amount #
#########################################################################
@@ -264,7 +285,6 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_equal("00", dec_tx['vin'][0]['scriptSig']['hex'])
rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
- fee = rawtxfund['fee']
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
totalOut = 0
matchingOuts = 0
@@ -281,7 +301,7 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_equal(matchingOuts, 1)
assert_equal(len(dec_tx['vout']), 2)
-
+ def test_two_vin(self):
###########################################
# test a fundrawtransaction with two VINs #
###########################################
@@ -295,7 +315,6 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])
rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
- fee = rawtxfund['fee']
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
totalOut = 0
matchingOuts = 0
@@ -315,6 +334,7 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_equal(matchingIns, 2) #we now must see two vins identical to vins given as params
+ def test_two_vin_two_vout(self):
#########################################################
# test a fundrawtransaction with two VINs and two vOUTs #
#########################################################
@@ -328,7 +348,6 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])
rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
- fee = rawtxfund['fee']
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
totalOut = 0
matchingOuts = 0
@@ -340,16 +359,16 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_equal(matchingOuts, 2)
assert_equal(len(dec_tx['vout']), 3)
+ def test_invalid_input(self):
##############################################
# test a fundrawtransaction with invalid vin #
##############################################
inputs = [ {'txid' : "1c7f966dab21119bac53213a2bc7532bff1fa844c124fd750a7d0b1332440bd1", 'vout' : 0} ] #invalid vin!
outputs = { self.nodes[0].getnewaddress() : 1.0}
rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
- dec_tx = self.nodes[2].decoderawtransaction(rawtx)
-
assert_raises_rpc_error(-4, "Insufficient funds", self.nodes[2].fundrawtransaction, rawtx)
+ def test_fee_p2pkh(self):
############################################################
#compare fee of a standard pubkeyhash transaction
inputs = []
@@ -363,9 +382,10 @@ class RawTransactionsTest(BitcoinTestFramework):
#compare fee
feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee)
- assert feeDelta >= 0 and feeDelta <= feeTolerance
+ assert feeDelta >= 0 and feeDelta <= self.fee_tolerance
############################################################
+ def test_fee_p2pkh_multi_out(self):
############################################################
#compare fee of a standard pubkeyhash transaction with multiple outputs
inputs = []
@@ -378,10 +398,10 @@ class RawTransactionsTest(BitcoinTestFramework):
#compare fee
feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee)
- assert feeDelta >= 0 and feeDelta <= feeTolerance
+ assert feeDelta >= 0 and feeDelta <= self.fee_tolerance
############################################################
-
+ def test_fee_p2sh(self):
############################################################
#compare fee of a 2of2 multisig p2sh transaction
@@ -405,10 +425,10 @@ class RawTransactionsTest(BitcoinTestFramework):
#compare fee
feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee)
- assert feeDelta >= 0 and feeDelta <= feeTolerance
+ assert feeDelta >= 0 and feeDelta <= self.fee_tolerance
############################################################
-
+ def test_fee_4of5(self):
############################################################
#compare fee of a standard pubkeyhash transaction
@@ -438,10 +458,10 @@ class RawTransactionsTest(BitcoinTestFramework):
#compare fee
feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee)
- assert feeDelta >= 0 and feeDelta <= feeTolerance
+ assert feeDelta >= 0 and feeDelta <= self.fee_tolerance
############################################################
-
+ def test_spend_2of2(self):
############################################################
# spend a 2of2 multisig transaction over fundraw
@@ -456,7 +476,7 @@ class RawTransactionsTest(BitcoinTestFramework):
# send 1.2 BTC to msig addr
- txId = self.nodes[0].sendtoaddress(mSigObj, 1.2)
+ self.nodes[0].sendtoaddress(mSigObj, 1.2)
self.sync_all()
self.nodes[1].generate(1)
self.sync_all()
@@ -468,7 +488,7 @@ class RawTransactionsTest(BitcoinTestFramework):
fundedTx = self.nodes[2].fundrawtransaction(rawtx)
signedTx = self.nodes[2].signrawtransactionwithwallet(fundedTx['hex'])
- txId = self.nodes[2].sendrawtransaction(signedTx['hex'])
+ self.nodes[2].sendrawtransaction(signedTx['hex'])
self.sync_all()
self.nodes[1].generate(1)
self.sync_all()
@@ -476,6 +496,7 @@ class RawTransactionsTest(BitcoinTestFramework):
# make sure funds are received at node1
assert_equal(oldBalance+Decimal('1.10000000'), self.nodes[1].getbalance())
+ def test_locked_wallet(self):
############################################################
# locked wallet test
self.nodes[1].encryptwallet("test")
@@ -485,7 +506,7 @@ class RawTransactionsTest(BitcoinTestFramework):
# This test is not meant to test fee estimation and we'd like
# to be sure all txs are sent at a consistent desired feerate
for node in self.nodes:
- node.settxfee(min_relay_tx_fee)
+ node.settxfee(self.min_relay_tx_fee)
connect_nodes_bi(self.nodes,0,1)
connect_nodes_bi(self.nodes,1,2)
@@ -493,7 +514,7 @@ class RawTransactionsTest(BitcoinTestFramework):
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.nodes[0].lockunspent(False, [{"txid": self.watchonly_txid, "vout": self.watchonly_vout}])
self.sync_all()
# drain the keypool
@@ -523,14 +544,14 @@ class RawTransactionsTest(BitcoinTestFramework):
#now we need to unlock
self.nodes[1].walletpassphrase("test", 600)
signedTx = self.nodes[1].signrawtransactionwithwallet(fundedTx['hex'])
- txId = self.nodes[1].sendrawtransaction(signedTx['hex'])
+ self.nodes[1].sendrawtransaction(signedTx['hex'])
self.nodes[1].generate(1)
self.sync_all()
# make sure funds are received at node1
assert_equal(oldBalance+Decimal('51.10000000'), self.nodes[0].getbalance())
-
+ def test_many_inputs_fee(self):
###############################################
# multiple (~19) inputs tx test | Compare fee #
###############################################
@@ -558,9 +579,9 @@ class RawTransactionsTest(BitcoinTestFramework):
#compare fee
feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee)
- assert feeDelta >= 0 and feeDelta <= feeTolerance*19 #~19 inputs
-
+ assert feeDelta >= 0 and feeDelta <= self.fee_tolerance * 19 #~19 inputs
+ def test_many_inputs_send(self):
#############################################
# multiple (~19) inputs tx test | sign/send #
#############################################
@@ -584,12 +605,13 @@ class RawTransactionsTest(BitcoinTestFramework):
rawtx = self.nodes[1].createrawtransaction(inputs, outputs)
fundedTx = self.nodes[1].fundrawtransaction(rawtx)
fundedAndSignedTx = self.nodes[1].signrawtransactionwithwallet(fundedTx['hex'])
- txId = self.nodes[1].sendrawtransaction(fundedAndSignedTx['hex'])
+ self.nodes[1].sendrawtransaction(fundedAndSignedTx['hex'])
self.sync_all()
self.nodes[0].generate(1)
self.sync_all()
assert_equal(oldBalance+Decimal('50.19000000'), self.nodes[0].getbalance()) #0.19+block reward
+ def test_op_return(self):
#####################################################
# test fundrawtransaction with OP_RETURN and no vin #
#####################################################
@@ -606,40 +628,41 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_greater_than(len(dec_tx['vin']), 0) # at least one vin
assert_equal(len(dec_tx['vout']), 2) # one change output added
-
+ def test_watchonly(self):
##################################################
# test a fundrawtransaction using only watchonly #
##################################################
inputs = []
- outputs = {self.nodes[2].getnewaddress() : watchonly_amount / 2}
+ outputs = {self.nodes[2].getnewaddress(): self.watchonly_amount / 2}
rawtx = self.nodes[3].createrawtransaction(inputs, outputs)
result = self.nodes[3].fundrawtransaction(rawtx, {'includeWatching': True })
res_dec = self.nodes[0].decoderawtransaction(result["hex"])
assert_equal(len(res_dec["vin"]), 1)
- assert_equal(res_dec["vin"][0]["txid"], watchonly_txid)
+ assert_equal(res_dec["vin"][0]["txid"], self.watchonly_txid)
assert "fee" in result.keys()
assert_greater_than(result["changepos"], -1)
+ def test_all_watched_funds(self):
###############################################################
# test fundrawtransaction using the entirety of watched funds #
###############################################################
inputs = []
- outputs = {self.nodes[2].getnewaddress() : watchonly_amount}
+ outputs = {self.nodes[2].getnewaddress(): self.watchonly_amount}
rawtx = self.nodes[3].createrawtransaction(inputs, outputs)
# Backward compatibility test (2nd param is includeWatching)
result = self.nodes[3].fundrawtransaction(rawtx, True)
res_dec = self.nodes[0].decoderawtransaction(result["hex"])
assert_equal(len(res_dec["vin"]), 2)
- assert res_dec["vin"][0]["txid"] == watchonly_txid or res_dec["vin"][1]["txid"] == watchonly_txid
+ assert res_dec["vin"][0]["txid"] == self.watchonly_txid or res_dec["vin"][1]["txid"] == self.watchonly_txid
assert_greater_than(result["fee"], 0)
assert_greater_than(result["changepos"], -1)
- assert_equal(result["fee"] + res_dec["vout"][result["changepos"]]["value"], watchonly_amount / 10)
+ assert_equal(result["fee"] + res_dec["vout"][result["changepos"]]["value"], self.watchonly_amount / 10)
signedtx = self.nodes[3].signrawtransactionwithwallet(result["hex"])
assert not signedtx["complete"]
@@ -649,6 +672,7 @@ class RawTransactionsTest(BitcoinTestFramework):
self.nodes[0].generate(1)
self.sync_all()
+ def test_option_feerate(self):
#######################
# Test feeRate option #
#######################
@@ -659,18 +683,20 @@ class RawTransactionsTest(BitcoinTestFramework):
inputs = []
outputs = {self.nodes[3].getnewaddress() : 1}
rawtx = self.nodes[3].createrawtransaction(inputs, outputs)
- result = self.nodes[3].fundrawtransaction(rawtx) # uses min_relay_tx_fee (set by settxfee)
- result2 = self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 2*min_relay_tx_fee})
- result3 = self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 10*min_relay_tx_fee})
+ result = self.nodes[3].fundrawtransaction(rawtx) # uses self.min_relay_tx_fee (set by settxfee)
+ result2 = self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 2 * self.min_relay_tx_fee})
+ result3 = self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 10 * self.min_relay_tx_fee})
assert_raises_rpc_error(-4, "Fee exceeds maximum configured by -maxtxfee", self.nodes[3].fundrawtransaction, rawtx, {"feeRate": 1})
result_fee_rate = result['fee'] * 1000 / count_bytes(result['hex'])
assert_fee_amount(result2['fee'], count_bytes(result2['hex']), 2 * result_fee_rate)
assert_fee_amount(result3['fee'], count_bytes(result3['hex']), 10 * result_fee_rate)
+ def test_address_reuse(self):
################################
# Test no address reuse occurs #
################################
+ rawtx = self.nodes[3].createrawtransaction(inputs=[], outputs={self.nodes[3].getnewaddress(): 1})
result3 = self.nodes[3].fundrawtransaction(rawtx)
res_dec = self.nodes[0].decoderawtransaction(result3["hex"])
changeaddress = ""
@@ -682,6 +708,7 @@ class RawTransactionsTest(BitcoinTestFramework):
# Now the change address key should be removed from the keypool
assert changeaddress != nextaddr
+ def test_option_subtract_fee_from_outputs(self):
######################################
# Test subtractFeeFromOutputs option #
######################################
@@ -693,11 +720,11 @@ class RawTransactionsTest(BitcoinTestFramework):
outputs = {self.nodes[2].getnewaddress(): 1}
rawtx = self.nodes[3].createrawtransaction(inputs, outputs)
- result = [self.nodes[3].fundrawtransaction(rawtx), # uses min_relay_tx_fee (set by settxfee)
- self.nodes[3].fundrawtransaction(rawtx, {"subtractFeeFromOutputs": []}), # empty subtraction list
- self.nodes[3].fundrawtransaction(rawtx, {"subtractFeeFromOutputs": [0]}), # uses min_relay_tx_fee (set by settxfee)
- self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 2*min_relay_tx_fee}),
- self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 2*min_relay_tx_fee, "subtractFeeFromOutputs": [0]})]
+ result = [self.nodes[3].fundrawtransaction(rawtx), # uses self.min_relay_tx_fee (set by settxfee)
+ self.nodes[3].fundrawtransaction(rawtx, {"subtractFeeFromOutputs": []}), # empty subtraction list
+ self.nodes[3].fundrawtransaction(rawtx, {"subtractFeeFromOutputs": [0]}), # uses self.min_relay_tx_fee (set by settxfee)
+ self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 2 * self.min_relay_tx_fee}),
+ self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 2 * self.min_relay_tx_fee, "subtractFeeFromOutputs": [0]}),]
dec_tx = [self.nodes[3].decoderawtransaction(tx_['hex']) for tx_ in result]
output = [d['vout'][1 - r['changepos']]['value'] for d, r in zip(dec_tx, result)]
diff --git a/test/functional/rpc_invalidateblock.py b/test/functional/rpc_invalidateblock.py
index 3d3f694fd3..595b40f7cb 100755
--- a/test/functional/rpc_invalidateblock.py
+++ b/test/functional/rpc_invalidateblock.py
@@ -8,7 +8,7 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE
from test_framework.util import (
assert_equal,
- connect_nodes_bi,
+ connect_nodes,
wait_until,
)
@@ -33,7 +33,7 @@ class InvalidateTest(BitcoinTestFramework):
assert_equal(self.nodes[1].getblockcount(), 6)
self.log.info("Connect nodes to force a reorg")
- connect_nodes_bi(self.nodes, 0, 1)
+ connect_nodes(self.nodes[0], 1)
self.sync_blocks(self.nodes[0:2])
assert_equal(self.nodes[0].getblockcount(), 6)
badhash = self.nodes[1].getblockhash(2)
@@ -44,7 +44,7 @@ class InvalidateTest(BitcoinTestFramework):
assert_equal(self.nodes[0].getbestblockhash(), besthash_n0)
self.log.info("Make sure we won't reorg to a lower work chain:")
- connect_nodes_bi(self.nodes, 1, 2)
+ connect_nodes(self.nodes[1], 2)
self.log.info("Sync node 2 to node 1 so both have 6 blocks")
self.sync_blocks(self.nodes[1:3])
assert_equal(self.nodes[2].getblockcount(), 6)
diff --git a/test/functional/rpc_net.py b/test/functional/rpc_net.py
index b12eb1d9ec..104c66aaa7 100755
--- a/test/functional/rpc_net.py
+++ b/test/functional/rpc_net.py
@@ -20,7 +20,32 @@ from test_framework.util import (
wait_until,
)
from test_framework.mininode import P2PInterface
-from test_framework.messages import CAddress, msg_addr, NODE_NETWORK, NODE_WITNESS
+from test_framework.messages import (
+ CAddress,
+ msg_addr,
+ NODE_NETWORK,
+ NODE_WITNESS,
+ NODE_GETUTXO,NODE_BLOOM,
+ NODE_NETWORK_LIMITED,
+)
+
+def assert_net_servicesnames(servicesflag, servicenames):
+ """Utility that checks if all flags are correctly decoded in
+ `getpeerinfo` and `getnetworkinfo`.
+
+ :param servicesflag: The services as an integer.
+ :param servicesnames: The list of decoded services names, as strings.
+ """
+ if servicesflag & NODE_NETWORK:
+ assert "NETWORK" in servicenames
+ if servicesflag & NODE_GETUTXO:
+ assert "GETUTXO" in servicenames
+ if servicesflag & NODE_BLOOM:
+ assert "BLOOM" in servicenames
+ if servicesflag & NODE_WITNESS:
+ assert "WITNESS" in servicenames
+ if servicesflag & NODE_NETWORK_LIMITED:
+ assert "NETWORK_LIMITED" in servicenames
class NetTest(BitcoinTestFramework):
def set_test_params(self):
@@ -31,7 +56,7 @@ class NetTest(BitcoinTestFramework):
def run_test(self):
self._test_connection_count()
self._test_getnettotals()
- self._test_getnetworkinginfo()
+ self._test_getnetworkinfo()
self._test_getaddednodeinfo()
self._test_getpeerinfo()
self._test_getnodeaddresses()
@@ -70,7 +95,7 @@ class NetTest(BitcoinTestFramework):
assert_greater_than_or_equal(after['bytesrecv_per_msg'].get('pong', 0), before['bytesrecv_per_msg'].get('pong', 0) + 32)
assert_greater_than_or_equal(after['bytessent_per_msg'].get('ping', 0), before['bytessent_per_msg'].get('ping', 0) + 32)
- def _test_getnetworkinginfo(self):
+ def _test_getnetworkinfo(self):
assert_equal(self.nodes[0].getnetworkinfo()['networkactive'], True)
assert_equal(self.nodes[0].getnetworkinfo()['connections'], 2)
@@ -84,6 +109,11 @@ class NetTest(BitcoinTestFramework):
assert_equal(self.nodes[0].getnetworkinfo()['networkactive'], True)
assert_equal(self.nodes[0].getnetworkinfo()['connections'], 2)
+ # check the `servicesnames` field
+ network_info = [node.getnetworkinfo() for node in self.nodes]
+ for info in network_info:
+ assert_net_servicesnames(int(info["localservices"]), info["localservicesnames"])
+
def _test_getaddednodeinfo(self):
assert_equal(self.nodes[0].getaddednodeinfo(), [])
# add a node (node2) to node0
@@ -104,6 +134,9 @@ class NetTest(BitcoinTestFramework):
assert_equal(peer_info[1][0]['addrbind'], peer_info[0][0]['addr'])
assert_equal(peer_info[0][0]['minfeefilter'], Decimal("0.00000500"))
assert_equal(peer_info[1][0]['minfeefilter'], Decimal("0.00001000"))
+ # check the `servicesnames` field
+ for info in peer_info:
+ assert_net_servicesnames(int(info[0]["services"]), info[0]["servicesnames"])
def _test_getnodeaddresses(self):
self.nodes[0].add_p2p_connection(P2PInterface())
diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py
index 493e025e43..cb8320a6c2 100755
--- a/test/functional/rpc_psbt.py
+++ b/test/functional/rpc_psbt.py
@@ -27,6 +27,11 @@ class PSBTTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = False
self.num_nodes = 3
+ self.extra_args = [
+ ["-walletrbf=1"],
+ ["-walletrbf=0"],
+ []
+ ]
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
@@ -207,18 +212,18 @@ class PSBTTest(BitcoinTestFramework):
# replaceable arg
block_height = self.nodes[0].getblockcount()
unspent = self.nodes[0].listunspent()[0]
- psbtx_info = self.nodes[0].walletcreatefundedpsbt([{"txid":unspent["txid"], "vout":unspent["vout"]}], [{self.nodes[2].getnewaddress():unspent["amount"]+1}], block_height+2, {"replaceable":True}, False)
+ psbtx_info = self.nodes[0].walletcreatefundedpsbt([{"txid":unspent["txid"], "vout":unspent["vout"]}], [{self.nodes[2].getnewaddress():unspent["amount"]+1}], block_height+2, {"replaceable": False}, False)
decoded_psbt = self.nodes[0].decodepsbt(psbtx_info["psbt"])
for tx_in, psbt_in in zip(decoded_psbt["tx"]["vin"], decoded_psbt["inputs"]):
- assert_equal(tx_in["sequence"], MAX_BIP125_RBF_SEQUENCE)
+ assert_greater_than(tx_in["sequence"], MAX_BIP125_RBF_SEQUENCE)
assert "bip32_derivs" not in psbt_in
assert_equal(decoded_psbt["tx"]["locktime"], block_height+2)
- # Same construction with only locktime set
- psbtx_info = self.nodes[0].walletcreatefundedpsbt([{"txid":unspent["txid"], "vout":unspent["vout"]}], [{self.nodes[2].getnewaddress():unspent["amount"]+1}], block_height, {}, True)
+ # Same construction with only locktime set and RBF explicitly enabled
+ psbtx_info = self.nodes[0].walletcreatefundedpsbt([{"txid":unspent["txid"], "vout":unspent["vout"]}], [{self.nodes[2].getnewaddress():unspent["amount"]+1}], block_height, {"replaceable": True}, True)
decoded_psbt = self.nodes[0].decodepsbt(psbtx_info["psbt"])
for tx_in, psbt_in in zip(decoded_psbt["tx"]["vin"], decoded_psbt["inputs"]):
- assert tx_in["sequence"] > MAX_BIP125_RBF_SEQUENCE
+ assert_equal(tx_in["sequence"], MAX_BIP125_RBF_SEQUENCE)
assert "bip32_derivs" in psbt_in
assert_equal(decoded_psbt["tx"]["locktime"], block_height)
@@ -226,9 +231,16 @@ class PSBTTest(BitcoinTestFramework):
psbtx_info = self.nodes[0].walletcreatefundedpsbt([{"txid":unspent["txid"], "vout":unspent["vout"]}], [{self.nodes[2].getnewaddress():unspent["amount"]+1}])
decoded_psbt = self.nodes[0].decodepsbt(psbtx_info["psbt"])
for tx_in in decoded_psbt["tx"]["vin"]:
- assert tx_in["sequence"] > MAX_BIP125_RBF_SEQUENCE
+ assert_equal(tx_in["sequence"], MAX_BIP125_RBF_SEQUENCE)
assert_equal(decoded_psbt["tx"]["locktime"], 0)
+ # Same construction without optional arguments, for a node with -walletrbf=0
+ unspent1 = self.nodes[1].listunspent()[0]
+ psbtx_info = self.nodes[1].walletcreatefundedpsbt([{"txid":unspent1["txid"], "vout":unspent1["vout"]}], [{self.nodes[2].getnewaddress():unspent1["amount"]+1}], block_height)
+ decoded_psbt = self.nodes[1].decodepsbt(psbtx_info["psbt"])
+ for tx_in in decoded_psbt["tx"]["vin"]:
+ assert_greater_than(tx_in["sequence"], MAX_BIP125_RBF_SEQUENCE)
+
# Make sure change address wallet does not have P2SH innerscript access to results in success
# when attempting BnB coin selection
self.nodes[0].walletcreatefundedpsbt([], [{self.nodes[2].getnewaddress():unspent["amount"]+1}], block_height+2, {"changeAddress":self.nodes[1].getnewaddress()}, False)
diff --git a/test/functional/rpc_scantxoutset.py b/test/functional/rpc_scantxoutset.py
index a1cd33ad54..9f94d11a93 100755
--- a/test/functional/rpc_scantxoutset.py
+++ b/test/functional/rpc_scantxoutset.py
@@ -58,6 +58,13 @@ class ScantxoutsetTest(BitcoinTestFramework):
self.start_node(0)
self.nodes[0].generate(110)
+ scan = self.nodes[0].scantxoutset("start", [])
+ info = self.nodes[0].gettxoutsetinfo()
+ assert_equal(scan['success'], True)
+ assert_equal(scan['height'], info['height'])
+ assert_equal(scan['txouts'], info['txouts'])
+ assert_equal(scan['bestblock'], info['bestblock'])
+
self.restart_node(0, ['-nowallet'])
self.log.info("Test if we have found the non HD unspent outputs.")
assert_equal(self.nodes[0].scantxoutset("start", [ "pkh(" + pubk1 + ")", "pkh(" + pubk2 + ")", "pkh(" + pubk3 + ")"])['total_amount'], Decimal("0.002"))
diff --git a/test/functional/rpc_setban.py b/test/functional/rpc_setban.py
new file mode 100755
index 0000000000..b1d2b6f431
--- /dev/null
+++ b/test/functional/rpc_setban.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python3
+# Copyright (c) 2015-2019 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Test the setban rpc call."""
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ connect_nodes,
+ p2p_port
+)
+
+class SetBanTests(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 2
+ self.setup_clean_chain = True
+ self.extra_args = [[],[]]
+
+ def run_test(self):
+ # Node 0 connects to Node 1, check that the noban permission is not granted
+ connect_nodes(self.nodes[0], 1)
+ peerinfo = self.nodes[1].getpeerinfo()[0]
+ assert(not 'noban' in peerinfo['permissions'])
+
+ # Node 0 get banned by Node 1
+ self.nodes[1].setban("127.0.0.1", "add")
+
+ # Node 0 should not be able to reconnect
+ with self.nodes[1].assert_debug_log(expected_msgs=['dropped (banned)\n'], timeout=5):
+ self.restart_node(1, [])
+ self.nodes[0].addnode("127.0.0.1:" + str(p2p_port(1)), "onetry")
+
+ # However, node 0 should be able to reconnect if it has noban permission
+ self.restart_node(1, ['-whitelist=127.0.0.1'])
+ connect_nodes(self.nodes[0], 1)
+ peerinfo = self.nodes[1].getpeerinfo()[0]
+ assert('noban' in peerinfo['permissions'])
+
+ # If we remove the ban, Node 0 should be able to reconnect even without noban permission
+ self.nodes[1].setban("127.0.0.1", "remove")
+ self.restart_node(1, [])
+ connect_nodes(self.nodes[0], 1)
+ peerinfo = self.nodes[1].getpeerinfo()[0]
+ assert(not 'noban' in peerinfo['permissions'])
+
+if __name__ == '__main__':
+ SetBanTests().main()
diff --git a/test/functional/test_framework/address.py b/test/functional/test_framework/address.py
index f36cffe957..194f2f061b 100644
--- a/test/functional/test_framework/address.py
+++ b/test/functional/test_framework/address.py
@@ -4,6 +4,8 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Encode and decode BASE58, P2PKH and P2SH addresses."""
+import enum
+
from .script import hash256, hash160, sha256, CScript, OP_0
from .util import hex_str_to_bytes
@@ -11,6 +13,13 @@ from . import segwit_addr
ADDRESS_BCRT1_UNSPENDABLE = 'bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3xueyj'
+
+class AddressType(enum.Enum):
+ bech32 = 'bech32'
+ p2sh_segwit = 'p2sh-segwit'
+ legacy = 'legacy' # P2PKH
+
+
chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
diff --git a/test/functional/test_framework/blocktools.py b/test/functional/test_framework/blocktools.py
index 7ac044d0d0..d741b00ba0 100644
--- a/test/functional/test_framework/blocktools.py
+++ b/test/functional/test_framework/blocktools.py
@@ -22,13 +22,14 @@ from .messages import (
ToHex,
hash256,
hex_str_to_bytes,
- ser_string,
ser_uint256,
sha256,
uint256_from_str,
)
from .script import (
CScript,
+ CScriptNum,
+ CScriptOp,
OP_0,
OP_1,
OP_CHECKMULTISIG,
@@ -89,20 +90,14 @@ 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:
- return r
- neg = value < 0
- absvalue = -value if neg else value
- while (absvalue):
- r.append(int(absvalue & 0xff))
- absvalue >>= 8
- if r[-1] & 0x80:
- r.append(0x80 if neg else 0)
- elif neg:
- r[-1] |= 0x80
- return r
+
+def script_BIP34_coinbase_height(height):
+ if height <= 16:
+ res = CScriptOp.encode_op_n(height)
+ # Append dummy to increase scriptSig size above 2 (see bad-cb-length consensus rule)
+ return CScript([res, OP_1])
+ return CScript([CScriptNum(height)])
+
def create_coinbase(height, pubkey=None):
"""Create a coinbase transaction, assuming no miner fees.
@@ -110,8 +105,7 @@ def create_coinbase(height, pubkey=None):
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), script_BIP34_coinbase_height(height), 0xffffffff))
coinbaseoutput = CTxOut()
coinbaseoutput.nValue = 50 * COIN
halvings = int(height / 150) # regtest
diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py
index 89a5a65e64..3cb5f56bee 100755
--- a/test/functional/test_framework/messages.py
+++ b/test/functional/test_framework/messages.py
@@ -43,8 +43,8 @@ COIN = 100000000 # 1 btc in satoshis
BIP125_SEQUENCE_NUMBER = 0xfffffffd # Sequence number that is BIP 125 opt-in and BIP 68-opt-out
NODE_NETWORK = (1 << 0)
-# NODE_GETUTXO = (1 << 1)
-# NODE_BLOOM = (1 << 2)
+NODE_GETUTXO = (1 << 1)
+NODE_BLOOM = (1 << 2)
NODE_WITNESS = (1 << 3)
NODE_NETWORK_LIMITED = (1 << 10)
@@ -803,7 +803,9 @@ class HeaderAndShortIDs:
return [ key0, key1 ]
# Version 2 compact blocks use wtxid in shortids (rather than txid)
- def initialize_from_block(self, block, nonce=0, prefill_list = [0], use_witness = False):
+ def initialize_from_block(self, block, nonce=0, prefill_list=None, use_witness=False):
+ if prefill_list is None:
+ prefill_list = [0]
self.header = CBlockHeader(block)
self.nonce = nonce
self.prefilled_txn = [ PrefilledTransaction(i, block.vtx[i]) for i in prefill_list ]
diff --git a/test/functional/test_framework/mininode.py b/test/functional/test_framework/mininode.py
index cc3a4cc72a..166438cf70 100755
--- a/test/functional/test_framework/mininode.py
+++ b/test/functional/test_framework/mininode.py
@@ -111,7 +111,7 @@ class P2PConnection(asyncio.Protocol):
def is_connected(self):
return self._transport is not None
- def peer_connect(self, dstaddr, dstport, net="regtest"):
+ def peer_connect(self, dstaddr, dstport, *, net):
assert not self.is_connected
self.dstaddr = dstaddr
self.dstport = dstport
@@ -363,6 +363,7 @@ class P2PInterface(P2PConnection):
def wait_for_tx(self, txid, timeout=60):
def test_function():
+ assert self.is_connected
if not self.last_message.get('tx'):
return False
return self.last_message['tx'].tx.rehash() == txid
@@ -370,11 +371,15 @@ class P2PInterface(P2PConnection):
wait_until(test_function, timeout=timeout, lock=mininode_lock)
def wait_for_block(self, blockhash, timeout=60):
- test_function = lambda: self.last_message.get("block") and self.last_message["block"].block.rehash() == blockhash
+ def test_function():
+ assert self.is_connected
+ return self.last_message.get("block") and self.last_message["block"].block.rehash() == blockhash
+
wait_until(test_function, timeout=timeout, lock=mininode_lock)
def wait_for_header(self, blockhash, timeout=60):
def test_function():
+ assert self.is_connected
last_headers = self.last_message.get('headers')
if not last_headers:
return False
@@ -389,7 +394,11 @@ class P2PInterface(P2PConnection):
value must be explicitly cleared before calling this method, or this will return
immediately with success. TODO: change this method to take a hash value and only
return true if the correct block/tx has been requested."""
- test_function = lambda: self.last_message.get("getdata")
+
+ def test_function():
+ assert self.is_connected
+ return self.last_message.get("getdata")
+
wait_until(test_function, timeout=timeout, lock=mininode_lock)
def wait_for_getheaders(self, timeout=60):
@@ -399,20 +408,30 @@ class P2PInterface(P2PConnection):
value must be explicitly cleared before calling this method, or this will return
immediately with success. TODO: change this method to take a hash value and only
return true if the correct block header has been requested."""
- test_function = lambda: self.last_message.get("getheaders")
+
+ def test_function():
+ assert self.is_connected
+ return self.last_message.get("getheaders")
+
wait_until(test_function, timeout=timeout, lock=mininode_lock)
def wait_for_inv(self, expected_inv, timeout=60):
"""Waits for an INV message and checks that the first inv object in the message was as expected."""
if len(expected_inv) > 1:
raise NotImplementedError("wait_for_inv() will only verify the first inv object")
- test_function = lambda: self.last_message.get("inv") and \
+
+ def test_function():
+ assert self.is_connected
+ return self.last_message.get("inv") and \
self.last_message["inv"].inv[0].type == expected_inv[0].type and \
self.last_message["inv"].inv[0].hash == expected_inv[0].hash
+
wait_until(test_function, timeout=timeout, lock=mininode_lock)
def wait_for_verack(self, timeout=60):
- test_function = lambda: self.message_count["verack"]
+ def test_function():
+ return self.message_count["verack"]
+
wait_until(test_function, timeout=timeout, lock=mininode_lock)
# Message sending helper functions
@@ -424,7 +443,11 @@ class P2PInterface(P2PConnection):
# Sync up with the node
def sync_with_ping(self, timeout=60):
self.send_message(msg_ping(nonce=self.ping_counter))
- test_function = lambda: self.last_message.get("pong") and self.last_message["pong"].nonce == self.ping_counter
+
+ def test_function():
+ assert self.is_connected
+ return self.last_message.get("pong") and self.last_message["pong"].nonce == self.ping_counter
+
wait_until(test_function, timeout=timeout, lock=mininode_lock)
self.ping_counter += 1
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index aa674f9ebe..9aff08fdc7 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -91,6 +91,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
def __init__(self):
"""Sets test framework defaults. Do not override this method. Instead, override the set_test_params() method"""
+ self.chain = 'regtest'
self.setup_clean_chain = False
self.nodes = []
self.network_thread = None
@@ -192,18 +193,18 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
self.setup_network()
self.run_test()
success = TestStatus.PASSED
- except JSONRPCException as e:
+ except JSONRPCException:
self.log.exception("JSONRPC error")
except SkipTest as e:
self.log.warning("Test Skipped: %s" % e.message)
success = TestStatus.SKIPPED
- except AssertionError as e:
+ except AssertionError:
self.log.exception("Assertion failed")
- except KeyError as e:
+ except KeyError:
self.log.exception("Key error")
- except Exception as e:
+ except Exception:
self.log.exception("Unexpected exception caught during testing")
- except KeyboardInterrupt as e:
+ except KeyboardInterrupt:
self.log.warning("Exiting after keyboard interrupt")
if success == TestStatus.FAILED and self.options.pdbonfailure:
@@ -342,6 +343,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
self.nodes.append(TestNode(
i,
get_datadir_path(self.options.tmpdir, i),
+ chain=self.chain,
rpchost=rpchost,
timewait=self.rpc_timeout,
bitcoind=binary[i],
@@ -477,11 +479,12 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
if not os.path.isdir(cache_node_dir):
self.log.debug("Creating cache directory {}".format(cache_node_dir))
- initialize_datadir(self.options.cachedir, CACHE_NODE_ID)
+ initialize_datadir(self.options.cachedir, CACHE_NODE_ID, self.chain)
self.nodes.append(
TestNode(
CACHE_NODE_ID,
cache_node_dir,
+ chain=self.chain,
extra_conf=["bind=127.0.0.1"],
extra_args=['-disablewallet'],
rpchost=None,
@@ -515,7 +518,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
self.nodes = []
def cache_path(*paths):
- return os.path.join(cache_node_dir, "regtest", *paths)
+ return os.path.join(cache_node_dir, self.chain, *paths)
os.rmdir(cache_path('wallets')) # Remove empty wallets dir
for entry in os.listdir(cache_path()):
@@ -526,7 +529,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
self.log.debug("Copy cache directory {} to node {}".format(cache_node_dir, i))
to_dir = get_datadir_path(self.options.tmpdir, i)
shutil.copytree(cache_node_dir, to_dir)
- initialize_datadir(self.options.tmpdir, i) # Overwrite port/rpcport in bitcoin.conf
+ initialize_datadir(self.options.tmpdir, i, self.chain) # Overwrite port/rpcport in bitcoin.conf
def _initialize_chain_clean(self):
"""Initialize empty blockchain for use by the test.
@@ -534,7 +537,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
Create an empty blockchain and num_nodes wallets.
Useful if a test case wants complete control over initialization."""
for i in range(self.num_nodes):
- initialize_datadir(self.options.tmpdir, i)
+ initialize_datadir(self.options.tmpdir, i, self.chain)
def skip_if_no_py3_zmq(self):
"""Attempt to import the zmq package and skip the test if the import fails."""
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index 3311377090..d8bfdfd040 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -59,7 +59,7 @@ 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, bitcoind, bitcoin_cli, coverage_dir, cwd, extra_conf=None, extra_args=None, use_cli=False, start_perf=False):
+ def __init__(self, i, datadir, *, chain, rpchost, timewait, bitcoind, bitcoin_cli, coverage_dir, cwd, extra_conf=None, extra_args=None, use_cli=False, start_perf=False):
"""
Kwargs:
start_perf (bool): If True, begin profiling the node with `perf` as soon as
@@ -68,8 +68,10 @@ class TestNode():
self.index = i
self.datadir = datadir
+ self.bitcoinconf = os.path.join(self.datadir, "bitcoin.conf")
self.stdout_dir = os.path.join(self.datadir, "stdout")
self.stderr_dir = os.path.join(self.datadir, "stderr")
+ self.chain = chain
self.rpchost = rpchost
self.rpc_timeout = timewait
self.binary = bitcoind
@@ -197,7 +199,7 @@ class TestNode():
# 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)
+ delete_cookie_file(self.datadir, self.chain)
# 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")
@@ -219,7 +221,7 @@ class TestNode():
raise FailedToStartError(self._node_msg(
'bitcoind exited with status {} during initialization'.format(self.process.returncode)))
try:
- rpc = get_rpc_proxy(rpc_url(self.datadir, self.index, self.rpchost), self.index, timeout=self.rpc_timeout, coveragedir=self.coverage_dir)
+ rpc = get_rpc_proxy(rpc_url(self.datadir, self.index, self.chain, self.rpchost), self.index, timeout=self.rpc_timeout, coveragedir=self.coverage_dir)
rpc.getblockcount()
# If the call to getblockcount() succeeds then the RPC connection is up
self.log.debug("RPC successfully started")
@@ -305,21 +307,30 @@ class TestNode():
wait_until(self.is_node_stopped, timeout=timeout)
@contextlib.contextmanager
- def assert_debug_log(self, expected_msgs):
- debug_log = os.path.join(self.datadir, 'regtest', 'debug.log')
+ def assert_debug_log(self, expected_msgs, timeout=2):
+ time_end = time.time() + timeout
+ debug_log = os.path.join(self.datadir, self.chain, 'debug.log')
with open(debug_log, encoding='utf-8') as dl:
dl.seek(0, 2)
prev_size = dl.tell()
- try:
- yield
- finally:
+
+ yield
+
+ while True:
+ found = True
with open(debug_log, encoding='utf-8') as dl:
dl.seek(prev_size)
log = dl.read()
print_log = " - " + "\n - ".join(log.splitlines())
for expected_msg in expected_msgs:
if re.search(re.escape(expected_msg), log, flags=re.MULTILINE) is None:
- self._raise_assertion_error('Expected message "{}" does not partially match log:\n\n{}\n\n'.format(expected_msg, print_log))
+ found = False
+ if found:
+ return
+ if time.time() >= time_end:
+ break
+ time.sleep(0.05)
+ self._raise_assertion_error('Expected messages "{}" does not partially match log:\n\n{}\n\n'.format(str(expected_msgs), print_log))
@contextlib.contextmanager
def assert_memory_usage_stable(self, *, increase_allowed=0.03):
@@ -478,7 +489,7 @@ class TestNode():
if 'dstaddr' not in kwargs:
kwargs['dstaddr'] = '127.0.0.1'
- p2p_conn.peer_connect(**kwargs)()
+ p2p_conn.peer_connect(**kwargs, net=self.chain)()
self.p2ps.append(p2p_conn)
if wait_for_verack:
p2p_conn.wait_for_verack()
diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py
index efd962ea93..f9f5fe553e 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -7,7 +7,6 @@
from base64 import b64encode
from binascii import unhexlify
from decimal import Decimal, ROUND_DOWN
-import hashlib
import inspect
import json
import logging
@@ -57,7 +56,9 @@ def assert_raises_message(exc, message, fun, *args, **kwds):
raise AssertionError("Use assert_raises_rpc_error() to test RPC failures")
except exc as e:
if message is not None and message not in e.error['message']:
- raise AssertionError("Expected substring not found:" + e.error['message'])
+ raise AssertionError(
+ "Expected substring not found in error message:\nsubstring: '{}'\nerror message: '{}'.".format(
+ message, e.error['message']))
except Exception as e:
raise AssertionError("Unexpected exception raised: " + type(e).__name__)
else:
@@ -117,7 +118,9 @@ def try_rpc(code, message, fun, *args, **kwds):
if (code is not None) and (code != e.error["code"]):
raise AssertionError("Unexpected JSONRPC error code %i" % e.error["code"])
if (message is not None) and (message not in e.error['message']):
- raise AssertionError("Expected substring not found:" + e.error['message'])
+ raise AssertionError(
+ "Expected substring not found in error message:\nsubstring: '{}'\nerror message: '{}'.".format(
+ message, e.error['message']))
return True
except Exception as e:
raise AssertionError("Unexpected exception raised: " + type(e).__name__)
@@ -183,12 +186,6 @@ def check_json_precision():
def count_bytes(hex_string):
return len(bytearray.fromhex(hex_string))
-def hash256(byte_str):
- sha256 = hashlib.sha256()
- sha256.update(byte_str)
- sha256d = hashlib.sha256()
- sha256d.update(sha256.digest())
- return sha256d.digest()[::-1]
def hex_str_to_bytes(hex_str):
return unhexlify(hex_str.encode('ascii'))
@@ -271,8 +268,8 @@ def p2p_port(n):
def rpc_port(n):
return PORT_MIN + PORT_RANGE + n + (MAX_NODES * PortSeed.n) % (PORT_RANGE - 1 - MAX_NODES)
-def rpc_url(datadir, i, rpchost=None):
- rpc_u, rpc_p = get_auth_cookie(datadir)
+def rpc_url(datadir, i, chain, rpchost):
+ rpc_u, rpc_p = get_auth_cookie(datadir, chain)
host = '127.0.0.1'
port = rpc_port(i)
if rpchost:
@@ -286,20 +283,29 @@ def rpc_url(datadir, i, rpchost=None):
# Node functions
################
-def initialize_datadir(dirname, n):
+def initialize_datadir(dirname, n, chain):
datadir = get_datadir_path(dirname, n)
if not os.path.isdir(datadir):
os.makedirs(datadir)
+ # Translate chain name to config name
+ if chain == 'testnet3':
+ chain_name_conf_arg = 'testnet'
+ chain_name_conf_section = 'test'
+ else:
+ chain_name_conf_arg = chain
+ chain_name_conf_section = chain
with open(os.path.join(datadir, "bitcoin.conf"), 'w', encoding='utf8') as f:
- f.write("regtest=1\n")
- f.write("[regtest]\n")
+ f.write("{}=1\n".format(chain_name_conf_arg))
+ f.write("[{}]\n".format(chain_name_conf_section))
f.write("port=" + str(p2p_port(n)) + "\n")
f.write("rpcport=" + str(rpc_port(n)) + "\n")
f.write("server=1\n")
f.write("keypool=1\n")
f.write("discover=0\n")
+ f.write("dnsseed=0\n")
f.write("listenonion=0\n")
f.write("printtoconsole=0\n")
+ f.write("upnp=0\n")
os.makedirs(os.path.join(datadir, 'stderr'), exist_ok=True)
os.makedirs(os.path.join(datadir, 'stdout'), exist_ok=True)
return datadir
@@ -312,7 +318,7 @@ def append_config(datadir, options):
for option in options:
f.write(option + "\n")
-def get_auth_cookie(datadir):
+def get_auth_cookie(datadir, chain):
user = None
password = None
if os.path.isfile(os.path.join(datadir, "bitcoin.conf")):
@@ -325,7 +331,7 @@ def get_auth_cookie(datadir):
assert password is None # Ensure that there is only one rpcpassword line
password = line.split("=")[1].strip("\n")
try:
- with open(os.path.join(datadir, "regtest", ".cookie"), 'r', encoding="ascii") as f:
+ with open(os.path.join(datadir, chain, ".cookie"), 'r', encoding="ascii") as f:
userpass = f.read()
split_userpass = userpass.split(':')
user = split_userpass[0]
@@ -337,14 +343,14 @@ def get_auth_cookie(datadir):
return user, password
# If a cookie file exists in the given datadir, delete it.
-def delete_cookie_file(datadir):
- if os.path.isfile(os.path.join(datadir, "regtest", ".cookie")):
+def delete_cookie_file(datadir, chain):
+ if os.path.isfile(os.path.join(datadir, chain, ".cookie")):
logger.debug("Deleting leftover cookie file")
- os.remove(os.path.join(datadir, "regtest", ".cookie"))
+ os.remove(os.path.join(datadir, chain, ".cookie"))
-def get_bip9_status(node, key):
- info = node.getblockchaininfo()
- return info['bip9_softforks'][key]
+def softfork_active(node, key):
+ """Return whether a softfork is active."""
+ return node.getblockchaininfo()['softforks'][key]['active']
def set_node_times(nodes, t):
for node in nodes:
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 7ad2b78d4e..9c92091f1d 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -76,7 +76,6 @@ EXTENDED_SCRIPTS = [
BASE_SCRIPTS = [
# Scripts that are run by default.
# Longest test should go first, to favor running tests in parallel
- 'feature_fee_estimation.py',
'wallet_hd.py',
'wallet_backup.py',
# vv Tests less than 5m vv
@@ -91,6 +90,7 @@ BASE_SCRIPTS = [
'wallet_labels.py',
'p2p_segwit.py',
'p2p_timeouts.py',
+ 'p2p_tx_download.py',
'wallet_dump.py',
'wallet_listtransactions.py',
# vv Tests less than 60s vv
@@ -110,6 +110,7 @@ BASE_SCRIPTS = [
'feature_abortnode.py',
# vv Tests less than 30s vv
'wallet_keypool_topup.py',
+ 'feature_fee_estimation.py',
'interface_zmq.py',
'interface_bitcoin_cli.py',
'mempool_resurrect.py',
@@ -128,6 +129,9 @@ BASE_SCRIPTS = [
'wallet_multiwallet.py --usecli',
'wallet_createwallet.py',
'wallet_createwallet.py --usecli',
+ 'wallet_watchonly.py',
+ 'wallet_watchonly.py --usecli',
+ 'wallet_reorgsrestore.py',
'interface_http.py',
'interface_rpc.py',
'rpc_psbt.py',
@@ -143,6 +147,7 @@ BASE_SCRIPTS = [
'rpc_net.py',
'wallet_keypool.py',
'p2p_mempool.py',
+ 'rpc_setban.py',
'p2p_blocksonly.py',
'mining_prioritisetransaction.py',
'p2p_invalid_locator.py',
@@ -192,6 +197,7 @@ BASE_SCRIPTS = [
'feature_uacomment.py',
'wallet_coinbase_category.py',
'feature_filelock.py',
+ 'p2p_dos_header_tree.py',
'p2p_unrequested_blocks.py',
'feature_includeconf.py',
'rpc_deriveaddresses.py',
@@ -199,6 +205,7 @@ BASE_SCRIPTS = [
'rpc_scantxoutset.py',
'feature_logging.py',
'p2p_node_network_limited.py',
+ 'p2p_permissions.py',
'feature_blocksdir.py',
'feature_config_args.py',
'rpc_help.py',
@@ -226,6 +233,7 @@ def main():
epilog='''
Help text and arguments for individual test script:''',
formatter_class=argparse.RawTextHelpFormatter)
+ parser.add_argument('--ansi', action='store_true', default=sys.stdout.isatty(), help="Use ANSI colors and dots in output (enabled by default when standard output is a TTY)")
parser.add_argument('--combinedlogslen', '-c', type=int, default=0, metavar='n', help='On failure, print a log (of length n lines) to the console, combined from the test framework and all test nodes.')
parser.add_argument('--coverage', action='store_true', help='generate a basic coverage report for the RPC interface')
parser.add_argument('--ci', action='store_true', help='Run checks and code that are usually only enabled in a continuous integration environment')
@@ -238,7 +246,14 @@ def main():
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')
parser.add_argument('--filter', help='filter scripts to run by regular expression')
+
args, unknown_args = parser.parse_known_args()
+ if not args.ansi:
+ global BOLD, GREEN, RED, GREY
+ BOLD = ("", "")
+ GREEN = ("", "")
+ RED = ("", "")
+ GREY = ("", "")
# args to be passed on always start with two dashes; tests are the remaining unknown args
tests = [arg for arg in unknown_args if arg[:2] != "--"]
@@ -340,9 +355,10 @@ def main():
combined_logs_len=args.combinedlogslen,
failfast=args.failfast,
runs_ci=args.ci,
+ use_term_control=args.ansi,
)
-def run_tests(*, test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage=False, args=None, combined_logs_len=0, failfast=False, runs_ci):
+def run_tests(*, test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage=False, args=None, combined_logs_len=0, failfast=False, runs_ci, use_term_control):
args = args or []
# Warn if bitcoind is already running (unix only)
@@ -384,6 +400,7 @@ def run_tests(*, test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage=
test_list=test_list,
flags=flags,
timeout_duration=40 * 60 if runs_ci else float('inf'), # in seconds
+ use_term_control=use_term_control,
)
start_time = time.time()
test_results = []
@@ -467,7 +484,7 @@ class TestHandler:
Trigger the test scripts passed in via the list.
"""
- def __init__(self, *, num_tests_parallel, tests_dir, tmpdir, test_list, flags, timeout_duration):
+ def __init__(self, *, num_tests_parallel, tests_dir, tmpdir, test_list, flags, timeout_duration, use_term_control):
assert num_tests_parallel >= 1
self.num_jobs = num_tests_parallel
self.tests_dir = tests_dir
@@ -477,6 +494,7 @@ class TestHandler:
self.flags = flags
self.num_running = 0
self.jobs = []
+ self.use_term_control = use_term_control
def get_next(self):
while self.num_running < self.num_jobs and self.test_list:
@@ -528,11 +546,13 @@ class TestHandler:
status = "Failed"
self.num_running -= 1
self.jobs.remove(job)
- clearline = '\r' + (' ' * dot_count) + '\r'
- print(clearline, end='', flush=True)
+ if self.use_term_control:
+ clearline = '\r' + (' ' * dot_count) + '\r'
+ print(clearline, end='', flush=True)
dot_count = 0
return TestResult(name, status, int(time.time() - start_time)), testdir, stdout, stderr
- print('.', end='', flush=True)
+ if self.use_term_control:
+ print('.', end='', flush=True)
dot_count += 1
def kill_and_join(self):
diff --git a/test/functional/tool_wallet.py b/test/functional/tool_wallet.py
index 28a65f7823..355cd7af75 100755
--- a/test/functional/tool_wallet.py
+++ b/test/functional/tool_wallet.py
@@ -19,6 +19,7 @@ class ToolWalletTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.setup_clean_chain = True
+ self.rpc_timeout = 120
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
diff --git a/test/functional/wallet_address_types.py b/test/functional/wallet_address_types.py
index a40613dfc7..f8318555b1 100755
--- a/test/functional/wallet_address_types.py
+++ b/test/functional/wallet_address_types.py
@@ -64,7 +64,10 @@ from test_framework.util import (
assert_raises_rpc_error,
connect_nodes_bi,
)
-
+from test_framework.segwit_addr import (
+ encode,
+ decode,
+)
class AddressTypeTest(BitcoinTestFramework):
def set_test_params(self):
@@ -97,6 +100,13 @@ class AddressTypeTest(BitcoinTestFramework):
else:
return [self.nodes[i].getunconfirmedbalance() for i in range(4)]
+ # Quick test of python bech32 implementation
+ def test_python_bech32(self, addr):
+ hrp = addr[:4]
+ assert_equal(hrp, "bcrt")
+ (witver, witprog) = decode(hrp, addr)
+ assert_equal(encode(hrp, witver, witprog), addr)
+
def test_address(self, node, address, multisig, typ):
"""Run sanity checks on an address."""
info = self.nodes[node].getaddressinfo(address)
@@ -121,6 +131,7 @@ class AddressTypeTest(BitcoinTestFramework):
assert_equal(info['witness_version'], 0)
assert_equal(len(info['witness_program']), 40)
assert 'pubkey' in info
+ self.test_python_bech32(info["address"])
elif typ == 'legacy':
# P2SH-multisig
assert info['isscript']
@@ -146,6 +157,7 @@ class AddressTypeTest(BitcoinTestFramework):
assert_equal(info['witness_version'], 0)
assert_equal(len(info['witness_program']), 64)
assert 'pubkeys' in info
+ self.test_python_bech32(info["address"])
else:
# Unknown type
assert False
@@ -175,6 +187,10 @@ class AddressTypeTest(BitcoinTestFramework):
assert info['desc'] == descsum_create(info['desc'][:-9])
# Verify that stripping the checksum and feeding it to getdescriptorinfo roundtrips
assert info['desc'] == self.nodes[0].getdescriptorinfo(info['desc'][:-9])['descriptor']
+ assert_equal(info['desc'][-8:], self.nodes[0].getdescriptorinfo(info['desc'][:-9])['checksum'])
+ # Verify that keeping the checksum and feeding it to getdescriptorinfo roundtrips
+ assert info['desc'] == self.nodes[0].getdescriptorinfo(info['desc'])['descriptor']
+ assert_equal(info['desc'][-8:], self.nodes[0].getdescriptorinfo(info['desc'])['checksum'])
if not multisig and typ == 'legacy':
# P2PKH
diff --git a/test/functional/wallet_backup.py b/test/functional/wallet_backup.py
index 55c517e92f..93178c5ab2 100755
--- a/test/functional/wallet_backup.py
+++ b/test/functional/wallet_backup.py
@@ -49,6 +49,7 @@ class WalletBackupTest(BitcoinTestFramework):
self.setup_clean_chain = True
# nodes 1, 2,3 are spenders, let's give them a keypool=100
self.extra_args = [["-keypool=100"], ["-keypool=100"], ["-keypool=100"], []]
+ self.rpc_timeout = 120
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py
index 34e84fcf55..ce04110240 100755
--- a/test/functional/wallet_basic.py
+++ b/test/functional/wallet_basic.py
@@ -499,6 +499,36 @@ class WalletTest(BitcoinTestFramework):
self.nodes[0].setlabel(change, 'foobar')
assert_equal(self.nodes[0].getaddressinfo(change)['ischange'], False)
+ # Test gettransaction response with different arguments.
+ self.log.info("Testing gettransaction response with different arguments...")
+ self.nodes[0].setlabel(change, 'baz')
+ baz = self.nodes[0].listtransactions(label="baz", count=1)[0]
+ expected_receive_vout = {"label": "baz",
+ "address": baz["address"],
+ "amount": baz["amount"],
+ "category": baz["category"],
+ "vout": baz["vout"]}
+ expected_fields = frozenset({'amount', 'bip125-replaceable', 'confirmations', 'details', 'fee',
+ 'hex', 'time', 'timereceived', 'trusted', 'txid', 'walletconflicts'})
+ verbose_field = "decoded"
+ expected_verbose_fields = expected_fields | {verbose_field}
+
+ self.log.debug("Testing gettransaction response without verbose")
+ tx = self.nodes[0].gettransaction(txid=txid)
+ assert_equal(set([*tx]), expected_fields)
+ assert_array_result(tx["details"], {"category": "receive"}, expected_receive_vout)
+
+ self.log.debug("Testing gettransaction response with verbose set to False")
+ tx = self.nodes[0].gettransaction(txid=txid, verbose=False)
+ assert_equal(set([*tx]), expected_fields)
+ assert_array_result(tx["details"], {"category": "receive"}, expected_receive_vout)
+
+ self.log.debug("Testing gettransaction response with verbose set to True")
+ tx = self.nodes[0].gettransaction(txid=txid, verbose=True)
+ assert_equal(set([*tx]), expected_verbose_fields)
+ assert_array_result(tx["details"], {"category": "receive"}, expected_receive_vout)
+ assert_equal(tx[verbose_field], self.nodes[0].decoderawtransaction(tx["hex"]))
+
if __name__ == '__main__':
WalletTest().main()
diff --git a/test/functional/wallet_createwallet.py b/test/functional/wallet_createwallet.py
index 294f90a0fa..e302e499f4 100755
--- a/test/functional/wallet_createwallet.py
+++ b/test/functional/wallet_createwallet.py
@@ -116,11 +116,20 @@ class CreateWalletTest(BitcoinTestFramework):
walletinfo = w6.getwalletinfo()
assert_equal(walletinfo['keypoolsize'], 1)
assert_equal(walletinfo['keypoolsize_hd_internal'], 1)
- # Empty passphrase, error
- assert_raises_rpc_error(-16, 'Cannot encrypt a wallet with a blank password', self.nodes[0].createwallet, 'w7', False, False, '')
+ # Allow empty passphrase, but there should be a warning
+ resp = self.nodes[0].createwallet(wallet_name='w7', disable_private_keys=False, blank=False, passphrase='')
+ assert_equal(resp['warning'], 'Empty string given as passphrase, wallet will not be encrypted.')
+ w7 = node.get_wallet_rpc('w7')
+ assert_raises_rpc_error(-15, 'Error: running with an unencrypted wallet, but walletpassphrase was called.', w7.walletpassphrase, '', 10)
+
+ self.log.info('Test making a wallet with avoid reuse flag')
+ self.nodes[0].createwallet('w8', False, False, '', True) # Use positional arguments to check for bug where avoid_reuse could not be set for wallets without needing them to be encrypted
+ w8 = node.get_wallet_rpc('w8')
+ assert_raises_rpc_error(-15, 'Error: running with an unencrypted wallet, but walletpassphrase was called.', w7.walletpassphrase, '', 10)
+ assert_equal(w8.getwalletinfo()["avoid_reuse"], True)
self.log.info('Using a passphrase with private keys disabled returns error')
- assert_raises_rpc_error(-4, 'Passphrase provided but private keys are disabled. A passphrase is only used to encrypt private keys, so cannot be used for wallets with private keys disabled.', self.nodes[0].createwallet, wallet_name='w8', disable_private_keys=True, passphrase='thisisapassphrase')
+ assert_raises_rpc_error(-4, 'Passphrase provided but private keys are disabled. A passphrase is only used to encrypt private keys, so cannot be used for wallets with private keys disabled.', self.nodes[0].createwallet, wallet_name='w9', disable_private_keys=True, passphrase='thisisapassphrase')
if __name__ == '__main__':
CreateWalletTest().main()
diff --git a/test/functional/wallet_import_rescan.py b/test/functional/wallet_import_rescan.py
index 47c97f62bf..4e20892596 100755
--- a/test/functional/wallet_import_rescan.py
+++ b/test/functional/wallet_import_rescan.py
@@ -20,6 +20,7 @@ happened previously.
"""
from test_framework.test_framework import BitcoinTestFramework
+from test_framework.address import AddressType
from test_framework.util import (
connect_nodes,
assert_equal,
@@ -27,21 +28,30 @@ from test_framework.util import (
)
import collections
+from decimal import Decimal
import enum
import itertools
+import random
Call = enum.Enum("Call", "single multiaddress multiscript")
Data = enum.Enum("Data", "address pub priv")
Rescan = enum.Enum("Rescan", "no yes late_timestamp")
-class Variant(collections.namedtuple("Variant", "call data rescan prune")):
+class Variant(collections.namedtuple("Variant", "call data address_type rescan prune")):
"""Helper for importing one key and verifying scanned transactions."""
def do_import(self, timestamp):
"""Call one key import RPC."""
rescan = self.rescan == Rescan.yes
+ assert_equal(self.address["solvable"], True)
+ assert_equal(self.address["isscript"], self.address_type == AddressType.p2sh_segwit)
+ assert_equal(self.address["iswitness"], self.address_type == AddressType.bech32)
+ if self.address["isscript"]:
+ assert_equal(self.address["embedded"]["isscript"], False)
+ assert_equal(self.address["embedded"]["iswitness"], True)
+
if self.call == Call.single:
if self.data == Data.address:
response = self.node.importaddress(address=self.address["address"], label=self.label, rescan=rescan)
@@ -52,7 +62,7 @@ class Variant(collections.namedtuple("Variant", "call data rescan prune")):
assert_equal(response, None)
elif self.call in (Call.multiaddress, Call.multiscript):
- response = self.node.importmulti([{
+ request = {
"scriptPubKey": {
"address": self.address["address"]
} if self.call == Call.multiaddress else self.address["scriptPubKey"],
@@ -61,13 +71,21 @@ class Variant(collections.namedtuple("Variant", "call data rescan prune")):
"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)})
+ }
+ if self.address_type == AddressType.p2sh_segwit and self.data != Data.address:
+ # We need solving data when providing a pubkey or privkey as data
+ request.update({"redeemscript": self.address['embedded']['scriptPubKey']})
+ response = self.node.importmulti(
+ requests=[request],
+ options={"rescan": self.rescan in (Rescan.yes, Rescan.late_timestamp)},
+ )
assert_equal(response, [{"success": True}])
- def check(self, txid=None, amount=None, confirmations=None):
+ def check(self, txid=None, amount=None, confirmation_height=None):
"""Verify that listtransactions/listreceivedbyaddress return expected values."""
txs = self.node.listtransactions(label=self.label, count=10000, include_watchonly=True)
+ current_height = self.node.getblockcount()
assert_equal(len(txs), self.expected_txs)
addresses = self.node.listreceivedbyaddress(minconf=0, include_watchonly=True, address_filter=self.address['address'])
@@ -82,13 +100,13 @@ class Variant(collections.namedtuple("Variant", "call data rescan prune")):
assert_equal(tx["category"], "receive")
assert_equal(tx["label"], self.label)
assert_equal(tx["txid"], txid)
- assert_equal(tx["confirmations"], confirmations)
+ assert_equal(tx["confirmations"], 1 + current_height - confirmation_height)
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)
+ assert_equal(address["confirmations"], 1 + current_height - confirmation_height)
# 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
@@ -102,7 +120,7 @@ class Variant(collections.namedtuple("Variant", "call data rescan prune")):
# List of Variants for each way a key or address could be imported.
-IMPORT_VARIANTS = [Variant(*variants) for variants in itertools.product(Call, Data, Rescan, (False, True))]
+IMPORT_VARIANTS = [Variant(*variants) for variants in itertools.product(Call, Data, AddressType, Rescan, (False, True))]
# List of nodes to import keys to. Half the nodes will have pruning disabled,
# half will have it enabled. Different nodes will be used for imports that are
@@ -116,6 +134,13 @@ IMPORT_NODES = [ImportNode(*fields) for fields in itertools.product((False, True
# Rescans start at the earliest block up to 2 hours before the key timestamp.
TIMESTAMP_WINDOW = 2 * 60 * 60
+AMOUNT_DUST = 0.00000546
+
+
+def get_rand_amount():
+ r = random.uniform(AMOUNT_DUST, 1)
+ return Decimal(str(round(r, 8)))
+
class ImportRescanTest(BitcoinTestFramework):
def set_test_params(self):
@@ -125,12 +150,12 @@ class ImportRescanTest(BitcoinTestFramework):
self.skip_if_no_wallet()
def setup_network(self):
- extra_args = [["-addresstype=legacy"] for _ in range(self.num_nodes)]
+ self.extra_args = [[]] * self.num_nodes
for i, import_node in enumerate(IMPORT_NODES, 2):
if import_node.prune:
- extra_args[i] += ["-prune=1"]
+ self.extra_args[i] += ["-prune=1"]
- self.add_nodes(self.num_nodes, extra_args=extra_args)
+ self.add_nodes(self.num_nodes, extra_args=self.extra_args)
# Import keys with pruning disabled
self.start_nodes(extra_args=[[]] * self.num_nodes)
@@ -147,17 +172,23 @@ class ImportRescanTest(BitcoinTestFramework):
# 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(
+ label=variant.label,
+ address_type=variant.address_type.value,
+ ))
variant.key = self.nodes[1].dumpprivkey(variant.address["address"])
- variant.initial_amount = 1 - (i + 1) / 64
+ variant.initial_amount = get_rand_amount()
variant.initial_txid = self.nodes[0].sendtoaddress(variant.address["address"], variant.initial_amount)
+ self.nodes[0].generate(1) # Generate one block for each send
+ variant.confirmation_height = self.nodes[0].getblockcount()
+ variant.timestamp = self.nodes[0].getblockheader(self.nodes[0].getbestblockhash())["time"]
- # Generate a block containing the initial transactions, then another
- # block further in the future (past the rescan window).
- self.nodes[0].generate(1)
+ # Generate a block further in the future (past the rescan window).
assert_equal(self.nodes[0].getrawmempool(), [])
- timestamp = self.nodes[0].getblockheader(self.nodes[0].getbestblockhash())["time"]
- set_node_times(self.nodes, timestamp + TIMESTAMP_WINDOW + 1)
+ set_node_times(
+ self.nodes,
+ self.nodes[0].getblockheader(self.nodes[0].getbestblockhash())["time"] + TIMESTAMP_WINDOW + 1,
+ )
self.nodes[0].generate(1)
self.sync_all()
@@ -167,11 +198,11 @@ class ImportRescanTest(BitcoinTestFramework):
self.log.info('Run import for variant {}'.format(variant))
expect_rescan = variant.rescan == Rescan.yes
variant.node = self.nodes[2 + IMPORT_NODES.index(ImportNode(variant.prune, expect_rescan))]
- variant.do_import(timestamp)
+ variant.do_import(variant.timestamp)
if expect_rescan:
variant.expected_balance = variant.initial_amount
variant.expected_txs = 1
- variant.check(variant.initial_txid, variant.initial_amount, 2)
+ variant.check(variant.initial_txid, variant.initial_amount, variant.confirmation_height)
else:
variant.expected_balance = 0
variant.expected_txs = 0
@@ -179,11 +210,11 @@ class ImportRescanTest(BitcoinTestFramework):
# Create new transactions sending to each address.
for i, variant in enumerate(IMPORT_VARIANTS):
- variant.sent_amount = 1 - (2 * i + 1) / 128
+ variant.sent_amount = get_rand_amount()
variant.sent_txid = self.nodes[0].sendtoaddress(variant.address["address"], variant.sent_amount)
+ self.nodes[0].generate(1) # Generate one block for each send
+ variant.confirmation_height = self.nodes[0].getblockcount()
- # Generate a block containing the new transactions.
- self.nodes[0].generate(1)
assert_equal(self.nodes[0].getrawmempool(), [])
self.sync_all()
@@ -192,7 +223,7 @@ class ImportRescanTest(BitcoinTestFramework):
self.log.info('Run check for variant {}'.format(variant))
variant.expected_balance += variant.sent_amount
variant.expected_txs += 1
- variant.check(variant.sent_txid, variant.sent_amount, 1)
+ variant.check(variant.sent_txid, variant.sent_amount, variant.confirmation_height)
if __name__ == "__main__":
ImportRescanTest().main()
diff --git a/test/functional/wallet_importmulti.py b/test/functional/wallet_importmulti.py
index e19c7919a9..23748e5dd7 100755
--- a/test/functional/wallet_importmulti.py
+++ b/test/functional/wallet_importmulti.py
@@ -44,8 +44,10 @@ class ImportMultiTest(BitcoinTestFramework):
def setup_network(self):
self.setup_nodes()
- def test_importmulti(self, req, success, error_code=None, error_message=None, warnings=[]):
+ def test_importmulti(self, req, success, error_code=None, error_message=None, warnings=None):
"""Run importmulti and assert success"""
+ if warnings is None:
+ warnings = []
result = self.nodes[1].importmulti([req])
observed_warnings = []
if 'warnings' in result[0]:
@@ -552,7 +554,7 @@ class ImportMultiTest(BitcoinTestFramework):
"keys": [key.privkey]},
success=False,
error_code=-5,
- error_message="Descriptor is invalid")
+ error_message="Missing checksum")
# Test importing of a P2SH-P2WPKH address via descriptor + private key
key = get_key(self.nodes[0])
diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py
index 984ffab5a4..68bc45f986 100755
--- a/test/functional/wallet_multiwallet.py
+++ b/test/functional/wallet_multiwallet.py
@@ -17,6 +17,8 @@ from test_framework.util import (
assert_raises_rpc_error,
)
+FEATURE_LATEST = 169900
+
class MultiWalletTest(BitcoinTestFramework):
def set_test_params(self):
@@ -27,6 +29,13 @@ class MultiWalletTest(BitcoinTestFramework):
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
+ def add_options(self, parser):
+ parser.add_argument(
+ '--data_wallets_dir',
+ default=os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data/wallets/'),
+ help='Test data with wallet directories (default: %(default)s)',
+ )
+
def run_test(self):
node = self.nodes[0]
@@ -93,12 +102,12 @@ class MultiWalletTest(BitcoinTestFramework):
# should not initialize if one wallet is a copy of another
shutil.copyfile(wallet_dir('w8'), wallet_dir('w8_copy'))
- exp_stderr = "BerkeleyBatch: Can't open database w8_copy \(duplicates fileid \w+ from w8\)"
+ exp_stderr = r"BerkeleyBatch: Can't open database w8_copy \(duplicates fileid \w+ from w8\)"
self.nodes[0].assert_start_raises_init_error(['-wallet=w8', '-wallet=w8_copy'], exp_stderr, match=ErrorMatch.PARTIAL_REGEX)
# should not initialize if wallet file is a symlink
os.symlink('w8', wallet_dir('w8_symlink'))
- self.nodes[0].assert_start_raises_init_error(['-wallet=w8_symlink'], 'Error: Invalid -wallet path \'w8_symlink\'\. .*', match=ErrorMatch.FULL_REGEX)
+ self.nodes[0].assert_start_raises_init_error(['-wallet=w8_symlink'], r'Error: Invalid -wallet path \'w8_symlink\'\. .*', match=ErrorMatch.FULL_REGEX)
# should not initialize if the specified walletdir does not exist
self.nodes[0].assert_start_raises_init_error(['-walletdir=bad'], 'Error: Specified -walletdir "bad" does not exist')
@@ -139,7 +148,7 @@ class MultiWalletTest(BitcoinTestFramework):
competing_wallet_dir = os.path.join(self.options.tmpdir, 'competing_walletdir')
os.mkdir(competing_wallet_dir)
self.restart_node(0, ['-walletdir=' + competing_wallet_dir])
- exp_stderr = "Error: Error initializing wallet database environment \"\S+competing_walletdir\"!"
+ exp_stderr = r"Error: Error initializing wallet database environment \"\S+competing_walletdir\"!"
self.nodes[1].assert_start_raises_init_error(['-walletdir=' + competing_wallet_dir], exp_stderr, match=ErrorMatch.PARTIAL_REGEX)
self.restart_node(0, extra_args)
@@ -323,6 +332,22 @@ class MultiWalletTest(BitcoinTestFramework):
self.nodes[0].unloadwallet(wallet)
self.nodes[1].loadwallet(wallet)
+ # Fail to load if wallet is downgraded
+ shutil.copytree(os.path.join(self.options.data_wallets_dir, 'high_minversion'), wallet_dir('high_minversion'))
+ self.restart_node(0, extra_args=['-upgradewallet={}'.format(FEATURE_LATEST)])
+ assert {'name': 'high_minversion'} in self.nodes[0].listwalletdir()['wallets']
+ self.log.info("Fail -upgradewallet that results in downgrade")
+ assert_raises_rpc_error(
+ -4,
+ "Wallet loading failed.",
+ lambda: self.nodes[0].loadwallet(filename='high_minversion'),
+ )
+ self.stop_node(
+ i=0,
+ expected_stderr='Error: Error loading {}: Wallet requires newer version of Bitcoin Core'.format(
+ wallet_dir('high_minversion', 'wallet.dat')),
+ )
+
if __name__ == '__main__':
MultiWalletTest().main()
diff --git a/test/functional/wallet_reorgsrestore.py b/test/functional/wallet_reorgsrestore.py
new file mode 100755
index 0000000000..f48018e9fb
--- /dev/null
+++ b/test/functional/wallet_reorgsrestore.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python3
+# Copyright (c) 2019 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 tx status in case of reorgs while wallet being shutdown.
+
+Wallet txn status rely on block connection/disconnection for its
+accuracy. In case of reorgs happening while wallet being shutdown
+block updates are not going to be received. At wallet loading, we
+check against chain if confirmed txn are still in chain and change
+their status if block in which they have been included has been
+disconnected.
+"""
+
+from decimal import Decimal
+import os
+import shutil
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+ connect_nodes,
+ disconnect_nodes,
+)
+
+class ReorgsRestoreTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 3
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+
+ def run_test(self):
+ # Send a tx from which to conflict outputs later
+ txid_conflict_from = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), Decimal("10"))
+ self.nodes[0].generate(1)
+ self.sync_blocks()
+
+ # Disconnect node1 from others to reorg its chain later
+ disconnect_nodes(self.nodes[0], 1)
+ disconnect_nodes(self.nodes[1], 2)
+ connect_nodes(self.nodes[0], 2)
+
+ # Send a tx to be unconfirmed later
+ txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), Decimal("10"))
+ tx = self.nodes[0].gettransaction(txid)
+ self.nodes[0].generate(4)
+ tx_before_reorg = self.nodes[0].gettransaction(txid)
+ assert_equal(tx_before_reorg["confirmations"], 4)
+
+ # Disconnect node0 from node2 to broadcast a conflict on their respective chains
+ disconnect_nodes(self.nodes[0], 2)
+ nA = next(tx_out["vout"] for tx_out in self.nodes[0].gettransaction(txid_conflict_from)["details"] if tx_out["amount"] == Decimal("10"))
+ inputs = []
+ inputs.append({"txid": txid_conflict_from, "vout": nA})
+ outputs_1 = {}
+ outputs_2 = {}
+
+ # Create a conflicted tx broadcast on node0 chain and conflicting tx broadcast on node1 chain. Both spend from txid_conflict_from
+ outputs_1[self.nodes[0].getnewaddress()] = Decimal("9.99998")
+ outputs_2[self.nodes[0].getnewaddress()] = Decimal("9.99998")
+ conflicted = self.nodes[0].signrawtransactionwithwallet(self.nodes[0].createrawtransaction(inputs, outputs_1))
+ conflicting = self.nodes[0].signrawtransactionwithwallet(self.nodes[0].createrawtransaction(inputs, outputs_2))
+
+ conflicted_txid = self.nodes[0].sendrawtransaction(conflicted["hex"])
+ self.nodes[0].generate(1)
+ conflicting_txid = self.nodes[2].sendrawtransaction(conflicting["hex"])
+ self.nodes[2].generate(9)
+
+ # Reconnect node0 and node2 and check that conflicted_txid is effectively conflicted
+ connect_nodes(self.nodes[0], 2)
+ self.sync_blocks([self.nodes[0], self.nodes[2]])
+ conflicted = self.nodes[0].gettransaction(conflicted_txid)
+ conflicting = self.nodes[0].gettransaction(conflicting_txid)
+ assert_equal(conflicted["confirmations"], -9)
+ assert_equal(conflicted["walletconflicts"][0], conflicting["txid"])
+
+ # Node0 wallet is shutdown
+ self.stop_node(0)
+ self.start_node(0)
+
+ # The block chain re-orgs and the tx is included in a different block
+ self.nodes[1].generate(9)
+ self.nodes[1].sendrawtransaction(tx["hex"])
+ self.nodes[1].generate(1)
+ self.nodes[1].sendrawtransaction(conflicted["hex"])
+ self.nodes[1].generate(1)
+
+ # Node0 wallet file is loaded on longest sync'ed node1
+ self.stop_node(1)
+ self.nodes[0].backupwallet(os.path.join(self.nodes[0].datadir, 'wallet.bak'))
+ shutil.copyfile(os.path.join(self.nodes[0].datadir, 'wallet.bak'), os.path.join(self.nodes[1].datadir, 'regtest', 'wallet.dat'))
+ self.start_node(1)
+ tx_after_reorg = self.nodes[1].gettransaction(txid)
+ # Check that normal confirmed tx is confirmed again but with different blockhash
+ assert_equal(tx_after_reorg["confirmations"], 2)
+ assert(tx_before_reorg["blockhash"] != tx_after_reorg["blockhash"])
+ conflicted_after_reorg = self.nodes[1].gettransaction(conflicted_txid)
+ # Check that conflicted tx is confirmed again with blockhash different than previously conflicting tx
+ assert_equal(conflicted_after_reorg["confirmations"], 1)
+ assert(conflicting["blockhash"] != conflicted_after_reorg["blockhash"])
+
+if __name__ == '__main__':
+ ReorgsRestoreTest().main()
diff --git a/test/functional/wallet_watchonly.py b/test/functional/wallet_watchonly.py
new file mode 100755
index 0000000000..be8d7714fb
--- /dev/null
+++ b/test/functional/wallet_watchonly.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python3
+# Copyright (c) 2018-2019 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 createwallet arguments.
+"""
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+ assert_raises_rpc_error
+)
+
+
+class CreateWalletWatchonlyTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = False
+ self.num_nodes = 1
+ self.supports_cli = True
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+
+ def run_test(self):
+ node = self.nodes[0]
+
+ self.nodes[0].createwallet(wallet_name='default')
+ def_wallet = node.get_wallet_rpc('default')
+
+ a1 = def_wallet.getnewaddress()
+ wo_change = def_wallet.getnewaddress()
+ wo_addr = def_wallet.getnewaddress()
+
+ self.nodes[0].createwallet(wallet_name='wo', disable_private_keys=True)
+ wo_wallet = node.get_wallet_rpc('wo')
+
+ wo_wallet.importpubkey(pubkey=def_wallet.getaddressinfo(wo_addr)['pubkey'])
+ wo_wallet.importpubkey(pubkey=def_wallet.getaddressinfo(wo_change)['pubkey'])
+
+ # generate some btc for testing
+ node.generatetoaddress(101, a1)
+
+ # send 1 btc to our watch-only address
+ txid = def_wallet.sendtoaddress(wo_addr, 1)
+ self.nodes[0].generate(1)
+
+ # getbalance
+ self.log.info('include_watchonly should default to true for watch-only wallets')
+ self.log.info('Testing getbalance watch-only defaults')
+ assert_equal(wo_wallet.getbalance(), 1)
+ assert_equal(len(wo_wallet.listtransactions()), 1)
+ assert_equal(wo_wallet.getbalance(include_watchonly=False), 0)
+
+ self.log.info('Testing listreceivedbyaddress watch-only defaults')
+ result = wo_wallet.listreceivedbyaddress()
+ assert_equal(len(result), 1)
+ assert_equal(result[0]["involvesWatchonly"], True)
+ result = wo_wallet.listreceivedbyaddress(include_watchonly=False)
+ assert_equal(len(result), 0)
+
+ self.log.info('Testing listreceivedbylabel watch-only defaults')
+ result = wo_wallet.listreceivedbylabel()
+ assert_equal(len(result), 1)
+ assert_equal(result[0]["involvesWatchonly"], True)
+ result = wo_wallet.listreceivedbylabel(include_watchonly=False)
+ assert_equal(len(result), 0)
+
+ self.log.info('Testing listtransactions watch-only defaults')
+ result = wo_wallet.listtransactions()
+ assert_equal(len(result), 1)
+ assert_equal(result[0]["involvesWatchonly"], True)
+ result = wo_wallet.listtransactions(include_watchonly=False)
+ assert_equal(len(result), 0)
+
+ self.log.info('Testing listsinceblock watch-only defaults')
+ result = wo_wallet.listsinceblock()
+ assert_equal(len(result["transactions"]), 1)
+ assert_equal(result["transactions"][0]["involvesWatchonly"], True)
+ result = wo_wallet.listsinceblock(include_watchonly=False)
+ assert_equal(len(result["transactions"]), 0)
+
+ self.log.info('Testing gettransaction watch-only defaults')
+ result = wo_wallet.gettransaction(txid)
+ assert_equal(result["details"][0]["involvesWatchonly"], True)
+ result = wo_wallet.gettransaction(txid=txid, include_watchonly=False)
+ assert_equal(len(result["details"]), 0)
+
+ self.log.info('Testing walletcreatefundedpsbt watch-only defaults')
+ inputs = []
+ outputs = [{a1: 0.5}]
+ options = {'changeAddress': wo_change}
+ no_wo_options = {'changeAddress': wo_change, 'includeWatching': False}
+
+ result = wo_wallet.walletcreatefundedpsbt(inputs=inputs, outputs=outputs, options=options)
+ assert_equal("psbt" in result, True)
+ assert_raises_rpc_error(-4, "Insufficient funds", wo_wallet.walletcreatefundedpsbt, inputs, outputs, 0, no_wo_options)
+
+ self.log.info('Testing fundrawtransaction watch-only defaults')
+ rawtx = wo_wallet.createrawtransaction(inputs=inputs, outputs=outputs)
+ result = wo_wallet.fundrawtransaction(hexstring=rawtx, options=options)
+ assert_equal("hex" in result, True)
+ assert_raises_rpc_error(-4, "Insufficient funds", wo_wallet.fundrawtransaction, rawtx, no_wo_options)
+
+
+
+if __name__ == '__main__':
+ CreateWalletWatchonlyTest().main()
diff --git a/test/lint/check-doc.py b/test/lint/check-doc.py
index 1d6122a13d..bd947d194c 100755
--- a/test/lint/check-doc.py
+++ b/test/lint/check-doc.py
@@ -15,8 +15,8 @@ import re
FOLDER_GREP = 'src'
FOLDER_TEST = 'src/test/'
-REGEX_ARG = '(?:ForceSet|SoftSet|Get|Is)(?:Bool)?Args?(?:Set)?\("(-[^"]+)"'
-REGEX_DOC = 'AddArg\("(-[^"=]+?)(?:=|")'
+REGEX_ARG = r'(?:ForceSet|SoftSet|Get|Is)(?:Bool)?Args?(?:Set)?\("(-[^"]+)"'
+REGEX_DOC = r'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_WALLET_ARGS = r"git grep --function-context 'void WalletInit::AddWalletOptions' -- {} | grep AddArg".format(CMD_ROOT_DIR)
diff --git a/test/lint/check-rpc-mappings.py b/test/lint/check-rpc-mappings.py
index 137cc82b5d..a33ab17f3f 100755
--- a/test/lint/check-rpc-mappings.py
+++ b/test/lint/check-rpc-mappings.py
@@ -48,13 +48,13 @@ def process_commands(fname):
for line in f:
line = line.rstrip()
if not in_rpcs:
- if re.match("static const CRPCCommand .*\[\] =", line):
+ if re.match(r"static const CRPCCommand .*\[\] =", line):
in_rpcs = True
else:
if line.startswith('};'):
in_rpcs = False
elif '{' in line and '"' in line:
- m = re.search('{ *("[^"]*"), *("[^"]*"), *&([^,]*), *{([^}]*)} *},', line)
+ m = re.search(r'{ *("[^"]*"), *("[^"]*"), *&([^,]*), *{([^}]*)} *},', line)
assert m, 'No match to table expression: %s' % line
name = parse_string(m.group(2))
args_str = m.group(4).strip()
@@ -80,7 +80,7 @@ def process_mapping(fname):
if line.startswith('};'):
in_rpcs = False
elif '{' in line and '"' in line:
- m = re.search('{ *("[^"]*"), *([0-9]+) *, *("[^"]*") *},', line)
+ m = re.search(r'{ *("[^"]*"), *([0-9]+) *, *("[^"]*") *},', line)
assert m, 'No match to table expression: %s' % line
name = parse_string(m.group(1))
idx = int(m.group(2))
diff --git a/test/lint/lint-format-strings.py b/test/lint/lint-format-strings.py
index 47ad896589..9f34d0f4dd 100755
--- a/test/lint/lint-format-strings.py
+++ b/test/lint/lint-format-strings.py
@@ -55,7 +55,7 @@ def normalize(s):
assert type(s) is str
s = s.replace("\n", " ")
s = s.replace("\t", " ")
- s = re.sub("/\*.*?\*/", " ", s)
+ s = re.sub(r"/\*.*?\*/", " ", s)
s = re.sub(" {2,}", " ", s)
return s.strip()
diff --git a/test/lint/lint-includes.sh b/test/lint/lint-includes.sh
index 4b9e2615b6..d27e45a23f 100755
--- a/test/lint/lint-includes.sh
+++ b/test/lint/lint-includes.sh
@@ -11,6 +11,9 @@
export LC_ALL=C
IGNORE_REGEXP="/(leveldb|secp256k1|univalue)/"
+# cd to root folder of git repo for git ls-files to work properly
+cd "$(dirname $0)/../.." || exit 1
+
filter_suffix() {
git ls-files | grep -E "^src/.*\.${1}"'$' | grep -Ev "${IGNORE_REGEXP}"
}
diff --git a/test/lint/lint-python-dead-code-whitelist b/test/lint/lint-python-dead-code-whitelist
new file mode 100644
index 0000000000..2522c8fa1c
--- /dev/null
+++ b/test/lint/lint-python-dead-code-whitelist
@@ -0,0 +1,45 @@
+BadInputOutpointIndex # unused class (test/functional/data/invalid_txs.py)
+_.carbon_path # unused attribute (contrib/macdeploy/custom_dsstore.py)
+connection_lost # unused function (test/functional/test_framework/mininode.py)
+connection_made # unused function (test/functional/test_framework/mininode.py)
+_.converter # unused attribute (test/functional/test_framework/test_framework.py)
+_.daemon # unused attribute (test/functional/test_framework/socks5.py)
+data_received # unused function (test/functional/test_framework/mininode.py)
+DuplicateInput # unused class (test/functional/data/invalid_txs.py)
+_.filename # unused attribute (contrib/macdeploy/custom_dsstore.py)
+InvalidOPIFConstruction # unused class (test/functional/data/invalid_txs.py)
+_.is_compressed # unused property (test/functional/test_framework/key.py)
+legacy # unused variable (test/functional/test_framework/address.py)
+msg_generic # unused class (test/functional/test_framework/messages.py)
+NonexistentInput # unused class (test/functional/data/invalid_txs.py)
+on_addr # unused function (test/functional/test_framework/mininode.py)
+on_blocktxn # unused function (test/functional/test_framework/mininode.py)
+on_block # unused function (test/functional/test_framework/mininode.py)
+on_cmpctblock # unused function (test/functional/test_framework/mininode.py)
+on_feefilter # unused function (test/functional/test_framework/mininode.py)
+on_getaddr # unused function (test/functional/test_framework/mininode.py)
+on_getblocks # unused function (test/functional/test_framework/mininode.py)
+on_getblocktxn # unused function (test/functional/test_framework/mininode.py)
+on_getdata # unused function (test/functional/test_framework/mininode.py)
+on_getheaders # unused function (test/functional/test_framework/mininode.py)
+on_headers # unused function (test/functional/test_framework/mininode.py)
+on_inv # unused function (test/functional/test_framework/mininode.py)
+on_mempool # unused function (test/functional/test_framework/mininode.py)
+on_notfound # unused function (test/functional/test_framework/mininode.py)
+on_ping # unused function (test/functional/test_framework/mininode.py)
+on_pong # unused function (test/functional/test_framework/mininode.py)
+on_reject # unused function (test/functional/test_framework/mininode.py)
+on_sendcmpct # unused function (test/functional/test_framework/mininode.py)
+on_sendheaders # unused function (test/functional/test_framework/mininode.py)
+on_tx # unused function (test/functional/test_framework/mininode.py)
+on_verack # unused function (test/functional/test_framework/mininode.py)
+on_version # unused function (test/functional/test_framework/mininode.py)
+_.optionxform # unused attribute (test/util/bitcoin-util-test.py)
+OutputMissing # unused class (test/functional/data/invalid_txs.py)
+_.posix_path # unused attribute (contrib/macdeploy/custom_dsstore.py)
+profile_with_perf # unused function (test/functional/test_framework/test_node.py)
+SizeTooSmall # unused class (test/functional/data/invalid_txs.py)
+SpendNegative # unused class (test/functional/data/invalid_txs.py)
+SpendTooMuch # unused class (test/functional/data/invalid_txs.py)
+TooManySigops # unused class (test/functional/data/invalid_txs.py)
+verify_ecdsa # unused function (test/functional/test_framework/key.py)
diff --git a/test/lint/lint-python-dead-code.sh b/test/lint/lint-python-dead-code.sh
index 588ba428d7..77bf5990a7 100755
--- a/test/lint/lint-python-dead-code.sh
+++ b/test/lint/lint-python-dead-code.sh
@@ -15,5 +15,5 @@ fi
vulture \
--min-confidence 60 \
- --ignore-names "argtypes,connection_lost,connection_made,converter,data_received,daemon,errcheck,is_compressed,is_valid,verify_ecdsa,msg_generic,on_*,optionxform,restype,profile_with_perf" \
- $(git ls-files -- "*.py" ":(exclude)contrib/" ":(exclude)test/functional/data/invalid_txs.py")
+ $(git rev-parse --show-toplevel) \
+ $(dirname "${BASH_SOURCE[0]}")/lint-python-dead-code-whitelist
diff --git a/test/lint/lint-python-mutable-default-parameters.sh b/test/lint/lint-python-mutable-default-parameters.sh
new file mode 100755
index 0000000000..1f9f035d30
--- /dev/null
+++ b/test/lint/lint-python-mutable-default-parameters.sh
@@ -0,0 +1,52 @@
+#!/usr/bin/env bash
+#
+# Copyright (c) 2019 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#
+# Detect when a mutable list or dict is used as a default parameter value in a Python function.
+
+export LC_ALL=C
+EXIT_CODE=0
+OUTPUT=$(git grep -E '^\s*def [a-zA-Z0-9_]+\(.*=\s*(\[|\{)' -- "*.py")
+if [[ ${OUTPUT} != "" ]]; then
+ echo "A mutable list or dict seems to be used as default parameter value:"
+ echo
+ echo "${OUTPUT}"
+ echo
+ cat << EXAMPLE
+This is how mutable list and dict default parameter values behave:
+
+>>> def f(i, j=[], k={}):
+... j.append(i)
+... k[i] = True
+... return j, k
+...
+>>> f(1)
+([1], {1: True})
+>>> f(1)
+([1, 1], {1: True})
+>>> f(2)
+([1, 1, 2], {1: True, 2: True})
+
+The intended behaviour was likely:
+
+>>> def f(i, j=None, k=None):
+... if j is None:
+... j = []
+... if k is None:
+... k = {}
+... j.append(i)
+... k[i] = True
+... return j, k
+...
+>>> f(1)
+([1], {1: True})
+>>> f(1)
+([1], {1: True})
+>>> f(2)
+([2], {2: True})
+EXAMPLE
+ EXIT_CODE=1
+fi
+exit ${EXIT_CODE}
diff --git a/test/lint/lint-python.sh b/test/lint/lint-python.sh
index a76806003f..3c82ec19e3 100755
--- a/test/lint/lint-python.sh
+++ b/test/lint/lint-python.sh
@@ -73,7 +73,6 @@ enabled=(
W291 # trailing whitespace
W292 # no newline at end of file
W293 # blank line contains whitespace
- W504 # line break after binary operator
W601 # .has_key() is deprecated, use "in"
W602 # deprecated form of raising exception
W603 # "<>" is deprecated, use "!="
diff --git a/test/lint/lint-spelling.ignore-words.txt b/test/lint/lint-spelling.ignore-words.txt
index a25de2435b..b08837c1d4 100644
--- a/test/lint/lint-spelling.ignore-words.txt
+++ b/test/lint/lint-spelling.ignore-words.txt
@@ -12,3 +12,4 @@ cachable
errorstring
keyserver
homogenous
+setban
diff --git a/test/lint/lint-spelling.sh b/test/lint/lint-spelling.sh
index 5d672698a7..e70b73e1cc 100755
--- a/test/lint/lint-spelling.sh
+++ b/test/lint/lint-spelling.sh
@@ -9,6 +9,11 @@
export LC_ALL=C
+if ! command -v codespell > /dev/null; then
+ echo "Skipping spell check linting since codespell is not installed."
+ exit 0
+fi
+
IGNORE_WORDS_FILE=test/lint/lint-spelling.ignore-words.txt
if ! codespell --check-filenames --disable-colors --quiet-level=7 --ignore-words=${IGNORE_WORDS_FILE} $(git ls-files -- ":(exclude)build-aux/m4/" ":(exclude)contrib/seeds/*.txt" ":(exclude)depends/" ":(exclude)doc/release-notes/" ":(exclude)src/leveldb/" ":(exclude)src/qt/locale/" ":(exclude)src/secp256k1/" ":(exclude)src/univalue/"); then
echo "^ Warning: codespell identified likely spelling errors. Any false positives? Add them to the list of ignored words in ${IGNORE_WORDS_FILE}"
diff --git a/test/util/data/txcreateoutpubkey1.json b/test/util/data/txcreateoutpubkey1.json
index 32097b3ebe..42b519bb21 100644
--- a/test/util/data/txcreateoutpubkey1.json
+++ b/test/util/data/txcreateoutpubkey1.json
@@ -15,11 +15,7 @@
"scriptPubKey": {
"asm": "02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397 OP_CHECKSIG",
"hex": "2102a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397ac",
- "reqSigs": 1,
- "type": "pubkey",
- "addresses": [
- "1FoG2386FG2tAJS9acMuiDsKy67aGg9MKz"
- ]
+ "type": "pubkey"
}
}
],